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,186 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module CAS
|
4
|
+
# ___ _ _
|
5
|
+
# | _ (_)___ __ _____ __ _(_)___ ___
|
6
|
+
# | _/ / -_) _/ -_) V V / (_-</ -_)
|
7
|
+
# |_| |_\___\__\___|\_/\_/|_/__/\___|
|
8
|
+
|
9
|
+
##
|
10
|
+
# Piecewise function. The function returns when called a result
|
11
|
+
# that dependes upon the evaluation of a condition. In practice:
|
12
|
+
#
|
13
|
+
# ```
|
14
|
+
# /
|
15
|
+
# | f(x) if condition(x) is True
|
16
|
+
# <
|
17
|
+
# | g(x) otherwise
|
18
|
+
# \
|
19
|
+
# ```
|
20
|
+
#
|
21
|
+
# From this class other classes will inherit like `CAS::Max` and `CAS::Min` classes
|
22
|
+
class Piecewise < CAS::BinaryOp
|
23
|
+
attr_reader :condition
|
24
|
+
|
25
|
+
# Initialize a new piecewise function. It requires first the function
|
26
|
+
# that returns when condition is true, than the function when condition is
|
27
|
+
# false, and finally the condition that must be of class `CAS::Condition`
|
28
|
+
#
|
29
|
+
# * **argument**: `CAS::Op` first function
|
30
|
+
# * **argument**: `CAS::Op` second function
|
31
|
+
# * **argument**: `CAS::Condition` evaluated condition
|
32
|
+
# * **returns**: `CAS::Piecewise` new instance
|
33
|
+
def initialize(x, y, condition)
|
34
|
+
CAS::Help.assert(condition, CAS::Condition)
|
35
|
+
|
36
|
+
super(x, y)
|
37
|
+
@condition = condition
|
38
|
+
end
|
39
|
+
|
40
|
+
# Derivative of a function is performed as derivative of the two internal functions
|
41
|
+
# while condition is unchanged
|
42
|
+
#
|
43
|
+
# warning:: Piecewise functions are in general not differentiable. Thus differentiability
|
44
|
+
# is left to the user
|
45
|
+
#
|
46
|
+
# ```
|
47
|
+
# / /
|
48
|
+
# d | f(x) if condition(x) is True | f'(x) if condition(x) is True
|
49
|
+
# -- < = <
|
50
|
+
# dx | g(x) otherwise | g'(x) otherwise
|
51
|
+
# \ \
|
52
|
+
# ```
|
53
|
+
#
|
54
|
+
# * **argument**: `CAS::Op` argument of derivative
|
55
|
+
# * **returns**: `CAS::Piecewise` with derivated functions and unchanged condition
|
56
|
+
def diff(v)
|
57
|
+
CAS::Help.assert(v, CAS::Op)
|
58
|
+
return CAS::Piecewise.new(@x.diff(v).simplify, @y.diff(v).simplify, @condition)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Executes the condition. If it is `true` it returns the first function,
|
62
|
+
# else it returns the value of the second function.
|
63
|
+
#
|
64
|
+
# * **argument**: `Hash` with value tables
|
65
|
+
# * **returns**: `Numeric` the result of the call
|
66
|
+
def call(fd)
|
67
|
+
CAS::Help.assert(fd, Hash)
|
68
|
+
(@condition.call(fd) ? @x.call(fd) : @y.call(fd))
|
69
|
+
end
|
70
|
+
|
71
|
+
# Checks if two `CAS::Piecewise` are equal. Checks equality on all functions+
|
72
|
+
# and conditions
|
73
|
+
#
|
74
|
+
# * **argument**: `CAS::Op` to be checked against
|
75
|
+
# * **returns**: `TrueClass` or `FalseClass`
|
76
|
+
def ==(op)
|
77
|
+
CAS::Help.assert(op, CAS::Op)
|
78
|
+
if self.class != op.class
|
79
|
+
return false
|
80
|
+
else
|
81
|
+
return ((@x == op.x) and (@y == op.y) and (@condition == op.condition))
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Convert the piecewise funtion to a String of Ruby code
|
86
|
+
#
|
87
|
+
# * **returns**: `String` of code
|
88
|
+
def to_code
|
89
|
+
"(#{@condition.to_code} ? (#{@x.to_code}) : (#{@y.to_code}))"
|
90
|
+
end
|
91
|
+
|
92
|
+
# Convert the piecewise function into a String
|
93
|
+
#
|
94
|
+
# * **returns**: `String`
|
95
|
+
def to_s
|
96
|
+
"(#{@condition} ? #{@x} : #{@y})"
|
97
|
+
end
|
98
|
+
|
99
|
+
# Convert piecewise function into LaTeX representation
|
100
|
+
#
|
101
|
+
# * **returns**: `String` of LaTeX code
|
102
|
+
def to_latex
|
103
|
+
"\\left\\{ \\begin{array}{lr} #{@x.to_latex} & #{@condition.to_latex} \\\\ #{@y.to_latex} \\end{array} \\right."
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# __ __ _ __ __
|
108
|
+
# | \/ (_)_ _ | \/ |__ ___ __
|
109
|
+
# | |\/| | | ' \| |\/| / _` \ \ /
|
110
|
+
# |_| |_|_|_||_|_| |_\__,_/_\_\
|
111
|
+
|
112
|
+
##
|
113
|
+
# Class MinMax is an intermediate class for Min and Max functions. It contains shared code
|
114
|
+
# and methods
|
115
|
+
class MinMax < CAS::Piecewise
|
116
|
+
# Convert MinMax function into LaTeX representation
|
117
|
+
#
|
118
|
+
# * **returns**: `String` of LaTeX code
|
119
|
+
def to_latex
|
120
|
+
"\\mathrm{#{@type}}\\left( \\begin{array}{c} #{@x.to_latex} \\\\ #{@y.to_latex} \\end{array} \\right)"
|
121
|
+
end
|
122
|
+
|
123
|
+
# Returns a string representation for the current operation
|
124
|
+
#
|
125
|
+
# * **returns**: `String`
|
126
|
+
def to_s
|
127
|
+
"#{@type}(#{@x}, #{@y})"
|
128
|
+
end
|
129
|
+
end # MinMax
|
130
|
+
|
131
|
+
# __ __
|
132
|
+
# | \/ |__ ___ __
|
133
|
+
# | |\/| / _` \ \ /
|
134
|
+
# |_| |_\__,_/_\_\
|
135
|
+
|
136
|
+
# Max class represent a piecewise in which the condition is `f(x) ≥ g(x)`. Derivate a `CAS::Max`
|
137
|
+
# will return a `CAS::Piecewise` (since condition will not depend anymore on object functions)
|
138
|
+
class Max < CAS::Piecewise
|
139
|
+
# To initialize `CAS::Max` only the two functions are necessary. The condition is automatically
|
140
|
+
# generated
|
141
|
+
#
|
142
|
+
# * **argument**: `CAS::Op` first function
|
143
|
+
# * **argument**: `CAS::Op` second function
|
144
|
+
def initialize(x, y)
|
145
|
+
super(x, y, CAS::greater_equal(x, y))
|
146
|
+
@type = "max"
|
147
|
+
end
|
148
|
+
end # Max
|
149
|
+
|
150
|
+
# __ __ _
|
151
|
+
# | \/ (_)_ _
|
152
|
+
# | |\/| | | ' \
|
153
|
+
# |_| |_|_|_||_|
|
154
|
+
|
155
|
+
# Min class represent a piecewise in which the condition is `f(x) ≤ g(x)`. Derivate a `CAS::Min`
|
156
|
+
# will return a `CAS::Piecewise` (since condition will not depend anymore on object functions)
|
157
|
+
class Min < CAS::Piecewise
|
158
|
+
# To initialize `CAS::Min` only the two functions are necessary. The condition is automatically
|
159
|
+
# generated
|
160
|
+
#
|
161
|
+
# * **argument**: `CAS::Op` first function
|
162
|
+
# * **argument**: `CAS::Op` second function
|
163
|
+
def initialize(x, y)
|
164
|
+
super(x, y, CAS::smaller_equal(x, y))
|
165
|
+
@type = "min"
|
166
|
+
end
|
167
|
+
end # Min
|
168
|
+
|
169
|
+
# Shortcut for `CAS::Max` initializer
|
170
|
+
#
|
171
|
+
# * **argument**: `CAS::Op` left function
|
172
|
+
# * **argument**: `CAS::Op` right function
|
173
|
+
# * **returns**: `CAS::Max` new instance
|
174
|
+
def self.max(x, y)
|
175
|
+
CAS::Max.new(x, y)
|
176
|
+
end
|
177
|
+
|
178
|
+
# Shortcut for `CAS::Min` initializer
|
179
|
+
#
|
180
|
+
# * **argument**: `CAS::Op` left function
|
181
|
+
# * **argument**: `CAS::Op` right function
|
182
|
+
# * **returns**: `CAS::Min` new instance
|
183
|
+
def self.min(x, y)
|
184
|
+
CAS::Min.new(x, y, CAS::smaller_equal(x, y))
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module CAS
|
4
|
+
|
5
|
+
# ___ _
|
6
|
+
# | _ \_ _ ___ __| |
|
7
|
+
# | _/ '_/ _ \/ _` |
|
8
|
+
# |_| |_| \___/\__,_|
|
9
|
+
|
10
|
+
##
|
11
|
+
# Product class. Performs the product between two elements.
|
12
|
+
# This class will be soon modified as an n-ary operator.
|
13
|
+
class Prod < CAS::NaryOp
|
14
|
+
# The new element of a sum accumulates inside the
|
15
|
+
# vector that holds the elements
|
16
|
+
def *(op)
|
17
|
+
CAS::Help.assert(op, CAS::Op)
|
18
|
+
@x << op
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
# Performs the product between two `CAS::Op`
|
23
|
+
#
|
24
|
+
# ```
|
25
|
+
# d
|
26
|
+
# ---- (f(x) * g(x) * h(x)) = f'(x) * g(x) * h(x) +
|
27
|
+
# dx
|
28
|
+
# + f(x) * g'(x) * h(x) +
|
29
|
+
#
|
30
|
+
# + f(x) * g(x) * h'(x)
|
31
|
+
# ```
|
32
|
+
#
|
33
|
+
# * **argument**: `CAS::Op` argument of derivative
|
34
|
+
# * **returns**: `CAS::Op` derivative
|
35
|
+
def diff(v)
|
36
|
+
xdiff = @x.map { |y| y.diff(v) }
|
37
|
+
|
38
|
+
xdiff.each_with_index { |y, i|
|
39
|
+
xdiff[i] = y * CAS::Prod.new(@x[0...i] + @x[(i + 1)..-1])
|
40
|
+
}
|
41
|
+
|
42
|
+
return CAS::Sum.new(xdiff)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Call resolves the operation tree in a `Numeric` (if `Fixnum`)
|
46
|
+
# or `Float` (depends upon promotions).
|
47
|
+
# As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
|
48
|
+
# as keys, and a `Numeric` as a value. In this case it will call
|
49
|
+
# the `Fixnum#overloaded_plus`, that is the old plus function.
|
50
|
+
#
|
51
|
+
# * **argument**: `Hash` with feed dictionary
|
52
|
+
# * **returns**: `Numeric`
|
53
|
+
def call(f)
|
54
|
+
CAS::Help.assert(f, Hash)
|
55
|
+
|
56
|
+
return @x.inject { |p, y| p = p.overloaded_mul(y.call(f)) }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Convert expression to string
|
60
|
+
#
|
61
|
+
# * **returns**: `String` to print on screen
|
62
|
+
def to_s
|
63
|
+
"(#{@x.map(&:to_s).join(" * ")})"
|
64
|
+
end
|
65
|
+
|
66
|
+
# Same as `CAS::Op`
|
67
|
+
#
|
68
|
+
# Simplifcation engine supports:
|
69
|
+
#
|
70
|
+
# * x * 0 = x * y = 0
|
71
|
+
# * 1 * y = y
|
72
|
+
# * x * 1 = x
|
73
|
+
# * x * x = x²
|
74
|
+
# * a * b = c (constants reduction)
|
75
|
+
#
|
76
|
+
# * **returns**: `CAS::Op` simplified version
|
77
|
+
def simplify
|
78
|
+
super
|
79
|
+
return CAS::Zero if @x.include? CAS::Zero
|
80
|
+
@x = @x - [CAS::One]
|
81
|
+
return CAS::One if @x.size == 0
|
82
|
+
return @x[0] if @x.size == 1
|
83
|
+
|
84
|
+
@x = self.__reduce_constants(@x) do |cs, xs|
|
85
|
+
[cs.inject { |t, c| t *= c.call({}) }] + xs
|
86
|
+
end
|
87
|
+
|
88
|
+
@x = self.__reduce_multeplicity(@x) do |op, count|
|
89
|
+
count > 1 ? (op ** count) : op
|
90
|
+
end
|
91
|
+
return self
|
92
|
+
end
|
93
|
+
|
94
|
+
# Convert expression to code (internal, for `CAS::Op#to_proc` method)
|
95
|
+
#
|
96
|
+
# * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
|
97
|
+
def to_code
|
98
|
+
"(#{@x.map(&:to_code).join(" * ")})"
|
99
|
+
end
|
100
|
+
end # Prod
|
101
|
+
CAS::Prod.init_simplify_dict
|
102
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module CAS
|
2
|
+
# ___
|
3
|
+
# / __|_ _ _ __
|
4
|
+
# \__ \ || | ' \
|
5
|
+
# |___/\_,_|_|_|_|
|
6
|
+
|
7
|
+
##
|
8
|
+
# **Sum basic operation**. As for now it is implemented as a simple
|
9
|
+
# binary operation. It will be implemented as n-ary op.
|
10
|
+
class Sum < CAS::NaryOp
|
11
|
+
# Performs the sum between arbitrary number of `CAS::Op`
|
12
|
+
#
|
13
|
+
# ```
|
14
|
+
# d
|
15
|
+
# ---- (f(x) + g(x) + h(x)) = f'(x) + g'(x) + h'(x)
|
16
|
+
# dx
|
17
|
+
# ```
|
18
|
+
#
|
19
|
+
# * **argument**: `CAS::Op` argument of derivative
|
20
|
+
# * **returns**: `CAS::Op` derivative
|
21
|
+
def diff(v)
|
22
|
+
@x.map { |x| x.diff(v) }.inject { |sum_x, dx| sum_x += dx }
|
23
|
+
end
|
24
|
+
|
25
|
+
# The added element of a sum accumulates inside the
|
26
|
+
# vector that holds the elements
|
27
|
+
def +(op)
|
28
|
+
CAS::Help.assert(op, CAS::Op)
|
29
|
+
@x << op
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
# Call resolves the operation tree in a `Numeric` (if `Fixnum`)
|
34
|
+
# or `Float` (depends upon promotions).
|
35
|
+
# As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
|
36
|
+
# as keys, and a `Numeric` as a value. In this case it will call
|
37
|
+
# the `Fixnum#overloaded_plus`, that is the old plus function.
|
38
|
+
#
|
39
|
+
# * **argument**: `Hash` with feed dictionary
|
40
|
+
# * **returns**: `Numeric`
|
41
|
+
def call(f)
|
42
|
+
CAS::Help.assert(f, Hash)
|
43
|
+
return @x.inject { |val, x_i| val += x_i.call(f) }
|
44
|
+
end
|
45
|
+
|
46
|
+
# Convert expression to string
|
47
|
+
#
|
48
|
+
# * **returns**: `String` to print on screen
|
49
|
+
def to_s
|
50
|
+
"(#{@x.map(&:to_s).join(" + ")})"
|
51
|
+
end
|
52
|
+
|
53
|
+
# Same as `CAS::Op`
|
54
|
+
#
|
55
|
+
# Simplifcation engine supports:
|
56
|
+
#
|
57
|
+
# * x + 0 = x
|
58
|
+
# * 0 + y = y
|
59
|
+
# * x + x = 2 x
|
60
|
+
# * x + (-x) = 0
|
61
|
+
# * x + (-y) = x - y
|
62
|
+
# * 1 + 2 = 3 (constants reduction)
|
63
|
+
#
|
64
|
+
# * **returns**: `CAS::Op` simplified version
|
65
|
+
def simplify
|
66
|
+
super
|
67
|
+
return @x[0] if @x.size == 1
|
68
|
+
|
69
|
+
# return CAS::Zero if @x == -@y or -@x == @y
|
70
|
+
# return (@x - @y.x) if @y.is_a? CAS::Invert
|
71
|
+
# return CAS.const(self.call({})) if (@x.is_a? CAS::Constant and @y.is_a? CAS::Constant)
|
72
|
+
# Removing Zeros
|
73
|
+
@x = @x - [CAS::Zero]
|
74
|
+
return CAS::Zero if @x.size == 0
|
75
|
+
# Reduce constants
|
76
|
+
@x = self.__reduce_constants(@x) do |cs, xs|
|
77
|
+
xs + [cs.inject { |t, c| t += c.call({}) }]
|
78
|
+
end
|
79
|
+
# Multeplicity and associativity executed
|
80
|
+
return self.reduce_associativity
|
81
|
+
end
|
82
|
+
|
83
|
+
# Reduces from an associative point of view, by a segregation
|
84
|
+
# of "negative" and positive elements. Negatives comes from
|
85
|
+
# Diff operations and Invert operations. All the others are considered
|
86
|
+
# positive. This function implements an internal heuristic. Should
|
87
|
+
# not be used outside
|
88
|
+
#
|
89
|
+
# * **returns**: A `CAS::Diff` or a `CAS::Sum`
|
90
|
+
def reduce_associativity
|
91
|
+
pos, neg = [], []
|
92
|
+
|
93
|
+
@x.each do |x_el|
|
94
|
+
case x_el
|
95
|
+
when CAS::Invert
|
96
|
+
neg << x_el.x
|
97
|
+
when CAS::Diff
|
98
|
+
pos << x_el.x
|
99
|
+
neg << x_el.y
|
100
|
+
else
|
101
|
+
pos << x_el
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
pos, neg = self.reduce_associativity_array pos, neg
|
106
|
+
pos = self.__reduce_multeplicity(pos)
|
107
|
+
neg = self.__reduce_multeplicity neg
|
108
|
+
|
109
|
+
# TODO : Add rules for simplifications
|
110
|
+
left, right = nil, nil
|
111
|
+
left = CAS::Sum.new(pos) if pos.size > 1
|
112
|
+
left = pos[0] if pos.size == 1
|
113
|
+
right = CAS::Sum.new(neg) if neg.size > 1
|
114
|
+
right = neg[0] if neg.size == 1
|
115
|
+
|
116
|
+
return CAS::Zero unless left || right
|
117
|
+
return left unless right
|
118
|
+
return -right unless left
|
119
|
+
return left - right
|
120
|
+
end
|
121
|
+
|
122
|
+
# Reduce the positive and the negative associative part of
|
123
|
+
# the sum to perform the symbolic difference. Does not take into account
|
124
|
+
# multeplicity
|
125
|
+
#
|
126
|
+
# * **requires**: positive `Array`
|
127
|
+
# * **requires**: negative `Array`
|
128
|
+
# * **returns**: positive, reduced `Array` and negative `Array`
|
129
|
+
def reduce_associativity_array(p_old, n_old)
|
130
|
+
p_del, n_del = [], []
|
131
|
+
p_old.each do |p|
|
132
|
+
n_old.each do |n|
|
133
|
+
if p == n
|
134
|
+
p_del << p
|
135
|
+
n_del << n
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
return (p_old - p_del), (n_old - n_del)
|
141
|
+
end
|
142
|
+
|
143
|
+
# Convert expression to code (internal, for `CAS::Op#to_proc` method)
|
144
|
+
#
|
145
|
+
# * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
|
146
|
+
def to_code
|
147
|
+
"(#{@x.map(&:to_code).join(" + ")})"
|
148
|
+
end
|
149
|
+
end # Sum
|
150
|
+
CAS::Sum.init_simplify_dict
|
151
|
+
end
|
@@ -0,0 +1,489 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module CAS
|
4
|
+
# ___ _
|
5
|
+
# / __(_)_ _
|
6
|
+
# \__ \ | ' \
|
7
|
+
# |___/_|_||_|
|
8
|
+
|
9
|
+
##
|
10
|
+
# Representation for the `sin(x)` function. It is implemented
|
11
|
+
# as a `CAS::Op`
|
12
|
+
class Sin < CAS::Op
|
13
|
+
# Return the derivative of the `sin(x)` function using the chain
|
14
|
+
# rule. The input is a `CAS::Op` because it can handle derivatives
|
15
|
+
# with respect to functions.
|
16
|
+
#
|
17
|
+
# ```
|
18
|
+
# d
|
19
|
+
# -- sin(f(x)) = f'(x) cos(fx)
|
20
|
+
# dx
|
21
|
+
# ```
|
22
|
+
#
|
23
|
+
# * **argument**: `CAS::Op` object of the derivative
|
24
|
+
# * **returns**: `CAS::Op` a derivated object, or `CAS::Zero` for constants
|
25
|
+
def diff(v)
|
26
|
+
if @x.depend? v
|
27
|
+
return @x.diff(v) * CAS.cos(@x)
|
28
|
+
else
|
29
|
+
return CAS::Zero
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Call resolves the operation tree in a `Numeric` (if `Fixnum`)
|
34
|
+
# or `Float` (depends upon promotions).
|
35
|
+
# As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
|
36
|
+
# as keys, and a `Numeric` as a value
|
37
|
+
#
|
38
|
+
# * **argument**: `Hash` with feed dictionary
|
39
|
+
# * **returns**: `Numeric`
|
40
|
+
def call(f)
|
41
|
+
CAS::Help.assert(f, Hash)
|
42
|
+
Math::sin(@x.call(f))
|
43
|
+
end
|
44
|
+
|
45
|
+
# Convert expression to string
|
46
|
+
#
|
47
|
+
# * **returns**: `String` to print on screen
|
48
|
+
def to_s
|
49
|
+
"sin(#{@x})"
|
50
|
+
end
|
51
|
+
|
52
|
+
# Simplification callback. It simplify the subgraph of each node
|
53
|
+
# until all possible simplification are performed (thus the execution
|
54
|
+
# time is not deterministic).
|
55
|
+
#
|
56
|
+
# * **returns**: `CAS::Op` simplified version
|
57
|
+
def simplify
|
58
|
+
super
|
59
|
+
return @x.x if @x.is_a? CAS::Asin
|
60
|
+
return self.simplify_dictionary
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.init_simplify_dict
|
64
|
+
@simplify_dict = {
|
65
|
+
CAS::Zero => CAS::Zero,
|
66
|
+
CAS::Pi => CAS::Zero,
|
67
|
+
CAS::Pi/2 => CAS::One
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
# Convert expression to code (internal, for `CAS::Op#to_proc` method)
|
72
|
+
#
|
73
|
+
# * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
|
74
|
+
def to_code
|
75
|
+
"Math::sin(#{@x.to_code})"
|
76
|
+
end
|
77
|
+
end # Sin
|
78
|
+
CAS::Sin.init_simplify_dict
|
79
|
+
|
80
|
+
# Shortcut for `CAS::Sin#new`
|
81
|
+
#
|
82
|
+
# * **argument**: `CAS::Op` argument of the function
|
83
|
+
# * **returns**: `CAS::Sin` operation
|
84
|
+
def self.sin(x)
|
85
|
+
CAS::Sin.new x
|
86
|
+
end
|
87
|
+
|
88
|
+
# _ _
|
89
|
+
# /_\ __(_)_ _
|
90
|
+
# / _ \ (_-< | ' \
|
91
|
+
# /_/ \_\/__/_|_||_|
|
92
|
+
|
93
|
+
##
|
94
|
+
# Representation for the `arcsin(x)` function. It is implemented
|
95
|
+
# as a `CAS::Op`. It is the inverse of the `sin(x)` function
|
96
|
+
class Asin < CAS::Op
|
97
|
+
# Return the derivative of the `arcsin(x)` function using the chain
|
98
|
+
# rule. The input is a `CAS::Op`
|
99
|
+
#
|
100
|
+
# * **argument**: `CAS::Op` object of the derivative
|
101
|
+
# * **returns**: `CAS::Op` a derivated object, or `CAS::Zero` for constants
|
102
|
+
def diff(v)
|
103
|
+
if @x.depend? v
|
104
|
+
return @x.diff(v) / CAS.sqrt(CAS::One - CAS.pow(@x, CAS::Two))
|
105
|
+
else
|
106
|
+
return CAS::Zero
|
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
|
114
|
+
#
|
115
|
+
# * **argument**: `Hash` with feed dictionary
|
116
|
+
# * **returns**: `Numeric`
|
117
|
+
def call(f)
|
118
|
+
CAS::Help.assert(f, Hash)
|
119
|
+
Math::acos(@x.call(f))
|
120
|
+
end
|
121
|
+
|
122
|
+
# Convert expression to string
|
123
|
+
#
|
124
|
+
# * **returns**: `String` to print on screen
|
125
|
+
def to_s
|
126
|
+
"asin(#{@x})"
|
127
|
+
end
|
128
|
+
|
129
|
+
# Simplification callback. It simplify the subgraph of each node
|
130
|
+
# until all possible simplification are performed (thus the execution
|
131
|
+
# time is not deterministic).
|
132
|
+
#
|
133
|
+
# * **returns**: `CAS::Op` simplified version
|
134
|
+
def simplify
|
135
|
+
super
|
136
|
+
return @x.x if @x.is_a? CAS::Sin
|
137
|
+
return self.simplify_dictionary
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.init_simplify_dict
|
141
|
+
@simplify_dict = {
|
142
|
+
CAS::Zero => CAS::Zero,
|
143
|
+
CAS::One => (CAS::Pi / 2)
|
144
|
+
}
|
145
|
+
end
|
146
|
+
|
147
|
+
# Convert expression to code (internal, for `CAS::Op#to_proc` method)
|
148
|
+
#
|
149
|
+
# * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
|
150
|
+
def to_code
|
151
|
+
"Math::asin(#{@x.to_code})"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
CAS::Asin.init_simplify_dict
|
155
|
+
|
156
|
+
class << self
|
157
|
+
# Shortcuts for `CAS::Asin#new`
|
158
|
+
#
|
159
|
+
# * **argument**: `CAS::Op` argument of the function
|
160
|
+
# * **returns**: `CAS::Asin` operation
|
161
|
+
def asin(x)
|
162
|
+
CAS::Asin.new x
|
163
|
+
end
|
164
|
+
alias :arcsin :asin
|
165
|
+
end
|
166
|
+
|
167
|
+
# ___
|
168
|
+
# / __|___ ___
|
169
|
+
# | (__/ _ (_-<
|
170
|
+
# \___\___/__/
|
171
|
+
|
172
|
+
##
|
173
|
+
# Representation for the `cos(x)` function. It is implemented
|
174
|
+
# as a `CAS::Op`.
|
175
|
+
class Cos < CAS::Op
|
176
|
+
# Return the derivative of the `cos(x)` function using the chain
|
177
|
+
# rule. The input is a `CAS::Op` because it can handle derivatives
|
178
|
+
# with respect to functions.
|
179
|
+
#
|
180
|
+
# ```
|
181
|
+
# d
|
182
|
+
# -- cos(f(x)) = -f'(x) sin(fx)
|
183
|
+
# dx
|
184
|
+
# ```
|
185
|
+
#
|
186
|
+
# * **argument**: `CAS::Op` object of the derivative
|
187
|
+
# * **returns**: `CAS::Op` a derivated object, or `CAS::Zero` for constants
|
188
|
+
def diff(v)
|
189
|
+
if @x.depend? v
|
190
|
+
return CAS.invert(@x.diff(v) * CAS.sin(@x))
|
191
|
+
else
|
192
|
+
return CAS::Zero
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# Call resolves the operation tree in a `Numeric` (if `Fixnum`)
|
197
|
+
# or `Float` (depends upon promotions).
|
198
|
+
# As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
|
199
|
+
# as keys, and a `Numeric` as a value
|
200
|
+
#
|
201
|
+
# * **argument**: `Hash` with feed dictionary
|
202
|
+
# * **returns**: `Numeric`
|
203
|
+
def call(f)
|
204
|
+
CAS::Help.assert(f, Hash)
|
205
|
+
Math::cos(@x.call(f))
|
206
|
+
end
|
207
|
+
|
208
|
+
# Convert expression to string
|
209
|
+
#
|
210
|
+
# * **returns**: `String` to print on screen
|
211
|
+
def to_s
|
212
|
+
"cos(#{@x})"
|
213
|
+
end
|
214
|
+
|
215
|
+
# Simplification callback. It simplify the subgraph of each node
|
216
|
+
# until all possible simplification are performed (thus the execution
|
217
|
+
# time is not deterministic).
|
218
|
+
#
|
219
|
+
# * **returns**: `CAS::Op` simplified version
|
220
|
+
def simplify
|
221
|
+
super
|
222
|
+
return @x.x if @x.is_a? CAS::Acos
|
223
|
+
return self.simplify_dictionary
|
224
|
+
end
|
225
|
+
|
226
|
+
def self.init_simplify_dict
|
227
|
+
@simplify_dict = {
|
228
|
+
CAS::Zero => CAS::One,
|
229
|
+
CAS::Pi => CAS::One,
|
230
|
+
CAS::Pi/2 => CAS::Zero
|
231
|
+
}
|
232
|
+
end
|
233
|
+
|
234
|
+
# Convert expression to code (internal, for `CAS::Op#to_proc` method)
|
235
|
+
#
|
236
|
+
# * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
|
237
|
+
def to_code
|
238
|
+
"Math::cos(#{@x.to_code})"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
CAS::Cos.init_simplify_dict
|
242
|
+
|
243
|
+
# Shortcut for `CAS::Cos#new`
|
244
|
+
#
|
245
|
+
# * **argument**: `CAS::Op` argument of the function
|
246
|
+
# * **returns**: `CAS::Cos` operation
|
247
|
+
def self.cos(x)
|
248
|
+
CAS::Cos.new x
|
249
|
+
end
|
250
|
+
|
251
|
+
# _
|
252
|
+
# /_\ __ ___ ___
|
253
|
+
# / _ \/ _/ _ (_-<
|
254
|
+
# /_/ \_\__\___/__/
|
255
|
+
|
256
|
+
##
|
257
|
+
# Representation for the `arccos(x)` function. It is implemented
|
258
|
+
# as a `CAS::Op`. It is the inverse of the `cos(x)` function
|
259
|
+
class Acos < CAS::Op
|
260
|
+
def diff(v)
|
261
|
+
if @x.depend? v
|
262
|
+
return CAS.invert(@x.diff(v)/CAS.sqrt(CAS::One - CAS.pow(@x, CAS::Two)))
|
263
|
+
else
|
264
|
+
return CAS::Zero
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# Call resolves the operation tree in a `Numeric` (if `Fixnum`)
|
269
|
+
# or `Float` (depends upon promotions).
|
270
|
+
# As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
|
271
|
+
# as keys, and a `Numeric` as a value
|
272
|
+
#
|
273
|
+
# * **argument**: `Hash` with feed dictionary
|
274
|
+
# * **returns**: `Numeric`
|
275
|
+
def call(f)
|
276
|
+
CAS::Help.assert(f, Hash)
|
277
|
+
return Math::acos(@x.call(f))
|
278
|
+
end
|
279
|
+
|
280
|
+
# Convert expression to string
|
281
|
+
#
|
282
|
+
# * **returns**: `String` to print on screen
|
283
|
+
def to_s
|
284
|
+
"acos(#{@x})"
|
285
|
+
end
|
286
|
+
|
287
|
+
# Simplification callback. It simplify the subgraph of each node
|
288
|
+
# until all possible simplification are performed (thus the execution
|
289
|
+
# time is not deterministic).
|
290
|
+
#
|
291
|
+
# * **returns**: `CAS::Op` simplified version
|
292
|
+
def simplify
|
293
|
+
super
|
294
|
+
return @x.x if @x.is_a? CAS::Cos
|
295
|
+
return self.simplify_dictionary
|
296
|
+
end
|
297
|
+
|
298
|
+
def self.init_simplify_dict
|
299
|
+
@simplify_dict = {
|
300
|
+
CAS::Zero => (CAS::Pi / 2),
|
301
|
+
CAS::One => CAS::Zero
|
302
|
+
}
|
303
|
+
end
|
304
|
+
|
305
|
+
# Convert expression to code (internal, for `CAS::Op#to_proc` method)
|
306
|
+
#
|
307
|
+
# * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
|
308
|
+
def to_code
|
309
|
+
"Math::acos(#{@x.to_code})"
|
310
|
+
end
|
311
|
+
end
|
312
|
+
CAS::Acos.init_simplify_dict
|
313
|
+
|
314
|
+
class << self
|
315
|
+
# Shortcut for `CAS::Acos#new`
|
316
|
+
#
|
317
|
+
# * **argument**: `CAS::Op` argument of the function
|
318
|
+
# * **returns**: `CAS::Acos` operation
|
319
|
+
def acos(x)
|
320
|
+
CAS::Acos.new x
|
321
|
+
end
|
322
|
+
alias :arccos :acos
|
323
|
+
end
|
324
|
+
|
325
|
+
# _____
|
326
|
+
# |_ _|_ _ _ _
|
327
|
+
# | |/ _` | ' \
|
328
|
+
# |_|\__,_|_||_|
|
329
|
+
|
330
|
+
##
|
331
|
+
# Representation for the `tan(x)` function. It is implemented
|
332
|
+
# as a `CAS::Op`.
|
333
|
+
class Tan < CAS::Op
|
334
|
+
# Return the derivative of the `tan(x)` function using the chain
|
335
|
+
# rule. The input is a `CAS::Op` because it can handle derivatives
|
336
|
+
# with respect to functions. E.g.:
|
337
|
+
#
|
338
|
+
# ```
|
339
|
+
# d f'(x)
|
340
|
+
# -- sin(f(x)) = -------
|
341
|
+
# dx cos²(x)
|
342
|
+
# ```
|
343
|
+
#
|
344
|
+
# * **argument**: `CAS::Op` object of the derivative
|
345
|
+
# * **returns**: `CAS::Op` a derivated object, or `CAS::Zero` for constants
|
346
|
+
def diff(v)
|
347
|
+
if @x.depend? v
|
348
|
+
return @x.diff(v) * CAS.pow(CAS::One/CAS.cos(@x), CAS::Two)
|
349
|
+
else
|
350
|
+
return CAS::Zero
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
# Call resolves the operation tree in a `Numeric` (if `Fixnum`)
|
355
|
+
# or `Float` (depends upon promotions).
|
356
|
+
# As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
|
357
|
+
# as keys, and a `Numeric` as a value
|
358
|
+
#
|
359
|
+
# * **argument**: `Hash` with feed dictionary
|
360
|
+
# * **returns**: `Numeric`
|
361
|
+
def call(f)
|
362
|
+
CAS::Help.assert(f, Hash)
|
363
|
+
Math::tan(@x.call(f))
|
364
|
+
end
|
365
|
+
|
366
|
+
# Convert expression to string
|
367
|
+
#
|
368
|
+
# * **returns**: `String` to print on screen
|
369
|
+
def to_s
|
370
|
+
"tan(#{@x})"
|
371
|
+
end
|
372
|
+
|
373
|
+
# Simplification callback. It simplify the subgraph of each node
|
374
|
+
# until all possible simplification are performed (thus the execution
|
375
|
+
# time is not deterministic).
|
376
|
+
#
|
377
|
+
# * **returns**: `CAS::Op` simplified version
|
378
|
+
def simplify
|
379
|
+
super
|
380
|
+
return @x.x if @x.is_a? CAS::Atan
|
381
|
+
return self.simplify_dictionary
|
382
|
+
end
|
383
|
+
|
384
|
+
def self.init_simplify_dict
|
385
|
+
@simplify_dict = {
|
386
|
+
CAS::Zero => CAS::Zero,
|
387
|
+
CAS::Pi => CAS::Zero,
|
388
|
+
CAS::Pi/2 => CAS::Infinity
|
389
|
+
}
|
390
|
+
end
|
391
|
+
|
392
|
+
# Convert expression to code (internal, for `CAS::Op#to_proc` method)
|
393
|
+
#
|
394
|
+
# * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
|
395
|
+
def to_code
|
396
|
+
"Math::tan(#{@x.to_code})"
|
397
|
+
end
|
398
|
+
end
|
399
|
+
CAS::Tan.init_simplify_dict
|
400
|
+
|
401
|
+
# Shortcut for `CAS::Tan#new`
|
402
|
+
#
|
403
|
+
# * **argument**: `CAS::Op` argument of the function
|
404
|
+
# * **returns**: `CAS::Tan` operation
|
405
|
+
def self.tan(x)
|
406
|
+
CAS::Tan.new x
|
407
|
+
end
|
408
|
+
|
409
|
+
# _ _
|
410
|
+
# /_\| |_ __ _ _ _
|
411
|
+
# / _ \ _/ _` | ' \
|
412
|
+
# /_/ \_\__\__,_|_||_|
|
413
|
+
|
414
|
+
##
|
415
|
+
# Representation for the `arctan(x)` function. It is implemented
|
416
|
+
# as a `CAS::Op`. It is the inverse of the `tan(x)` function
|
417
|
+
class Atan < CAS::Op
|
418
|
+
# Return the derivative of the `arctan(x)` function using the chain
|
419
|
+
# rule. The input is a `CAS::Op` because it can handle derivatives
|
420
|
+
# with respect to functions.
|
421
|
+
#
|
422
|
+
# * **argument**: `CAS::Op` object of the derivative
|
423
|
+
# * **returns**: `CAS::Op` a derivated object, or `CAS::Zero` for constants
|
424
|
+
def diff(v)
|
425
|
+
if @x.depend? v
|
426
|
+
return (@x.diff(v) / (CAS.pow(@x, CAS::Two) + CAS::One))
|
427
|
+
else
|
428
|
+
return CAS::Zero
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
# Call resolves the operation tree in a `Numeric` (if `Fixnum`)
|
433
|
+
# or `Float` (depends upon promotions).
|
434
|
+
# As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
|
435
|
+
# as keys, and a `Numeric` as a value
|
436
|
+
#
|
437
|
+
# * **argument**: `Hash` with feed dictionary
|
438
|
+
# * **returns**: `Numeric`
|
439
|
+
def call(f)
|
440
|
+
CAS::Help.assert(f, Hash)
|
441
|
+
Math::atan(@x.call(f))
|
442
|
+
end
|
443
|
+
|
444
|
+
# Convert expression to string
|
445
|
+
#
|
446
|
+
# * **returns**: `String` to print on screen
|
447
|
+
def to_s
|
448
|
+
"atan(#{@x})"
|
449
|
+
end
|
450
|
+
|
451
|
+
# Simplification callback. It simplify the subgraph of each node
|
452
|
+
# until all possible simplification are performed (thus the execution
|
453
|
+
# time is not deterministic).
|
454
|
+
#
|
455
|
+
# * **returns**: `CAS::Op` simplified version
|
456
|
+
def simplify
|
457
|
+
super
|
458
|
+
return @x.x if @x.is_a? CAS::Tan
|
459
|
+
return self.simplify_dictionary
|
460
|
+
end
|
461
|
+
|
462
|
+
def self.init_simplify_dict
|
463
|
+
@simplify_dict = {
|
464
|
+
CAS::Zero => CAS::Zero,
|
465
|
+
CAS::One => (CAS::Pi/4),
|
466
|
+
CAS::Infinity => (CAS::Pi/2)
|
467
|
+
}
|
468
|
+
end
|
469
|
+
|
470
|
+
# Convert expression to code (internal, for `CAS::Op#to_proc` method)
|
471
|
+
#
|
472
|
+
# * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
|
473
|
+
def to_code
|
474
|
+
"Math::atan(#{@x.to_code})"
|
475
|
+
end
|
476
|
+
end
|
477
|
+
CAS::Atan.init_simplify_dict
|
478
|
+
|
479
|
+
class << self
|
480
|
+
# Shortcut for `CAS::Atan#new`
|
481
|
+
#
|
482
|
+
# * **argument**: `CAS::Op` argument of the function
|
483
|
+
# * **returns**: `CAS::Atan` operation
|
484
|
+
def atan(x)
|
485
|
+
CAS::Atan.new x
|
486
|
+
end
|
487
|
+
alias :arctan :atan
|
488
|
+
end
|
489
|
+
end
|