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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1f5aedceb3a3141a14a807fdbbac42e5bc66fd85
4
- data.tar.gz: caa36f84a6e77cac0f1a6c5410c63cd2940297bd
3
+ metadata.gz: 84aec56cb941ff1f7b44e118dc3bed327272591d
4
+ data.tar.gz: 350d3a2064312c08c3a900633e2b0672bf140aa3
5
5
  SHA512:
6
- metadata.gz: 9a56cda36bc0ac20cb444e1ad663b29c4ddeaac92e97239edfbc8631ef7ec054bc4d626919ed7efbf7200898f62bd7e731380f708bb0631345e60677eba684c2
7
- data.tar.gz: 7c9ac6f8cb07d8a0a967b8afd655790a4d8d147fbcf4c2f9d939d763427e2e18405513c24b1bd0a4eb1bd72cee11d4c8ef710ed8c47e775afdda49ce3a12a606
6
+ metadata.gz: e2a944fe15271c1ef65184a6ab5dd8610617294648cfdb0d67efd351167f4a4e1bd764eeadb0f0c7bfbaf1c35f9d00ff16ca2cca2458c94039faf4018eb6d00c
7
+ data.tar.gz: 519a211ffcc69806c6c8b366a588e0258a73114f73dd0e8fd5445c9118e93c4f9ae2f71b75ff7682f3ad2ed3ae32bb9bf50726e715f8255f371035249d09dcc9
checksums.yaml.gz.sig CHANGED
Binary file
@@ -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
- CAS::const(-1.0) * @x.diff(v)
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(x) * (@x/CAS.abs(@x))
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
- # Shortcut for creating a new box condition. It requires four arguments:
276
- #
277
- # * **argument**: `CAS::Op` function for condition
278
- # * **argument**: `CAS::Constant` lower limit
279
- # * **argument**: `CAs::Constant` upper limit
280
- # * **argument**: `Symbol` of condition type it can be:
281
- # - `:closed` for `CAs::BoxConditionClosed`
282
- # - `:open` for `CAs::BoxConditionOpen`
283
- # - `:upper_closed` for `CAs::BoxConditionUpperClosed`
284
- # - `:lower_closed` for `CAs::BoxConditionLowerClosed`
285
- # * **returns**: `CAS::BoxCondition` new instance
286
- def self.box(x, a, b, type=:closed)
287
- case type
288
- when :closed
289
- return CAS::BoxConditionClosed.new(x, a, b)
290
- when :open
291
- return CAS::BoxConditionOpen.new(x, a, b)
292
- when :upper_closed
293
- return CAS::BoxConditionUpperClosed.new(x, a, b)
294
- when :lower_closed
295
- return CAS::BoxConditionLowerClosed.new(x, a, b)
296
- else
297
- raise CAS::CASError, "Unknown box condition type"
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
@@ -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
- # Shortcut for `CAS::Asin#new`
156
- #
157
- # * **argument**: `CAS::Op` argument of the function
158
- # * **returns**: `CAS::Asin` operation
159
- def self.asin(x)
160
- CAS::Asin.new x
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
- # Shortcut for `CAS::Acos#new`
311
- #
312
- # * **argument**: `CAS::Op` argument of the function
313
- # * **returns**: `CAS::Acos` operation
314
- def self.acos(x)
315
- CAS::Acos.new x
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
- # Shortcut for `CAS::Atan#new`
480
- #
481
- # * **argument**: `CAS::Op` argument of the function
482
- # * **returns**: `CAS::Atan` operation
483
- def self.atan(x)
484
- CAS::Atan.new x
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
@@ -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
- # Shortcut for `CAS::Ln#new`
182
- #
183
- # * **argument**: `CAS::Op` argument of the function
184
- # * **returns**: `CAS::Ln` operation
185
- def self.ln(x)
186
- CAS::Ln.new x
187
- end
188
-
189
- # Shortcut for `CAS::Ln#new`
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
@@ -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
@@ -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
- @@vars = {}
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
- @@vars
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
- @@vars.keys.size
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
- @@vars[s] || CAS::vars(s)
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
- @@vars.keys.include? name
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
- @@vars[@name] = self
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
- @@vars[name] || super
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.
@@ -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
- dep = false
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
- if self.class.simplify_dict(@x)
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[k]
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
@@ -8,6 +8,6 @@ module CAS
8
8
  # * Major version
9
9
  # * Minor version
10
10
  # * Patchlevel
11
- VERSION = [0, 2, 3]
11
+ VERSION = [0, 2, 5]
12
12
  end
13
13
 
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.3
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-07 00:00:00.000000000 Z
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