polly 0.0.4
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.
- data/.gitignore +17 -0
- data/.irb_tempfile +0 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +12 -0
- data/LICENSE +22 -0
- data/README.md +39 -0
- data/Rakefile +3 -0
- data/TODO +5 -0
- data/lib/polly/calculation.rb +46 -0
- data/lib/polly/common.rb +8 -0
- data/lib/polly/context.rb +49 -0
- data/lib/polly/env.rb +52 -0
- data/lib/polly/extensions/array.rb +4 -0
- data/lib/polly/extensions/numeric.rb +5 -0
- data/lib/polly/extensions/object.rb +15 -0
- data/lib/polly/math.rb +53 -0
- data/lib/polly/sexpr.rb +176 -0
- data/lib/polly/version.rb +3 -0
- data/lib/polly.rb +13 -0
- data/polly.gemspec +20 -0
- data/spec/calculation.rb +52 -0
- data/spec/context.rb +65 -0
- data/spec/env.rb +60 -0
- data/spec/examples.rb +77 -0
- data/spec/math.rb +87 -0
- data/spec/sexpr.rb +128 -0
- data/spec/spec_helper.rb +15 -0
- metadata +81 -0
data/.gitignore
ADDED
data/.irb_tempfile
ADDED
File without changes
|
data/.rspec
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.3@polly --create
|
data/Gemfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in polly.gemspec
|
4
|
+
gemspec
|
5
|
+
gem 'pry'
|
6
|
+
gem 'pry-nav'
|
7
|
+
gem 'pry-stack_explorer'
|
8
|
+
gem 'rake'
|
9
|
+
gem 'rspec'
|
10
|
+
gem 'rcov', '0.9.11'
|
11
|
+
gem 'simplecov', :require => false, :group => :test
|
12
|
+
gem 'metrical'
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Alex Skryl
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
## About
|
2
|
+
|
3
|
+
Polly is a DSL for manipulating and evaluating symbolic expressions.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
* Create calculations consisting of one or more s-expressions
|
8
|
+
* Evaluate full s-expressions or any constituent parts
|
9
|
+
* Add custom function primitives for complex calculations
|
10
|
+
* Print s-expression parse trees in prefix or infix notation
|
11
|
+
* Perform data dependence analysis
|
12
|
+
* Analyze runtime performance
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Add this line to your application's Gemfile:
|
17
|
+
|
18
|
+
gem 'polly'
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
$ bundle
|
23
|
+
|
24
|
+
Or install it yourself as:
|
25
|
+
|
26
|
+
$ gem install polly
|
27
|
+
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
TODO: for now just check out spec/examples.rb
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
1. Fork it
|
36
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
37
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
38
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
39
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/TODO
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
class Polly::Calculation
|
2
|
+
extend Forwardable
|
3
|
+
include Polly::Common
|
4
|
+
|
5
|
+
attr_reader :env, :context
|
6
|
+
def_delegators :@env, :print, :to_s, :inspect, :pretty_inspect, :atomic_variables,
|
7
|
+
:defined_variables, :undefined_variables
|
8
|
+
|
9
|
+
def_delegator :@env, :values, :result
|
10
|
+
def_delegator :@env, :values!, :result!
|
11
|
+
|
12
|
+
meta_eval { attr_accessor :verbose }
|
13
|
+
|
14
|
+
def initialize(env = {}, &block)
|
15
|
+
@env = Env.new(env)
|
16
|
+
@context = Context.new(@env)
|
17
|
+
@context.evaluate(block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def evaluate(inputs = {}, &block)
|
21
|
+
inputs.each { |k,v| @context.var(k,v) }
|
22
|
+
@context.evaluate(block) if block
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def method_missing(method, *args, &block)
|
27
|
+
method.match(/^(\w+)=?$/)
|
28
|
+
method_name = $1.to_sym
|
29
|
+
|
30
|
+
if @env.keys.include?(method_name)
|
31
|
+
method == method_name ? @env[method_name] : @context.var(method_name, args[0])
|
32
|
+
else super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def verbose_toggle
|
37
|
+
Calculation.verbose = !Calculation.verbose
|
38
|
+
end
|
39
|
+
|
40
|
+
# Rails compatible serialization
|
41
|
+
|
42
|
+
def dump; env.to_yaml end
|
43
|
+
def self.load(yml); new(YAML::load(yml)) if yml end
|
44
|
+
def self.dump(obj); obj.dump if obj end
|
45
|
+
|
46
|
+
end
|
data/lib/polly/common.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
class Polly::Context < BasicObject
|
2
|
+
include ::Polly::Common
|
3
|
+
include ::Kernel
|
4
|
+
|
5
|
+
def initialize(env)
|
6
|
+
@env = env
|
7
|
+
@env.clean.each { |name, expr| var_reader name.to_sym }
|
8
|
+
end
|
9
|
+
|
10
|
+
def evaluate(proc)
|
11
|
+
instance_eval(&proc) if proc
|
12
|
+
end
|
13
|
+
|
14
|
+
def var(name, val = nil, opts = {})
|
15
|
+
if @env[name]
|
16
|
+
@env[name].replace(val)
|
17
|
+
else
|
18
|
+
@env[name] = Sexpr.build(val, @env, name)
|
19
|
+
end
|
20
|
+
|
21
|
+
var_reader name
|
22
|
+
end
|
23
|
+
|
24
|
+
def Sexpr(val)
|
25
|
+
Polly::Sexpr.build(val, @env)
|
26
|
+
end
|
27
|
+
|
28
|
+
alias_method :const, :var
|
29
|
+
alias_method :eq, :var
|
30
|
+
|
31
|
+
# magix
|
32
|
+
|
33
|
+
# convert method calls on self to s-expressions
|
34
|
+
#
|
35
|
+
def method_missing(method, *args, &block)
|
36
|
+
Sexpr.build([method, *args], @env)
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.const_missing(name)
|
40
|
+
::Object.const_get(name)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def var_reader(name)
|
46
|
+
define_singleton_method(name) { @env[name] }
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
data/lib/polly/env.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
class Polly::Env < Hash
|
2
|
+
include Polly::Common
|
3
|
+
|
4
|
+
def initialize(env = {})
|
5
|
+
Math.singleton_methods.each do |m|
|
6
|
+
self[m.to_sym] = lambda { |*args| Math.send(m, *args) }
|
7
|
+
end
|
8
|
+
|
9
|
+
env.each { |name, expr| self[name] = Sexpr.build(expr, self) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def clean
|
13
|
+
Env[self.select { |name, expr| expr.is_a?(Sexpr) }]
|
14
|
+
end
|
15
|
+
|
16
|
+
def values
|
17
|
+
clean.inject({}) { |h, (name, expr)| h[name] = expr.value; h }
|
18
|
+
end
|
19
|
+
|
20
|
+
def values!
|
21
|
+
clean.inject({}) { |h, (name, expr)| h[name] = expr.value!; h }
|
22
|
+
end
|
23
|
+
|
24
|
+
def atomic_variables
|
25
|
+
clean.select { |name, expr| expr.atomic? }.keys
|
26
|
+
end
|
27
|
+
|
28
|
+
def defined_variables
|
29
|
+
clean.select { |name, expr| expr.atomic? && expr.defined? }.keys
|
30
|
+
end
|
31
|
+
|
32
|
+
def undefined_variables
|
33
|
+
clean.select { |name, expr| expr.atomic? && !expr.defined? }.keys
|
34
|
+
end
|
35
|
+
|
36
|
+
# printing and conversion
|
37
|
+
|
38
|
+
def print(opts = {}); puts to_s(opts) end
|
39
|
+
def to_s(opts = {}); clean.map { |(k,v)| "#{k.inspect} => #{v.to_s(opts)}" }.join("\n") end
|
40
|
+
def to_yaml(*args); dump.to_yaml(*args) end
|
41
|
+
|
42
|
+
def ==(env)
|
43
|
+
env.is_a?(Hash) ? Hash[self.clean] == Hash[env.clean] : false
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def dump
|
49
|
+
clean.inject({}) { |h, (name, expr)| h[name] = expr.to_ary; h }
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
data/lib/polly/math.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
module Polly::Math
|
2
|
+
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def min(*args)
|
6
|
+
args.min
|
7
|
+
end
|
8
|
+
|
9
|
+
def max(*args)
|
10
|
+
args.max
|
11
|
+
end
|
12
|
+
|
13
|
+
def ceil(val, ceil)
|
14
|
+
(val.to_f / ceil.to_i).to_i * ceil.to_i
|
15
|
+
end
|
16
|
+
|
17
|
+
def pv(i, length, pmt)
|
18
|
+
pmt.to_f / i * (1 - (1 + i) ** -length.to_f)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Some binary operators are not methods but part of Ruby's syntax. Since
|
22
|
+
# there is no way to latch on to them, they'll have to be redefined.
|
23
|
+
|
24
|
+
def br(test, exp1, exp2)
|
25
|
+
test ? exp1 : exp2
|
26
|
+
end
|
27
|
+
|
28
|
+
def and(val1, val2)
|
29
|
+
val1 && val2
|
30
|
+
end
|
31
|
+
|
32
|
+
def or(val1, val2)
|
33
|
+
val1 || val2
|
34
|
+
end
|
35
|
+
|
36
|
+
def not(val)
|
37
|
+
!val
|
38
|
+
end
|
39
|
+
alias_method :!, :not
|
40
|
+
|
41
|
+
def not_eq(val1, val2)
|
42
|
+
!(val1 == val2)
|
43
|
+
end
|
44
|
+
alias_method :!=, :not_eq
|
45
|
+
|
46
|
+
# NOOP
|
47
|
+
def self(obj)
|
48
|
+
obj
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
data/lib/polly/sexpr.rb
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
class Polly::Sexpr
|
2
|
+
include Polly::Common
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
# instance accessors
|
6
|
+
attr_reader :op, :args, :sexpr, :env
|
7
|
+
attr_accessor :name, :env, :dirty
|
8
|
+
protected :sexpr, :env=, :name=, :dirty=
|
9
|
+
|
10
|
+
# class accessors
|
11
|
+
meta_eval { protected :new }
|
12
|
+
|
13
|
+
def self.build(val, env = nil, name = nil)
|
14
|
+
val = \
|
15
|
+
case val
|
16
|
+
when Sexpr
|
17
|
+
val
|
18
|
+
when Symbol
|
19
|
+
env_val = env && env[val]
|
20
|
+
env_val.is_a?(Sexpr) ? env_val : Array(val)
|
21
|
+
when Array
|
22
|
+
if val.size > 1
|
23
|
+
val.cdr.map do |arg|
|
24
|
+
arg.is_a?(Sexpr) ? arg : Sexpr.build(arg, env, name)
|
25
|
+
end.unshift(val.car)
|
26
|
+
else
|
27
|
+
Sexpr.build(val.first, env, name)
|
28
|
+
end
|
29
|
+
when NilClass
|
30
|
+
UNDEFINED
|
31
|
+
else Array(val)
|
32
|
+
end
|
33
|
+
|
34
|
+
val.is_a?(Sexpr) ? Sexpr.update(val, env, name) : Sexpr.new(val, env, name)
|
35
|
+
end
|
36
|
+
|
37
|
+
# If an sexpr directly references another named sexpr then the named sexpr
|
38
|
+
# should be wrapped in a self call, which is a noop, in order to avoid
|
39
|
+
# having two references to the same sexpr.
|
40
|
+
#
|
41
|
+
def self.update(sexpr, env, name)
|
42
|
+
if name && sexpr.name && name != sexpr.name
|
43
|
+
Sexpr.build([:self, sexpr])
|
44
|
+
else
|
45
|
+
sexpr.send(:name=, name) unless sexpr.name
|
46
|
+
sexpr.send(:env=, env) unless sexpr.env
|
47
|
+
sexpr
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize(sexpr, env, name)
|
52
|
+
@sexpr = sexpr
|
53
|
+
@op = @sexpr[0]
|
54
|
+
@args = @sexpr.cdr
|
55
|
+
@name = name
|
56
|
+
@env = env || Env.new
|
57
|
+
end
|
58
|
+
|
59
|
+
# use cached values unless some part of expression tree is dirty
|
60
|
+
#
|
61
|
+
def value(env = @env)
|
62
|
+
@value = \
|
63
|
+
(clean? && @value) ||
|
64
|
+
(self.defined? ? (atomic? ? @sexpr.first : self.send(:eval)) : nil)
|
65
|
+
@dirty = false
|
66
|
+
@value
|
67
|
+
end
|
68
|
+
|
69
|
+
# force a recalc of all sub-expressions
|
70
|
+
#
|
71
|
+
def value!(env = @env)
|
72
|
+
each { |a| a.dirty = true }
|
73
|
+
value(env)
|
74
|
+
end
|
75
|
+
|
76
|
+
def replace(val)
|
77
|
+
@dirty = true
|
78
|
+
@sexpr = self.class.build(val, @env).sexpr
|
79
|
+
end
|
80
|
+
|
81
|
+
def atomic?
|
82
|
+
@sexpr.size == 1
|
83
|
+
end
|
84
|
+
|
85
|
+
def defined?; !undefined? end
|
86
|
+
def undefined?; any? { |s| s.sexpr == UNDEFINED } end
|
87
|
+
def undefined_variables; select { |s| s.atomic? && s.undefined? && s.name }.map(&:name) end
|
88
|
+
|
89
|
+
def clean?; !dirty? end
|
90
|
+
def dirty?; any? { |s| s.dirty } end
|
91
|
+
def dirty_variables; select { |s| s.atomic? && s.dirty? && s.name }.map(&:name) end
|
92
|
+
|
93
|
+
def ==(val)
|
94
|
+
case val
|
95
|
+
when Sexpr, Array
|
96
|
+
val == @sexpr
|
97
|
+
else
|
98
|
+
val == self.value
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def each(&block)
|
103
|
+
return to_enum unless block_given?
|
104
|
+
|
105
|
+
yield self
|
106
|
+
args.each { |s| s.each(&block) }
|
107
|
+
end
|
108
|
+
|
109
|
+
# magix
|
110
|
+
|
111
|
+
# convert any method call to an s-expression
|
112
|
+
#
|
113
|
+
def method_missing(method, *args, &block)
|
114
|
+
Sexpr.build([method, self, *args], @env)
|
115
|
+
end
|
116
|
+
|
117
|
+
# printing and conversion
|
118
|
+
|
119
|
+
def print(opts = {}); puts to_s(opts) end
|
120
|
+
def inspect(opts = {}); @sexpr.inspect end
|
121
|
+
def to_s(opts = {}); print(_to_ary(opts)) end
|
122
|
+
def to_ary(opts = {}); _to_ary(opts) end
|
123
|
+
|
124
|
+
protected
|
125
|
+
|
126
|
+
def _to_ary(opts = {})
|
127
|
+
if atomic?
|
128
|
+
Array(value)
|
129
|
+
else
|
130
|
+
args.map do |a|
|
131
|
+
if a.atomic? || (!opts[:expand] && a.name)
|
132
|
+
Array(opts[:numeric] ? a.value : (a.name || a.value))
|
133
|
+
else
|
134
|
+
a._to_ary(opts)
|
135
|
+
end
|
136
|
+
end.unshift(op)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def print(sexpr)
|
141
|
+
op, args = sexpr.car, sexpr.cdr
|
142
|
+
|
143
|
+
if args.empty?
|
144
|
+
op.inspect
|
145
|
+
elsif BINARY_OPS.include?(op)
|
146
|
+
"(#{print(args[0])} #{op} #{print(args[1])})"
|
147
|
+
else
|
148
|
+
"#{op}(#{args.map {|a| print(a)}.join(', ')})"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
# Wizard hats on ;)
|
155
|
+
|
156
|
+
def eval
|
157
|
+
atomic? ? value : apply
|
158
|
+
end
|
159
|
+
|
160
|
+
def apply
|
161
|
+
result = \
|
162
|
+
if @env[op].respond_to?(:call)
|
163
|
+
@env[op].call(*arg_values)
|
164
|
+
else
|
165
|
+
arg_values[0].send(op, *arg_values.cdr)
|
166
|
+
end
|
167
|
+
|
168
|
+
puts " -> #{op}(#{arg_values.join(', ')}) = #{result}" if Calculation.verbose
|
169
|
+
result
|
170
|
+
end
|
171
|
+
|
172
|
+
def arg_values
|
173
|
+
args.map { |a| a.value }
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
data/lib/polly.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'polly/version'
|
2
|
+
require 'polly/extensions/object'
|
3
|
+
require 'polly/extensions/array'
|
4
|
+
require 'polly/extensions/numeric'
|
5
|
+
|
6
|
+
module Polly; end
|
7
|
+
|
8
|
+
require 'polly/common'
|
9
|
+
require 'polly/env'
|
10
|
+
require 'polly/context'
|
11
|
+
require 'polly/sexpr'
|
12
|
+
require 'polly/math'
|
13
|
+
require 'polly/calculation'
|
data/polly.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib/', __FILE__)
|
3
|
+
$:.unshift lib unless $:.include?(lib)
|
4
|
+
|
5
|
+
require 'polly/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |gem|
|
8
|
+
gem.authors = ["Alex Skryl"]
|
9
|
+
gem.email = ["rut216@gmail.com"]
|
10
|
+
gem.description = %q{ A Polynomial Solver DSL }
|
11
|
+
gem.summary = %q{ A Rails compatible polynomial solver with persistence }
|
12
|
+
gem.homepage = ""
|
13
|
+
|
14
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
15
|
+
gem.files = `git ls-files`.split("\n")
|
16
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
gem.name = "polly"
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
gem.version = Polly::VERSION
|
20
|
+
end
|
data/spec/calculation.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Polly::Calculation do
|
4
|
+
|
5
|
+
let(:calc_class) { Polly::Calculation }
|
6
|
+
|
7
|
+
before :each do
|
8
|
+
@calc = calc_class.new do
|
9
|
+
var :a, 1
|
10
|
+
var :b, 2
|
11
|
+
var :c, 3
|
12
|
+
var :d, a + b + c
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should be able to toggle calculation options' do
|
17
|
+
@calc.verbose_toggle
|
18
|
+
calc_class.verbose.should be_true
|
19
|
+
@calc.verbose_toggle
|
20
|
+
calc_class.verbose.should be_false
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should eval the initialization block inside the context' do
|
24
|
+
@calc.context.should_not respond_to(:e, :f, :g)
|
25
|
+
@calc.context.should respond_to(:a, :b, :c, :d)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should get and set variables in the context' do
|
29
|
+
@calc.a.should == 1
|
30
|
+
@calc.a = 2
|
31
|
+
@calc.a.should == 2
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should respond to delegated methods' do
|
35
|
+
@calc.should respond_to(:atomic_variables)
|
36
|
+
@calc.should respond_to(:defined_variables)
|
37
|
+
@calc.should respond_to(:undefined_variables)
|
38
|
+
@calc.should respond_to(:evaluate)
|
39
|
+
@calc.should respond_to(:to_yaml)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should initialize from a yaml dump' do
|
43
|
+
dump = calc_class.dump(@calc)
|
44
|
+
c = calc_class.load(dump)
|
45
|
+
c.context.should respond_to(:a, :b, :c, :d)
|
46
|
+
c.a.should == 1
|
47
|
+
c.b.should == 2
|
48
|
+
c.c.should == 3
|
49
|
+
c.d.should == 6
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
data/spec/context.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Polly::Context do
|
4
|
+
|
5
|
+
let(:env_class) { Polly::Env }
|
6
|
+
let(:sexpr_class) { Polly::Sexpr }
|
7
|
+
let(:context_class) { Polly::Context }
|
8
|
+
|
9
|
+
before :each do
|
10
|
+
@c = context_class.new(env_class.new)
|
11
|
+
@c.instance_eval do
|
12
|
+
var :a
|
13
|
+
const :b, 2
|
14
|
+
const :c, 3
|
15
|
+
|
16
|
+
eq :calc, c * (a + b)
|
17
|
+
eq :complex, min(10, 15, max(10,20,30, a, b, c), calc)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should define aliases for specifying variables' do
|
22
|
+
[:const, :var, :eq].all? { |m| @c.should respond_to(m) }
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should define a variable as an s-expression' do
|
26
|
+
[:d, :e, :f].all? { |m| @c.should_not respond_to(m) }
|
27
|
+
[:a, :b, :c, :calc, :complex].all? { |m| @c.should respond_to(m) }
|
28
|
+
[:a, :b, :c, :calc, :complex].all? { |m| @c.send(m).should be_a(sexpr_class) }
|
29
|
+
@c.a.should == nil
|
30
|
+
@c.b.should == 2
|
31
|
+
@c.c.should == 3
|
32
|
+
@c.calc.should == nil
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should re-define a variable' do
|
36
|
+
@c.var(:a, 1)
|
37
|
+
@c.var(:calc, 2)
|
38
|
+
@c.a.should == 1
|
39
|
+
@c.calc.should == 2
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should respond to any unknown method by converting it to an s-expression' do
|
43
|
+
@c.foo.should be_a(sexpr_class)
|
44
|
+
@c.bar.should be_a(sexpr_class)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should cast a literal to an s-expression' do
|
48
|
+
@c.instance_eval { Sexpr(5) }.should be_a(sexpr_class)
|
49
|
+
@c.instance_eval { 5.to_sexpr }.should be_a(sexpr_class)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should evaluate a calculation block before or after initialization' do
|
53
|
+
p = Proc.new do
|
54
|
+
eq :another, min(complex, calc) ** 2
|
55
|
+
end
|
56
|
+
@c.evaluate(p)
|
57
|
+
|
58
|
+
[:a, :b, :c, :calc, :complex, :another].all? { |m| @c.should respond_to(m) }
|
59
|
+
[:a, :b, :c, :calc, :complex, :another].all? { |m| @c.send(m).should be_a(sexpr_class) }
|
60
|
+
@c.another.should == nil
|
61
|
+
@c.var(:a, 1)
|
62
|
+
@c.another.should == 81
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
data/spec/env.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Polly::Env do
|
4
|
+
|
5
|
+
let(:math_class) { Polly::Math }
|
6
|
+
let(:env_class) { Polly::Env }
|
7
|
+
let(:sexpr_class) { Polly::Sexpr }
|
8
|
+
|
9
|
+
before :each do
|
10
|
+
@env = env_class.new(
|
11
|
+
cat: sexpr_class.build("meow"),
|
12
|
+
dog: sexpr_class.build("gruff")
|
13
|
+
)
|
14
|
+
|
15
|
+
@env[:foo] = sexpr_class.build(0)
|
16
|
+
@env[:bar] = sexpr_class.build(:a)
|
17
|
+
@env[:buz] = sexpr_class.build('a')
|
18
|
+
@env[:him] = sexpr_class.build(true)
|
19
|
+
@env[:her] = sexpr_class.build(false)
|
20
|
+
@env[:it] = sexpr_class.build([:*,1,2])
|
21
|
+
@env[:nil] = sexpr_class.build(nil)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should include all functions included in the Math module' do
|
25
|
+
math_class.singleton_methods.all? { |m| @env[m] }.should == true
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should return only the s-expressions' do
|
29
|
+
@env.clean.should be_a(env_class)
|
30
|
+
@env.clean.size.should == 9
|
31
|
+
@env.clean.all? { |k,v| [:cat, :dog, :foo, :bar, :buz, :him, :her, :it, :nil].include?(k) }.should == true
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should return only the atomic s-expressions' do
|
35
|
+
@env.atomic_variables.size.should == 8
|
36
|
+
@env.atomic_variables.all? { |k,v| [:cat, :dog, :foo, :bar, :buz, :him, :her, :nil].include?(k) }.should == true
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should return only atomic, defined s-expressions' do
|
40
|
+
@env.defined_variables.size.should == 7
|
41
|
+
@env.defined_variables.all? { |k,v| [:cat, :dog, :foo, :bar, :buz, :him, :her].include?(k) }.should == true
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should return the names of the atomic, undefined s-expressions' do
|
45
|
+
@env.undefined_variables.size.should == 1
|
46
|
+
@env.undefined_variables.should include(:nil)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should marshal to native yaml' do
|
50
|
+
e = env_class.new(YAML::load(@env.to_yaml))
|
51
|
+
@env.should == e
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should return a value set' do
|
55
|
+
r = { cat: 'meow', dog: 'gruff', foo: 0, bar: :a, buz: 'a', him: true, her: false, it: 2, nil: nil }
|
56
|
+
@env.values.should == r
|
57
|
+
@env.values!.should == r
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
data/spec/examples.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Polly::Calculation do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@calc = \
|
7
|
+
Polly::Calculation.new do
|
8
|
+
name :name
|
9
|
+
version 1
|
10
|
+
|
11
|
+
# constants
|
12
|
+
const :a, 150.percent
|
13
|
+
const :b, 5000
|
14
|
+
const :c, 12
|
15
|
+
const :d, 80.percent
|
16
|
+
|
17
|
+
# inputs
|
18
|
+
var :e
|
19
|
+
var :f
|
20
|
+
var :g
|
21
|
+
var :h
|
22
|
+
var :i, b
|
23
|
+
end
|
24
|
+
|
25
|
+
@calc.evaluate do
|
26
|
+
# eq can nest other equations or vars
|
27
|
+
eq :eq1, (e * f + g) / 3.0 - f + -e
|
28
|
+
eq :eq2, br(h.length > 4, eq1.to_i, e)
|
29
|
+
eq :eq3, max(ceil(pv(a.round(1), c, eq2), 50), e)
|
30
|
+
eq :eq4, eq3
|
31
|
+
|
32
|
+
eq :final, 100.to_sexpr + Sexpr(1000) + eq4 + b
|
33
|
+
end
|
34
|
+
|
35
|
+
@calc.h = "str"
|
36
|
+
@calc.e = 3000
|
37
|
+
@calc.f = 2
|
38
|
+
@calc.g = 10000
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should perform a complex calculation' do
|
42
|
+
@calc.final.should == 9100
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should perform a complex calculation' do
|
46
|
+
@calc.h = "string"
|
47
|
+
@calc.final.should == 9100
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should perform a complex calculation' do
|
51
|
+
@calc.e = 20000
|
52
|
+
@calc.final.should == 26100
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should convert the calculation to an array' do
|
56
|
+
@calc.final.to_ary.should == \
|
57
|
+
[:+, [:+, [:+, [100], [1000]], [:self, [:eq3]]], [:b]]
|
58
|
+
@calc.final.to_ary(numeric: true).should == \
|
59
|
+
[:+, [:+, [:+, [100], [1000]], [:self, [3000]]], [5000]]
|
60
|
+
@calc.final.to_ary(expand: true).should == \
|
61
|
+
[:+, [:+, [:+, [100], [1000]], [:self, [:max, [:ceil, [:pv, [:round, [:a], [1]], [:c], [:br, [:>, [:length, [:h]], [4]], [:to_i, [:+, [:-, [:/, [:+, [:*, [:e], [:f]], [:g]], [3.0]], [:f]], [:-@, [:e]]]], [:e]]], [50]], [:e]]]], [:b]]
|
62
|
+
@calc.final.to_ary(numeric: true, expand: true).should == \
|
63
|
+
[:+, [:+, [:+, [100], [1000]], [:self, [:max, [:ceil, [:pv, [:round, [1.5], [1]], [12], [:br, [:>, [:length, ["str"]], [4]], [:to_i, [:+, [:-, [:/, [:+, [:*, [3000], [2]], [10000]], [3.0]], [2]], [:-@, [3000]]]], [3000]]], [50]], [3000]]]], [5000]]
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should convert the calculation to a human readable string' do
|
67
|
+
@calc.final.to_s.should == \
|
68
|
+
"(((100 + 1000) + self(:eq3)) + :b)"
|
69
|
+
@calc.final.to_s(numeric: true).should == \
|
70
|
+
"(((100 + 1000) + self(3000)) + 5000)"
|
71
|
+
@calc.final.to_s(expand: true).should == \
|
72
|
+
"(((100 + 1000) + self(max(ceil(pv(round(:a, 1), :c, br((length(:h) > 4), to_i((((((:e * :f) + :g) / 3.0) - :f) + -@(:e))), :e)), 50), :e))) + :b)"
|
73
|
+
@calc.final.to_s(numeric: true, expand: true).should == \
|
74
|
+
"(((100 + 1000) + self(max(ceil(pv(round(1.5, 1), 12, br((length(\"str\") > 4), to_i((((((3000 * 2) + 10000) / 3.0) - 2) + -@(3000))), 3000)), 50), 3000))) + 5000)"
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
data/spec/math.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
describe Polly::Math do
|
5
|
+
let(:math_class) { Polly::Math }
|
6
|
+
|
7
|
+
describe 'mathematical functions' do
|
8
|
+
|
9
|
+
it 'should perform a min function' do
|
10
|
+
nums = [34, 893, 12, 1234, 17]
|
11
|
+
math_class.min(0).should == 0
|
12
|
+
math_class.min(15,10).should == 10
|
13
|
+
math_class.min(*nums).should == 12
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should perform a max function' do
|
17
|
+
nums = [34, 893, 12, 1234, 17]
|
18
|
+
math_class.max(0).should == 0
|
19
|
+
math_class.max(15,10).should == 15
|
20
|
+
math_class.max(*nums).should == 1234
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should perform a ceiling function' do
|
24
|
+
math_class.ceil(153,10).should == 150
|
25
|
+
math_class.ceil(153,20).should == 140
|
26
|
+
math_class.ceil(153,30).should == 150
|
27
|
+
math_class.ceil(153,40).should == 120
|
28
|
+
math_class.ceil(153,50).should == 150
|
29
|
+
math_class.ceil(153,100).should == 100
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should perform a net present value function' do
|
33
|
+
math_class.pv(0.100, 12, 50).round(2).should == 340.68
|
34
|
+
math_class.pv(0.100, 12, 75).round(2).should == 511.03
|
35
|
+
math_class.pv(0.100, 12, 100).round(2).should == 681.37
|
36
|
+
|
37
|
+
math_class.pv(0.150, 1, 100).round(2).should == 86.96
|
38
|
+
math_class.pv(0.150, 6, 100).round(2).should == 378.45
|
39
|
+
math_class.pv(0.150, 12, 100).round(2).should == 542.06
|
40
|
+
|
41
|
+
math_class.pv(0.200, 12, 100).round(2).should == 443.92
|
42
|
+
math_class.pv(0.300, 12, 100).round(2).should == 319.03
|
43
|
+
math_class.pv(0.400, 12, 100).round(2).should == 245.59
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
describe 'ruby replacement functions' do
|
49
|
+
|
50
|
+
it 'should perform a branch function' do
|
51
|
+
math_class.br(true, 0, 1).should == 0
|
52
|
+
math_class.br(false, 0, 1).should == 1
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should perform an and function' do
|
56
|
+
math_class.and(true, true).should == true
|
57
|
+
math_class.and(true, false).should == false
|
58
|
+
math_class.and(false, true).should == false
|
59
|
+
math_class.and(false, false).should == false
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should perform an or function' do
|
63
|
+
math_class.or(true, true).should == true
|
64
|
+
math_class.or(true, false).should == true
|
65
|
+
math_class.or(false, true).should == true
|
66
|
+
math_class.or(false, false).should == false
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should perform a not function' do
|
70
|
+
math_class.not(true).should == false
|
71
|
+
math_class.not(false).should == true
|
72
|
+
|
73
|
+
math_class.!(true).should == false
|
74
|
+
math_class.!(false).should == true
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should perform a not equal function' do
|
78
|
+
math_class.not_eq(true, true).should == false
|
79
|
+
math_class.not_eq(true, false).should == true
|
80
|
+
|
81
|
+
math_class.!=(true, true).should == false
|
82
|
+
math_class.!=(true, false).should == true
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
data/spec/sexpr.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Polly::Sexpr do
|
4
|
+
|
5
|
+
let(:env_class) { Polly::Env }
|
6
|
+
let(:sexpr_class) { Polly::Sexpr }
|
7
|
+
|
8
|
+
before :each do
|
9
|
+
@env = env_class.new
|
10
|
+
|
11
|
+
@s = []
|
12
|
+
@s[0] = sexpr_class.build(nil, nil, 'a')
|
13
|
+
@s[1] = sexpr_class.build(0)
|
14
|
+
@s[2] = sexpr_class.build(:a)
|
15
|
+
@s[3] = sexpr_class.build('a')
|
16
|
+
@s[4] = sexpr_class.build(true)
|
17
|
+
@s[5] = sexpr_class.build(false)
|
18
|
+
@s[6] = sexpr_class.build([:+, 1, 2])
|
19
|
+
@s[7] = sexpr_class.build([:*, [:+, 1, 2], 3])
|
20
|
+
@s[8] = sexpr_class.build([:*, [:+, 1, 2], @s[0]])
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should recursively build an s-expression from a supported native type' do
|
24
|
+
@s.all? { |s| s.should be_a(sexpr_class) }
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should save the s-expressions name if provided' do
|
28
|
+
@s[0].name.should == 'a'
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should determine the operation and arguments of any s-expression' do
|
32
|
+
@s[0].op.should == nil
|
33
|
+
@s[0].args.should be_empty
|
34
|
+
@s[6].op.should == :+
|
35
|
+
@s[6].args.should == [1,2]
|
36
|
+
@s[7].op.should == :*
|
37
|
+
@s[7].args.should == [@s[6], 3]
|
38
|
+
@s[8].op.should == :*
|
39
|
+
@s[8].args.should == [@s[6], @s[0]]
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should compare an s-expression to another s-expression, an array, or a value' do
|
43
|
+
sexpr = [:*, [:+, 1, 2], 3]
|
44
|
+
@s[7].should == sexpr_class.build(sexpr, @env)
|
45
|
+
@s[7].should == sexpr
|
46
|
+
@s[7].should == 9
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should evaluate a valid s-expression' do
|
50
|
+
@s[0].should == nil
|
51
|
+
@s[1].should == 0
|
52
|
+
@s[2].should == :a
|
53
|
+
@s[3].should == 'a'
|
54
|
+
@s[4].should == true
|
55
|
+
@s[5].should == false
|
56
|
+
@s[6].should == 3
|
57
|
+
@s[7].should == 9
|
58
|
+
@s[8].should == nil
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should replace an s-expression value in place' do
|
62
|
+
old_oid = @s[0].object_id
|
63
|
+
@s[0].replace(1)
|
64
|
+
@s[0].should == 1
|
65
|
+
@s[0].object_id.should == old_oid
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should be able to tell if an s-expression is atomic' do
|
69
|
+
@s.values_at(0,1,2,3,4,5).all? { |s| s.atomic?.should be_true }
|
70
|
+
@s.values_at(6,7,8).all? { |s| s.atomic?.should be_false }
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should be able to tell if an s-expression is defined' do
|
74
|
+
@s.values_at(1,2,3,4,5,6,7).all? { |s| s.defined?.should be_true }
|
75
|
+
@s.values_at(0,8).all? { |s| s.defined?.should be_false }
|
76
|
+
@s.values_at(0,8).all? { |s| s.undefined?.should be_true}
|
77
|
+
@s[0].undefined_variables.should == ['a']
|
78
|
+
@s[8].undefined_variables.should == ['a']
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should mark a modified s-expression as dirty' do
|
82
|
+
@s[0].replace(1)
|
83
|
+
@s[0].should be_dirty
|
84
|
+
@s[8].should be_dirty
|
85
|
+
@s[7].should be_clean
|
86
|
+
@s[7].should_not be_dirty
|
87
|
+
@s[0].dirty_variables.should == ['a']
|
88
|
+
@s[8].dirty_variables.should == ['a']
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should convert all method calls to an s-expression' do
|
92
|
+
(@s[1] + 5).should be_a(sexpr_class)
|
93
|
+
@s[1].foo.should be_a(sexpr_class)
|
94
|
+
@s[1].bar(1,2).should be_a(sexpr_class)
|
95
|
+
|
96
|
+
(@s[1] + 5).should == [:+, 0, 5]
|
97
|
+
@s[1].foo.should == [:foo, 0]
|
98
|
+
@s[1].bar(1,2).should == [:bar, 0, 1, 2]
|
99
|
+
@s[1].foo.bar(1,2).should == [:bar, [:foo, 0], 1, 2]
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should convert to an array' do
|
103
|
+
@s[6].to_ary.should == [:+, [1], [2]]
|
104
|
+
@s[7].to_ary.should == [:*, [:+, [1], [2]], [3]]
|
105
|
+
end
|
106
|
+
|
107
|
+
describe 'make sure caching internals are working' do
|
108
|
+
|
109
|
+
before :each do
|
110
|
+
@s[7].value
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should re-evaluate same s-expression using cache' do
|
114
|
+
@s[7].should_not_receive(:eval)
|
115
|
+
@s[7].value
|
116
|
+
@s[7].value
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'should re-evaluate same s-expression and sub-expressions' do
|
120
|
+
@s[7].should_receive(:eval).twice
|
121
|
+
@s[7].value!
|
122
|
+
@s[7].value!
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'bundler/setup'
|
6
|
+
Bundler.require(:default)
|
7
|
+
|
8
|
+
require 'pry'
|
9
|
+
require 'polly'
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
13
|
+
config.filter_run :focus => true
|
14
|
+
config.run_all_when_everything_filtered = true
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: polly
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.4
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Alex Skryl
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-29 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: ! ' A Polynomial Solver DSL '
|
15
|
+
email:
|
16
|
+
- rut216@gmail.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- .gitignore
|
22
|
+
- .irb_tempfile
|
23
|
+
- .rspec
|
24
|
+
- .rvmrc
|
25
|
+
- Gemfile
|
26
|
+
- LICENSE
|
27
|
+
- README.md
|
28
|
+
- Rakefile
|
29
|
+
- TODO
|
30
|
+
- lib/polly.rb
|
31
|
+
- lib/polly/calculation.rb
|
32
|
+
- lib/polly/common.rb
|
33
|
+
- lib/polly/context.rb
|
34
|
+
- lib/polly/env.rb
|
35
|
+
- lib/polly/extensions/array.rb
|
36
|
+
- lib/polly/extensions/numeric.rb
|
37
|
+
- lib/polly/extensions/object.rb
|
38
|
+
- lib/polly/math.rb
|
39
|
+
- lib/polly/sexpr.rb
|
40
|
+
- lib/polly/version.rb
|
41
|
+
- polly.gemspec
|
42
|
+
- spec/calculation.rb
|
43
|
+
- spec/context.rb
|
44
|
+
- spec/env.rb
|
45
|
+
- spec/examples.rb
|
46
|
+
- spec/math.rb
|
47
|
+
- spec/sexpr.rb
|
48
|
+
- spec/spec_helper.rb
|
49
|
+
homepage: ''
|
50
|
+
licenses: []
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
requirements: []
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 1.8.23
|
70
|
+
signing_key:
|
71
|
+
specification_version: 3
|
72
|
+
summary: A Rails compatible polynomial solver with persistence
|
73
|
+
test_files:
|
74
|
+
- spec/calculation.rb
|
75
|
+
- spec/context.rb
|
76
|
+
- spec/env.rb
|
77
|
+
- spec/examples.rb
|
78
|
+
- spec/math.rb
|
79
|
+
- spec/sexpr.rb
|
80
|
+
- spec/spec_helper.rb
|
81
|
+
has_rdoc:
|