ragni-cas 0.2.3 → 0.2.5
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/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
|