Mr.CAS 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # ___ _ _ ___ _ _
4
+ # / __|_ _ __ _ _ __| |___ _(_)___ | _ \ |_ _ __ _(_)_ _
5
+ # | (_ | '_/ _` | '_ \ ' \ V / |_ / | _/ | || / _` | | ' \
6
+ # \___|_| \__,_| .__/_||_\_/|_/__| |_| |_|\_,_\__, |_|_||_|
7
+ # |_| |___/
8
+
9
+ module CAS
10
+ # ___ _ _
11
+ # / __|___ _ _| |_ __ _(_)_ _ ___ _ _ ___
12
+ # | (__/ _ \ ' \ _/ _` | | ' \/ -_) '_(_-<
13
+ # \___\___/_||_\__\__,_|_|_||_\___|_| /__/
14
+
15
+ class Op
16
+ # Return the local Graphviz node of the tree
17
+ #
18
+ # * **returns**: `String` of local Graphiz node
19
+ def dot_graph
20
+ cls = "#{self.class.to_s.gsub("CAS::", "")}_#{self.object_id}"
21
+ "#{cls} -> #{@x.dot_graph}\n"
22
+ end
23
+ end
24
+
25
+ class BinaryOp
26
+ # Return the local Graphviz node of the tree
27
+ #
28
+ # * **returns**: `String` of local Graphiz node
29
+ def dot_graph
30
+ cls = "#{self.class.to_s.gsub("CAS::", "")}_#{self.object_id}"
31
+ "#{cls} -> #{@x.dot_graph}\n #{cls} -> #{@y.dot_graph}"
32
+ end
33
+ end
34
+
35
+ class NaryOp
36
+ # Return the local Graphviz node of the tree
37
+ #
38
+ # * **returns**: `String` of local Graphiz node
39
+ def dot_graph
40
+ cls = "#{self.class.to_s.gsub("CAS::", "")}_#{self.object_id}"
41
+ ret = ""
42
+ @x.each do |x|
43
+ ret += "#{cls} -> #{x.dot_graph}\n"
44
+ end
45
+ return ret
46
+ end
47
+ end
48
+
49
+ class Variable
50
+ # Return the local Graphviz node of the tree
51
+ #
52
+ # * **returns**: `String` of local Graphiz node
53
+ def to_dot
54
+ "#{@name}"
55
+ end
56
+ end
57
+
58
+ class Constant
59
+ # Return the local Graphviz node of the tree
60
+ #
61
+ # * **returns**: `String` of local Graphiz node
62
+ def to_dot
63
+ "Const(#{@x})"
64
+ end
65
+ end
66
+
67
+ class Piecewise
68
+ # Convert piecewise function into a dot graphviz representation
69
+ #
70
+ # * **returns**: `String`
71
+ def dot_graph
72
+ cls = "#{self.class.to_s.gsub("CAS::", "")}_#{self.object_id}"
73
+ "#{cls} -> #{@x.dot_graph}\n #{cls} -> #{@y.dot_graph}\n #{cls} -> #{@condition.dot_graph}"
74
+ end
75
+ end
76
+
77
+ # Return a string representation of the graph that is
78
+ # a Graphviz tree. Requires a `CAS::Op` as argument.
79
+ # In the next releases probably it will be moved inside
80
+ # `CAS::Op`.
81
+ #
82
+ # * **argument**: `CAS::Op` instance
83
+ # * **returns**: `String`
84
+ def self.to_dot(op)
85
+ CAS::Help.assert(op, CAS::Op)
86
+ string = op.dot_graph
87
+ labels = ""
88
+
89
+ dot_subs_hash = {
90
+ "Sum" => "+",
91
+ "Diff" => "-",
92
+ "Prod" => "×",
93
+ "Div" => "÷",
94
+ "Sqrt" => "√(∙)",
95
+ "Abs" => "|∙|",
96
+ "Invert" => "-(∙)",
97
+ "Exp" => "exp(∙)",
98
+ "Log" => "log(∙)",
99
+ "Pow" => "(∙)^(∙)",
100
+ "ZERO_CONSTANT" => "0",
101
+ "ONE_CONSTANT" => "1",
102
+ "TWO_CONSTANT" => "2",
103
+ "PI_CONSTANT" => "π",
104
+ "INFINITY_CONSTANT" => "∞",
105
+ "E_CONSTANT" => "e",
106
+ "MINUS_ONE_CONSTANT" => "-1"
107
+ }
108
+
109
+ lab = {}
110
+ string.scan(/\w+\_\d+/) do |m|
111
+ if m =~ /(\w+)\_\d+/
112
+ lab[m] = dot_subs_hash[$1] || $1
113
+ end
114
+ end
115
+ lab.each { |k, v| labels += " #{k} [label=\"#{v}\"]\n" }
116
+
117
+ return "digraph Op {\n #{string}#{labels}}"
118
+ end
119
+
120
+ # Export the input `CAS::Op` graphviz representation to a file.
121
+ #
122
+ # * **argument**: `String` with filename
123
+ # * **argument**: `CAS::Op` with the tree
124
+ # * **returns**: `CAS::Op` in input
125
+ def self.export_dot(fl, op)
126
+ CAS::Help.assert(fl, String)
127
+ CAS::Help.assert(op, CAS::Op)
128
+
129
+ File.open(fl, "w") do |f| f.puts CAS.to_dot(op) end
130
+ return op
131
+ end
132
+ end
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # _ _____ ____ ___ _ _
4
+ # | | __ |_ _|__ /_ __ | _ \ |_ _ __ _(_)_ _
5
+ # | |__/ _` || | |_ \ \ / | _/ | || / _` | | ' \
6
+ # |____\__,_||_| |___/_\_\ |_| |_|\_,_\__, |_|_||_|
7
+ # |___/
8
+
9
+ module CAS
10
+ {
11
+ # Terminal nodes
12
+ CAS::Constant => Proc.new { "#{x}" },
13
+ CAS::Variable => Proc.new { "#{name}" },
14
+ CAS::PI_CONSTANT => Proc.new { "\\pi" },
15
+ CAS::INFINITY_CONSTANT => Proc.new { "\\infty" },
16
+ CAS::NEG_INFINITY_CONSTANT => Proc.new { "-\\infty" },
17
+ # Base functions
18
+ CAS::Sum => Proc.new { "\\left( #{x.latex} + #{y.latex} \\right)" },
19
+ CAS::Diff => Proc.new { "\\left( #{x.latex} - #{y.latex} \\right)" },
20
+ CAS::Prod => Proc.new { "\\left( #{x.latex} \\, #{y.latex} \\right)"},
21
+ CAS::Pow => Proc.new { "{#{x.latex}}^{#{y.latex}}" },
22
+ CAS::Div => Proc.new { "\\dfrac{#{x.latex}}{#{y.latex}}" },
23
+ CAS::Sqrt => Proc.new { "\\sqrt{#{x.latex}}" },
24
+ CAS::Invert => Proc.new { "-#{x.latex}" },
25
+ CAS::Abs => Proc.new { "\\left| #{}{x.latex} \\right|" },
26
+
27
+ # Trigonometric functions
28
+ CAS::Sin => Proc.new { "\\sin \\left( #{x.latex} \\right)" },
29
+ CAS::Asin => Proc.new { "\\arcsin \\left( #{x.latex} \\right)" },
30
+ CAS::Cos => Proc.new { "\\cos \\left( #{x.latex} \\right)" },
31
+ CAS::Acos => Proc.new { "\\arccos \\left( #{x.latex} \\right)" },
32
+ CAS::Tan => Proc.new { "\\tan \\left( #{x.latex} \\right)" },
33
+ CAS::Atan => Proc.new { "\\arctan \\left( #{x.latex} \\right)" },
34
+
35
+ # Trascendent functions
36
+ CAS::Exp => Proc.new { "e^#{x.latex}" },
37
+ CAS::Ln => Proc.new { "\\log \\left( #{x.latex} \\right)" },
38
+
39
+ # Box Conditions
40
+ CAS::BoxConditionOpen => Proc.new { "#{lower.latex} < #{x.latex} < #{upper.latex}" },
41
+ CAS::BoxConditionClosed => Proc.new { "#{lower.latex} \\leq #{x.latex} \\leq #{upper.latex}" },
42
+ CAS::BoxConditionUpperClosed => Proc.new { "#{lower.latex} < #{x.latex} \\leq #{upper.latex}" },
43
+ CAS::BoxConditionLowerClosed => Proc.new { "#{lower.latex} \\leq #{x.latex} < #{upper.latex}" },
44
+
45
+ # Conditions
46
+ CAS::Equal => Proc.new { "#{x.latex} = #{y.latex}" },
47
+ CAS::Smaller => Proc.new { "#{x.latex} < #{y.latex}" },
48
+ CAS::Greater => Proc.new { "#{x.latex} > #{y.latex}" },
49
+ CAS::SmallerEqual => Proc.new { "#{x.latex} \\leq #{y.latex}" },
50
+ CAS::GreaterEqual => Proc.new { "#{x.latex} \\geq #{y.latex}" },
51
+
52
+ # Piecewise
53
+ CAS::Piecewise => Proc.new {
54
+ "\\left\\{ " +
55
+ " \\begin{array}{lr} " +
56
+ " #{x.latex} & #{condition.latex} \\\\" +
57
+ " #{y.latex}" +
58
+ " \\end{array}" +
59
+ "\\right."
60
+ },
61
+ CAS::Max => Proc.new { "\\max \\left( #{x.latex}, \\, #{y.latex} \\right)" },
62
+ CAS::Min => Proc.new { "\\min \\left( #{x.latex}, \\, #{y.latex} \\right)" }
63
+
64
+
65
+ }.each do |cls, blk|
66
+ cls.send(:define_method, "to_latex", &blk)
67
+ end
68
+ end
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # __ __ _ _ _ ___ _ _
4
+ # | \/ |__ _| |_| |__ _| |__ | _ \ |_ _ __ _(_)_ _
5
+ # | |\/| / _` | _| / _` | '_ \ | _/ | || / _` | | ' \
6
+ # |_| |_\__,_|\__|_\__,_|_.__/ |_| |_|\_,_\__, |_|_||_|
7
+ # |___/
8
+
9
+ module CAS
10
+ {
11
+ # Terminal nodes
12
+ CAS::Constant => Proc.new { |_v| "#{x}", nil },
13
+ CAS::Variable => Proc.new { |_v| "#{name}", nil },
14
+ CAS::PI_CONSTANT => Proc.new { |_v| "pi", nil },
15
+ CAS::INFINITY_CONSTANT => Proc.new { |_v| "Inf", nil },
16
+ CAS::NEG_INFINITY_CONSTANT => Proc.new { |_v| "(-Inf)", nil },
17
+ CAS::E_CONSTANT => Proc.new { |_v|
18
+ },
19
+ # Base functions
20
+ CAS::Sum => Proc.new { "(#{x.to_c} + #{y.to_c})" },
21
+ CAS::Diff => Proc.new { "(#{x.to_c} - #{y.to_c})" },
22
+ CAS::Prod => Proc.new { "(#{x.to_c} * #{y.to_c})" },
23
+ CAS::Pow => Proc.new { "pow(#{x.to_c}, #{y.to_c})" },
24
+ CAS::Div => Proc.new { "(#{x.to_c}) / (#{y.to_c} + )" },
25
+ CAS::Sqrt => Proc.new { "sqrt(#{x.to_c})" },
26
+ CAS::Invert => Proc.new { "(-#{x.to_c})" },
27
+ CAS::Abs => Proc.new { "fabs(#{x.to_c})" },
28
+
29
+ # Trigonometric functions
30
+ CAS::Sin => Proc.new { "sin(#{x.to_c})" },
31
+ CAS::Asin => Proc.new { "asin(#{x.to_c})" },
32
+ CAS::Cos => Proc.new { "cos(#{x.to_c})" },
33
+ CAS::Acos => Proc.new { "acos(#{x.to_c})" },
34
+ CAS::Tan => Proc.new { "tan(#{x.to_c})" },
35
+ CAS::Atan => Proc.new { "atan(#{x.to_c})" },
36
+
37
+ # Trascendent functions
38
+ CAS::Exp => Proc.new { "exp(#{x.to_c})" },
39
+ CAS::Ln => Proc.new { "log(#{x.to_c})" },
40
+
41
+ # Box Conditions
42
+ # CAS::BoxConditionOpen => Proc.new {
43
+ # ["double __t_#{x.object_id} = #{x.to_c};",
44
+ # "(__t_#{x.object_id} > #{lower.latex} && __t_#{x.object_id} < #{upper.latex})"]
45
+ # },
46
+ # CAS::BoxConditionUpperClosed => Proc.new {
47
+ # ["double __t_#{x.object_id} = #{x.to_c};",
48
+ # "(__t_#{x.object_id} > #{lower.latex} && __t_#{x.object_id} <= #{upper.latex})"]
49
+ # },
50
+ # CAS::BoxConditionLowerClosed => Proc.new {
51
+ # ["double __t_#{x.object_id} = #{x.to_c};",
52
+ # "(__t_#{x.object_id} >= #{lower.latex} && __t_#{x.object_id} < #{upper.latex})"]
53
+ # },
54
+ # CAS::BoxConditionClosed => Proc.new {
55
+ # ["double __t_#{x.object_id} = #{x.to_c};",
56
+ # "(__t_#{x.object_id} >= #{lower.latex} && __t_#{x.object_id} <= #{upper.latex})"]
57
+ # },
58
+
59
+ # Conditions
60
+ CAS::Equal => Proc.new { "(#{x.to_c} == #{y.to_c})" },
61
+ CAS::Smaller => Proc.new { "(#{x.to_c} < #{y.to_c})" },
62
+ CAS::Greater => Proc.new { "(#{x.to_c} > #{y.to_c})" },
63
+ CAS::SmallerEqual => Proc.new { "(#{x.to_c} <= #{y.to_c})" },
64
+ CAS::GreaterEqual => Proc.new { "(#{x.to_c} >= #{y.to_c})" },
65
+
66
+ # Piecewise
67
+ CAS::Piecewise => Proc.new { raise CASError, "Not implemented yet" },
68
+ CAS::Max => Proc.new { raise CASError, "Not implemented yet" },
69
+ CAS::Min => Proc.new { raise CASError, "Not implemented yet" }
70
+ }.each do |cls, blk|
71
+ cls.send(:define_method, "__to_matlab", &blk)
72
+ end
73
+
74
+ class Op
75
+ def to_c_lib(name)
76
+ CAS::Help.assert(name, String)
77
+ [CAS::C_PLUGIN.write_header(self, name), CAS::C_PLUGIN.write_source(self, name)]
78
+ end
79
+ end
80
+
81
+ end
@@ -0,0 +1,515 @@
1
+ #!/usr/vin/env ruby
2
+
3
+ module CAS
4
+ # ___ _ __ __
5
+ # | \(_)/ _|/ _|
6
+ # | |) | | _| _/
7
+ # |___/|_|_| |_|
8
+
9
+ ##
10
+ # **Difference basic operation**. It's a binary operation. This cannot
11
+ # be implemented as a n-ary op, thus will not be changed
12
+ class Diff < CAS::BinaryOp
13
+ # Performs the difference between two `CAS::Op`s
14
+ #
15
+ # ```
16
+ # d
17
+ # ---- (f(x) - g(x)) = f'(x) - g'(x)
18
+ # dx
19
+ # ```
20
+ #
21
+ # * **argument**: `CAS::Op` argument of derivative
22
+ # * **returns**: `CAS::Op` derivative
23
+ def diff(v)
24
+ left, right = super v
25
+ return left if right == CAS::Zero
26
+ return CAS::Invert.new(right) if left == CAS::Zero
27
+ left - right
28
+ end
29
+
30
+ # Same as `CAS::Op`
31
+ def call(f)
32
+ CAS::Help.assert(f, Hash)
33
+
34
+ return @x.call(f).overloaded_minus(@y.call(f))
35
+ end
36
+
37
+ # Same as `CAS::Op`
38
+ def to_s
39
+ "(#{@x} - #{@y})"
40
+ end
41
+
42
+ # Same as `CAS::Op`
43
+ #
44
+ # Simplifcation engine supports:
45
+ #
46
+ # * 0 - y = -y
47
+ # * x - 0 = x
48
+ # * a - b = c (constants reduction)
49
+ # * x - x = 0
50
+ # * x - (-y) = x + y
51
+ #
52
+ # * **returns**: `CAS::Op` simplified version
53
+ def simplify
54
+ super
55
+ return CAS.invert(@y) if @x == CAS::Zero
56
+ return @x if @y == CAS::Zero
57
+ return CAS::Zero if @x == @y
58
+ return CAS.const(self.call({})) if (@x.is_a? CAS::Constant and @y.is_a? CAS::Constant)
59
+ return @x + @y.x if @y.is_a? CAS::Invert
60
+ return -(@x.x + @y) if @x.is_a? CAS::Invert
61
+ return self
62
+ end
63
+
64
+ # Convert expression to code (internal, for `CAS::Op#to_proc` method)
65
+ #
66
+ # * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
67
+ def to_code
68
+ "(#{@x.to_code} - #{@y.to_code})"
69
+ end
70
+ end # Difference
71
+ CAS::Diff.init_simplify_dict
72
+
73
+ # ___
74
+ # | _ \_____ __ __
75
+ # | _/ _ \ V V /
76
+ # |_| \___/\_/\_/
77
+
78
+ ##
79
+ # Power function.
80
+ class Pow < CAS::BinaryOp
81
+ # Performs the power between two `CAS::Op`
82
+ #
83
+ # ```
84
+ # d
85
+ # ---- (f(x)^a) = f(x)^(a - 1) * a * f'(x)
86
+ # dx
87
+ #
88
+ # d
89
+ # ---- (a^f(x)) = a^f(x) * f'(x) * ln a
90
+ # dx
91
+ #
92
+ # d
93
+ # ---- (f(x)^g(x)) = (f(x)^g(x)) * (g'(x) * ln f(x) + g(x) * f'(x) / f(x))
94
+ # dx
95
+ # ```
96
+ #
97
+ # * **argument**: `CAS::Op` argument of derivative
98
+ # * **returns**: `CAS::Op` derivative
99
+ def diff(v)
100
+ diff_x, diff_y = super v
101
+ if diff_y == CAS::Zero
102
+ return ((@x ** (@y - 1.0)) * @y * diff_x)
103
+ elsif diff_x == CAS::Zero
104
+ return (@x ** @y) * diff_y * CAS.ln(@x)
105
+ else
106
+ return (@x ** @y) * ((diff_y * CAS.ln(@x)) + (@y * diff_x / @x))
107
+ end
108
+ end
109
+
110
+ # Call resolves the operation tree in a `Numeric` (if `Fixnum`)
111
+ # or `Float` (depends upon promotions).
112
+ # As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
113
+ # as keys, and a `Numeric` as a value. In this case it will call
114
+ # the `Fixnum#overloaded_plus`, that is the old plus function.
115
+ #
116
+ # * **argument**: `Hash` with feed dictionary
117
+ # * **returns**: `Numeric`
118
+ def call(f)
119
+ CAS::Help.assert(f, Hash)
120
+ @x.call(f).overloaded_pow(@y.call(f))
121
+ end
122
+
123
+ # Convert expression to string
124
+ #
125
+ # * **returns**: `String` to print on screen
126
+ def to_s
127
+ "(#{@x})^(#{@y})"
128
+ end
129
+
130
+ # Same as `CAS::Op`
131
+ #
132
+ # Simplifcation engine supports:
133
+ #
134
+ # * 0 ^ y = 0
135
+ # * x ^ 0 = 1
136
+ # * a ^ b = c (constants reduction)
137
+ # * x ^ 1 = x
138
+ # * 1 ^ y = 1
139
+ #
140
+ # * **returns**: `CAS::Op` simplified version
141
+ def simplify
142
+ super
143
+ return self if (@x == CAS::Zero and @y == CAS::Zero)
144
+ return self if (@x == CAS::Infinity and @y == CAS::Infinity)
145
+ return self if (@x == CAS::Infinity and @y == CAS::Zero)
146
+ return self if (@x == CAS::Zero and @y == CAS::Infinity)
147
+
148
+ return CAS::Zero if @x == CAS::Zero
149
+ return CAS::One if @x == CAS::One
150
+ return @x if @y == CAS::One
151
+ return CAS::One if @y == CAS::Zero
152
+ return CAS.const(self.call({})) if (@x.is_a? CAS::Constant and @y.is_a? CAS::Constant)
153
+ return self
154
+ end
155
+
156
+ # Convert expression to code (internal, for `CAS::Op#to_proc` method)
157
+ #
158
+ # * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
159
+ def to_code
160
+ "(#{@x.to_code} ** #{@y.to_code})"
161
+ end
162
+ end # Pow
163
+ CAS::Pow.init_simplify_dict
164
+
165
+ # Shortcut for `CAS::Pow` initializer
166
+ #
167
+ # * **argument**: `CAS::Op` base
168
+ # * **argument**: `CAS::Op` exponent
169
+ # * **returns**: `CAS::Pow` new instance
170
+ def self.pow(x, y)
171
+ CAS::Pow.new x, y
172
+ end
173
+
174
+ # ___ _
175
+ # | \(_)_ __
176
+ # | |) | \ V /
177
+ # |___/|_|\_/
178
+
179
+ ##
180
+ # Division between two functions. A function divided by zero it is considered
181
+ # as an Infinity.
182
+ class Div < CAS::BinaryOp
183
+ # Performs the division between two `CAS::Op`
184
+ #
185
+ # ```
186
+ # d
187
+ # ---- (f(x) / g(x)) = (f'(x) * g(x) - f(x) * g'(x))/(g(x)^2)
188
+ # dx
189
+ # ```
190
+ #
191
+ # * **argument**: `CAS::Op` argument of derivative
192
+ # * **returns**: `CAS::Op` derivative
193
+ def diff(v)
194
+ diff_x, diff_y = super v
195
+ if diff_y == CAS::Zero
196
+ return (diff_x/@y)
197
+ elsif diff_x == CAS::Zero
198
+ return CAS.invert(@x * diff_y / CAS.pow(@y, CAS.const(2.0)))
199
+ else
200
+ return ((diff_x * @y) - (diff_y * @x))/CAS.pow(@y, CAS.const(2.0))
201
+ end
202
+ end
203
+
204
+ # Call resolves the operation tree in a `Numeric` (if `Fixnum`)
205
+ # or `Float` (depends upon promotions).
206
+ # As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
207
+ # as keys, and a `Numeric` as a value. In this case it will call
208
+ # the `Fixnum#overloaded_plus`, that is the old plus function.
209
+ #
210
+ # * **argument**: `Hash` with feed dictionary
211
+ # * **returns**: `Numeric`
212
+ def call(f)
213
+ CAS::Help.assert(f, Hash)
214
+
215
+ @x.call(f).overloaded_div(@y.call(f))
216
+ end
217
+
218
+ # Convert expression to string
219
+ #
220
+ # * **returns**: `String` to print on screen
221
+ def to_s
222
+ "(#{@x}) / (#{@y})"
223
+ end
224
+
225
+ # Same as `CAS::Op`
226
+ #
227
+ # Simplifcation engine supports:
228
+ #
229
+ # * 0 / y = 0
230
+ # * x / 0 = Inf
231
+ # * x / 1 = x
232
+ # * x / Inf = 0
233
+ # * a / b = c (constants reduction)
234
+ #
235
+ # * **returns**: `CAS::Op` simplified version
236
+ def simplify
237
+ super
238
+ return self if (@x == CAS::Zero and @y == CAS::Zero)
239
+ return self if (@x == CAS::Infinity and @y == CAS::Infinity)
240
+ return self if (@x == CAS::Infinity and @y == CAS::Zero)
241
+ return self if (@x == CAS::Zero and @y == CAS::Infinity)
242
+
243
+ return CAS::Zero if @x == CAS::Zero
244
+ return CAS::Infinity if @y == CAS::Zero
245
+ return @x if @y == CAS::One
246
+ return CAS::Zero if @y == CAS::Infinity
247
+ return CAS::One if @x == @y
248
+ return CAS.const(self.call({})) if (@x.is_a? CAS::Constant and @y.is_a? CAS::Constant)
249
+ return self
250
+ end
251
+
252
+ # Convert expression to code (internal, for `CAS::Op#to_proc` method)
253
+ #
254
+ # * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
255
+ def to_code
256
+ "(#{@x.to_code} / #{@y.to_code})"
257
+ end
258
+ end # Div
259
+ CAS::Div.init_simplify_dict
260
+
261
+ # ___ _
262
+ # / __| __ _ _ _| |_
263
+ # \__ \/ _` | '_| _|
264
+ # |___/\__, |_| \__|
265
+ # |_|
266
+
267
+ ##
268
+ # Square Root of a function. Even if it can be implemented as a power function,
269
+ # it is a separated class.
270
+ class Sqrt < CAS::Op
271
+ # Performs the square root between two `CAS::Op`
272
+ #
273
+ # ```
274
+ # d
275
+ # ---- √f(x) = 1/2 * f'(x) * √f(x)
276
+ # dx
277
+ # ```
278
+ #
279
+ # * **argument**: `CAS::Op` argument of derivative
280
+ # * **returns**: `CAS::Op` derivative
281
+ def diff(v)
282
+ if @x.depend? v
283
+ return (@x.diff(v) / (CAS.const(2.0) * CAS.sqrt(@x)))
284
+ else
285
+ return CAS::Zero
286
+ end
287
+ end
288
+
289
+ # Call resolves the operation tree in a `Numeric` (if `Fixnum`)
290
+ # or `Float` (depends upon promotions).
291
+ # As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
292
+ # as keys, and a `Numeric` as a value. In this case it will call
293
+ # the `Fixnum#overloaded_plus`, that is the old plus function.
294
+ #
295
+ # * **argument**: `Hash` with feed dictionary
296
+ # * **returns**: `Numeric`
297
+ def call(f)
298
+ CAS::Help.assert(f, Hash)
299
+
300
+ Math::sqrt @x.call(f)
301
+ end
302
+
303
+ # Convert expression to string
304
+ #
305
+ # * **returns**: `String` to print on screen
306
+ def to_s
307
+ "√(#{@x})"
308
+ end
309
+
310
+ # Same as `CAS::Op`
311
+ #
312
+ # Simplifcation engine supports:
313
+ #
314
+ # * √(x^z) = x^(z - 1/2)
315
+ # * √x = 0
316
+ # * √x = 1
317
+ # * √a = b (constants reduction)
318
+ #
319
+ # * **returns**: `CAS::Op` simplified version
320
+ def simplify
321
+ super
322
+ return (CAS.pow(@x.x, @x.y - 0.5)).simplify if @x.is_a? CAS::Pow
323
+ return CAS::Zero if @x == CAS::Zero
324
+ return CAS::One if @x == CAS::One
325
+ return CAS.const(self.call({})) if (@x.is_a? CAS::Constant and @y.is_a? CAS::Constant)
326
+ return self
327
+ end
328
+
329
+ # Convert expression to code (internal, for `CAS::Op#to_proc` method)
330
+ #
331
+ # * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
332
+ def to_code
333
+ "Math::sqrt(#{@x.to_code})"
334
+ end
335
+ end # Sqrt
336
+ CAS::Sqrt.init_simplify_dict
337
+
338
+ # Shortcut for `CAS::Sqrt` initializer
339
+ #
340
+ # * **argument**: `CAS::Op` argument of square root
341
+ # * **returns**: `CAS::Sqrt` new instance
342
+ def self.sqrt(x)
343
+ CAS::Sqrt.new x
344
+ end
345
+
346
+ # ___ _
347
+ # |_ _|_ ___ _____ _ _| |_
348
+ # | || ' \ V / -_) '_| _|
349
+ # |___|_||_\_/\___|_| \__|
350
+
351
+ ##
352
+ # Invert is the same as multiply by `-1` a function.
353
+ # `Invert(x)` is equal to `-x`
354
+ class Invert < CAS::Op
355
+ # Performs the inversion of a `CAS::Op`
356
+ #
357
+ # ```
358
+ # d
359
+ # ---- (-f(x)) = -f'(x)
360
+ # dx
361
+ # ```
362
+ #
363
+ # * **argument**: `CAS::Op` argument of derivative
364
+ # * **returns**: `CAS::Op` derivative
365
+ def diff(v)
366
+ if @x.depend? v
367
+ -@x.diff(v)
368
+ else
369
+ CAS::Zero
370
+ end
371
+ end
372
+
373
+ # Call resolves the operation tree in a `Numeric` (if `Fixnum`)
374
+ # or `Float` (depends upon promotions).
375
+ # As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
376
+ # as keys, and a `Numeric` as a value. In this case it will call
377
+ # the `Fixnum#overloaded_plus`, that is the old plus function.
378
+ #
379
+ # * **argument**: `Hash` with feed dictionary
380
+ # * **returns**: `Numeric`
381
+ def call(f)
382
+ CAS::Help.assert(f, Hash)
383
+
384
+ -1.0 * @x.call(f)
385
+ end
386
+
387
+ # Convert expression to string
388
+ #
389
+ # * **returns**: `String` to print on screen
390
+ def to_s
391
+ "-#{@x}"
392
+ end
393
+
394
+ # Same as `CAS::Op`
395
+ #
396
+ # Simplifcation engine supports:
397
+ #
398
+ # * -(-x) = x
399
+ # * -0 = 0
400
+ #
401
+ # * **returns**: `CAS::Op` simplified version
402
+ def simplify
403
+ super
404
+ return @x.x if @x.is_a? CAS::Invert
405
+ return self.simplify_dictionary
406
+ end
407
+
408
+ def self.init_simplify_dict
409
+ @simplify_dict = {
410
+ CAS::Zero => CAS::Zero
411
+ }
412
+ end
413
+
414
+ # Convert expression to code (internal, for `CAS::Op#to_proc` method)
415
+ #
416
+ # * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
417
+ def to_code
418
+ "(-#{@x.to_code})"
419
+ end
420
+ end # Invert
421
+ CAS::Invert.init_simplify_dict
422
+
423
+ # Shortcut for `CAs::Invert` initializer
424
+ #
425
+ # * **argument**: `CAs::Op` argument of the inversion
426
+ # * **returns**: `CAS::Invert` new instance
427
+ def self.invert(x)
428
+ CAS::Invert.new x
429
+ end
430
+
431
+ # _ _
432
+ # /_\ | |__ ___
433
+ # / _ \| '_ (_-<
434
+ # /_/ \_\_.__/__/
435
+
436
+ ##
437
+ # Absolute value of a function. It can be also implemented as a Piecewise function.
438
+ class Abs < CAS::Op
439
+ # Performs the absolute value of a `CAS::Op`
440
+ #
441
+ # ```
442
+ # d
443
+ # ---- |f(x)| = f'(x) * (f(x) / |f(x)|)
444
+ # dx
445
+ # ```
446
+ #
447
+ # * **argument**: `CAS::Op` argument of derivative
448
+ # * **returns**: `CAS::Op` derivative
449
+ def diff(v)
450
+ if @x.depend? v
451
+ return @x.diff(v) * (@x/CAS.abs(@x))
452
+ else
453
+ return CAS::Zero
454
+ end
455
+ end
456
+
457
+ # Call resolves the operation tree in a `Numeric` (if `Fixnum`)
458
+ # or `Float` (depends upon promotions).
459
+ # As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
460
+ # as keys, and a `Numeric` as a value. In this case it will call
461
+ # the `Fixnum#overloaded_plus`, that is the old plus function.
462
+ #
463
+ # * **argument**: `Hash` with feed dictionary
464
+ # * **returns**: `Numeric`
465
+ def call(f)
466
+ CAS::Help.assert(f, Hash)
467
+
468
+ s = (@x.call(f) >= 0 ? 1 : -1)
469
+ return s * @x.call(f)
470
+ end
471
+
472
+ # Convert expression to string
473
+ #
474
+ # * **returns**: `String` to print on screen
475
+ def to_s
476
+ "|#{@x}|"
477
+ end
478
+
479
+ # Same as `CAS::Op`
480
+ #
481
+ # Simplifcation engine supports:
482
+ #
483
+ # * |-x| = x
484
+ # * |0| = 0
485
+ #
486
+ # * **returns**: `CAS::Op` simplified version
487
+ def simplify
488
+ super
489
+ return CAS.abs(@x.x) if @x.is_a? CAS::Invert
490
+ return self.simplify_dictionary
491
+ end
492
+
493
+ def self.init_simplify_dict
494
+ @simplify_dict = {
495
+ CAS::Zero => CAS::Zero
496
+ }
497
+ end
498
+
499
+ # Convert expression to code (internal, for `CAS::Op#to_proc` method)
500
+ #
501
+ # * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
502
+ def to_code
503
+ "(#{@x.to_code}).abs"
504
+ end
505
+ end # Abs
506
+ CAS::Abs.init_simplify_dict
507
+
508
+ # Shortcut for `CAs::Abs` initializer
509
+ #
510
+ # * **argument**: `CAs::Op` argument of absolute value
511
+ # * **returns**: `CAS::Abs` new instance
512
+ def self.abs(x)
513
+ CAS::Abs.new x
514
+ end
515
+ end