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 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