keisan 0.4.0 → 0.5.0
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
- data/.travis.yml +3 -5
- data/README.md +112 -8
- data/bin/keisan +7 -0
- data/lib/keisan.rb +0 -1
- data/lib/keisan/ast.rb +24 -0
- data/lib/keisan/ast/assignment.rb +36 -10
- data/lib/keisan/ast/builder.rb +7 -1
- data/lib/keisan/ast/constant_literal.rb +6 -1
- data/lib/keisan/ast/exponent.rb +1 -1
- data/lib/keisan/ast/function.rb +7 -3
- data/lib/keisan/ast/list.rb +1 -1
- data/lib/keisan/ast/node.rb +1 -1
- data/lib/keisan/ast/parent.rb +1 -1
- data/lib/keisan/ast/plus.rb +11 -1
- data/lib/keisan/ast/string.rb +8 -0
- data/lib/keisan/ast/unary_operator.rb +1 -1
- data/lib/keisan/ast/variable.rb +8 -2
- data/lib/keisan/calculator.rb +16 -0
- data/lib/keisan/context.rb +39 -8
- data/lib/keisan/evaluator.rb +11 -1
- data/lib/keisan/function.rb +4 -0
- data/lib/keisan/functions/abs.rb +9 -0
- data/lib/keisan/functions/cbrt.rb +15 -0
- data/lib/keisan/functions/cmath_function.rb +11 -0
- data/lib/keisan/functions/cos.rb +15 -0
- data/lib/keisan/functions/cosh.rb +15 -0
- data/lib/keisan/functions/cot.rb +15 -0
- data/lib/keisan/functions/coth.rb +15 -0
- data/lib/keisan/functions/csc.rb +15 -0
- data/lib/keisan/functions/csch.rb +15 -0
- data/lib/keisan/functions/default_registry.rb +95 -1
- data/lib/keisan/functions/diff.rb +36 -14
- data/lib/keisan/functions/exp.rb +15 -0
- data/lib/keisan/functions/expression_function.rb +65 -15
- data/lib/keisan/functions/filter.rb +64 -0
- data/lib/keisan/functions/if.rb +15 -12
- data/lib/keisan/functions/imag.rb +9 -0
- data/lib/keisan/functions/log.rb +15 -0
- data/lib/keisan/functions/map.rb +57 -0
- data/lib/keisan/functions/math_function.rb +34 -0
- data/lib/keisan/functions/proc_function.rb +5 -3
- data/lib/keisan/functions/real.rb +9 -0
- data/lib/keisan/functions/reduce.rb +65 -0
- data/lib/keisan/functions/registry.rb +5 -1
- data/lib/keisan/functions/sec.rb +15 -0
- data/lib/keisan/functions/sech.rb +15 -0
- data/lib/keisan/functions/sin.rb +15 -0
- data/lib/keisan/functions/sinh.rb +15 -0
- data/lib/keisan/functions/sqrt.rb +15 -0
- data/lib/keisan/functions/tan.rb +15 -0
- data/lib/keisan/functions/tanh.rb +15 -0
- data/lib/keisan/repl.rb +105 -0
- data/lib/keisan/tokenizer.rb +5 -2
- data/lib/keisan/tokens/string.rb +1 -1
- data/lib/keisan/variables/registry.rb +14 -3
- data/lib/keisan/version.rb +1 -1
- data/screenshots/repl.png +0 -0
- metadata +30 -6
- data/lib/keisan/ast/functions/diff.rb +0 -57
- data/lib/keisan/ast/functions/if.rb +0 -47
- data/lib/keisan/function_definition_context.rb +0 -34
@@ -0,0 +1,15 @@
|
|
1
|
+
module Keisan
|
2
|
+
module Functions
|
3
|
+
class Csch < CMathFunction
|
4
|
+
def initialize
|
5
|
+
super("csch", Proc.new {|arg| 1 / CMath::sinh(arg)})
|
6
|
+
end
|
7
|
+
|
8
|
+
protected
|
9
|
+
|
10
|
+
def self.derivative(argument)
|
11
|
+
-Keisan::AST::Function.new([argument], "cosh") * Keisan::AST::Exponent.new([Keisan::AST::Function.new([argument], "sinh"), -2])
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,8 +1,32 @@
|
|
1
1
|
require_relative "if"
|
2
2
|
require_relative "diff"
|
3
3
|
require_relative "replace"
|
4
|
+
require_relative "map"
|
5
|
+
require_relative "filter"
|
6
|
+
require_relative "reduce"
|
4
7
|
require_relative "rand"
|
5
8
|
require_relative "sample"
|
9
|
+
require_relative "math_function"
|
10
|
+
require_relative "cmath_function"
|
11
|
+
require_relative "exp"
|
12
|
+
require_relative "log"
|
13
|
+
require_relative "sin"
|
14
|
+
require_relative "cos"
|
15
|
+
require_relative "sec"
|
16
|
+
require_relative "tan"
|
17
|
+
require_relative "cot"
|
18
|
+
require_relative "csc"
|
19
|
+
require_relative "sinh"
|
20
|
+
require_relative "cosh"
|
21
|
+
require_relative "sech"
|
22
|
+
require_relative "tanh"
|
23
|
+
require_relative "coth"
|
24
|
+
require_relative "csch"
|
25
|
+
require_relative "sqrt"
|
26
|
+
require_relative "cbrt"
|
27
|
+
require_relative "abs"
|
28
|
+
require_relative "real"
|
29
|
+
require_relative "imag"
|
6
30
|
|
7
31
|
module Keisan
|
8
32
|
module Functions
|
@@ -19,10 +43,40 @@ module Keisan
|
|
19
43
|
registry.register!(:if, Keisan::Functions::If.new, force: true)
|
20
44
|
registry.register!(:diff, Keisan::Functions::Diff.new, force: true)
|
21
45
|
registry.register!(:replace, Keisan::Functions::Replace.new, force: true)
|
46
|
+
registry.register!(:map, Keisan::Functions::Map.new, force: true)
|
47
|
+
registry.register!(:collect, Keisan::Functions::Map.new, force: true)
|
48
|
+
registry.register!(:filter, Keisan::Functions::Filter.new, force: true)
|
49
|
+
registry.register!(:select, Keisan::Functions::Filter.new, force: true)
|
50
|
+
registry.register!(:reduce, Keisan::Functions::Reduce.new, force: true)
|
51
|
+
registry.register!(:inject, Keisan::Functions::Reduce.new, force: true)
|
22
52
|
|
23
53
|
register_builtin_math!(registry)
|
24
54
|
register_array_methods!(registry)
|
25
55
|
register_random_methods!(registry)
|
56
|
+
|
57
|
+
registry.register!(:exp, Keisan::Functions::Exp.new, force: true)
|
58
|
+
registry.register!(:log, Keisan::Functions::Log.new, force: true)
|
59
|
+
|
60
|
+
registry.register!(:sin, Keisan::Functions::Sin.new, force: true)
|
61
|
+
registry.register!(:cos, Keisan::Functions::Cos.new, force: true)
|
62
|
+
registry.register!(:tan, Keisan::Functions::Tan.new, force: true)
|
63
|
+
registry.register!(:cot, Keisan::Functions::Cot.new, force: true)
|
64
|
+
registry.register!(:sec, Keisan::Functions::Sec.new, force: true)
|
65
|
+
registry.register!(:csc, Keisan::Functions::Csc.new, force: true)
|
66
|
+
|
67
|
+
registry.register!(:sinh, Keisan::Functions::Sinh.new, force: true)
|
68
|
+
registry.register!(:cosh, Keisan::Functions::Cosh.new, force: true)
|
69
|
+
registry.register!(:tanh, Keisan::Functions::Tanh.new, force: true)
|
70
|
+
registry.register!(:coth, Keisan::Functions::Coth.new, force: true)
|
71
|
+
registry.register!(:sech, Keisan::Functions::Sech.new, force: true)
|
72
|
+
registry.register!(:csch, Keisan::Functions::Csch.new, force: true)
|
73
|
+
|
74
|
+
registry.register!(:sqrt, Keisan::Functions::Sqrt.new, force: true)
|
75
|
+
registry.register!(:cbrt, Keisan::Functions::Cbrt.new, force: true)
|
76
|
+
|
77
|
+
registry.register!(:abs, Keisan::Functions::Abs.new, force: true)
|
78
|
+
registry.register!(:real, Keisan::Functions::Real.new, force: true)
|
79
|
+
registry.register!(:imag, Keisan::Functions::Imag.new, force: true)
|
26
80
|
end
|
27
81
|
|
28
82
|
def self.register_builtin_math!(registry)
|
@@ -30,6 +84,7 @@ module Keisan
|
|
30
84
|
registry.register!(
|
31
85
|
method,
|
32
86
|
Proc.new {|*args|
|
87
|
+
args = args.map(&:value)
|
33
88
|
Math.send(method, *args)
|
34
89
|
},
|
35
90
|
force: true
|
@@ -38,9 +93,48 @@ module Keisan
|
|
38
93
|
end
|
39
94
|
|
40
95
|
def self.register_array_methods!(registry)
|
41
|
-
%i(min max size).each do |method|
|
96
|
+
%i(min max size flatten reverse).each do |method|
|
42
97
|
registry.register!(method, Proc.new {|a| a.send(method)}, force: true)
|
43
98
|
end
|
99
|
+
|
100
|
+
# range(10) => Integers from 0 inclusive to 10 exclusively
|
101
|
+
# range(5, 15) => Integers from 5 inclusive to 15 exclusive
|
102
|
+
# range(10, -1, -2) => Integers from 10 inclusive to -1 exclusive, decreasing by twos
|
103
|
+
# i.e.: [10, 8, 6, 4, 2, 0]
|
104
|
+
registry.register!("range", Proc.new {|*args|
|
105
|
+
case args.count
|
106
|
+
when 1
|
107
|
+
(0...args[0]).to_a
|
108
|
+
when 2
|
109
|
+
(args[0]...args[1]).to_a
|
110
|
+
when 3
|
111
|
+
current = args[0]
|
112
|
+
final = args[1]
|
113
|
+
shift = args[2]
|
114
|
+
|
115
|
+
if shift == 0 or !shift.is_a?(Integer)
|
116
|
+
raise Keisan::Exceptions::InvalidFunctionError.new("range's 3rd argument must be non-zero integer")
|
117
|
+
end
|
118
|
+
|
119
|
+
result = []
|
120
|
+
|
121
|
+
if shift > 0
|
122
|
+
while current < final
|
123
|
+
result << current
|
124
|
+
current += shift
|
125
|
+
end
|
126
|
+
else
|
127
|
+
while current > final
|
128
|
+
result << current
|
129
|
+
current += shift
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
result
|
134
|
+
else
|
135
|
+
raise Keisan::Exceptions::InvalidFunctionError.new("range takes 1 to 3 arguments")
|
136
|
+
end
|
137
|
+
}, force: true)
|
44
138
|
end
|
45
139
|
|
46
140
|
def self.register_random_methods!(registry)
|
@@ -17,29 +17,46 @@ module Keisan
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def evaluate(ast_function, context = nil)
|
20
|
-
context ||= Context.new
|
21
|
-
function,
|
20
|
+
context ||= Keisan::Context.new
|
21
|
+
function, variables = function_and_variables(ast_function)
|
22
|
+
local = context_from(variables, context)
|
22
23
|
|
23
|
-
|
24
|
-
result = differentiate(result, variable,
|
24
|
+
result = variables.inject(function.evaluate(local)) do |result, variable|
|
25
|
+
result = differentiate(result, variable, local)
|
25
26
|
if !is_ast_derivative?(result)
|
26
|
-
result = result.evaluate(
|
27
|
+
result = result.evaluate(local)
|
27
28
|
end
|
28
29
|
result
|
29
30
|
end
|
31
|
+
|
32
|
+
case result
|
33
|
+
when Keisan::AST::Function
|
34
|
+
result.name == "diff" ? result : result.simplify(context)
|
35
|
+
else
|
36
|
+
result.simplify(context)
|
37
|
+
end
|
30
38
|
end
|
31
39
|
|
32
40
|
def simplify(ast_function, context = nil)
|
33
|
-
|
34
|
-
function,
|
41
|
+
raise Keisan::Exceptions::InternalError.new("received non-diff function") unless ast_function.name == "diff"
|
42
|
+
function, variables = function_and_variables(ast_function)
|
43
|
+
context ||= Keisan::Context.new
|
44
|
+
local = context_from(variables, context)
|
35
45
|
|
36
|
-
|
37
|
-
result = differentiate(result, variable,
|
46
|
+
result = variables.inject(function.simplify(local)) do |result, variable|
|
47
|
+
result = differentiate(result, variable, local)
|
38
48
|
if !is_ast_derivative?(result)
|
39
|
-
result = result.simplify(
|
49
|
+
result = result.simplify(local)
|
40
50
|
end
|
41
51
|
result
|
42
52
|
end
|
53
|
+
|
54
|
+
case result
|
55
|
+
when Keisan::AST::Function
|
56
|
+
result.name == "diff" ? result : result.simplify(context)
|
57
|
+
else
|
58
|
+
result.simplify(context)
|
59
|
+
end
|
43
60
|
end
|
44
61
|
|
45
62
|
private
|
@@ -61,22 +78,27 @@ module Keisan
|
|
61
78
|
)
|
62
79
|
end
|
63
80
|
|
64
|
-
def
|
81
|
+
def function_and_variables(ast_function)
|
65
82
|
unless ast_function.is_a?(Keisan::AST::Function) && ast_function.name == name
|
66
83
|
raise Keisan::Exceptions::InvalidFunctionError.new("Must receive diff function")
|
67
84
|
end
|
68
85
|
|
69
|
-
|
86
|
+
variables = ast_function.children[1..-1]
|
70
87
|
|
71
|
-
unless
|
88
|
+
unless variables.all? {|var| var.is_a?(AST::Variable)}
|
72
89
|
raise Keisan::Exceptions::InvalidFunctionError.new("Diff must differentiate with respect to variables")
|
73
90
|
end
|
74
91
|
|
75
92
|
[
|
76
93
|
ast_function.children.first,
|
77
|
-
|
94
|
+
variables
|
78
95
|
]
|
79
96
|
end
|
97
|
+
|
98
|
+
def context_from(variables, context = nil)
|
99
|
+
context ||= Keisan::Context.new(shadowed: variables.map(&:name))
|
100
|
+
context.spawn_child(shadowed: variables.map(&:name))
|
101
|
+
end
|
80
102
|
end
|
81
103
|
end
|
82
104
|
end
|
@@ -3,19 +3,17 @@ module Keisan
|
|
3
3
|
class ExpressionFunction < Keisan::Function
|
4
4
|
attr_reader :arguments, :expression
|
5
5
|
|
6
|
-
def initialize(name, arguments, expression,
|
6
|
+
def initialize(name, arguments, expression, transient_definitions)
|
7
7
|
super(name)
|
8
8
|
@expression = expression.deep_dup
|
9
9
|
@arguments = arguments
|
10
|
-
@
|
10
|
+
@transient_definitions = transient_definitions
|
11
11
|
end
|
12
12
|
|
13
13
|
def call(context, *args)
|
14
|
-
|
15
|
-
raise Keisan::Exceptions::InvalidFunctionError.new("Invalid number of arguments for #{name} function")
|
16
|
-
end
|
14
|
+
verify_argument_size!(args.count)
|
17
15
|
|
18
|
-
local =
|
16
|
+
local = local_context_for(context)
|
19
17
|
arguments.each.with_index do |arg_name, i|
|
20
18
|
local.register_variable!(arg_name, args[i])
|
21
19
|
end
|
@@ -24,28 +22,30 @@ module Keisan
|
|
24
22
|
end
|
25
23
|
|
26
24
|
def value(ast_function, context = nil)
|
25
|
+
verify_argument_size!(ast_function.children.count)
|
26
|
+
|
27
27
|
context ||= Keisan::Context.new
|
28
28
|
argument_values = ast_function.children.map {|child| child.value(context)}
|
29
29
|
call(context, *argument_values)
|
30
30
|
end
|
31
31
|
|
32
32
|
def evaluate(ast_function, context = nil)
|
33
|
+
verify_argument_size!(ast_function.children.count)
|
34
|
+
|
33
35
|
context ||= Keisan::Context.new
|
36
|
+
local = local_context_for(context)
|
34
37
|
|
35
|
-
ast_function.
|
36
|
-
:@children,
|
37
|
-
ast_function.children.map {|child| child.evaluate(context)}
|
38
|
-
)
|
38
|
+
argument_values = ast_function.children.map {|child| child.evaluate(context)}
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
else
|
43
|
-
ast_function
|
40
|
+
arguments.each.with_index do |arg_name, i|
|
41
|
+
local.register_variable!(arg_name, argument_values[i].evaluate(context))
|
44
42
|
end
|
43
|
+
|
44
|
+
expression.evaluate(local)
|
45
45
|
end
|
46
46
|
|
47
47
|
def simplify(ast_function, context = nil)
|
48
|
-
|
48
|
+
verify_argument_size!(ast_function.children.count)
|
49
49
|
|
50
50
|
ast_function.instance_variable_set(
|
51
51
|
:@children,
|
@@ -58,6 +58,56 @@ module Keisan
|
|
58
58
|
ast_function
|
59
59
|
end
|
60
60
|
end
|
61
|
+
|
62
|
+
# Multi-argument functions work as follows:
|
63
|
+
# Given f(x, y), in general we will take the derivative with respect to t,
|
64
|
+
# and x = x(t), y = y(t). For instance d/dt f(2*t, t+1).
|
65
|
+
# In this case, chain rule gives derivative:
|
66
|
+
# dx(t)/dt * f_x(x(t), y(t)) + dy(t)/dt * f_y(x(t), y(t)),
|
67
|
+
# where f_x and f_y are the x and y partial derivatives respectively.
|
68
|
+
def differentiate(ast_function, variable, context = nil)
|
69
|
+
verify_argument_size!(ast_function.children.count)
|
70
|
+
|
71
|
+
local = local_context_for(context)
|
72
|
+
|
73
|
+
# expression.differentiate(variable, context)
|
74
|
+
|
75
|
+
argument_values = ast_function.children.map {|child| child.evaluate(local)}
|
76
|
+
|
77
|
+
argument_derivatives = ast_function.children.map do |child|
|
78
|
+
child.differentiate(variable, context)
|
79
|
+
end
|
80
|
+
|
81
|
+
Keisan::AST::Plus.new(
|
82
|
+
argument_derivatives.map.with_index {|argument_derivative, i|
|
83
|
+
partial_derivative = partial_derivatives[i].replace(argument_variables[i], argument_values[i])
|
84
|
+
Keisan::AST::Times.new([argument_derivative, partial_derivative])
|
85
|
+
}
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def argument_variables
|
92
|
+
@argument_variables ||= arguments.map {|argument| Keisan::AST::Variable.new(argument)}
|
93
|
+
end
|
94
|
+
|
95
|
+
def partial_derivatives
|
96
|
+
@partial_derivatives ||= argument_variables.map.with_index do |variable, i|
|
97
|
+
partial_derivative = expression.differentiate(variable)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def verify_argument_size!(argument_size)
|
102
|
+
unless @arguments.count == argument_size
|
103
|
+
raise Keisan::Exceptions::InvalidFunctionError.new("Invalid number of arguments for #{name} function")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def local_context_for(context = nil)
|
108
|
+
context ||= Keisan::Context.new
|
109
|
+
context.spawn_child(definitions: @transient_definitions, shadowed: @arguments, transient: true)
|
110
|
+
end
|
61
111
|
end
|
62
112
|
end
|
63
113
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Keisan
|
2
|
+
module Functions
|
3
|
+
class Filter < Keisan::Function
|
4
|
+
# Filters (list, variable, expression)
|
5
|
+
# e.g. filter([1,2,3,4], x, x % 2 == 0)
|
6
|
+
# should give [2,4]
|
7
|
+
def initialize
|
8
|
+
@name = "filter"
|
9
|
+
end
|
10
|
+
|
11
|
+
def value(ast_function, context = nil)
|
12
|
+
evaluate(ast_function, context = nil)
|
13
|
+
end
|
14
|
+
|
15
|
+
def evaluate(ast_function, context = nil)
|
16
|
+
context ||= Context.new
|
17
|
+
simplify(ast_function, context).evaluate(context)
|
18
|
+
end
|
19
|
+
|
20
|
+
def simplify(ast_function, context = nil)
|
21
|
+
list, variable, expression = list_variable_expression_for(ast_function, context)
|
22
|
+
|
23
|
+
context ||= Keisan::Context.new
|
24
|
+
local = context.spawn_child(transient: false, shadowed: [variable.name])
|
25
|
+
|
26
|
+
Keisan::AST::List.new(
|
27
|
+
list.children.select do |element|
|
28
|
+
local.register_variable!(variable, element)
|
29
|
+
result = expression.evaluate(local)
|
30
|
+
|
31
|
+
case result
|
32
|
+
when Keisan::AST::Boolean
|
33
|
+
result.value
|
34
|
+
else
|
35
|
+
raise Keisan::Exceptions::InvalidFunctionError.new("Filter requires expression to be a logical expression")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def list_variable_expression_for(ast_function, context)
|
44
|
+
unless ast_function.children.size == 3
|
45
|
+
raise Keisan::Exceptions::InvalidFunctionError.new("Require 3 arguments to filter")
|
46
|
+
end
|
47
|
+
|
48
|
+
list = ast_function.children[0].simplify(context)
|
49
|
+
variable = ast_function.children[1]
|
50
|
+
expression = ast_function.children[2]
|
51
|
+
|
52
|
+
unless list.is_a?(Keisan::AST::List)
|
53
|
+
raise Keisan::Exceptions::InvalidFunctionError.new("First argument to filter must be a list")
|
54
|
+
end
|
55
|
+
|
56
|
+
unless variable.is_a?(Keisan::AST::Variable)
|
57
|
+
raise Keisan::Exceptions::InvalidFunctionError.new("Second argument to filter must be a variable")
|
58
|
+
end
|
59
|
+
|
60
|
+
[list, variable, expression]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/keisan/functions/if.rb
CHANGED
@@ -27,17 +27,8 @@ module Keisan
|
|
27
27
|
bool = ast_function.children[0].evaluate(context)
|
28
28
|
|
29
29
|
if bool.is_a?(Keisan::AST::Boolean)
|
30
|
-
|
31
|
-
|
32
|
-
else
|
33
|
-
ast_function.children[2].to_node.evaluate(context)
|
34
|
-
end
|
35
|
-
else
|
36
|
-
ast_function
|
37
|
-
end
|
38
|
-
|
39
|
-
if ast_function.children.all? {|child| child.well_defined?(context)}
|
40
|
-
value(ast_function, context).to_node.evaluate(context)
|
30
|
+
node = bool.value ? ast_function.children[1] : ast_function.children[2]
|
31
|
+
node.to_node.evaluate(context)
|
41
32
|
else
|
42
33
|
ast_function
|
43
34
|
end
|
@@ -49,7 +40,7 @@ module Keisan
|
|
49
40
|
|
50
41
|
if bool.is_a?(Keisan::AST::Boolean)
|
51
42
|
if bool.value
|
52
|
-
ast_function.children[1].simplify(context)
|
43
|
+
ast_function.children[1].to_node.simplify(context)
|
53
44
|
else
|
54
45
|
ast_function.children[2].to_node.simplify(context)
|
55
46
|
end
|
@@ -57,6 +48,18 @@ module Keisan
|
|
57
48
|
ast_function
|
58
49
|
end
|
59
50
|
end
|
51
|
+
|
52
|
+
def differentiate(ast_function, variable, context = nil)
|
53
|
+
context ||= Context.new
|
54
|
+
Keisan::AST::Function.new(
|
55
|
+
[
|
56
|
+
ast_function.children[0],
|
57
|
+
ast_function.children[1].differentiate(variable, context),
|
58
|
+
ast_function.children[2].differentiate(variable, context)
|
59
|
+
],
|
60
|
+
@name
|
61
|
+
)
|
62
|
+
end
|
60
63
|
end
|
61
64
|
end
|
62
65
|
end
|