latex_eval 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +86 -0
- data/lib/latex_eval.rb +21 -0
- data/lib/latex_eval/equation.rb +104 -0
- data/lib/latex_eval/latex.rb +128 -0
- data/lib/latex_eval/postfix_notation.rb +61 -0
- data/test/equation_test.rb +169 -0
- data/test/latex_eval_test.rb +14 -0
- data/test/latex_test.rb +134 -0
- data/test/postfix_notation_test.rb +54 -0
- metadata +87 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d0562d9054359a1d2a318e8dec10943b3a56bb8a
|
4
|
+
data.tar.gz: a1ff0986f7fd46f1c6a8adf4a07656a533b19ece
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 63370a4e0a0f39c47de118b18d695c77b0e10fb62ad38d7507ab0eb2c1991f52c086965826be26c327f8a95cae88b82c35a2a8552dde0fbf8f13d84d8c6d0702
|
7
|
+
data.tar.gz: d4d0b786b24b969ea3adfc9cba2a64c3d632ba4b7db8de44326608e676fff90fc4a5d591f7dd63b067cf935da4aa20120d22b2ae3fd2944b50ad9d8ad7157279
|
data/README.md
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
# latex\_eval
|
2
|
+
|
3
|
+
This gem can be used to safely evaluate simple latex expressions.
|
4
|
+
|
5
|
+
For example, if you want to evaluate `\frac{\xi}{2}+3` where `\xi = 3` you can do
|
6
|
+
|
7
|
+
LatexEval.eval('\frac{\xi}{2}+3', {xi: 3})
|
8
|
+
|
9
|
+
However, if you are going to be evaluating the expression many times, it is best to save the postfix notation first.
|
10
|
+
|
11
|
+
postfix = LatexEval.postfix_notation('\frac{\xi}{2}+3')
|
12
|
+
LatexEval::PostfixNotation.eval(postfix, {xi: 3})
|
13
|
+
|
14
|
+
This gem is made up of 3 classes. `Latex`, `Equation`, and `PostfixNotation`. I will explain their uses.
|
15
|
+
|
16
|
+
## Latex
|
17
|
+
|
18
|
+
The latex class is used to convert your latex into an equation. For example,
|
19
|
+
|
20
|
+
LatexEval::Latex.new('\frac{\xi}{2}+3').equation
|
21
|
+
|
22
|
+
will output `((xi)/(2)+3)`.
|
23
|
+
|
24
|
+
Currently, the `equation` method for the `Latex` class supports the following latex expressions:
|
25
|
+
|
26
|
+
- capital and lowercase Greek letters (`\alpha`, `\Beta`, etc.)
|
27
|
+
- single letter variables (`a`, `b`, ..., `z`)
|
28
|
+
- fractions in the form of `\frac{}{}` and `\dfrac{}{}`
|
29
|
+
- roots in the form `\sqrt{}` and `\sqrt[]{}`
|
30
|
+
- binary operators
|
31
|
+
- multiplication (\*, `\cdot`, `\times`)
|
32
|
+
- division(/)
|
33
|
+
- powers (^)
|
34
|
+
- modulus (%)
|
35
|
+
- addition (+)
|
36
|
+
- subtraction (-)
|
37
|
+
- unary operators
|
38
|
+
- positive (+)
|
39
|
+
- negative (-)
|
40
|
+
|
41
|
+
## Equation
|
42
|
+
|
43
|
+
The equation class is used to convert your equations into postfix notation. For example,
|
44
|
+
|
45
|
+
LatexEval::Equation.new('((xi)/(2)+3)').postfix_notation
|
46
|
+
|
47
|
+
will output `[3, :xi, 2, :divide, :add]`.
|
48
|
+
|
49
|
+
Currently, the `postfix_notation` method for the `Equation` class can interpret
|
50
|
+
|
51
|
+
- brackets
|
52
|
+
- binary operators
|
53
|
+
- multiplication (\*)
|
54
|
+
- division(/)
|
55
|
+
- powers (^)
|
56
|
+
- modulus (%)
|
57
|
+
- addition (+)
|
58
|
+
- subtraction (-)
|
59
|
+
- unary operators
|
60
|
+
- positive (+)
|
61
|
+
- negative (-)
|
62
|
+
|
63
|
+
and will adhere to the generally accepted order of operations.
|
64
|
+
|
65
|
+
## PostfixNotation
|
66
|
+
|
67
|
+
The postfix equation class is used to safely evaluate postfix notation. For example,
|
68
|
+
|
69
|
+
LatexEval::PostfixNotation.new([3, :xi, 2, :divide, :add]).eval({xi: 3})
|
70
|
+
|
71
|
+
will output `4.5`.
|
72
|
+
|
73
|
+
The `eval` method accepts one argument, which is a hash of variable values.
|
74
|
+
|
75
|
+
Currently, the `eval` method for the `PostfixNotation` class can interpret
|
76
|
+
|
77
|
+
- binary operators
|
78
|
+
- :multiply
|
79
|
+
- :divide
|
80
|
+
- :power
|
81
|
+
- :mod
|
82
|
+
- :add
|
83
|
+
- :subtract
|
84
|
+
- unary operators
|
85
|
+
- :positive
|
86
|
+
- :negative
|
data/lib/latex_eval.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative "./latex_eval/equation.rb"
|
2
|
+
require_relative "./latex_eval/postfix_notation.rb"
|
3
|
+
require_relative "./latex_eval/latex.rb"
|
4
|
+
|
5
|
+
module LatexEval
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def eval(latex, subs = {})
|
9
|
+
parsed_latex = LatexEval::Latex.new(latex).equation
|
10
|
+
parsed_notation = LatexEval::Equation.new(parsed_latex).postfix_notation
|
11
|
+
eval_latex = LatexEval::PostfixNotation.new(parsed_notation)
|
12
|
+
|
13
|
+
return eval_latex.eval(subs)
|
14
|
+
end
|
15
|
+
|
16
|
+
def postfix_notation(latex)
|
17
|
+
equation = LatexEval::Latex.new(latex).equation
|
18
|
+
return LatexEval::Equation.new(equation).postfix_notation
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module LatexEval
|
2
|
+
class Equation
|
3
|
+
|
4
|
+
attr_reader :binary_key, :equation, :unary_key
|
5
|
+
|
6
|
+
def initialize(equation)
|
7
|
+
@equation = equation
|
8
|
+
@binary_key = {
|
9
|
+
"+" => {
|
10
|
+
symbol: :add,
|
11
|
+
priority: 0,
|
12
|
+
right_priority: false
|
13
|
+
},
|
14
|
+
"-" => {
|
15
|
+
symbol: :subtract,
|
16
|
+
priority: 0,
|
17
|
+
right_priority: false
|
18
|
+
},
|
19
|
+
"*" => {
|
20
|
+
symbol: :multiply,
|
21
|
+
priority: 1,
|
22
|
+
right_priority: false
|
23
|
+
},
|
24
|
+
"/" => {
|
25
|
+
symbol: :divide,
|
26
|
+
priority: 1,
|
27
|
+
right_priority: false
|
28
|
+
},
|
29
|
+
"%" => {
|
30
|
+
symbol: :mod,
|
31
|
+
priority: 1,
|
32
|
+
right_priority: false
|
33
|
+
},
|
34
|
+
"^" => {
|
35
|
+
symbol: :power,
|
36
|
+
priority: 2,
|
37
|
+
right_priority: true
|
38
|
+
}
|
39
|
+
}
|
40
|
+
@unary_key = {
|
41
|
+
"-" => {
|
42
|
+
symbol: :negative,
|
43
|
+
},
|
44
|
+
"+" => {
|
45
|
+
symbol: :positive,
|
46
|
+
},
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def postfix_notation
|
51
|
+
out = []
|
52
|
+
bank = []
|
53
|
+
bracket = []
|
54
|
+
unary = []
|
55
|
+
|
56
|
+
# start with an assumed array
|
57
|
+
bank.push []
|
58
|
+
bracket.push []
|
59
|
+
unary.push []
|
60
|
+
|
61
|
+
equation.gsub(" ", "").split(/([%\)\(\^*+-\/])/).each do |value|
|
62
|
+
if value != ""
|
63
|
+
if value == "("
|
64
|
+
bank.push []
|
65
|
+
bracket.push []
|
66
|
+
unary.push []
|
67
|
+
elsif value == ")"
|
68
|
+
last = bracket.pop()
|
69
|
+
bracket.last.concat last
|
70
|
+
bracket.last.concat bank.pop().reverse.map { |e| binary_key[e][:symbol] }
|
71
|
+
bracket.last.concat unary.pop().reverse.map { |e| unary_key[e][:symbol] }
|
72
|
+
elsif unary_key.has_key? value and (bracket.last.empty? || bracket.last.length == bank.last.length)
|
73
|
+
unary.last.push value
|
74
|
+
elsif binary_key.has_key? value
|
75
|
+
num_popped = 0
|
76
|
+
bank.last.reverse_each do |b|
|
77
|
+
if (binary_key[b][:right_priority] ? binary_key[b][:priority] > binary_key[value][:priority] : binary_key[b][:priority] >= binary_key[value][:priority])
|
78
|
+
num_popped += 1
|
79
|
+
else
|
80
|
+
break
|
81
|
+
end
|
82
|
+
end
|
83
|
+
bracket.last.concat unary.pop().reverse.map { |e| unary_key[e][:symbol] }
|
84
|
+
unary.push []
|
85
|
+
bracket.last.concat bank.last.pop(num_popped).reverse.map { |e| binary_key[e][:symbol] }
|
86
|
+
bank.last.push value
|
87
|
+
else
|
88
|
+
if /[a-zA-Z]+/.match(value)
|
89
|
+
bracket.last.push value.to_sym
|
90
|
+
else
|
91
|
+
bracket.last.push value.to_f
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
out.concat bracket.pop()
|
98
|
+
out.concat unary.pop().reverse.map { |e| unary_key[e][:symbol] }
|
99
|
+
out.concat bank.pop().reverse.map { |e| binary_key[e][:symbol] }
|
100
|
+
|
101
|
+
return out
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module LatexEval
|
2
|
+
class Latex
|
3
|
+
attr_reader :latex, :greek_letters
|
4
|
+
|
5
|
+
def initialize(latex)
|
6
|
+
@latex = latex
|
7
|
+
@greek_letters = [
|
8
|
+
'Alpha',
|
9
|
+
'alpha',
|
10
|
+
'Beta',
|
11
|
+
'beta',
|
12
|
+
'Gamma',
|
13
|
+
'gamma',
|
14
|
+
'Delta',
|
15
|
+
'delta',
|
16
|
+
'Epsilon',
|
17
|
+
'epsilon',
|
18
|
+
'varepsilon',
|
19
|
+
'Zeta',
|
20
|
+
'zeta',
|
21
|
+
'Eta',
|
22
|
+
'eta',
|
23
|
+
'Theta',
|
24
|
+
'theta',
|
25
|
+
'vartheta',
|
26
|
+
'Iota',
|
27
|
+
'iota',
|
28
|
+
'Kappa',
|
29
|
+
'kappa',
|
30
|
+
'varkappa',
|
31
|
+
'Lambda',
|
32
|
+
'lambda',
|
33
|
+
'Mu',
|
34
|
+
'mu',
|
35
|
+
'Nu',
|
36
|
+
'nu',
|
37
|
+
'Xi',
|
38
|
+
'xi',
|
39
|
+
'Omicron',
|
40
|
+
'omicron',
|
41
|
+
'Pi',
|
42
|
+
'pi',
|
43
|
+
'varpi',
|
44
|
+
'Rho',
|
45
|
+
'rho',
|
46
|
+
'varrho',
|
47
|
+
'Sigma',
|
48
|
+
'sigma',
|
49
|
+
'varsigma',
|
50
|
+
'Tau',
|
51
|
+
'tau',
|
52
|
+
'Upsilon',
|
53
|
+
'upsilon',
|
54
|
+
'Phi',
|
55
|
+
'phi',
|
56
|
+
'varphi',
|
57
|
+
'Chi',
|
58
|
+
'chi',
|
59
|
+
'Psi',
|
60
|
+
'psi',
|
61
|
+
'Omega',
|
62
|
+
'omega',
|
63
|
+
]
|
64
|
+
end
|
65
|
+
|
66
|
+
def equation
|
67
|
+
parsed = latex
|
68
|
+
|
69
|
+
parsed.gsub!(/\\left/, "")
|
70
|
+
parsed.gsub!(/\\right/, "")
|
71
|
+
|
72
|
+
# fractions
|
73
|
+
regex = /\\d?frac{(.*)}{(.*)}/
|
74
|
+
while parsed.match(regex)
|
75
|
+
parsed.gsub!(regex, '((\1)/(\2))')
|
76
|
+
end
|
77
|
+
|
78
|
+
# sqrt
|
79
|
+
regex = /\\sqrt{(.*)}/
|
80
|
+
while parsed.match(regex)
|
81
|
+
parsed.gsub!(regex, '((\1)^(1/2))')
|
82
|
+
end
|
83
|
+
|
84
|
+
# nth root
|
85
|
+
regex = /\\sqrt(\[?(.*)(?=\])\]?)?{(.*)}/
|
86
|
+
while parsed.match(regex)
|
87
|
+
parsed.gsub!(regex, '((\3)^(1/(\2)))')
|
88
|
+
end
|
89
|
+
|
90
|
+
# \cdot and \times should be *
|
91
|
+
parsed.gsub!(/\\cdot/, "*")
|
92
|
+
parsed.gsub!(/\\times/, "*")
|
93
|
+
|
94
|
+
# remove remaining curly brackets
|
95
|
+
parsed.gsub!(/{/, "(")
|
96
|
+
parsed.gsub!(/}/, ")")
|
97
|
+
|
98
|
+
# cleanup variable names and multiplication
|
99
|
+
greek_letters.each do |letter|
|
100
|
+
parsed.gsub!(/(\\#{letter})/, ' \1 ')
|
101
|
+
end
|
102
|
+
|
103
|
+
# cleans up xyz to x*y*z
|
104
|
+
regex = /((?<!\\)\b)([0-9]?[A-Za-z])([A-Za-z])/
|
105
|
+
while parsed.match(regex)
|
106
|
+
parsed.gsub!(regex, '\2*\3')
|
107
|
+
end
|
108
|
+
|
109
|
+
parsed.gsub!(/([0-9])\s+\\([A-Za-z])/, '\1*\2')
|
110
|
+
parsed.gsub!(/([0-9])\s*?([A-Za-z])/, '\1*\2')
|
111
|
+
|
112
|
+
parsed.gsub!(/([A-Za-z])\s+\\([A-Za-z])/, '\1*\2')
|
113
|
+
parsed.gsub!(/([A-Za-z])\s+([A-Za-z])/, '\1*\2')
|
114
|
+
|
115
|
+
parsed.gsub!(/([A-Za-z])\s+([0-9])/, '\1*\2')
|
116
|
+
parsed.gsub!(/([A-Za-z])([0-9])/, '\1*\2')
|
117
|
+
|
118
|
+
parsed.gsub!(/(\))(\()/, '\1*\2')
|
119
|
+
|
120
|
+
parsed.gsub!(/\\/, "")
|
121
|
+
|
122
|
+
# cleanup spaces
|
123
|
+
parsed.gsub!(" ", "")
|
124
|
+
|
125
|
+
return parsed
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module LatexEval
|
2
|
+
class PostfixNotation
|
3
|
+
attr_reader :notation, :operations
|
4
|
+
|
5
|
+
def initialize(notation)
|
6
|
+
@notation = notation
|
7
|
+
@operations = {
|
8
|
+
add: {
|
9
|
+
args: 2,
|
10
|
+
operation: ->(a,b) { a + b },
|
11
|
+
},
|
12
|
+
subtract: {
|
13
|
+
args: 2,
|
14
|
+
operation: ->(a,b) { a - b },
|
15
|
+
},
|
16
|
+
multiply: {
|
17
|
+
args: 2,
|
18
|
+
operation: ->(a,b) { a * b },
|
19
|
+
},
|
20
|
+
divide: {
|
21
|
+
args: 2,
|
22
|
+
operation: ->(a,b) { a / b },
|
23
|
+
},
|
24
|
+
power: {
|
25
|
+
args: 2,
|
26
|
+
operation: ->(a,b) { a ** b },
|
27
|
+
},
|
28
|
+
mod: {
|
29
|
+
args: 2,
|
30
|
+
operation: ->(a,b) { a % b },
|
31
|
+
},
|
32
|
+
negative: {
|
33
|
+
args: 1,
|
34
|
+
operation: ->(a) { -a },
|
35
|
+
},
|
36
|
+
positive: {
|
37
|
+
args: 1,
|
38
|
+
operation: ->(a) { +a },
|
39
|
+
},
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
def eval(subs = {})
|
44
|
+
stack = []
|
45
|
+
|
46
|
+
notation.each do |elem|
|
47
|
+
if elem.is_a? Symbol
|
48
|
+
if operations.has_key? elem
|
49
|
+
stack.push operations[elem][:operation].call(*stack.pop(operations[elem][:args])).to_f
|
50
|
+
else
|
51
|
+
stack.push subs[elem].to_f
|
52
|
+
end
|
53
|
+
else
|
54
|
+
stack.push elem.to_f
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
return stack.first
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative './../lib/latex_eval.rb'
|
3
|
+
|
4
|
+
class TestEquation < Minitest::Test
|
5
|
+
def test_that_parser_parses_addition
|
6
|
+
equation = "1 + 2"
|
7
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, :add]
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_that_parser_parses_subtraction
|
11
|
+
equation = "1 - 2"
|
12
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, :subtract]
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_that_parser_parses_multiplication
|
16
|
+
equation = "1 * 2"
|
17
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, :multiply]
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_that_parser_parses_division
|
21
|
+
equation = "1 / 2"
|
22
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, :divide]
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_that_parser_parses_powers
|
26
|
+
equation = "1 ^ 2"
|
27
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, :power]
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_that_parser_respects_brackets
|
31
|
+
equation = "(1+2)^3"
|
32
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, :add, 3, :power]
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_that_parser_respects_nested_brackets
|
36
|
+
equation = "(1+(2-4))^3"
|
37
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, 4, :subtract, :add, 3, :power]
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_that_parser_respects_separate_brackets
|
41
|
+
equation = "(1+(2-4))^(3 * 2)"
|
42
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, 4, :subtract, :add, 3, 2, :multiply, :power]
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_that_parser_respects_separate_brackets_v2
|
46
|
+
equation = "(1-(2-4))^(3 * 2)"
|
47
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, 4, :subtract, :subtract, 3, 2, :multiply, :power]
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_that_parser_disregards_spaces
|
51
|
+
equation = "( 1 + 2 ) ^ 3"
|
52
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, :add, 3, :power]
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_that_parser_recognizes_single_letter_variables
|
56
|
+
equation = "x + y + z"
|
57
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [:x, :y, :add, :z, :add]
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_that_parser_recognizes_multi_letter_variables
|
61
|
+
equation = "alpha + beta + gamma"
|
62
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [:alpha, :beta, :add, :gamma, :add]
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_that_parser_evaluates_multiply_divide_in_order_of_appearance
|
66
|
+
equation = "1 * 2 / 3"
|
67
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, :multiply, 3, :divide]
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_that_parser_evaluates_add_subtract_in_order_of_appearance
|
71
|
+
equation = "1 + 2 - 3"
|
72
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, :add, 3, :subtract]
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_that_powers_are_given_proper_priority
|
76
|
+
equation = "3 ^ 2 ^ 2"
|
77
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [3, 2, 2, :power, :power]
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_that_parser_multiplication_priority_over_addition
|
81
|
+
equation = "1 + 2 * 3"
|
82
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, 3, :multiply, :add]
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_that_parser_multiplication_priority_over_subtraction
|
86
|
+
equation = "1 - 2 * 3"
|
87
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, 3, :multiply, :subtract]
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_that_parser_division_priority_over_addition
|
91
|
+
equation = "1 + 2 / 3"
|
92
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, 3, :divide, :add]
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_that_parser_division_priority_over_subtraction
|
96
|
+
equation = "1 - 2 / 3"
|
97
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, 3, :divide, :subtract]
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_that_parser_power_priority_over_addition
|
101
|
+
equation = "1 + 2 ^ 3"
|
102
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, 3, :power, :add]
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_that_parser_power_priority_over_subtraction
|
106
|
+
equation = "1 - 2 ^ 3"
|
107
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, 3, :power, :subtract]
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_that_parser_power_priority_over_multiplication
|
111
|
+
equation = "1 * 2 ^ 3"
|
112
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, 3, :power, :multiply]
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_that_parser_power_priority_over_division
|
116
|
+
equation = "1 / 2 ^ 3"
|
117
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, 3, :power, :divide]
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_that_parser_mod_priority_over_addition
|
121
|
+
equation = "1 + 2 % 3"
|
122
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, 3, :mod, :add]
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_that_parser_mod_priority_over_subtraction
|
126
|
+
equation = "1 - 2 % 3"
|
127
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [1, 2, 3, :mod, :subtract]
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_that_negative_works
|
131
|
+
equation = "- 2"
|
132
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [2, :negative]
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_that_positive_works
|
136
|
+
equation = "+ 2"
|
137
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [2, :positive]
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_that_negative_works_inside_brackets
|
141
|
+
equation = "(-2)"
|
142
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [2, :negative]
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_that_negative_works_outside_brackets
|
146
|
+
equation = "-(2)"
|
147
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [2, :negative]
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_that_negative_works_with_others
|
151
|
+
equation = "-2+3"
|
152
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [2, :negative, 3, :add]
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_that_negative_works_with_multiple_negatives
|
156
|
+
equation = "--2"
|
157
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [2, :negative, :negative]
|
158
|
+
end
|
159
|
+
|
160
|
+
def test_that_negative_works_with_multiplication_normal
|
161
|
+
equation = "2 * - 2"
|
162
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [2, 2, :negative, :multiply]
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_that_negative_works_with_multiple_negatives_2
|
166
|
+
equation = "2^(-2)"
|
167
|
+
assert_equal LatexEval::Equation.new(equation).postfix_notation, [2, 2, :negative, :power]
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative './../lib/latex_eval.rb'
|
3
|
+
|
4
|
+
class TestLatexEval < Minitest::Test
|
5
|
+
def test_it_all
|
6
|
+
latex = '\frac{1}{2}\cdot3\xi+4'
|
7
|
+
assert_equal LatexEval.eval(latex, {xi: 2}), 7
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_postfix_notation
|
11
|
+
latex = '1+3*2'
|
12
|
+
assert_equal LatexEval.postfix_notation(latex), [1, 3, 2, :multiply, :add]
|
13
|
+
end
|
14
|
+
end
|
data/test/latex_test.rb
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative './../lib/latex_eval.rb'
|
3
|
+
|
4
|
+
class TestLatex < Minitest::Test
|
5
|
+
def test_that_latex_parser_parses_fractions
|
6
|
+
latex = '\frac{1}{2}'
|
7
|
+
assert_equal LatexEval::Latex.new(latex).equation, '((1)/(2))'
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_that_latex_parser_parses_fractions_recursively
|
11
|
+
latex = '\frac{\frac{3}{4}}{2}'
|
12
|
+
assert_equal LatexEval::Latex.new(latex).equation, '((((3)/(4)))/(2))'
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_that_latex_parser_parses_dfractions
|
16
|
+
latex = '\dfrac{1}{2}'
|
17
|
+
assert_equal LatexEval::Latex.new(latex).equation, '((1)/(2))'
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_that_latex_parser_parses_dfractions_recursively
|
21
|
+
latex = '\dfrac{\dfrac{3}{4}}{2}'
|
22
|
+
assert_equal LatexEval::Latex.new(latex).equation, '((((3)/(4)))/(2))'
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_that_latex_parser_parses_cdot
|
26
|
+
latex = '1\cdot2'
|
27
|
+
assert_equal LatexEval::Latex.new(latex).equation, '1*2'
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_that_latex_parser_parses_times
|
31
|
+
latex = '1\times2'
|
32
|
+
assert_equal LatexEval::Latex.new(latex).equation, '1*2'
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_that_latex_parser_parses_powers
|
36
|
+
latex = '1^{23}'
|
37
|
+
assert_equal LatexEval::Latex.new(latex).equation, '1^(23)'
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_that_latex_parser_parses_variable_names
|
41
|
+
latex = '\xi'
|
42
|
+
assert_equal LatexEval::Latex.new(latex).equation, 'xi'
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_that_latex_parser_adds_missing_multiplication_before_variable
|
46
|
+
latex = '2\xi'
|
47
|
+
assert_equal LatexEval::Latex.new(latex).equation, '2*xi'
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_that_latex_parser_adds_missing_multiplication_after_variable
|
51
|
+
latex = '\xi2'
|
52
|
+
assert_equal LatexEval::Latex.new(latex).equation, 'xi*2'
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_that_latex_parser_adds_missing_multiplication_between_variable
|
56
|
+
latex = '\alpha\beta'
|
57
|
+
assert_equal LatexEval::Latex.new(latex).equation, 'alpha*beta'
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_that_latex_parser_adds_missing_multiplication_between_brackets
|
61
|
+
latex = '(1)(2)'
|
62
|
+
assert_equal LatexEval::Latex.new(latex).equation, '(1)*(2)'
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_that_latex_parser_parses_nth_root
|
66
|
+
latex = '\sqrt[3]{2}'
|
67
|
+
assert_equal LatexEval::Latex.new(latex).equation, '((2)^(1/(3)))'
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_that_latex_parser_parses_sqrt
|
71
|
+
latex = '\sqrt{2}'
|
72
|
+
assert_equal LatexEval::Latex.new(latex).equation, '((2)^(1/2))'
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_works_with_typical_variables
|
76
|
+
latex = '\xi x'
|
77
|
+
assert_equal LatexEval::Latex.new(latex).equation, 'xi*x'
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_works_with_typical_variable
|
81
|
+
latex = '2x'
|
82
|
+
assert_equal LatexEval::Latex.new(latex).equation, '2*x'
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_works_with_typical_variable_spaces
|
86
|
+
latex = '2 x'
|
87
|
+
assert_equal LatexEval::Latex.new(latex).equation, '2*x'
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_works_with_typical_variable_multiple_spaces
|
91
|
+
latex = '2 x'
|
92
|
+
assert_equal LatexEval::Latex.new(latex).equation, '2*x'
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_works_with_left_and_right
|
96
|
+
latex = '\left(x + 2\right)'
|
97
|
+
assert_equal LatexEval::Latex.new(latex).equation, '(x+2)'
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_works_with_abs
|
101
|
+
latex = '2 % 3'
|
102
|
+
assert_equal LatexEval::Latex.new(latex).equation, '2%3'
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_works_with_abs_and_variables
|
106
|
+
latex = 'x % y'
|
107
|
+
assert_equal LatexEval::Latex.new(latex).equation, 'x%y'
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_works_with_variable_spaces
|
111
|
+
latex = 'x y'
|
112
|
+
assert_equal LatexEval::Latex.new(latex).equation, 'x*y'
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_works_with_latex_variables
|
116
|
+
latex = '\xix'
|
117
|
+
assert_equal LatexEval::Latex.new(latex).equation, 'xi*x'
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_works_with_regular_variables
|
121
|
+
latex = 'xyz'
|
122
|
+
assert_equal LatexEval::Latex.new(latex).equation, 'x*y*z'
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_works_with_regular_variables_and_weird_numbers
|
126
|
+
latex = 'x*2yz'
|
127
|
+
assert_equal LatexEval::Latex.new(latex).equation, 'x*2*y*z'
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_works_with_regular_variables_complex
|
131
|
+
latex = '\frac{2xy}{3}'
|
132
|
+
assert_equal LatexEval::Latex.new(latex).equation, '((2*x*y)/(3))'
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative './../lib/latex_eval.rb'
|
3
|
+
|
4
|
+
class TestPostfixNotation < Minitest::Test
|
5
|
+
def test_that_eval_evals_add
|
6
|
+
parsed = [3, 5, :add]
|
7
|
+
assert_equal LatexEval::PostfixNotation.new(parsed).eval, 8
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_that_eval_evals_subtract
|
11
|
+
parsed = [3, 5, :subtract]
|
12
|
+
assert_equal LatexEval::PostfixNotation.new(parsed).eval, -2
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_that_eval_evals_multiply
|
16
|
+
parsed = [3, 5, :multiply]
|
17
|
+
assert_equal LatexEval::PostfixNotation.new(parsed).eval, 15
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_that_eval_evals_divide
|
21
|
+
parsed = [3, 5, :divide]
|
22
|
+
assert_equal LatexEval::PostfixNotation.new(parsed).eval, 0.6
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_that_eval_evals_power
|
26
|
+
parsed = [3, 5, :power]
|
27
|
+
assert_equal LatexEval::PostfixNotation.new(parsed).eval, 243
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_that_eval_evals_variables
|
31
|
+
parsed = [:x, :y, :power]
|
32
|
+
assert_equal LatexEval::PostfixNotation.new(parsed).eval({x: 13, y: 3}), 2197
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_that_negatives_work
|
36
|
+
parsed = [-2, -1, :subtract]
|
37
|
+
assert_equal LatexEval::PostfixNotation.new(parsed).eval, -1
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_that_uniary_negative_works
|
41
|
+
parsed = [-2, -1, :negative, :add]
|
42
|
+
assert_equal LatexEval::PostfixNotation.new(parsed).eval, -1
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_that_uniary_positive_works
|
46
|
+
parsed = [-2, -1, :positive, :add]
|
47
|
+
assert_equal LatexEval::PostfixNotation.new(parsed).eval, -3
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_that_modulus_works
|
51
|
+
parsed = [432, 123, :mod]
|
52
|
+
assert_equal LatexEval::PostfixNotation.new(parsed).eval, 63
|
53
|
+
end
|
54
|
+
end
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: latex_eval
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Josh Dunn
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-02-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: minitest
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.11'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 5.11.3
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '5.11'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 5.11.3
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rake
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '12.3'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '12.3'
|
47
|
+
description: This gem can be used to parse and then evaluate simple latex expressions.
|
48
|
+
email: jdunn45@alumni.uwo.ca
|
49
|
+
executables: []
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- README.md
|
54
|
+
- lib/latex_eval.rb
|
55
|
+
- lib/latex_eval/equation.rb
|
56
|
+
- lib/latex_eval/latex.rb
|
57
|
+
- lib/latex_eval/postfix_notation.rb
|
58
|
+
- test/equation_test.rb
|
59
|
+
- test/latex_eval_test.rb
|
60
|
+
- test/latex_test.rb
|
61
|
+
- test/postfix_notation_test.rb
|
62
|
+
homepage: https://rubygems.org/gems/latex_eval
|
63
|
+
licenses:
|
64
|
+
- MIT
|
65
|
+
metadata:
|
66
|
+
source_code_uri: https://github.com/joshddunn/latex_eval
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options: []
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
requirements: []
|
82
|
+
rubyforge_project:
|
83
|
+
rubygems_version: 2.6.11
|
84
|
+
signing_key:
|
85
|
+
specification_version: 4
|
86
|
+
summary: Evaluate simple latex expressions.
|
87
|
+
test_files: []
|