symbolic 0.3.7 → 0.3.8
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +20 -4
- data/Rakefile +5 -16
- data/lib/symbolic.rb +30 -19
- data/lib/symbolic/constant.rb +27 -0
- data/lib/symbolic/constants.rb +15 -0
- data/lib/symbolic/expression.rb +18 -4
- data/lib/symbolic/extensions/kernel.rb +1 -1
- data/lib/symbolic/extensions/matrix.rb +8 -6
- data/lib/symbolic/extensions/module.rb +5 -0
- data/lib/symbolic/extensions/numeric.rb +8 -0
- data/lib/symbolic/extensions/rational.rb +18 -12
- data/lib/symbolic/factors.rb +26 -48
- data/lib/symbolic/function.rb +100 -19
- data/lib/symbolic/math.rb +31 -7
- data/lib/symbolic/misc.rb +5 -0
- data/lib/symbolic/plugins/var_name.rb +42 -0
- data/lib/symbolic/printer.rb +79 -0
- data/lib/symbolic/statistics.rb +13 -8
- data/lib/symbolic/sum.rb +43 -0
- data/lib/symbolic/summands.rb +36 -31
- data/lib/symbolic/variable.rb +23 -11
- data/spec/integration_spec.rb +193 -0
- data/spec/spec_helper.rb +7 -4
- data/spec/symbolic/equal_spec.rb +18 -0
- data/spec/symbolic_spec.rb +31 -123
- data/symbolic.gemspec +64 -0
- metadata +53 -45
- data/.gitignore +0 -22
- data/spec/spec.opts +0 -1
data/README.rdoc
CHANGED
@@ -2,6 +2,8 @@ Symbolic math for ruby.
|
|
2
2
|
|
3
3
|
== Installation
|
4
4
|
|
5
|
+
Symbolic needs Ruby 1.9.
|
6
|
+
|
5
7
|
gem install symbolic
|
6
8
|
|
7
9
|
== Introduction
|
@@ -11,7 +13,7 @@ This gem can help you
|
|
11
13
|
- if you want to speed up similar calculations
|
12
14
|
- if you need an abstraction layer for math
|
13
15
|
|
14
|
-
Symbolic doesn't have any external dependencies.
|
16
|
+
Symbolic doesn't have any external dependencies.
|
15
17
|
|
16
18
|
== Tutorial
|
17
19
|
|
@@ -40,6 +42,14 @@ To get value of symbolic expression you just call value:
|
|
40
42
|
|
41
43
|
f.value # => 7
|
42
44
|
|
45
|
+
You can accomplish the same thing with subs:
|
46
|
+
|
47
|
+
f.subs(x,3) # => 7
|
48
|
+
|
49
|
+
Or make a more complicated substitution:
|
50
|
+
|
51
|
+
f.subs(x,x**2) # => 2*x**2+1
|
52
|
+
|
43
53
|
If symbolic expression contains variables without value then it returns nil.
|
44
54
|
|
45
55
|
z = var
|
@@ -67,16 +77,22 @@ So you can get a list of variables without value:
|
|
67
77
|
|
68
78
|
(x+y+1).variables.select {|var| var.value.nil? }
|
69
79
|
|
70
|
-
You can get
|
80
|
+
You can get information about the number of different operations used in a symbolic expression:
|
71
81
|
|
72
82
|
f = (2*x-y+2)*x-2**(x*y)
|
73
83
|
f.operations # => {"+"=>1, "-"=>2, "*"=>3, "/"=>0, "**"=>1, "-@"=>0}
|
74
84
|
|
85
|
+
You can also take derivitives and do taylor expansions:
|
86
|
+
|
87
|
+
Symbolic::Math.cos(x**2).diff(x)
|
88
|
+
# => -2*(sin(x**2))*x
|
89
|
+
Symbolic::Math.cos(x).taylor(x,0,3)
|
90
|
+
# => -0.5*x**2+1.0
|
91
|
+
|
75
92
|
|
76
93
|
== TODO
|
77
94
|
- a lot of refactoring (code is pretty messy at this stage)
|
78
95
|
- plotting capabilities
|
79
|
-
- derivatives
|
80
96
|
- integrals
|
81
97
|
- thorough documentation
|
82
98
|
|
@@ -85,4 +101,4 @@ You can get an information about number of different operations used in a symbol
|
|
85
101
|
brainopia (ravwar at gmail.com).
|
86
102
|
|
87
103
|
I am ready to help with any questions related to Symbolic.
|
88
|
-
I welcome any contribution.
|
104
|
+
I welcome any contribution.
|
data/Rakefile
CHANGED
@@ -5,13 +5,13 @@ begin
|
|
5
5
|
require 'jeweler'
|
6
6
|
Jeweler::Tasks.new do |gem|
|
7
7
|
gem.name = "symbolic"
|
8
|
-
gem.version = '0.3.
|
8
|
+
gem.version = '0.3.8'
|
9
9
|
gem.summary = 'Symbolic math for ruby'
|
10
10
|
gem.description = 'Symbolic math for ruby. This gem can help if you want to get a simplified form of a big equation or to speed up similar calculations or you need an abstraction layer for math. Symbolic does not have any external dependencies. It uses only pure ruby (less than 400 lines of code).'
|
11
11
|
gem.email = "ravwar@gmail.com"
|
12
12
|
gem.homepage = "http://brainopia.github.com/symbolic"
|
13
13
|
gem.authors = ["brainopia"]
|
14
|
-
gem.add_development_dependency "rspec", ">=
|
14
|
+
gem.add_development_dependency "rspec", ">= 2.4"
|
15
15
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
16
|
end
|
17
17
|
Jeweler::GemcutterTasks.new
|
@@ -19,18 +19,7 @@ rescue LoadError
|
|
19
19
|
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
20
20
|
end
|
21
21
|
|
22
|
-
require '
|
23
|
-
|
24
|
-
spec.libs << 'lib' << 'spec'
|
25
|
-
spec.spec_files = FileList['spec/**/*_spec.rb']
|
26
|
-
end
|
27
|
-
|
28
|
-
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
29
|
-
spec.libs << 'lib' << 'spec'
|
30
|
-
spec.pattern = 'spec/**/*_spec.rb'
|
31
|
-
spec.rcov = true
|
32
|
-
end
|
33
|
-
|
34
|
-
task :spec => :check_dependencies
|
22
|
+
require 'rspec/core/rake_task'
|
23
|
+
RSpec::Core::RakeTask.new(:spec)
|
35
24
|
|
36
|
-
task :default => :spec
|
25
|
+
task :default => :spec
|
data/lib/symbolic.rb
CHANGED
@@ -1,17 +1,3 @@
|
|
1
|
-
require 'symbolic/coerced'
|
2
|
-
require 'symbolic/variable'
|
3
|
-
require 'symbolic/expression'
|
4
|
-
require 'symbolic/summands'
|
5
|
-
require 'symbolic/factors'
|
6
|
-
require 'symbolic/function'
|
7
|
-
require 'symbolic/math'
|
8
|
-
require 'symbolic/statistics'
|
9
|
-
|
10
|
-
require 'symbolic/extensions/kernel'
|
11
|
-
require 'symbolic/extensions/numeric'
|
12
|
-
require 'symbolic/extensions/matrix' if Object.const_defined? 'Matrix'
|
13
|
-
require 'symbolic/extensions/rational' if RUBY_VERSION == '1.8.7'
|
14
|
-
|
15
1
|
module Symbolic
|
16
2
|
def +@
|
17
3
|
self
|
@@ -45,13 +31,38 @@ module Symbolic
|
|
45
31
|
[Coerced.new(self), numeric]
|
46
32
|
end
|
47
33
|
|
34
|
+
def to_s
|
35
|
+
Printer.print(self)
|
36
|
+
end
|
37
|
+
|
48
38
|
def inspect
|
49
39
|
"Symbolic(#{to_s})"
|
50
40
|
end
|
51
41
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
42
|
+
def taylor(var, about, numterms=5)
|
43
|
+
term = self
|
44
|
+
#inject needs initial value to prevent it from eating the first term
|
45
|
+
(0..numterms-1).inject(0) do |sum,n|
|
46
|
+
to_add = term.subs(var,about) * (var - about) ** n / factorial(n)
|
47
|
+
term = term.diff(var) #save a little time by not having to do all the derivites every time
|
48
|
+
sum + to_add
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
#make multiple substitutions using a hash. Ex: (x+y+z).subs({x=>2*y,z=>y**2}) results in y**2+3*y
|
53
|
+
def subs(hsh)
|
54
|
+
temp = self
|
55
|
+
hsh.each{|k,v| temp = temp.subs(k,v)}
|
56
|
+
temp
|
56
57
|
end
|
57
|
-
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
#in order they should be loaded
|
62
|
+
['symbolic/expression.rb','symbolic/coerced.rb','symbolic/constants.rb','symbolic/factors.rb',
|
63
|
+
'symbolic/printer.rb','symbolic/sum.rb','symbolic/variable.rb','symbolic/constant.rb',
|
64
|
+
'symbolic/function.rb','symbolic/misc.rb','symbolic/statistics.rb',
|
65
|
+
'symbolic/summands.rb','symbolic/extensions/kernel.rb','symbolic/extensions/matrix.rb','symbolic/extensions/module.rb',
|
66
|
+
'symbolic/extensions/numeric.rb','symbolic/extensions/rational.rb','symbolic/math.rb'].each do |file|
|
67
|
+
require File.dirname(__FILE__) + '/' + file
|
68
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Symbolic
|
2
|
+
class Constant
|
3
|
+
include Symbolic
|
4
|
+
attr_reader :name, :value
|
5
|
+
|
6
|
+
# Create a new Symbolic::Variable, with optional name, value and proc
|
7
|
+
def initialize(value , name = nil)
|
8
|
+
@name, @value = name, value
|
9
|
+
@name = @name.to_s if @name
|
10
|
+
end
|
11
|
+
def subs(to_replace, replacement=nil)
|
12
|
+
if replacement == nil and to_replace.is_a?(Hash)
|
13
|
+
super(to_replace)
|
14
|
+
else
|
15
|
+
return replacement if self == to_replace
|
16
|
+
self
|
17
|
+
end
|
18
|
+
end
|
19
|
+
def diff(wrt)
|
20
|
+
0
|
21
|
+
end
|
22
|
+
#yeah, it's not a variable, but it acts like one as far as value is concerned
|
23
|
+
def variables
|
24
|
+
[self]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'complex'
|
4
|
+
module Symbolic::Constants
|
5
|
+
require "#{File.dirname(__FILE__)}/constant.rb"
|
6
|
+
if RUBY_VERSION < '1.9'
|
7
|
+
PI = Symbolic::Constant.new(::Math::PI,'PI')
|
8
|
+
I = Symbolic::Constant.new(::Complex::I,'i')
|
9
|
+
E = Symbolic::Constant.new(::Math::E,'e')
|
10
|
+
else #we can use unicode
|
11
|
+
PI = Symbolic::Constant.new(::Math::PI,'π')
|
12
|
+
I = Symbolic::Constant.new(::Complex::I,'ⅈ')
|
13
|
+
E = Symbolic::Constant.new(::Math::E,'ⅇ')
|
14
|
+
end
|
15
|
+
end
|
data/lib/symbolic/expression.rb
CHANGED
@@ -23,7 +23,7 @@ module Symbolic
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def unite_numeric(numeric1, numeric2)
|
26
|
-
numeric1.send
|
26
|
+
numeric1.send self::OPERATION, numeric2
|
27
27
|
end
|
28
28
|
|
29
29
|
def convert(var)
|
@@ -39,7 +39,7 @@ module Symbolic
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def one(symbolic)
|
42
|
-
new
|
42
|
+
new self::IDENTITY, symbolic => 1
|
43
43
|
end
|
44
44
|
|
45
45
|
def simple?(var)
|
@@ -63,11 +63,25 @@ module Symbolic
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def variables
|
66
|
-
@symbolic.map {|k,v| [
|
66
|
+
@symbolic.map {|k,v| [k.variables, v.variables] }.flatten.uniq
|
67
67
|
end
|
68
68
|
|
69
69
|
def ==(object)
|
70
|
-
|
70
|
+
# We need to make sure the classes are the same because both Factors and
|
71
|
+
# Summands have .numeric and .symbolic, but we can't say they're equal
|
72
|
+
object.class == self.class and
|
73
|
+
object.numeric == @numeric and
|
74
|
+
# Make sure that we have the same number of elements, otherwise the
|
75
|
+
# next step could give false positives
|
76
|
+
object.symbolic.size == @symbolic.size and
|
77
|
+
# hash's == function only checks that the object_ids are equal, but we
|
78
|
+
# could have different instances of the same object (mathematically speaking). We
|
79
|
+
# need to check that each thing in @symbolic appears in object.symbolic as well.
|
80
|
+
object.symbolic.inject(true) do |memo,(key,value)| # go through each kv pair in object.symbolic
|
81
|
+
memo and @symbolic.inject(false) do |memo2,(key2,value2)|# and make sure it appears in @symbolic
|
82
|
+
memo2 or (key2 == key and value2 == value)
|
83
|
+
end
|
84
|
+
end
|
71
85
|
end
|
72
86
|
end
|
73
87
|
end
|
@@ -1,9 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
if Object.const_defined? 'Matrix'
|
2
|
+
class Matrix
|
3
|
+
def value
|
4
|
+
map {|it| it.value }
|
5
|
+
end
|
5
6
|
|
6
|
-
|
7
|
-
|
7
|
+
def variables
|
8
|
+
map {|it| it.variables }.to_a.flatten.uniq
|
9
|
+
end
|
8
10
|
end
|
9
11
|
end
|
@@ -1,13 +1,19 @@
|
|
1
|
-
# Fix for rational on 1.
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
# Fix for Integer#** defined in stdlib rational.rb on ruby < 1.9
|
2
|
+
# We need to redefine Integer#**(other)
|
3
|
+
# Because, with Rational, it uses a comparaison such as other >= 0
|
4
|
+
# That cannot work with Symbolic::Variable (or make sense)
|
5
|
+
if RUBY_VERSION < '1.9'
|
6
|
+
require 'rational'
|
7
|
+
[Fixnum,Bignum].each do |klass|
|
8
|
+
klass.class_eval do
|
9
|
+
alias :old_power :**
|
10
|
+
def **(other)
|
11
|
+
if Numeric === other # If other is Numeric, we can use rpower(the new #** defined in stdlib rational.rb)
|
12
|
+
old_power(other)
|
13
|
+
else # But if not, we want the old behaviour(that was aliased to power!, and does not check if >= 0)
|
14
|
+
power!(other)
|
15
|
+
end
|
9
16
|
end
|
10
|
-
end
|
11
|
-
|
12
|
-
|
13
|
-
end
|
17
|
+
end # class_eval
|
18
|
+
end # each
|
19
|
+
end # if
|
data/lib/symbolic/factors.rb
CHANGED
@@ -1,14 +1,8 @@
|
|
1
1
|
module Symbolic
|
2
2
|
class Factors < Expression
|
3
|
+
OPERATION = :*
|
4
|
+
IDENTITY = 1
|
3
5
|
class << self
|
4
|
-
def operation
|
5
|
-
'*'
|
6
|
-
end
|
7
|
-
|
8
|
-
def identity_element
|
9
|
-
1
|
10
|
-
end
|
11
|
-
|
12
6
|
def summands(summands)
|
13
7
|
one summands
|
14
8
|
end
|
@@ -48,23 +42,23 @@ module Symbolic
|
|
48
42
|
end
|
49
43
|
|
50
44
|
def simplify_expression!(factors)
|
51
|
-
factors[1].delete_if {|base, exp| (base ==
|
52
|
-
factors[0] = 0 if factors[1].any? {|base,
|
45
|
+
factors[1].delete_if {|base, exp| (base == IDENTITY) || (exp == 0) }
|
46
|
+
factors[0] = 0 if factors[1].any? {|base, _| base == 0 }
|
53
47
|
end
|
54
48
|
|
55
49
|
def simplify(numeric, symbolic)
|
56
50
|
if numeric == 0 || symbolic.empty?
|
57
51
|
(numeric.round == numeric) ? numeric.to_i : numeric.to_f
|
58
|
-
elsif numeric ==
|
59
|
-
symbolic.
|
52
|
+
elsif numeric == IDENTITY && symbolic.size == 1 && symbolic.first[1] == 1
|
53
|
+
symbolic.first[0]
|
60
54
|
end
|
61
55
|
end
|
62
56
|
|
63
57
|
def unite_exponents(base, exponent)
|
64
58
|
if base.is_a? Factors
|
65
|
-
|
59
|
+
[base.numeric**exponent, Hash[*base.symbolic.map {|b,e| [b, e*exponent] }.flatten]]
|
66
60
|
else
|
67
|
-
[
|
61
|
+
[IDENTITY, { base => exponent }]
|
68
62
|
end
|
69
63
|
end
|
70
64
|
end
|
@@ -74,48 +68,32 @@ module Symbolic
|
|
74
68
|
end
|
75
69
|
|
76
70
|
def value
|
77
|
-
if variables.all?
|
71
|
+
if variables.all?(&:value)
|
78
72
|
@symbolic.inject(numeric) {|value, (base, exp)| value * base.value ** exp.value }
|
79
73
|
end
|
80
74
|
end
|
81
75
|
|
82
|
-
def
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
end
|
89
|
-
|
90
|
-
def exponent_to_string(base, exponent)
|
91
|
-
"#{brackets base}#{"**#{brackets exponent}" if exponent != 1}"
|
92
|
-
end
|
93
|
-
|
94
|
-
def brackets(var)
|
95
|
-
[Numeric, Variable, Function].any? {|klass| var.is_a? klass } ? var : "(#{var})"
|
96
|
-
end
|
97
|
-
|
98
|
-
def simplify_output
|
99
|
-
groups = @symbolic.group_by {|b,e| e.is_a?(Numeric) && e < 0 }
|
100
|
-
reversed_factors = groups[true] ? [1, Hash[*groups[true].flatten] ] : nil
|
101
|
-
factors = groups[false] ? [@numeric, Hash[*groups[false].flatten] ] : nil
|
102
|
-
output = '' << (factors ? output(factors) : rational_to_string(@numeric))
|
103
|
-
output << "/#{reversed_output reversed_factors}" if reversed_factors
|
104
|
-
output
|
105
|
-
end
|
106
|
-
|
107
|
-
def output(factors)
|
108
|
-
coefficient_to_string(factors[0]) <<
|
109
|
-
factors[1].map {|base,exp| exponent_to_string base,exp }.join('*')
|
76
|
+
def subs(to_replace, replacement=nil)
|
77
|
+
if replacement == nil and to_replace.is_a?(Hash)
|
78
|
+
super(to_replace)
|
79
|
+
else
|
80
|
+
@symbolic.inject(@numeric){|m,(base,exponential)| m * base.subs(to_replace, replacement) ** exponential.subs(to_replace, replacement)}
|
81
|
+
end
|
110
82
|
end
|
111
83
|
|
112
|
-
def
|
113
|
-
|
114
|
-
(factors[1].length > 1) ? "(#{result})" : result
|
84
|
+
def value
|
85
|
+
@symbolic.inject(@numeric){|m,(base,exponential)| m * base.value ** exponential.value}
|
115
86
|
end
|
116
87
|
|
117
|
-
def
|
118
|
-
|
88
|
+
def diff(wrt)
|
89
|
+
return 0 unless self.variables.include?(wrt) #speed things up a bit
|
90
|
+
first_base, first_exp = @symbolic.to_a[0]
|
91
|
+
first = first_base ** first_exp #the first factor
|
92
|
+
self_without_first = self / first #the expression with the first factor removed
|
93
|
+
#product rule to find derivitive
|
94
|
+
udv = self_without_first.is_a?(Symbolic) ? first * self_without_first.diff(wrt) : 0
|
95
|
+
vdu = first_base.is_a?(Symbolic) ? first_exp * first_base ** (first_exp - 1 ) * first_base.diff(wrt) * self_without_first : 0
|
96
|
+
udv + vdu
|
119
97
|
end
|
120
98
|
end
|
121
99
|
end
|
data/lib/symbolic/function.rb
CHANGED
@@ -1,26 +1,107 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
=end
|
5
|
-
include Symbolic
|
6
|
-
|
7
|
-
def initialize(argument, operation)
|
8
|
-
@argument, @operation = argument, operation
|
9
|
-
end
|
1
|
+
module Symbolic
|
2
|
+
class Function
|
3
|
+
attr_reader :name, :deriv, :operation
|
10
4
|
|
11
|
-
|
12
|
-
|
13
|
-
|
5
|
+
#deriv can be either a function or a proc that takes an argument -- it can be set later
|
6
|
+
#operation can be passed as a block, a proc, or set later
|
7
|
+
def initialize(name, deriv = nil, op = nil, &block)
|
8
|
+
@name, @deriv = name, deriv
|
9
|
+
unless op == nil
|
10
|
+
@operation = op
|
11
|
+
else
|
12
|
+
@operation = block
|
13
|
+
end
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
#it may be easier to set the derivitve after the object is made
|
17
|
+
def set_derivative(deriv)
|
18
|
+
#only allow it to be set if @derivative is nil
|
19
|
+
@deriv = deriv if @deriv == nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def set_operation(op)
|
23
|
+
#only allow it to be set if @derivative is nil
|
24
|
+
@operation = op if @operation == nil
|
25
|
+
end
|
18
26
|
|
19
|
-
|
20
|
-
|
27
|
+
#returns a FunctionWrapper with the argument or a number
|
28
|
+
def [] (arg)
|
29
|
+
if arg.is_a?(::Numeric) #take care of the case where arg is a number
|
30
|
+
self.call(arg)
|
31
|
+
else
|
32
|
+
FunctionWrapper.new(arg, self)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
#same but with different syntax
|
37
|
+
def arg(arg)
|
38
|
+
self[arg]
|
39
|
+
end
|
40
|
+
|
41
|
+
#returns the derivitve with arg plugged in -- for use with chainrule
|
42
|
+
def derivative(arg)
|
43
|
+
if @deriv.is_a?(Proc)
|
44
|
+
@deriv.call(arg)
|
45
|
+
elsif @deriv.is_a?(Function)
|
46
|
+
@deriv[arg]
|
47
|
+
else #by process of elimination, it's a Symbolic
|
48
|
+
@deriv.subs(Symbolic::Math::Arg,arg)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def call(arg)
|
53
|
+
@operation.call(arg)
|
54
|
+
end
|
21
55
|
end
|
22
56
|
|
23
|
-
|
24
|
-
|
57
|
+
#class combines a function with an argument or arguments
|
58
|
+
#this class is what allows functions to be used in symbolic expressions
|
59
|
+
class FunctionWrapper
|
60
|
+
include Symbolic
|
61
|
+
attr_reader :argument, :function
|
62
|
+
|
63
|
+
def initialize(arg, fctn)
|
64
|
+
@argument, @function = arg, fctn
|
65
|
+
end
|
66
|
+
|
67
|
+
def name
|
68
|
+
@function.name
|
69
|
+
end
|
70
|
+
|
71
|
+
def value
|
72
|
+
@function.call(@argument.value)
|
73
|
+
end
|
74
|
+
|
75
|
+
def variables
|
76
|
+
@argument.variables
|
77
|
+
end
|
78
|
+
|
79
|
+
def detailed_operations
|
80
|
+
@argument.detailed_operations.tap {|it| it[@operation] += 1}
|
81
|
+
end
|
82
|
+
|
83
|
+
def subs(to_replace, replacement=nil)
|
84
|
+
if replacement == nil and to_replace.is_a?(Hash)
|
85
|
+
super(to_replace)
|
86
|
+
else
|
87
|
+
@function[@argument.subs(to_replace, replacement)]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
#simply dumps @argument in to the function -- no gaurentee that the function
|
92
|
+
#will know how to handle it. Useful in some circumstances
|
93
|
+
def eval
|
94
|
+
@function.call(@argument)
|
95
|
+
end
|
96
|
+
|
97
|
+
def ==(object)
|
98
|
+
(object.function == @function and object.argument == @argument) rescue false
|
99
|
+
end
|
100
|
+
|
101
|
+
def diff(wrt)
|
102
|
+
return 0 unless self.variables.member?(wrt)
|
103
|
+
#chain rule
|
104
|
+
@function.derivative(@argument) * @argument.diff(wrt)
|
105
|
+
end
|
25
106
|
end
|
26
107
|
end
|