vector_space 0.1.2
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/LICENSE +19 -0
- data/README.markdown +110 -0
- data/TODO +3 -0
- data/lib/vector_space.rb +13 -0
- data/lib/vector_space/arithmetic.rb +33 -0
- data/lib/vector_space/component_reflection.rb +27 -0
- data/lib/vector_space/dimension_reflection.rb +11 -0
- data/lib/vector_space/family.rb +45 -0
- data/lib/vector_space/index_reflection.rb +7 -0
- data/lib/vector_space/partial_order.rb +16 -0
- data/lib/vector_space/simple_indexed_vector.rb +30 -0
- data/lib/vector_space/simple_vector.rb +70 -0
- data/lib/vector_space/vector_space.rb +101 -0
- metadata +67 -0
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2009 Tom Stuart
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
VectorSpace
|
2
|
+
===========
|
3
|
+
|
4
|
+
A Ruby library for treating multidimensional values as elements of a vector space.
|
5
|
+
|
6
|
+
Examples
|
7
|
+
--------
|
8
|
+
|
9
|
+
A simple vector space:
|
10
|
+
|
11
|
+
class Round < VectorSpace::SimpleVector
|
12
|
+
has_dimension :hoegaarden
|
13
|
+
has_dimension :franziskaner
|
14
|
+
has_dimension :fruli
|
15
|
+
has_dimension :water
|
16
|
+
end
|
17
|
+
|
18
|
+
>> Round.new
|
19
|
+
=> 0 and 0 and 0 and 0
|
20
|
+
|
21
|
+
>> Round.new :hoegaarden => 5, :fruli => 3
|
22
|
+
=> 5 and 0 and 3 and 0
|
23
|
+
|
24
|
+
With custom descriptions:
|
25
|
+
|
26
|
+
class Round < VectorSpace::SimpleVector
|
27
|
+
has_dimension :hoegaarden, :describe => lambda { |n| "#{n} Hoegaardens" unless n.zero? }
|
28
|
+
has_dimension :franziskaner, :describe => lambda { |n| "#{n} Franziskaners" unless n.zero? }
|
29
|
+
has_dimension :fruli, :describe => lambda { |n| "#{n} Frülis" unless n.zero? }
|
30
|
+
has_dimension :water, :describe => lambda { |n| "#{n} waters" unless n.zero? }
|
31
|
+
has_zero_description 'no drinks'
|
32
|
+
end
|
33
|
+
|
34
|
+
>> Round.new
|
35
|
+
=> no drinks
|
36
|
+
|
37
|
+
>> Round.new :hoegaarden => 5, :fruli => 3
|
38
|
+
=> 5 Hoegaardens and 3 Frülis
|
39
|
+
|
40
|
+
>> Round.new(:hoegaarden => 2, :franziskaner => 3) + Round.new(:water => 2, :fruli => 2, :hoegaarden => 1)
|
41
|
+
=> 3 Hoegaardens and 3 Franziskaners and 2 Frülis and 2 waters
|
42
|
+
|
43
|
+
>> (Round.new(:hoegaarden => 2, :franziskaner => 3) * 2) - Round.new(:hoegaarden => 1)
|
44
|
+
=> 3 Hoegaardens and 6 Franziskaners
|
45
|
+
|
46
|
+
An indexed family of vector spaces with custom descriptions:
|
47
|
+
|
48
|
+
class Cocktail < VectorSpace::SimpleIndexedVector
|
49
|
+
indexed_by :units
|
50
|
+
has_dimension :gin, :describe => lambda { |units, n| "#{n}#{units} gin" unless n.zero? }
|
51
|
+
has_dimension :vermouth, :describe => lambda { |units, n| "#{n}#{units} vermouth" unless n.zero? }
|
52
|
+
has_dimension :whisky, :describe => lambda { |units, n| "#{n}#{units} whisky" unless n.zero? }
|
53
|
+
has_dimension :vodka, :describe => lambda { |units, n| "#{n}#{units} vodka" unless n.zero? }
|
54
|
+
has_dimension :kahlua, :describe => lambda { |units, n| "#{n}#{units} Kahlúa" unless n.zero? }
|
55
|
+
has_dimension :cream, :describe => lambda { |units, n| "#{n}#{units} cream" unless n.zero? }
|
56
|
+
end
|
57
|
+
|
58
|
+
>> martini = Cocktail.new :units => :cl, :gin => 5.5, :vermouth => 1.5
|
59
|
+
=> 5.5cl gin and 1.5cl vermouth
|
60
|
+
|
61
|
+
>> manhattan = Cocktail.new :units => :cl, :whisky => 5, :vermouth => 2
|
62
|
+
=> 2cl vermouth and 5cl whisky
|
63
|
+
|
64
|
+
>> white_russian = Cocktail.new :units => :oz, :vodka => 2, :kahlua => 1, :cream => 1.5
|
65
|
+
=> 2oz vodka and 1oz Kahlúa and 1.5oz cream
|
66
|
+
|
67
|
+
>> martini * 2
|
68
|
+
=> 11cl gin and 3cl vermouth
|
69
|
+
|
70
|
+
>> martini + manhattan
|
71
|
+
=> 5.5cl gin and 3.5cl vermouth and 5cl whisky
|
72
|
+
|
73
|
+
>> martini + white_russian
|
74
|
+
ArgumentError: can't add 5.5cl gin and 1.5cl vermouth to 2oz vodka and 1oz Kahlúa and 1.5oz cream
|
75
|
+
|
76
|
+
A real (one-dimensional) example:
|
77
|
+
|
78
|
+
class Money < VectorSpace::SimpleIndexedVector
|
79
|
+
indexed_by :currency, :default => Currency::GBP
|
80
|
+
has_dimension :cents,
|
81
|
+
:coerce => lambda { |n| n.round }, # or just :coerce => :round if you have Symbol#to_proc
|
82
|
+
:describe => lambda { |currency, cents| "#{currency.symbol}#{cents / 100}.#{sprintf('%02d', cents % 100)}" }
|
83
|
+
end
|
84
|
+
|
85
|
+
>> a = Money.new(:currency => Currency::GBP, :cents => 9900)
|
86
|
+
=> £99.00
|
87
|
+
|
88
|
+
>> b = Money.new(:currency => Currency::GBP, :cents => 1500)
|
89
|
+
=> £15.00
|
90
|
+
|
91
|
+
>> a < b
|
92
|
+
=> false
|
93
|
+
|
94
|
+
>> a > b
|
95
|
+
=> true
|
96
|
+
|
97
|
+
>> a + b
|
98
|
+
=> £114.00
|
99
|
+
|
100
|
+
>> c = Money.new(:currency => Currency::USD, :cents => 99)
|
101
|
+
=> $0.99
|
102
|
+
|
103
|
+
>> a < c
|
104
|
+
=> false
|
105
|
+
|
106
|
+
>> c < a
|
107
|
+
=> false
|
108
|
+
|
109
|
+
>> a + c
|
110
|
+
ArgumentError: can't add $0.99 to £99.00
|
data/TODO
ADDED
data/lib/vector_space.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
require 'vector_space/component_reflection'
|
4
|
+
require 'vector_space/dimension_reflection'
|
5
|
+
|
6
|
+
require 'vector_space/partial_order'
|
7
|
+
require 'vector_space/arithmetic'
|
8
|
+
require 'vector_space/vector_space'
|
9
|
+
require 'vector_space/simple_vector'
|
10
|
+
|
11
|
+
require 'vector_space/index_reflection'
|
12
|
+
require 'vector_space/family'
|
13
|
+
require 'vector_space/simple_indexed_vector'
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module VectorSpace
|
2
|
+
# These operations depend only upon addition and multiplication of the underlying values, so work in a vector space
|
3
|
+
module Arithmetic
|
4
|
+
# Vector addition
|
5
|
+
def +(vector)
|
6
|
+
if compatible_with?(vector)
|
7
|
+
operate_on_values(vector) { |a, b| a + b }
|
8
|
+
else
|
9
|
+
raise ArgumentError, "can't add #{vector.inspect} to #{self.inspect}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Scalar multiplication
|
14
|
+
def *(n)
|
15
|
+
operate_on_values { |value| value * n }
|
16
|
+
end
|
17
|
+
|
18
|
+
# Additive inverse
|
19
|
+
def -@
|
20
|
+
operate_on_values { |value| -value }
|
21
|
+
end
|
22
|
+
|
23
|
+
# Vector subtraction (by addition)
|
24
|
+
def -(vector)
|
25
|
+
self + (-vector)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Scalar division (by multiplication)
|
29
|
+
def /(n)
|
30
|
+
self * (1.0 / n)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module VectorSpace
|
2
|
+
class ComponentReflection
|
3
|
+
def initialize(component, options = {})
|
4
|
+
@component, @options = component, options
|
5
|
+
end
|
6
|
+
|
7
|
+
def to_s
|
8
|
+
"#{component} component (#{default.class})"
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :component, :options
|
12
|
+
|
13
|
+
def getter
|
14
|
+
options[:getter] || component.to_sym
|
15
|
+
end
|
16
|
+
|
17
|
+
def value(value)
|
18
|
+
value ||= default
|
19
|
+
|
20
|
+
if options[:coerce]
|
21
|
+
options[:coerce].to_proc.call(value)
|
22
|
+
else
|
23
|
+
value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module VectorSpace
|
2
|
+
module Family
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
include VectorSpace
|
6
|
+
extend ClassMethods
|
7
|
+
include InstanceMethods
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
attr_reader :index
|
13
|
+
|
14
|
+
def indexed_by(index, options = {})
|
15
|
+
@index = index
|
16
|
+
(@component_reflections ||= {})[index] = IndexReflection.new(index, options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def components
|
20
|
+
[index] + super
|
21
|
+
end
|
22
|
+
|
23
|
+
def zero(attributes = {})
|
24
|
+
new Hash[*([index, attributes[index] || reflect_on_component(index).default] + dimensions.map { |dimension| [dimension, reflect_on_component(dimension).zero] }).flatten]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module InstanceMethods
|
29
|
+
def self.included(base)
|
30
|
+
base.class_eval do
|
31
|
+
def_delegator :'self.class', :index
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def compatible_with?(other)
|
36
|
+
super && project(index) == other.project(index)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
def operate_on_values(*vectors, &block)
|
41
|
+
self.class.new Hash[*(map_components(index, *vectors) { |*values| values.inject { |a, b| a if a == b } } + map_components(dimensions, *vectors, &block)).flatten]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module VectorSpace
|
2
|
+
module PartialOrder
|
3
|
+
# Ruby's Comparable isn't well-behaved for partial orders: its operations raise ArgumentError if #<=> returns nil.
|
4
|
+
# This module provides operations which simply return false when two values are incomparable.
|
5
|
+
|
6
|
+
[:<, :<=, :==, :>=, :>].each do |operation|
|
7
|
+
define_method operation do |other|
|
8
|
+
((result = self <=> other) && result.send(operation, 0)) == true
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def comparable_with?(other)
|
13
|
+
!(self <=> other).nil?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module VectorSpace
|
2
|
+
class SimpleIndexedVector < SimpleVector
|
3
|
+
include Family
|
4
|
+
|
5
|
+
def initialize(attributes = {})
|
6
|
+
if index_value = attributes[index] || reflect_on_component(index).default
|
7
|
+
super attributes.merge(index => index_value)
|
8
|
+
else
|
9
|
+
raise ArgumentError, "must provide value for #{index} index or configure a default"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def describe_value(dimension)
|
15
|
+
if describe = reflect_on_component(dimension).options[:describe]
|
16
|
+
describe.to_proc.call(project(index), project(dimension))
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def indexed_by(index, attributes = {})
|
24
|
+
define_method (reflection = super(index, attributes)).getter do
|
25
|
+
reflection.value(@attributes[index])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module VectorSpace
|
2
|
+
class SimpleVector
|
3
|
+
include Enumerable
|
4
|
+
include VectorSpace
|
5
|
+
def_delegators :'self.class', :description_prefix, :description_suffix, :description_separator, :zero_description
|
6
|
+
|
7
|
+
def initialize(attributes = {})
|
8
|
+
@attributes = attributes
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
if zero? && zero_description
|
13
|
+
zero_description
|
14
|
+
else
|
15
|
+
[
|
16
|
+
description_prefix,
|
17
|
+
dimensions.map { |dimension| describe_value(dimension) }.compact.join(description_separator),
|
18
|
+
description_suffix
|
19
|
+
].join
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def [](component)
|
24
|
+
project(component)
|
25
|
+
end
|
26
|
+
|
27
|
+
def each
|
28
|
+
dimensions.each do |dimension| yield [dimension, project(dimension)] end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def describe_value(dimension)
|
33
|
+
if describe = reflect_on_component(dimension).options[:describe]
|
34
|
+
describe.to_proc.call(project(dimension))
|
35
|
+
else
|
36
|
+
project(dimension)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class << self
|
41
|
+
attr_reader :description_prefix, :description_suffix, :zero_description
|
42
|
+
|
43
|
+
def has_dimension(dimension, attributes = {})
|
44
|
+
define_method (reflection = super(dimension, attributes)).getter do
|
45
|
+
reflection.value(@attributes[dimension])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def has_description_prefix(description_prefix)
|
50
|
+
@description_prefix = description_prefix
|
51
|
+
end
|
52
|
+
|
53
|
+
def has_description_suffix(description_suffix)
|
54
|
+
@description_suffix = description_suffix
|
55
|
+
end
|
56
|
+
|
57
|
+
def has_description_separator(description_separator)
|
58
|
+
@description_separator = description_separator
|
59
|
+
end
|
60
|
+
|
61
|
+
def has_zero_description(zero_description)
|
62
|
+
@zero_description = zero_description
|
63
|
+
end
|
64
|
+
|
65
|
+
def description_separator
|
66
|
+
@description_separator || ' and '
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module VectorSpace
|
2
|
+
def self.included(base)
|
3
|
+
base.class_eval do
|
4
|
+
extend ClassMethods
|
5
|
+
include InstanceMethods
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
attr_reader :dimensions
|
11
|
+
|
12
|
+
def components
|
13
|
+
dimensions
|
14
|
+
end
|
15
|
+
|
16
|
+
def reflect_on_component(component)
|
17
|
+
@component_reflections[component]
|
18
|
+
end
|
19
|
+
|
20
|
+
def zero
|
21
|
+
new Hash[*dimensions.map { |dimension| [dimension, reflect_on_component(dimension).zero] }.flatten]
|
22
|
+
end
|
23
|
+
|
24
|
+
def order
|
25
|
+
@order || :product
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def has_dimension(dimension, options = {})
|
30
|
+
(@dimensions ||= []) << dimension
|
31
|
+
(@component_reflections ||= {})[dimension] = DimensionReflection.new(dimension, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def has_order(order)
|
35
|
+
@order = order
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module InstanceMethods
|
40
|
+
def self.included(base)
|
41
|
+
base.class_eval do
|
42
|
+
extend Forwardable
|
43
|
+
def_delegators :'self.class', :components, :dimensions, :order, :reflect_on_component
|
44
|
+
|
45
|
+
include PartialOrder
|
46
|
+
include Arithmetic
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def project(component)
|
51
|
+
reflection = reflect_on_component(component)
|
52
|
+
reflection.value(send(reflection.getter))
|
53
|
+
end
|
54
|
+
|
55
|
+
def map_components(components, *vectors)
|
56
|
+
Array(components).map { |component| [component, yield(*([self] + vectors).map { |vector| vector.project(component) })] }
|
57
|
+
end
|
58
|
+
|
59
|
+
def map_values(components, *vectors, &block)
|
60
|
+
map_components(components, *vectors, &block).map { |component, value| value }
|
61
|
+
end
|
62
|
+
|
63
|
+
def zero?
|
64
|
+
map_values(dimensions) { |value| value.zero? }.all?
|
65
|
+
end
|
66
|
+
|
67
|
+
# Decides whether the receiver is compatible with some other object for general operations (e.g. arithmetic and comparison).
|
68
|
+
def compatible_with?(other)
|
69
|
+
other.class == self.class
|
70
|
+
end
|
71
|
+
|
72
|
+
# Compares the receiver against another object, returning -1, 0, +1 or nil depending on whether the receiver is less than,
|
73
|
+
# equal to, greater than, or incomparable with the other object.
|
74
|
+
# This works just like the #<=> expected by Comparable, but also returns nil iff the two objects are incomparable.
|
75
|
+
def <=>(other)
|
76
|
+
if compatible_with?(other)
|
77
|
+
comparisons = map_values(dimensions, other) { |a, b| a <=> b }
|
78
|
+
|
79
|
+
case order
|
80
|
+
when :product
|
81
|
+
comparisons.inject { |a, b| a + b if a && b && a * b >= 0 }
|
82
|
+
when :lexicographic
|
83
|
+
comparisons.inject { |a, b| a == 0 ? b : a }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def eql?(other)
|
89
|
+
map_values(dimensions, other) { |a, b| a.eql?(b) }.all?
|
90
|
+
end
|
91
|
+
|
92
|
+
def hash
|
93
|
+
map_values(dimensions) { |value| value }.hash
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
def operate_on_values(*vectors, &block)
|
98
|
+
self.class.new Hash[*map_components(dimensions, *vectors, &block).flatten]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
metadata
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vector_space
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tom Stuart
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-23 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: tom@experthuman.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- lib/vector_space/arithmetic.rb
|
26
|
+
- lib/vector_space/component_reflection.rb
|
27
|
+
- lib/vector_space/dimension_reflection.rb
|
28
|
+
- lib/vector_space/family.rb
|
29
|
+
- lib/vector_space/index_reflection.rb
|
30
|
+
- lib/vector_space/partial_order.rb
|
31
|
+
- lib/vector_space/simple_indexed_vector.rb
|
32
|
+
- lib/vector_space/simple_vector.rb
|
33
|
+
- lib/vector_space/vector_space.rb
|
34
|
+
- lib/vector_space.rb
|
35
|
+
- LICENSE
|
36
|
+
- README.markdown
|
37
|
+
- TODO
|
38
|
+
has_rdoc: true
|
39
|
+
homepage: http://github.com/tomstuart/vector_space
|
40
|
+
licenses: []
|
41
|
+
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: "0"
|
52
|
+
version:
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
version:
|
59
|
+
requirements: []
|
60
|
+
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 1.3.5
|
63
|
+
signing_key:
|
64
|
+
specification_version: 3
|
65
|
+
summary: A Ruby library for treating multidimensional values as elements of a vector space
|
66
|
+
test_files: []
|
67
|
+
|