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,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
|
data/lib/Mr.CAS/latex.rb
ADDED
@@ -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
|