ragni-cas 0.2.5 → 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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +1 -2
- data/lib/{ragni-cas.rb → Mr.CAS.rb} +3 -2
- 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/{ragni-cas/plugins → Mr.CAS}/graphviz.rb +0 -0
- data/lib/{ragni-cas/plugins → Mr.CAS}/latex.rb +0 -0
- data/lib/Mr.CAS/matlab.rb +81 -0
- data/lib/functions/fnc-base.rb +1 -157
- data/lib/functions/fnc-prod.rb +102 -0
- data/lib/functions/fnc-sum.rb +151 -0
- data/lib/numbers/functions.rb +27 -18
- data/lib/operators/bary-op.rb +14 -13
- data/lib/operators/nary-op.rb +66 -7
- data/lib/operators/op.rb +8 -7
- data/lib/version.rb +1 -2
- metadata +13 -7
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 435c49ab6e4a5cbd771acfc5c6afc16a5334141b
|
4
|
+
data.tar.gz: 1a94331f8b29fc383ab7f411ff065ff2d9230a22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d56a2fe673ef2e00b4574a39f683050c034838af81a5b822e0d8c6b87288ef09a5ad40848d57fcb2d5e81ffe7e32f1ebbb05e159eb3af283b7ce1bb34cf2c6e3
|
7
|
+
data.tar.gz: ff85830bb67960b656e31a3b6579993f6e40211ad06a3894223029bd99daf4a29210b94053880a9bcaf3c2f2201c76a8e78ed2d9ae753807a6cb8ad510beca4e
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
@@ -1,2 +1 @@
|
|
1
|
-
|
2
|
-
�֘�� �dT ܦ%��ͱW�O0�Qm���q��bbVY`��}���b��`����悔�~�g��n>A5�]�p�K)��;�������
|
1
|
+
Sk���q�8~t��6c�i��e�;�tE���m�H��Z��T`�IZ�V�����s�`����m����.��r��L��g��m~�+mE��@"��pC�qD
|
@@ -1,8 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
##
|
4
|
-
#
|
5
|
-
# A
|
4
|
+
# Mr.CAS
|
5
|
+
# A minmalistic CAS engine with encapsuled graph
|
6
6
|
# representation. This will make impossible to
|
7
7
|
# perform complex high level simplifications, but
|
8
8
|
# it is powerful enough to define simple algorithm
|
@@ -58,6 +58,7 @@ end
|
|
58
58
|
|
59
59
|
%w|operators/op.rb operators/bary-op.rb operators/nary-op.rb
|
60
60
|
numbers/constants.rb numbers/variables.rb numbers/functions.rb
|
61
|
+
functions/fnc-sum.rb functions/fnc-prod.rb
|
61
62
|
functions/fnc-base.rb functions/fnc-trig.rb functions/fnc-trsc.rb
|
62
63
|
functions/fnc-conditions.rb functions/fnc-box-conditions.rb functions/fnc-piecewise.rb
|
63
64
|
overloading/fixnum.rb overloading/float.rb
|
@@ -0,0 +1,129 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module CAS
|
4
|
+
module AutoDiff
|
5
|
+
class DualNumber
|
6
|
+
include Math
|
7
|
+
attr_reader :x, :y
|
8
|
+
|
9
|
+
def initialize(x, y)
|
10
|
+
@x, @y = x, y
|
11
|
+
end
|
12
|
+
|
13
|
+
def +(v)
|
14
|
+
DualNumber.new @x + v.x, @y + v.y
|
15
|
+
end
|
16
|
+
|
17
|
+
def -(v)
|
18
|
+
DualNumber.new @x - v.x, @y - v.y
|
19
|
+
end
|
20
|
+
|
21
|
+
def *(v)
|
22
|
+
DualNumber.new @x * v.x, @y * v.x + @x * v.y
|
23
|
+
end
|
24
|
+
|
25
|
+
def /(v)
|
26
|
+
DualNumber.new @x / v.x, (@y * v.x - @x * v.y) / (v.x ** 2)
|
27
|
+
end
|
28
|
+
|
29
|
+
def -@
|
30
|
+
DualNumber.new -@x, -@y
|
31
|
+
end
|
32
|
+
|
33
|
+
def **(v)
|
34
|
+
t = (v.y == 0 ? 0 : @x * log(@x) * v.y)
|
35
|
+
DualNumber.new @x ** v.x, (@x ** (v.x - 1)) * (v.x * @y + t)
|
36
|
+
end
|
37
|
+
|
38
|
+
def abs
|
39
|
+
return DualNumber.new(0, 0) if @x == 0
|
40
|
+
DualNumber.new @x.abs, @y * (@x <=> 0)
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s; self.inspect; end
|
44
|
+
def inspect; "<#{@x},#{@y}>"; end
|
45
|
+
def real; @x; end
|
46
|
+
def diff; @y; end
|
47
|
+
end # DualNumbers
|
48
|
+
|
49
|
+
def self.const(x)
|
50
|
+
DualNumber.new x, 0
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.vars(x)
|
54
|
+
DualNumber.new x, 1
|
55
|
+
end
|
56
|
+
|
57
|
+
Zero = self.const 0
|
58
|
+
One = self.const 1
|
59
|
+
Two = self.const 2
|
60
|
+
E = self.const Math::E
|
61
|
+
Pi = self.const Math::PI
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
{
|
66
|
+
# Terminal nodes
|
67
|
+
CAS::Variable => Proc.new { |fd| CAS::AutoDiff.vars (fd[CAS::Variable[@name]] || fd[@name]) },
|
68
|
+
CAS::Constant => Proc.new { |_fd| CAS::AutoDiff.const @x },
|
69
|
+
CAS::Function => Proc.new { |_fd| raise RuntimeError, "Impossible for implicit functions" },
|
70
|
+
|
71
|
+
# Base functions
|
72
|
+
CAS::Sum => Proc.new { |fd| @x.auto_diff(fd) + @y.auto_diff(fd) },
|
73
|
+
CAS::Diff => Proc.new { |fd| @x.auto_diff(fd) - @y.auto_diff(fd) },
|
74
|
+
CAS::Prod => Proc.new { |fd| @x.auto_diff(fd) * @y.auto_diff(fd) },
|
75
|
+
CAS::Pow => Proc.new { |fd| @x.auto_diff(fd) ** @y.auto_diff(fd) },
|
76
|
+
CAS::Div => Proc.new { |fd| @x.auto_diff(fd) / @y.auto_diff(fd) },
|
77
|
+
CAS::Sqrt => Proc.new { |fd| @x.auto_diff(fd) ** (CAS::AutoDiff::One / CAS::AutoDiff::Two) },
|
78
|
+
CAS::Invert => Proc.new { |fd| -@x.auto_diff(fd) },
|
79
|
+
CAS::Abs => Proc.new { |fd| @x.auto_diff(fd).abs },
|
80
|
+
|
81
|
+
# Trigonometric functions
|
82
|
+
CAS::Sin => Proc.new { |fd|
|
83
|
+
u = @x.auto_diff(fd)
|
84
|
+
CAS::AutoDiff::DualNumber.new Math.sin(u.x), Math.cos(u.x) * u.y
|
85
|
+
},
|
86
|
+
CAS::Asin => Proc.new { |fd|
|
87
|
+
u = @x.auto_diff(fd)
|
88
|
+
CAS::AutoDiff::DualNumber.new Math.asin(u.x), -(Math.sin(u.x) * u.y)
|
89
|
+
},
|
90
|
+
CAS::Cos => Proc.new { |fd|
|
91
|
+
u = @x.auto_diff(fd)
|
92
|
+
CAS::AutoDiff::DualNumber.new Math.cos(u.x), -(Math.sin(u.x) * u.y)
|
93
|
+
},
|
94
|
+
CAS::Acos => Proc.new { |fd|
|
95
|
+
u = @x.auto_diff(fd)
|
96
|
+
CAS::AutoDiff::DualNumber.new Math.acos(u.x), -u.y / Math.sqrt(1 + u.x ** 2)
|
97
|
+
},
|
98
|
+
CAS::Tan => Proc.new { |fd|
|
99
|
+
u = @x.auto_diff(fd)
|
100
|
+
CAS::AutoDiff::DualNumber.new Math.tan(u.x), u.y / (Math.cos(u.x) ** 2)
|
101
|
+
},
|
102
|
+
CAS::Atan => Proc.new { |fd|
|
103
|
+
u = @x.auto_diff(fd)
|
104
|
+
CAS::AutoDiff::DualNumber.new Math.atan(u.x), u.y / (1 + u.x ** 2)
|
105
|
+
},
|
106
|
+
|
107
|
+
# Trascendent functions
|
108
|
+
CAS::Exp => Proc.new { |fd| CAS::AutoDiff::E ** @x.auto_diff(fd) },
|
109
|
+
CAS::Ln => Proc.new { |fd|
|
110
|
+
u = @x.auto_diff(fd)
|
111
|
+
CAS::AutoDiff::DualNumber.new Math.log(u.x), u.y / u.x
|
112
|
+
},
|
113
|
+
|
114
|
+
# Piecewise
|
115
|
+
CAS::Piecewise => Proc.new { |_fd| raise RuntimeError, "Not implemented auto_diff for Piecewise" },
|
116
|
+
CAS::Max => Proc.new { |fd|
|
117
|
+
a = @x.auto_diff(fd)
|
118
|
+
b = @y.auto_diff(fd)
|
119
|
+
(a.x >= b.x ? a : b)
|
120
|
+
},
|
121
|
+
CAS::Min => Proc.new { |fd|
|
122
|
+
a = @x.auto_diff(fd)
|
123
|
+
b = @y.auto_diff(fd)
|
124
|
+
(a.x >= b.x ? a : b)
|
125
|
+
}
|
126
|
+
}.each do |cls, blk|
|
127
|
+
cls.send(:define_method, "auto_diff", &blk)
|
128
|
+
end
|
129
|
+
end
|
data/lib/Mr.CAS/c-opt.rb
ADDED
@@ -0,0 +1,225 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# ___ ___ _ _
|
4
|
+
# / __| | _ \ |_ _ __ _(_)_ _
|
5
|
+
# | (__ | _/ | || / _` | | ' \
|
6
|
+
# \___| |_| |_|\_,_\__, |_|_||_|
|
7
|
+
# |___/
|
8
|
+
|
9
|
+
require 'pry'
|
10
|
+
|
11
|
+
module CAS
|
12
|
+
class CLib < Hash
|
13
|
+
def self.create(name, &blk)
|
14
|
+
a = CLib.new(name)
|
15
|
+
a.instance_eval(&blk)
|
16
|
+
return a
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(name)
|
20
|
+
raise ArgumentError, "Name for the library undefined" unless name.is_a? String
|
21
|
+
@name = name
|
22
|
+
@define = {}
|
23
|
+
@include = {}
|
24
|
+
@include[:std] = []
|
25
|
+
@include[:local] = []
|
26
|
+
@type = "double"
|
27
|
+
|
28
|
+
# Default definitions
|
29
|
+
# self.define "M_PI", Math::PI
|
30
|
+
# self.define "M_INFINITY","HUGE_VAL"
|
31
|
+
# self.define "M_E", Math::E
|
32
|
+
# self.define "M_EPSILON", 1E-16
|
33
|
+
|
34
|
+
# Default inclusions
|
35
|
+
self.include "math.h"
|
36
|
+
end
|
37
|
+
|
38
|
+
def as_double; @type = "double"; end
|
39
|
+
def as_float; @type = "float"; end
|
40
|
+
def as_int; @type = "int"; end
|
41
|
+
def as_long_int; @type = "long int"; end
|
42
|
+
|
43
|
+
def define(k, v)
|
44
|
+
raise ArgumentError, "k must be a String, received a #{k.class}" unless k.is_a? String
|
45
|
+
@define[k] = v.to_s
|
46
|
+
@define
|
47
|
+
end
|
48
|
+
|
49
|
+
def undefine(k)
|
50
|
+
@define.delete k
|
51
|
+
return @define
|
52
|
+
end
|
53
|
+
|
54
|
+
def include_type(type, lib)
|
55
|
+
raise ArgumentError, "type must be a Symbol (:std, :local), received #{type}" unless [:std, :local].include? type
|
56
|
+
raise ArgumentError, "lib must be a String, received a #{lib.class}" unless lib.is_a? String
|
57
|
+
@include[type] << lib unless @include[type].include? lib
|
58
|
+
end
|
59
|
+
def include(lib); self.include_type(:std, lib); end
|
60
|
+
def include_local(lib); self.include_type(:local, lib); end
|
61
|
+
|
62
|
+
def implements_as(name, op); self[name] = op; end
|
63
|
+
|
64
|
+
def header
|
65
|
+
<<-TO_HEADER
|
66
|
+
// Header file for library: #{@name}.c
|
67
|
+
|
68
|
+
#ifndef #{@name}_H
|
69
|
+
#define #{@name}_H
|
70
|
+
|
71
|
+
// Standard Libraries
|
72
|
+
#{ @include[:std].map { |e| "#include <#{e}>" }.join("\n") }
|
73
|
+
|
74
|
+
// Local Libraries
|
75
|
+
#{ @include[:local].map { |e| "#include \"#{e}\"" }.join("\n") }
|
76
|
+
|
77
|
+
// Definitions
|
78
|
+
#{ @define.map { |k, v| "#define #{k} #{v}" }.join("\n") }
|
79
|
+
|
80
|
+
// Functions
|
81
|
+
#{
|
82
|
+
self.keys.map do |fname|
|
83
|
+
"#{@type} #{fname}(#{ self[fname].args.map { |x| "#{@type} #{x.name}" }.join(", ")});"
|
84
|
+
end.join("\n")
|
85
|
+
}
|
86
|
+
|
87
|
+
#endif // #{@name}_H
|
88
|
+
TO_HEADER
|
89
|
+
end
|
90
|
+
|
91
|
+
def source
|
92
|
+
functions = []
|
93
|
+
|
94
|
+
self.each do |fname, op|
|
95
|
+
c_op = op.to_c.sort_by { |_k, c| c[:id] }
|
96
|
+
nf = <<-NEWFUNCTION
|
97
|
+
#{@type} #{fname}(#{ op.args.map { |x| "#{@type} #{x.name}" }.join(", ")}) {
|
98
|
+
#{c_op.map { |e| " double #{e[1][:var]} = #{e[1][:def]};"}.join("\n")}
|
99
|
+
|
100
|
+
return #{c_op[-1][1][:var]};
|
101
|
+
}
|
102
|
+
NEWFUNCTION
|
103
|
+
functions << nf
|
104
|
+
end
|
105
|
+
|
106
|
+
<<-TO_SOURCE
|
107
|
+
// Source file for library: #{@name}.c
|
108
|
+
|
109
|
+
#include "#{@name}.h"
|
110
|
+
|
111
|
+
#{functions.join("\n")}
|
112
|
+
// end of #{@name}.c
|
113
|
+
TO_SOURCE
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
{
|
118
|
+
# Terminal nodes
|
119
|
+
CAS::Constant => Proc.new { |_v| "#{@x}" },
|
120
|
+
CAS::Variable => Proc.new { |_v| "#{@name}" },
|
121
|
+
CAS::PI_CONSTANT => Proc.new { |_v| "M_PI" },
|
122
|
+
CAS::INFINITY_CONSTANT => Proc.new { |_v| "M_INFINITY" },
|
123
|
+
CAS::NEG_INFINITY_CONSTANT => Proc.new { |_v| "(-M_INFINITY)" },
|
124
|
+
CAS::E_CONSTANT => Proc.new { |_v| "M_E" }
|
125
|
+
}.each do |cls, blk|
|
126
|
+
cls.send(:define_method, "__to_c", &blk)
|
127
|
+
end.each do |cls, _blk|
|
128
|
+
cls.send(:define_method, "to_c", &Proc.new do
|
129
|
+
v = {}; self.__to_c(v); v
|
130
|
+
end)
|
131
|
+
end
|
132
|
+
|
133
|
+
{
|
134
|
+
# Base functions
|
135
|
+
CAS::Sum => Proc.new { |v| "(#{@x.map { |e| e.__to_c(v) }.join(" + ")})" },
|
136
|
+
CAS::Diff => Proc.new { |v| "(#{@x.__to_c(v)} - #{@y.__to_c(v)})" },
|
137
|
+
CAS::Prod => Proc.new { |v| "(#{@x.map { |e| e.__to_c(v) }.join(" + ")})" },
|
138
|
+
CAS::Pow => Proc.new { |v| "pow(#{@x.__to_c(v)}, #{@y.__to_c(v)})" },
|
139
|
+
CAS::Div => Proc.new { |v| "(#{@x.__to_c(v)}) / (#{@y.__to_c(v)} + M_EPSILON)" },
|
140
|
+
CAS::Sqrt => Proc.new { |v| "sqrt(#{@x.__to_c(v)})" },
|
141
|
+
CAS::Invert => Proc.new { |v| "(-#{@x.__to_c(v)})" },
|
142
|
+
CAS::Abs => Proc.new { |v| "fabs(#{@x.__to_c(v)})" },
|
143
|
+
|
144
|
+
# Trigonometric functions
|
145
|
+
CAS::Sin => Proc.new { |v| "sin(#{@x.__to_c(v)})" },
|
146
|
+
CAS::Asin => Proc.new { |v| "asin(#{@x.__to_c(v)})" },
|
147
|
+
CAS::Cos => Proc.new { |v| "cos(#{@x.__to_c(v)})" },
|
148
|
+
CAS::Acos => Proc.new { |v| "acos(#{@x.__to_c(v)})" },
|
149
|
+
CAS::Tan => Proc.new { |v| "tan(#{@x.__to_c(v)})" },
|
150
|
+
CAS::Atan => Proc.new { |v| "atan(#{@x.__to_c(v)})" },
|
151
|
+
|
152
|
+
# Trascendent functions
|
153
|
+
CAS::Exp => Proc.new { |v| "exp(#{@x.__to_c(v)})" },
|
154
|
+
CAS::Ln => Proc.new { |v| "log(#{@x.__to_c(v)})" },
|
155
|
+
|
156
|
+
# # Box Conditions
|
157
|
+
# CAS::BoxConditionOpen => Proc.new {
|
158
|
+
# ["double __t_#{x.object_id} = #{x.__to_c(v)};",
|
159
|
+
# "(__t_#{x.object_id} > #{lower.latex} && __t_#{x.object_id} < #{upper.latex})"]
|
160
|
+
# },
|
161
|
+
# CAS::BoxConditionUpperClosed => Proc.new {
|
162
|
+
# ["double __t_#{x.object_id} = #{x.__to_c(v)};",
|
163
|
+
# "(__t_#{x.object_id} > #{lower.latex} && __t_#{x.object_id} <= #{upper.latex})"]
|
164
|
+
# },
|
165
|
+
# CAS::BoxConditionLowerClosed => Proc.new {
|
166
|
+
# ["double __t_#{x.object_id} = #{x.__to_c(v)};",
|
167
|
+
# "(__t_#{x.object_id} >= #{lower.latex} && __t_#{x.object_id} < #{upper.latex})"]
|
168
|
+
# },
|
169
|
+
# CAS::BoxConditionClosed => Proc.new {
|
170
|
+
# ["double __t_#{x.object_id} = #{x.__to_c(v)};",
|
171
|
+
# "(__t_#{x.object_id} >= #{lower.latex} && __t_#{x.object_id} <= #{upper.latex})"]
|
172
|
+
# },
|
173
|
+
|
174
|
+
# Conditions
|
175
|
+
# CAS::Equal => Proc.new { "(#{x.__to_c(v)} == #{y.__to_c(v)})" },
|
176
|
+
# CAS::Smaller => Proc.new { "(#{x.__to_c(v)} < #{y.__to_c(v)})" },
|
177
|
+
# CAS::Greater => Proc.new { "(#{x.__to_c(v)} > #{y.__to_c(v)})" },
|
178
|
+
# CAS::SmallerEqual => Proc.new { "(#{x.__to_c(v)} <= #{y.__to_c(v)})" },
|
179
|
+
# CAS::GreaterEqual => Proc.new { "(#{x.__to_c(v)} >= #{y.__to_c(v)})" },
|
180
|
+
# TODO there is no sense in exporting conditions in C...
|
181
|
+
|
182
|
+
# Piecewise
|
183
|
+
# CAS::Piecewise => Proc.new { raise CASError, "Not implemented yet" },
|
184
|
+
# CAS::Max => Proc.new { raise CASError, "Not implemented yet" },
|
185
|
+
# CAS::Min => Proc.new { raise CASError, "Not implemented yet" }
|
186
|
+
CAS::Function => Proc.new { |v| "#{@c_name}(#{@x.map {|e| e.__to_c(v)}.join(", ")})" }
|
187
|
+
}.each do |cls, blk|
|
188
|
+
cls.send(:define_method, "__to_c_impl", &blk)
|
189
|
+
end.each do |cls, _blk|
|
190
|
+
cls.send(:define_method, "__to_c", &Proc.new do |v|
|
191
|
+
sym = self.to_s.to_sym
|
192
|
+
v[sym] = {
|
193
|
+
:def => "#{self.__to_c_impl(v)}",
|
194
|
+
:var => "__t_#{v.keys.size}",
|
195
|
+
:id => v.keys.size
|
196
|
+
} unless v[sym] # Pleas note that the order here is foundamental!
|
197
|
+
v[sym][:var]
|
198
|
+
end)
|
199
|
+
end.each do |cls, _blk|
|
200
|
+
cls.send(:define_method, "to_c", &Proc.new do
|
201
|
+
v = {}; self.__to_c(v); v
|
202
|
+
end)
|
203
|
+
end
|
204
|
+
|
205
|
+
class Op
|
206
|
+
def to_c_lib(name)
|
207
|
+
CAS::Help.assert(name, String)
|
208
|
+
[CAS::C_PLUGIN.write_header(self, name), CAS::C_PLUGIN.write_source(self, name)]
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
class Function
|
213
|
+
def c_name=(s)
|
214
|
+
CAS::Help.assert_name s
|
215
|
+
@c_name = s
|
216
|
+
end
|
217
|
+
|
218
|
+
def Function.new(name, *xs)
|
219
|
+
a = super
|
220
|
+
a.c_name = name
|
221
|
+
return a
|
222
|
+
end
|
223
|
+
end
|
224
|
+
Function.list.each do |k, v| v.c_name = k; end
|
225
|
+
end
|
data/lib/Mr.CAS/c.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# ___ ___ _ _
|
4
|
+
# / __| | _ \ |_ _ __ _(_)_ _
|
5
|
+
# | (__ | _/ | || / _` | | ' \
|
6
|
+
# \___| |_| |_|\_,_\__, |_|_||_|
|
7
|
+
# |___/
|
8
|
+
|
9
|
+
module CAS
|
10
|
+
module C_PLUGIN
|
11
|
+
C_STD_LIBRARIES = [
|
12
|
+
"math.h"
|
13
|
+
]
|
14
|
+
|
15
|
+
C_LOCAL_LIBRARIES = [ ]
|
16
|
+
|
17
|
+
C_DEFINES = {
|
18
|
+
"M_PI" => Math::PI.to_s,
|
19
|
+
"M_INFINITY" => "HUGE_VAL",
|
20
|
+
"M_E" => Math::E.to_s,
|
21
|
+
"M_EPSILON" => (1E-16).to_s
|
22
|
+
}
|
23
|
+
|
24
|
+
def self.write_header(op, name)
|
25
|
+
<<-TO_HEADER
|
26
|
+
#ifndef #{name}_HEADER
|
27
|
+
#define #{name}_HEADER
|
28
|
+
|
29
|
+
// Standard Libraries
|
30
|
+
#{ CAS::C_PLUGIN::C_STD_LIBRARIES.map { |e| "#include <#{e}>" }.join("\n") }
|
31
|
+
|
32
|
+
// Local Libraries
|
33
|
+
#{ CAS::C_PLUGIN::C_LOCAL_LIBRARIES.map { |e| "#include <#{e}>" }.join("\n") }
|
34
|
+
|
35
|
+
// Definitions
|
36
|
+
#{ CAS::C_PLUGIN::C_DEFINES.map { |k, v| "#define #{k} #{v}" }.join("\n") }
|
37
|
+
|
38
|
+
// Function
|
39
|
+
double #{name}(#{ op.args.map { |x| "double #{x.name}"}.join(", ") });
|
40
|
+
|
41
|
+
#endif // #{name}_HEADER
|
42
|
+
TO_HEADER
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.write_source(op, name)
|
46
|
+
<<-TO_SOURCE
|
47
|
+
#include "#{name}.h"
|
48
|
+
|
49
|
+
double #{name}(#{ op.args.map { |x| "double #{x.name}"}.join(", ") }) {
|
50
|
+
return #{op.to_c};
|
51
|
+
}
|
52
|
+
TO_SOURCE
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
{
|
57
|
+
# Terminal nodes
|
58
|
+
CAS::Constant => Proc.new { "#{x}" },
|
59
|
+
CAS::Variable => Proc.new { "#{name}" },
|
60
|
+
CAS::PI_CONSTANT => Proc.new { "M_PI" },
|
61
|
+
CAS::INFINITY_CONSTANT => Proc.new { "M_INFINITY" },
|
62
|
+
CAS::NEG_INFINITY_CONSTANT => Proc.new { "(-M_INFINITY)" },
|
63
|
+
CAS::E_CONSTANT => Proc.new { "M_E" },
|
64
|
+
# Base functions
|
65
|
+
CAS::Sum => Proc.new { "(#{x.to_c} + #{y.to_c})" },
|
66
|
+
CAS::Diff => Proc.new { "(#{x.to_c} - #{y.to_c})" },
|
67
|
+
CAS::Prod => Proc.new { "(#{x.to_c} * #{y.to_c})" },
|
68
|
+
CAS::Pow => Proc.new { "pow(#{x.to_c}, #{y.to_c})" },
|
69
|
+
CAS::Div => Proc.new { "(#{x.to_c}) / (#{y.to_c} + )" },
|
70
|
+
CAS::Sqrt => Proc.new { "sqrt(#{x.to_c})" },
|
71
|
+
CAS::Invert => Proc.new { "(-#{x.to_c})" },
|
72
|
+
CAS::Abs => Proc.new { "fabs(#{x.to_c})" },
|
73
|
+
|
74
|
+
# Trigonometric functions
|
75
|
+
CAS::Sin => Proc.new { "sin(#{x.to_c})" },
|
76
|
+
CAS::Asin => Proc.new { "asin(#{x.to_c})" },
|
77
|
+
CAS::Cos => Proc.new { "cos(#{x.to_c})" },
|
78
|
+
CAS::Acos => Proc.new { "acos(#{x.to_c})" },
|
79
|
+
CAS::Tan => Proc.new { "tan(#{x.to_c})" },
|
80
|
+
CAS::Atan => Proc.new { "atan(#{x.to_c})" },
|
81
|
+
|
82
|
+
# Trascendent functions
|
83
|
+
CAS::Exp => Proc.new { "exp(#{x.to_c})" },
|
84
|
+
CAS::Ln => Proc.new { "log(#{x.to_c})" },
|
85
|
+
|
86
|
+
# Box Conditions
|
87
|
+
CAS::BoxConditionOpen => Proc.new {
|
88
|
+
["double __t_#{x.object_id} = #{x.to_c};",
|
89
|
+
"(__t_#{x.object_id} > #{lower.latex} && __t_#{x.object_id} < #{upper.latex})"]
|
90
|
+
},
|
91
|
+
CAS::BoxConditionUpperClosed => Proc.new {
|
92
|
+
["double __t_#{x.object_id} = #{x.to_c};",
|
93
|
+
"(__t_#{x.object_id} > #{lower.latex} && __t_#{x.object_id} <= #{upper.latex})"]
|
94
|
+
},
|
95
|
+
CAS::BoxConditionLowerClosed => Proc.new {
|
96
|
+
["double __t_#{x.object_id} = #{x.to_c};",
|
97
|
+
"(__t_#{x.object_id} >= #{lower.latex} && __t_#{x.object_id} < #{upper.latex})"]
|
98
|
+
},
|
99
|
+
CAS::BoxConditionClosed => Proc.new {
|
100
|
+
["double __t_#{x.object_id} = #{x.to_c};",
|
101
|
+
"(__t_#{x.object_id} >= #{lower.latex} && __t_#{x.object_id} <= #{upper.latex})"]
|
102
|
+
},
|
103
|
+
|
104
|
+
# Conditions
|
105
|
+
CAS::Equal => Proc.new { "(#{x.to_c} == #{y.to_c})" },
|
106
|
+
CAS::Smaller => Proc.new { "(#{x.to_c} < #{y.to_c})" },
|
107
|
+
CAS::Greater => Proc.new { "(#{x.to_c} > #{y.to_c})" },
|
108
|
+
CAS::SmallerEqual => Proc.new { "(#{x.to_c} <= #{y.to_c})" },
|
109
|
+
CAS::GreaterEqual => Proc.new { "(#{x.to_c} >= #{y.to_c})" },
|
110
|
+
|
111
|
+
# Piecewise
|
112
|
+
CAS::Piecewise => Proc.new { raise CASError, "Not implemented yet" },
|
113
|
+
CAS::Max => Proc.new { raise CASError, "Not implemented yet" },
|
114
|
+
CAS::Min => Proc.new { raise CASError, "Not implemented yet" }
|
115
|
+
}.each do |cls, blk|
|
116
|
+
cls.send(:define_method, "to_c", &blk)
|
117
|
+
end
|
118
|
+
|
119
|
+
class Op
|
120
|
+
def to_c_lib(name)
|
121
|
+
CAS::Help.assert(name, String)
|
122
|
+
[CAS::C_PLUGIN.write_header(self, name), CAS::C_PLUGIN.write_source(self, name)]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|