Mr.CAS 0.2.6

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.
@@ -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