ragni-cas 0.2.3 → 0.2.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/functions/fnc-base.rb +14 -3
- data/lib/functions/fnc-box-conditions.rb +26 -23
- data/lib/functions/fnc-trig.rb +33 -31
- data/lib/functions/fnc-trsc.rb +10 -14
- data/lib/numbers/constants.rb +0 -8
- data/lib/numbers/functions.rb +185 -0
- data/lib/numbers/variables.rb +12 -21
- data/lib/operators/nary-op.rb +1 -3
- data/lib/operators/op.rb +5 -6
- data/lib/ragni-cas/plugins/graphviz.rb +132 -0
- data/lib/ragni-cas/plugins/latex.rb +68 -0
- data/lib/ragni-cas.rb +13 -2
- data/lib/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +5 -2
- 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: 84aec56cb941ff1f7b44e118dc3bed327272591d
|
4
|
+
data.tar.gz: 350d3a2064312c08c3a900633e2b0672bf140aa3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e2a944fe15271c1ef65184a6ab5dd8610617294648cfdb0d67efd351167f4a4e1bd764eeadb0f0c7bfbaf1c35f9d00ff16ca2cca2458c94039faf4018eb6d00c
|
7
|
+
data.tar.gz: 519a211ffcc69806c6c8b366a588e0258a73114f73dd0e8fd5445c9118e93c4f9ae2f71b75ff7682f3ad2ed3ae32bb9bf50726e715f8255f371035249d09dcc9
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/lib/functions/fnc-base.rb
CHANGED
@@ -296,6 +296,11 @@ module CAS
|
|
296
296
|
# * **returns**: `CAS::Op` simplified version
|
297
297
|
def simplify
|
298
298
|
super
|
299
|
+
return self if (@x == CAS::Zero and @y == CAS::Zero)
|
300
|
+
return self if (@x == CAS::Infinity and @y == CAS::Infinity)
|
301
|
+
return self if (@x == CAS::Infinity and @y == CAS::Zero)
|
302
|
+
return self if (@x == CAS::Zero and @y == CAS::Infinity)
|
303
|
+
|
299
304
|
return CAS::Zero if @x == CAS::Zero
|
300
305
|
return CAS::One if @x == CAS::One
|
301
306
|
return @x if @y == CAS::One
|
@@ -386,10 +391,16 @@ module CAS
|
|
386
391
|
# * **returns**: `CAS::Op` simplified version
|
387
392
|
def simplify
|
388
393
|
super
|
394
|
+
return self if (@x == CAS::Zero and @y == CAS::Zero)
|
395
|
+
return self if (@x == CAS::Infinity and @y == CAS::Infinity)
|
396
|
+
return self if (@x == CAS::Infinity and @y == CAS::Zero)
|
397
|
+
return self if (@x == CAS::Zero and @y == CAS::Infinity)
|
398
|
+
|
389
399
|
return CAS::Zero if @x == CAS::Zero
|
390
400
|
return CAS::Infinity if @y == CAS::Zero
|
391
401
|
return @x if @y == CAS::One
|
392
402
|
return CAS::Zero if @y == CAS::Infinity
|
403
|
+
return CAS::One if @x == @y
|
393
404
|
return CAS.const(self.call({})) if (@x.is_a? CAS::Constant and @y.is_a? CAS::Constant)
|
394
405
|
return self
|
395
406
|
end
|
@@ -464,7 +475,7 @@ module CAS
|
|
464
475
|
# * **returns**: `CAS::Op` simplified version
|
465
476
|
def simplify
|
466
477
|
super
|
467
|
-
return CAS.pow(@x.x, @y - 0.5) if @x.is_a? CAS::Pow
|
478
|
+
return (CAS.pow(@x.x, @x.y - 0.5)).simplify if @x.is_a? CAS::Pow
|
468
479
|
return CAS::Zero if @x == CAS::Zero
|
469
480
|
return CAS::One if @x == CAS::One
|
470
481
|
return CAS.const(self.call({})) if (@x.is_a? CAS::Constant and @y.is_a? CAS::Constant)
|
@@ -509,7 +520,7 @@ module CAS
|
|
509
520
|
# * **returns**: `CAS::Op` derivative
|
510
521
|
def diff(v)
|
511
522
|
if @x.depend? v
|
512
|
-
|
523
|
+
-@x.diff(v)
|
513
524
|
else
|
514
525
|
CAS::Zero
|
515
526
|
end
|
@@ -593,7 +604,7 @@ module CAS
|
|
593
604
|
# * **returns**: `CAS::Op` derivative
|
594
605
|
def diff(v)
|
595
606
|
if @x.depend? v
|
596
|
-
return @x.diff(
|
607
|
+
return @x.diff(v) * (@x/CAS.abs(@x))
|
597
608
|
else
|
598
609
|
return CAS::Zero
|
599
610
|
end
|
@@ -272,30 +272,33 @@ module CAS
|
|
272
272
|
end
|
273
273
|
end # BoxConditionUpperClosed
|
274
274
|
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
275
|
+
class << self
|
276
|
+
# Shortcut for creating a new box condition. It requires four arguments:
|
277
|
+
#
|
278
|
+
# * **argument**: `CAS::Op` function for condition
|
279
|
+
# * **argument**: `CAS::Constant` lower limit
|
280
|
+
# * **argument**: `CAs::Constant` upper limit
|
281
|
+
# * **argument**: `Symbol` of condition type it can be:
|
282
|
+
# - `:closed` for `CAs::BoxConditionClosed`
|
283
|
+
# - `:open` for `CAs::BoxConditionOpen`
|
284
|
+
# - `:upper_closed` for `CAs::BoxConditionUpperClosed`
|
285
|
+
# - `:lower_closed` for `CAs::BoxConditionLowerClosed`
|
286
|
+
# * **returns**: `CAS::BoxCondition` new instance
|
287
|
+
def box(x, a, b, type=:closed)
|
288
|
+
case type
|
289
|
+
when :closed
|
290
|
+
return CAS::BoxConditionClosed.new(x, a, b)
|
291
|
+
when :open
|
292
|
+
return CAS::BoxConditionOpen.new(x, a, b)
|
293
|
+
when :upper_closed
|
294
|
+
return CAS::BoxConditionUpperClosed.new(x, a, b)
|
295
|
+
when :lower_closed
|
296
|
+
return CAS::BoxConditionLowerClosed.new(x, a, b)
|
297
|
+
else
|
298
|
+
raise CAS::CASError, "Unknown box condition type"
|
299
|
+
end
|
298
300
|
end
|
301
|
+
alias :in :box
|
299
302
|
end
|
300
303
|
|
301
304
|
class Op
|
data/lib/functions/fnc-trig.rb
CHANGED
@@ -63,7 +63,8 @@ module CAS
|
|
63
63
|
def self.init_simplify_dict
|
64
64
|
@simplify_dict = {
|
65
65
|
CAS::Zero => CAS::Zero,
|
66
|
-
CAS::Pi => CAS::Zero
|
66
|
+
CAS::Pi => CAS::Zero,
|
67
|
+
CAS::Pi/2 => CAS::One
|
67
68
|
}
|
68
69
|
end
|
69
70
|
|
@@ -152,14 +153,16 @@ module CAS
|
|
152
153
|
end
|
153
154
|
CAS::Asin.init_simplify_dict
|
154
155
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
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
|
161
165
|
end
|
162
|
-
# alias :arcsin :asin
|
163
166
|
|
164
167
|
# ___
|
165
168
|
# / __|___ ___
|
@@ -223,7 +226,8 @@ module CAS
|
|
223
226
|
def self.init_simplify_dict
|
224
227
|
@simplify_dict = {
|
225
228
|
CAS::Zero => CAS::One,
|
226
|
-
CAS::Pi => CAS::One
|
229
|
+
CAS::Pi => CAS::One,
|
230
|
+
CAS::Pi/2 => CAS::Zero
|
227
231
|
}
|
228
232
|
end
|
229
233
|
|
@@ -307,14 +311,16 @@ module CAS
|
|
307
311
|
end
|
308
312
|
CAS::Acos.init_simplify_dict
|
309
313
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
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
|
316
323
|
end
|
317
|
-
# alias :arccos :acos
|
318
324
|
|
319
325
|
# _____
|
320
326
|
# |_ _|_ _ _ _
|
@@ -378,7 +384,8 @@ module CAS
|
|
378
384
|
def self.init_simplify_dict
|
379
385
|
@simplify_dict = {
|
380
386
|
CAS::Zero => CAS::Zero,
|
381
|
-
CAS::Pi => CAS::Zero
|
387
|
+
CAS::Pi => CAS::Zero,
|
388
|
+
CAS::Pi/2 => CAS::Infinity
|
382
389
|
}
|
383
390
|
end
|
384
391
|
|
@@ -466,22 +473,17 @@ module CAS
|
|
466
473
|
def to_code
|
467
474
|
"Math::atan(#{@x.to_code})"
|
468
475
|
end
|
469
|
-
|
470
|
-
# Returns the latex representation of the current Op.
|
471
|
-
#
|
472
|
-
# * **returns**: `String`
|
473
|
-
def to_latex
|
474
|
-
"\\arctan\\left( #{@x.to_latex} \\right)"
|
475
|
-
end
|
476
476
|
end
|
477
477
|
CAS::Atan.init_simplify_dict
|
478
478
|
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
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
|
485
488
|
end
|
486
|
-
# alias :arctan :atan
|
487
489
|
end
|
data/lib/functions/fnc-trsc.rb
CHANGED
@@ -83,6 +83,7 @@ module CAS
|
|
83
83
|
"e^{#{@x.to_latex}}"
|
84
84
|
end
|
85
85
|
end # Exp
|
86
|
+
CAS::Exp.init_simplify_dict
|
86
87
|
|
87
88
|
# Shortcut for `CAS::Exp#new`
|
88
89
|
#
|
@@ -178,19 +179,14 @@ module CAS
|
|
178
179
|
end # Ln
|
179
180
|
CAS::Ln.init_simplify_dict
|
180
181
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
#
|
191
|
-
# * **argument**: `CAS::Op` argument of the function
|
192
|
-
# * **returns**: `CAS::Ln` operation
|
193
|
-
def self.log(x)
|
194
|
-
CAS::Ln.new x
|
182
|
+
class << self
|
183
|
+
# Shortcut for `CAS::Ln#new`
|
184
|
+
#
|
185
|
+
# * **argument**: `CAS::Op` argument of the function
|
186
|
+
# * **returns**: `CAS::Ln` operation
|
187
|
+
def ln(x)
|
188
|
+
CAS::Ln.new x
|
189
|
+
end
|
190
|
+
alias :log :ln
|
195
191
|
end
|
196
192
|
end
|
data/lib/numbers/constants.rb
CHANGED
@@ -95,14 +95,6 @@ module CAS
|
|
95
95
|
def inspect
|
96
96
|
"Const(#{self})"
|
97
97
|
end
|
98
|
-
|
99
|
-
# Return the local Graphviz node of the tree
|
100
|
-
#
|
101
|
-
# * **returns**: `String` of local Graphiz node
|
102
|
-
def dot_graph
|
103
|
-
n = "#{self.class.to_s.gsub("CAS::", "")}_#{self.object_id}"
|
104
|
-
"#{n};\n #{n} [label=\"#{@x}\"];"
|
105
|
-
end
|
106
98
|
end
|
107
99
|
|
108
100
|
# Allows to define a series of new constants.
|
@@ -0,0 +1,185 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module CAS
|
4
|
+
# ___ _ _
|
5
|
+
# | __| _ _ _ __| |_(_)___ _ _
|
6
|
+
# | _| || | ' \/ _| _| / _ \ ' \
|
7
|
+
# |_| \_,_|_||_\__|\__|_\___/_||_|
|
8
|
+
|
9
|
+
# Unknown function class. Will allow to make symbolic differentiation and
|
10
|
+
# so on.
|
11
|
+
class Function < CAS::NaryOp
|
12
|
+
# Contains all defined functions. Container is a `Hash` with name of the
|
13
|
+
# function as key and the function as the value
|
14
|
+
@@container = {}
|
15
|
+
|
16
|
+
# Return the `Hash` of the functions
|
17
|
+
#
|
18
|
+
# * **returns**: `Hash`
|
19
|
+
def self.list; @@container; end
|
20
|
+
|
21
|
+
# Return `true` if a function was already defined
|
22
|
+
#
|
23
|
+
# * **argument**: name of the function to be checked
|
24
|
+
def self.exist?(name)
|
25
|
+
CAS::Help.assert_name name
|
26
|
+
(@@container[name] ? true : false)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Return the number of functions defined
|
30
|
+
#
|
31
|
+
# * **returns**: `Fixnum`
|
32
|
+
def self.size; @@container.keys.size; end
|
33
|
+
|
34
|
+
# Returns a function given its name
|
35
|
+
#
|
36
|
+
# * **argument**: `Object` name of the function
|
37
|
+
# * **returns**: `CAS::Function` instance if exists, raises a `CAS::CASError`
|
38
|
+
# if not
|
39
|
+
def self.[](s)
|
40
|
+
return @@container[s] if self.exist? s
|
41
|
+
raise CASError, "Function #{s} not found"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns `true` if a function exists in container
|
45
|
+
#
|
46
|
+
# * **argument**: `String` or `Symbol` that represent the functions
|
47
|
+
# * **returns**: `TrueClass` if variable exists, `FalseClass` if not
|
48
|
+
def self.exist?(name); @@container.keys.include?(name); end
|
49
|
+
|
50
|
+
# The attribute `name` identifies the current function. A function with the
|
51
|
+
# same name of an existing function connot be defined
|
52
|
+
attr_reader :name
|
53
|
+
|
54
|
+
# Initializes a new function. It requires a name and a series of arguments
|
55
|
+
# that will be the functions on which it depends.
|
56
|
+
#
|
57
|
+
# * **argument**: `String` or `Symbol` name of the variable
|
58
|
+
# * **argument**: `Array` of `CAS::Variable` that are argument of the function
|
59
|
+
# * **returns**: `CAS::Function`
|
60
|
+
def initialize(name, *xs)
|
61
|
+
xs.flatten!
|
62
|
+
CAS::Help.assert_name name
|
63
|
+
xs.each do |x|
|
64
|
+
CAS::Help.assert x, CAS::Variable
|
65
|
+
end
|
66
|
+
raise CASError, "Function #{name} already exists" if CAS::Function.exist? name
|
67
|
+
|
68
|
+
@x = xs.uniq
|
69
|
+
@name = name
|
70
|
+
@@container[@name] = self
|
71
|
+
end
|
72
|
+
|
73
|
+
# Overrides new method. This will return an existing function if in the function container
|
74
|
+
#
|
75
|
+
# * **requires**: `String` or `Symbol` that is the name of the function
|
76
|
+
# * **requires**: `Array` of `CAS::Variable`
|
77
|
+
# * **returns**: a new `CAS::Function` or the old one
|
78
|
+
def Function.new(name, *xs)
|
79
|
+
xs.flatten!
|
80
|
+
if @@container[name]
|
81
|
+
return @@container[name] if (@@container[name].args - xs.uniq == [] or xs.size == 0)
|
82
|
+
raise CASError, "Function #{name} already defined with different arguments!"
|
83
|
+
end
|
84
|
+
super
|
85
|
+
end
|
86
|
+
|
87
|
+
# Returns an array containing `CAS::Variable`s argument of the function
|
88
|
+
#
|
89
|
+
# * **returns**: `Array` containing `CAs:;Variable`
|
90
|
+
def args
|
91
|
+
@x
|
92
|
+
end
|
93
|
+
|
94
|
+
# Simplifications cannot be performed on anonymous function, thus it will always return
|
95
|
+
# the `self` `CAS::Function` object
|
96
|
+
#
|
97
|
+
# * **returns**: `CAS::Function` self instance
|
98
|
+
def simplify; self; end
|
99
|
+
|
100
|
+
# Tries to convert an anonymous function into Ruby code will always raise a `CASError` because it
|
101
|
+
# is not possible to generate code for such a fuction
|
102
|
+
#
|
103
|
+
# * **raises**: `CAS::CASError`: Ruby code for CAs::Function cannot be generated
|
104
|
+
def to_code
|
105
|
+
raise CASError, "Ruby code for #{self.class} cannot be generated"
|
106
|
+
end
|
107
|
+
|
108
|
+
# Substitutions in which a function is involved directly generates a CAS::Error unless the substitution will
|
109
|
+
# involve another variable. Example:
|
110
|
+
#
|
111
|
+
# ``` ruby
|
112
|
+
# (CAS.declare :f [x, y, z]).subs { x => x ** 2 } # this raises CASError
|
113
|
+
# (CAS.declare :f [x, y, z]).subs { x => y } # this returns f(y, z)
|
114
|
+
# ```
|
115
|
+
#
|
116
|
+
# * **requires**: a substitution `Hash`
|
117
|
+
# * **returns**: a `CAS::Function` with modified argument list
|
118
|
+
# * **raises**: `CASError` if something different with resppect to a `CAS::Variable` is a active substitution
|
119
|
+
def subs(s)
|
120
|
+
s.each do |k, v|
|
121
|
+
next unless self.depend? k
|
122
|
+
if v.is_a? CAS::Variable
|
123
|
+
(@x.collect! { |e| (e == k) ? v : e }).uniq!
|
124
|
+
next
|
125
|
+
end
|
126
|
+
raise CASError, "Cannot perform a substitution in #{self.class}"
|
127
|
+
end
|
128
|
+
self
|
129
|
+
end
|
130
|
+
|
131
|
+
# Performs the derivative with respect to one of the variable. The new function
|
132
|
+
# has a name with respect to a schema that for now is fixed (TODO: make it variable and user defined).
|
133
|
+
#
|
134
|
+
# * **requires**: a `CAS::Variable` for derivative
|
135
|
+
# * **returns**: the `CAS::Variable` derivated function
|
136
|
+
def diff(v)
|
137
|
+
if self.depend? v
|
138
|
+
return CAS.declare :"d#{@name}[#{v}]", @x
|
139
|
+
else
|
140
|
+
return CAS::Zero
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Trying to call a `CAS::Function` will always return a `CAS::Error`
|
145
|
+
#
|
146
|
+
# * **raises**: `CAS::CASError`
|
147
|
+
def call(_v)
|
148
|
+
raise CASError, "Cannot call a #{self.class}"
|
149
|
+
end
|
150
|
+
|
151
|
+
# Returns the inspect string of the function, that is similar to `CAS::Function#to_s`
|
152
|
+
#
|
153
|
+
# * **returns**: inspection `String`
|
154
|
+
def inspect; self.to_s; end
|
155
|
+
|
156
|
+
# Returns a description `String` for the `CAS::Function`
|
157
|
+
#
|
158
|
+
# * **returns**: `String`
|
159
|
+
def to_s
|
160
|
+
"#{@name}(#{@x.map(&:to_s).join(", ")})"
|
161
|
+
end
|
162
|
+
|
163
|
+
# Checks if two functions can be considered equal (same name, same args)
|
164
|
+
#
|
165
|
+
# * **requires**: another op to be checked against
|
166
|
+
# * **returns**: `TrueClass` if functions are equal, `FalseClass` if not equal
|
167
|
+
def ==(op)
|
168
|
+
return false if not self.class == op.class
|
169
|
+
return false if not (@name == op.name and @x == op.args)
|
170
|
+
true
|
171
|
+
end
|
172
|
+
end # Function
|
173
|
+
|
174
|
+
class << self
|
175
|
+
# This shortcut allows to declare a new function
|
176
|
+
#
|
177
|
+
# * **requires**: `String` or `Symbol` that is the name of the function
|
178
|
+
# * **requires**: `Array` of `CAS::Variable`
|
179
|
+
# * **returns**: a new `CAS::Function` or the old one
|
180
|
+
def declare(name, *xs)
|
181
|
+
xs.flatten!
|
182
|
+
CAS::Function.new(name, xs)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
data/lib/numbers/variables.rb
CHANGED
@@ -12,20 +12,20 @@ module CAS
|
|
12
12
|
class Variable < CAS::Op
|
13
13
|
# Contains all define variable, in an hash. Variables are
|
14
14
|
# accessible through variable name.
|
15
|
-
@@
|
15
|
+
@@container = {}
|
16
16
|
|
17
17
|
# Returns the `Hash` that contains all the variable
|
18
18
|
#
|
19
19
|
# * **returns**: `Hash`
|
20
20
|
def self.list
|
21
|
-
@@
|
21
|
+
@@container
|
22
22
|
end
|
23
23
|
|
24
24
|
# Return the number of variable defined
|
25
25
|
#
|
26
26
|
# * **returns**: `Fixnum`
|
27
27
|
def self.size
|
28
|
-
@@
|
28
|
+
@@container.keys.size
|
29
29
|
end
|
30
30
|
|
31
31
|
# Returns a variable given its name
|
@@ -33,7 +33,7 @@ module CAS
|
|
33
33
|
# * **argument**: `Object` name of the variable
|
34
34
|
# * **returns**: `CAS::Variable` instance if exists, creates a new variable if does not
|
35
35
|
def self.[](s)
|
36
|
-
@@
|
36
|
+
@@container[s] || CAS::vars(s)
|
37
37
|
end
|
38
38
|
|
39
39
|
# Returns `true` if a variable already exists
|
@@ -41,19 +41,23 @@ module CAS
|
|
41
41
|
# * **argument**: `Object` that represent the variable
|
42
42
|
# * **returns**: `TrueClass` if variable exists, `FalseClass` if not
|
43
43
|
def self.exist?(name)
|
44
|
-
@@
|
44
|
+
@@container.keys.include? name
|
45
45
|
end
|
46
46
|
|
47
|
+
# The attribute `name` identifies the current variable. A variable with the
|
48
|
+
# same name of an existing variable connot be defined
|
47
49
|
attr_reader :name
|
50
|
+
|
48
51
|
# Variable is a container for an atomic simbol that becomes a number
|
49
52
|
# when `CAS::Op#call` method is used.
|
50
53
|
#
|
51
54
|
# * **argument**: `Object` that is a identifier for the variable
|
52
55
|
# * **returns**: `CAS::Variable` instance
|
53
56
|
def initialize(name)
|
57
|
+
CAS::Help.assert_name name
|
54
58
|
raise CASError, "Variable #{name} already exists" if CAS::Variable.exist? name
|
55
59
|
@name = name
|
56
|
-
@@
|
60
|
+
@@container[@name] = self
|
57
61
|
end
|
58
62
|
|
59
63
|
# Overrides new method. This will return an existing variable if in variable container
|
@@ -61,8 +65,9 @@ module CAS
|
|
61
65
|
# * **requires**: `Object` that is an identifier for the variable
|
62
66
|
# * **returns**: new variable instance o
|
63
67
|
def Variable.new(name)
|
64
|
-
@@
|
68
|
+
@@container[name] || super
|
65
69
|
end
|
70
|
+
|
66
71
|
# Returns the derivative of a variable
|
67
72
|
#
|
68
73
|
# ```
|
@@ -176,20 +181,6 @@ module CAS
|
|
176
181
|
def simplify
|
177
182
|
self
|
178
183
|
end
|
179
|
-
|
180
|
-
# Return the local Graphviz node of the tree
|
181
|
-
#
|
182
|
-
# * **returns**: `String` of local Graphiz node
|
183
|
-
def dot_graph
|
184
|
-
"#{@name};"
|
185
|
-
end
|
186
|
-
|
187
|
-
# Returns the latex representation of the current Op.
|
188
|
-
#
|
189
|
-
# * **returns**: `String`
|
190
|
-
def to_latex
|
191
|
-
self.to_s
|
192
|
-
end
|
193
184
|
end # Number
|
194
185
|
|
195
186
|
# Allows to define a series of new variables.
|
data/lib/operators/nary-op.rb
CHANGED
@@ -33,9 +33,7 @@ module CAS
|
|
33
33
|
# * **returns**: `TrueClass` or `FalseClass`
|
34
34
|
def depend?(v)
|
35
35
|
CAS::Help.assert(v, CAS::Op)
|
36
|
-
|
37
|
-
@x.each { |x| dep = (x.depend?(v) or dep) }
|
38
|
-
return dep
|
36
|
+
@x.include? v
|
39
37
|
end
|
40
38
|
|
41
39
|
# Return a list of derivative using the chain rule. The input is a
|
data/lib/operators/op.rb
CHANGED
@@ -201,11 +201,7 @@ module CAS
|
|
201
201
|
#
|
202
202
|
# * **returns**: `CAS::Op` self
|
203
203
|
def simplify_dictionary
|
204
|
-
|
205
|
-
return self.class.simplify_dict(@x)
|
206
|
-
else
|
207
|
-
return self
|
208
|
-
end
|
204
|
+
self.class.simplify_dict(@x) || self
|
209
205
|
end
|
210
206
|
|
211
207
|
# Initializes the simplification dictionary (one for each class)
|
@@ -217,7 +213,10 @@ module CAS
|
|
217
213
|
|
218
214
|
# Returns an element of a
|
219
215
|
def self.simplify_dict(k)
|
220
|
-
@simplify_dict
|
216
|
+
@simplify_dict.keys.each do |op|
|
217
|
+
return @simplify_dict[op] if op.simplify == k.simplify
|
218
|
+
end
|
219
|
+
return nil
|
221
220
|
end
|
222
221
|
|
223
222
|
# Inspector for the current object
|
@@ -0,0 +1,132 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# ___ _ _ ___ _ _
|
4
|
+
# / __|_ _ __ _ _ __| |___ _(_)___ | _ \ |_ _ __ _(_)_ _
|
5
|
+
# | (_ | '_/ _` | '_ \ ' \ V / |_ / | _/ | || / _` | | ' \
|
6
|
+
# \___|_| \__,_| .__/_||_\_/|_/__| |_| |_|\_,_\__, |_|_||_|
|
7
|
+
# |_| |___/
|
8
|
+
|
9
|
+
module CAS
|
10
|
+
# ___ _ _
|
11
|
+
# / __|___ _ _| |_ __ _(_)_ _ ___ _ _ ___
|
12
|
+
# | (__/ _ \ ' \ _/ _` | | ' \/ -_) '_(_-<
|
13
|
+
# \___\___/_||_\__\__,_|_|_||_\___|_| /__/
|
14
|
+
|
15
|
+
class Op
|
16
|
+
# Return the local Graphviz node of the tree
|
17
|
+
#
|
18
|
+
# * **returns**: `String` of local Graphiz node
|
19
|
+
def dot_graph
|
20
|
+
cls = "#{self.class.to_s.gsub("CAS::", "")}_#{self.object_id}"
|
21
|
+
"#{cls} -> #{@x.dot_graph}\n"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class BinaryOp
|
26
|
+
# Return the local Graphviz node of the tree
|
27
|
+
#
|
28
|
+
# * **returns**: `String` of local Graphiz node
|
29
|
+
def dot_graph
|
30
|
+
cls = "#{self.class.to_s.gsub("CAS::", "")}_#{self.object_id}"
|
31
|
+
"#{cls} -> #{@x.dot_graph}\n #{cls} -> #{@y.dot_graph}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class NaryOp
|
36
|
+
# Return the local Graphviz node of the tree
|
37
|
+
#
|
38
|
+
# * **returns**: `String` of local Graphiz node
|
39
|
+
def dot_graph
|
40
|
+
cls = "#{self.class.to_s.gsub("CAS::", "")}_#{self.object_id}"
|
41
|
+
ret = ""
|
42
|
+
@x.each do |x|
|
43
|
+
ret += "#{cls} -> #{x.dot_graph}\n"
|
44
|
+
end
|
45
|
+
return ret
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Variable
|
50
|
+
# Return the local Graphviz node of the tree
|
51
|
+
#
|
52
|
+
# * **returns**: `String` of local Graphiz node
|
53
|
+
def to_dot
|
54
|
+
"#{@name}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class Constant
|
59
|
+
# Return the local Graphviz node of the tree
|
60
|
+
#
|
61
|
+
# * **returns**: `String` of local Graphiz node
|
62
|
+
def to_dot
|
63
|
+
"Const(#{@x})"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Piecewise
|
68
|
+
# Convert piecewise function into a dot graphviz representation
|
69
|
+
#
|
70
|
+
# * **returns**: `String`
|
71
|
+
def dot_graph
|
72
|
+
cls = "#{self.class.to_s.gsub("CAS::", "")}_#{self.object_id}"
|
73
|
+
"#{cls} -> #{@x.dot_graph}\n #{cls} -> #{@y.dot_graph}\n #{cls} -> #{@condition.dot_graph}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Return a string representation of the graph that is
|
78
|
+
# a Graphviz tree. Requires a `CAS::Op` as argument.
|
79
|
+
# In the next releases probably it will be moved inside
|
80
|
+
# `CAS::Op`.
|
81
|
+
#
|
82
|
+
# * **argument**: `CAS::Op` instance
|
83
|
+
# * **returns**: `String`
|
84
|
+
def self.to_dot(op)
|
85
|
+
CAS::Help.assert(op, CAS::Op)
|
86
|
+
string = op.dot_graph
|
87
|
+
labels = ""
|
88
|
+
|
89
|
+
dot_subs_hash = {
|
90
|
+
"Sum" => "+",
|
91
|
+
"Diff" => "-",
|
92
|
+
"Prod" => "×",
|
93
|
+
"Div" => "÷",
|
94
|
+
"Sqrt" => "√(∙)",
|
95
|
+
"Abs" => "|∙|",
|
96
|
+
"Invert" => "-(∙)",
|
97
|
+
"Exp" => "exp(∙)",
|
98
|
+
"Log" => "log(∙)",
|
99
|
+
"Pow" => "(∙)^(∙)",
|
100
|
+
"ZERO_CONSTANT" => "0",
|
101
|
+
"ONE_CONSTANT" => "1",
|
102
|
+
"TWO_CONSTANT" => "2",
|
103
|
+
"PI_CONSTANT" => "π",
|
104
|
+
"INFINITY_CONSTANT" => "∞",
|
105
|
+
"E_CONSTANT" => "e",
|
106
|
+
"MINUS_ONE_CONSTANT" => "-1"
|
107
|
+
}
|
108
|
+
|
109
|
+
lab = {}
|
110
|
+
string.scan(/\w+\_\d+/) do |m|
|
111
|
+
if m =~ /(\w+)\_\d+/
|
112
|
+
lab[m] = dot_subs_hash[$1] || $1
|
113
|
+
end
|
114
|
+
end
|
115
|
+
lab.each { |k, v| labels += " #{k} [label=\"#{v}\"]\n" }
|
116
|
+
|
117
|
+
return "digraph Op {\n #{string}#{labels}}"
|
118
|
+
end
|
119
|
+
|
120
|
+
# Export the input `CAS::Op` graphviz representation to a file.
|
121
|
+
#
|
122
|
+
# * **argument**: `String` with filename
|
123
|
+
# * **argument**: `CAS::Op` with the tree
|
124
|
+
# * **returns**: `CAS::Op` in input
|
125
|
+
def self.export_dot(fl, op)
|
126
|
+
CAS::Help.assert(fl, String)
|
127
|
+
CAS::Help.assert(op, CAS::Op)
|
128
|
+
|
129
|
+
File.open(fl, "w") do |f| f.puts CAS.to_dot(op) end
|
130
|
+
return op
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# _ _____ ____ ___ _ _
|
4
|
+
# | | __ |_ _|__ /_ __ | _ \ |_ _ __ _(_)_ _
|
5
|
+
# | |__/ _` || | |_ \ \ / | _/ | || / _` | | ' \
|
6
|
+
# |____\__,_||_| |___/_\_\ |_| |_|\_,_\__, |_|_||_|
|
7
|
+
# |___/
|
8
|
+
|
9
|
+
module CAS
|
10
|
+
{
|
11
|
+
# Terminal nodes
|
12
|
+
CAS::Constant => Proc.new { "#{x}" },
|
13
|
+
CAS::Variable => Proc.new { "#{name}" },
|
14
|
+
CAS::PI_CONSTANT => Proc.new { "\\pi" },
|
15
|
+
CAS::INFINITY_CONSTANT => Proc.new { "\\infty" },
|
16
|
+
CAS::NEG_INFINITY_CONSTANT => Proc.new { "-\\infty" },
|
17
|
+
# Base functions
|
18
|
+
CAS::Sum => Proc.new { "\\left( #{x.latex} + #{y.latex} \\right)" },
|
19
|
+
CAS::Diff => Proc.new { "\\left( #{x.latex} - #{y.latex} \\right)" },
|
20
|
+
CAS::Prod => Proc.new { "\\left( #{x.latex} \\, #{y.latex} \\right)"},
|
21
|
+
CAS::Pow => Proc.new { "{#{x.latex}}^{#{y.latex}}" },
|
22
|
+
CAS::Div => Proc.new { "\\dfrac{#{x.latex}}{#{y.latex}}" },
|
23
|
+
CAS::Sqrt => Proc.new { "\\sqrt{#{x.latex}}" },
|
24
|
+
CAS::Invert => Proc.new { "-#{x.latex}" },
|
25
|
+
CAS::Abs => Proc.new { "\\left| #{}{x.latex} \\right|" },
|
26
|
+
|
27
|
+
# Trigonometric functions
|
28
|
+
CAS::Sin => Proc.new { "\\sin \\left( #{x.latex} \\right)" },
|
29
|
+
CAS::Asin => Proc.new { "\\arcsin \\left( #{x.latex} \\right)" },
|
30
|
+
CAS::Cos => Proc.new { "\\cos \\left( #{x.latex} \\right)" },
|
31
|
+
CAS::Acos => Proc.new { "\\arccos \\left( #{x.latex} \\right)" },
|
32
|
+
CAS::Tan => Proc.new { "\\tan \\left( #{x.latex} \\right)" },
|
33
|
+
CAS::Atan => Proc.new { "\\arctan \\left( #{x.latex} \\right)" },
|
34
|
+
|
35
|
+
# Trascendent functions
|
36
|
+
CAS::Exp => Proc.new { "e^#{x.latex}" },
|
37
|
+
CAS::Ln => Proc.new { "\\log \\left( #{x.latex} \\right)" },
|
38
|
+
|
39
|
+
# Box Conditions
|
40
|
+
CAS::BoxConditionOpen => Proc.new { "#{lower.latex} < #{x.latex} < #{upper.latex}" },
|
41
|
+
CAS::BoxConditionClosed => Proc.new { "#{lower.latex} \\leq #{x.latex} \\leq #{upper.latex}" },
|
42
|
+
CAS::BoxConditionUpperClosed => Proc.new { "#{lower.latex} < #{x.latex} \\leq #{upper.latex}" },
|
43
|
+
CAS::BoxConditionLowerClosed => Proc.new { "#{lower.latex} \\leq #{x.latex} < #{upper.latex}" },
|
44
|
+
|
45
|
+
# Conditions
|
46
|
+
CAS::Equal => Proc.new { "#{x.latex} = #{y.latex}" },
|
47
|
+
CAS::Smaller => Proc.new { "#{x.latex} < #{y.latex}" },
|
48
|
+
CAS::Greater => Proc.new { "#{x.latex} > #{y.latex}" },
|
49
|
+
CAS::SmallerEqual => Proc.new { "#{x.latex} \\leq #{y.latex}" },
|
50
|
+
CAS::GreaterEqual => Proc.new { "#{x.latex} \\geq #{y.latex}" },
|
51
|
+
|
52
|
+
# Piecewise
|
53
|
+
CAS::Piecewise => Proc.new {
|
54
|
+
"\\left\\{ " +
|
55
|
+
" \\begin{array}{lr} " +
|
56
|
+
" #{x.latex} & #{condition.latex} \\\\" +
|
57
|
+
" #{y.latex}" +
|
58
|
+
" \\end{array}" +
|
59
|
+
"\\right."
|
60
|
+
},
|
61
|
+
CAS::Max => Proc.new { "\\max \\left( #{x.latex}, \\, #{y.latex} \\right)" },
|
62
|
+
CAS::Min => Proc.new { "\\min \\left( #{x.latex}, \\, #{y.latex} \\right)" }
|
63
|
+
|
64
|
+
|
65
|
+
}.each do |cls, blk|
|
66
|
+
cls.send(:define_method, "to_latex", &blk)
|
67
|
+
end
|
68
|
+
end
|
data/lib/ragni-cas.rb
CHANGED
@@ -27,7 +27,7 @@ module CAS
|
|
27
27
|
# Support functions are in this separate Helper class
|
28
28
|
module Help
|
29
29
|
# Check input `obj.class` against a `type` class
|
30
|
-
# raises an ArgumentError if check fails
|
30
|
+
# raises an `ArgumentError` if check fails
|
31
31
|
#
|
32
32
|
# * **argument**: object to be cecked
|
33
33
|
# * **argument**: type to be checked against
|
@@ -36,6 +36,17 @@ module CAS
|
|
36
36
|
raise ArgumentError, "required #{type}, received #{obj.class}" unless obj.is_a? type
|
37
37
|
return true
|
38
38
|
end
|
39
|
+
|
40
|
+
# Check if input object is feasible to be a name of a `CAS::Variable` or a `CAS::Function`
|
41
|
+
# raise an `ArgumentError` if the check fails. To be feasible the object must be a `String`
|
42
|
+
# instance or `Symbol` instance
|
43
|
+
#
|
44
|
+
# * **argument**: object to be checked
|
45
|
+
# * **returns**: `TrueClass` or raises `ArgumentError`
|
46
|
+
def self.assert_name(obj)
|
47
|
+
raise ArgumentError, "Input name must be a String/Symbol" unless [Symbol, String].include? obj.class
|
48
|
+
return true
|
49
|
+
end
|
39
50
|
end
|
40
51
|
end
|
41
52
|
|
@@ -46,7 +57,7 @@ end
|
|
46
57
|
# |_|
|
47
58
|
|
48
59
|
%w|operators/op.rb operators/bary-op.rb operators/nary-op.rb
|
49
|
-
numbers/constants.rb numbers/variables.rb
|
60
|
+
numbers/constants.rb numbers/variables.rb numbers/functions.rb
|
50
61
|
functions/fnc-base.rb functions/fnc-trig.rb functions/fnc-trsc.rb
|
51
62
|
functions/fnc-conditions.rb functions/fnc-box-conditions.rb functions/fnc-piecewise.rb
|
52
63
|
overloading/fixnum.rb overloading/float.rb
|
data/lib/version.rb
CHANGED
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ragni-cas
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matteo Ragni
|
@@ -29,7 +29,7 @@ cert_chain:
|
|
29
29
|
XorZtzkkLImvKFj35xKLFfVkv0Vd8tGQoiL8vdmQNJjAjtE+C+Y7OI4dpiZPKO4G
|
30
30
|
R/8JOvUuk9jPbyLxjQH/sFaFqqYGX2xo1zk2CRy/A0WhJrSaXVw1r5lEi7b0W5gg
|
31
31
|
-----END CERTIFICATE-----
|
32
|
-
date: 2016-10-
|
32
|
+
date: 2016-10-13 00:00:00.000000000 Z
|
33
33
|
dependencies: []
|
34
34
|
description:
|
35
35
|
email: info@ragni.me
|
@@ -44,6 +44,7 @@ files:
|
|
44
44
|
- lib/functions/fnc-trig.rb
|
45
45
|
- lib/functions/fnc-trsc.rb
|
46
46
|
- lib/numbers/constants.rb
|
47
|
+
- lib/numbers/functions.rb
|
47
48
|
- lib/numbers/variables.rb
|
48
49
|
- lib/operators/bary-op.rb
|
49
50
|
- lib/operators/nary-op.rb
|
@@ -51,6 +52,8 @@ files:
|
|
51
52
|
- lib/overloading/fixnum.rb
|
52
53
|
- lib/overloading/float.rb
|
53
54
|
- lib/ragni-cas.rb
|
55
|
+
- lib/ragni-cas/plugins/graphviz.rb
|
56
|
+
- lib/ragni-cas/plugins/latex.rb
|
54
57
|
- lib/version.rb
|
55
58
|
homepage: https://github.com/MatteoRagni/cas-rb
|
56
59
|
licenses:
|
metadata.gz.sig
CHANGED
Binary file
|