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