rational_choice 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4d9c8ce05833c10354ed5a50430c8a2d23c605b8
4
+ data.tar.gz: 6f3f5990f6484cd086da5226903e79bd2948a643
5
+ SHA512:
6
+ metadata.gz: 09fadb723ab85663042ce147028c5144b62fd80c605b601d638a00285963e7970bd4018e20bbd42404ccba2707f5c026a1e620e83e8535517b6f28e24ec8e07d
7
+ data.tar.gz: 68b35d64dc97aba4466da942f77dc161eb2b3933dc5a98c77a500239edd042f6031df5c1e9f6ca5b481ad03e689cedb64bae53b508b2079a24a4b98ab317581f
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ rvm:
2
+ - 2.0.0
3
+ - 2.1.1
4
+ - 2.2.0
5
+ cache: bundler
6
+ sudo: false
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ group :development do
4
+ gem 'yard'
5
+ gem "rspec", "~> 3.4.0"
6
+ gem "rdoc", "~> 3.12"
7
+ gem "bundler", "~> 1.0"
8
+ gem "jeweler", "~> 2.0.1"
9
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2015 WeTransfer
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # rational_choice
2
+
3
+ Makes a fuzzy logic choice based on a continuum of values. For when a "yes or no" is not-quite-good-enough.
4
+
5
+ ## Choice on one dimension of values
6
+
7
+ Primary use is for things like load balancing. Imagine you have a server handling 14 connections,
8
+ and you know that it can take about 20 maximum. When you decide whether to send the connection
9
+ number 17 to it, you want to take a little margin and only send that connection sometimes, to
10
+ balance the choices - so you want to use a softer bound (a bit of a fuzzy logic).
11
+
12
+ will_accept_connection = RationalChoice::Dimension.new(20, 10) # ten connections is always safe
13
+ if will_accept_connection.choose(server.current_connection_count + 1) # will give you a fuzzy choice
14
+ server.accept(new_client)
15
+ else
16
+ ... # over capacity at the moment, try later
17
+ end
18
+
19
+ This way the return value of `choose()` will be determined by the probability of the value being `true` - i.e.
20
+ how close it is to the upper bound. Values at or below the lower bound will always choose `false`. Values at
21
+ or above the upper bound will always choose `true`.
22
+
23
+ ## Choice on multiple dimensions
24
+
25
+ Useful to give rational choices on multiple values, averaged together.
26
+
27
+ num_clients = Dimension.new(200, 100)
28
+ bandwidth = Dimension.new(2048, 1024)
29
+ has_capacity = ManyDimensions.new(num_clients, bandwidth)
30
+
31
+ will_accept_connection = has_capacity.choose(current_client_count, current_bandwidth)
32
+
33
+ ## Contributing to rational_choice
34
+
35
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
36
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
37
+ * Fork the project.
38
+ * Start a feature/bugfix branch.
39
+ * Commit and push until you are happy with your contribution.
40
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
41
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
42
+
43
+ ## Copyright
44
+
45
+ Copyright (c) 2015 WeTransfer. See LICENSE.txt for
46
+ further details.
47
+
data/Rakefile ADDED
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+ require_relative 'lib/rational_choice'
14
+
15
+ require 'jeweler'
16
+ Jeweler::Tasks.new do |gem|
17
+ # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
18
+ gem.version = RationalChoice::VERSION
19
+ gem.name = "rational_choice"
20
+ gem.homepage = "https://github.com/wetransfer/rational_choice"
21
+ gem.license = "MIT"
22
+ gem.description = %Q{Fuzzy logic gate}
23
+ gem.summary = %Q{Makes life-concerning choices based on an informed coin toss}
24
+ gem.email = "me@julik.nl"
25
+ gem.authors = ["Julik Tarkhanov"]
26
+ # dependencies defined in Gemfile
27
+ end
28
+ Jeweler::RubygemsDotOrgTasks.new
29
+
30
+ require 'rspec/core'
31
+ require 'rspec/core/rake_task'
32
+ RSpec::Core::RakeTask.new(:spec) do |spec|
33
+ spec.pattern = FileList['spec/**/*_spec.rb']
34
+ end
35
+
36
+ task :default => :spec
37
+
38
+ require 'yard'
39
+ YARD::Rake::YardocTask.new do |t|
40
+ t.files = ['lib/**/*.rb', 'README*'] # optional
41
+ t.options = ['--markup markdown'] # optional
42
+ t.stats_options = ['--list-undoc'] # optional
43
+ end
@@ -0,0 +1,110 @@
1
+ # Tiny fuzzy-logic gate for making choices based on a continuum of permitted values
2
+ # as opposed to a hard condition.
3
+ module RationalChoice
4
+ VERSION = '1.0.0'
5
+
6
+ # Gets raised when a multidimensional choice has to be made with a wrong number
7
+ # of values versus the number of dimensions
8
+ CardinalityError = Class.new(ArgumentError)
9
+
10
+ # Gets raised when a dimension has to be created with the same parameters
11
+ DomainError = Class.new(ArgumentError)
12
+
13
+ # Represents a fuzzy choice on a single dimension (one real number)
14
+ class Dimension
15
+ # Initializes a new Dimension to evaluate values
16
+ #
17
+ # @param false_bound[#to_f, #<=>] the lower bound, at or below which the value will be considered false
18
+ # @param true_bound[#to_f, #<=>] the upper bound, at or above which the value will be considered true
19
+ def initialize(false_bound, true_bound)
20
+ raise DomainError, "Bounds were the same at #{false_bound}" if false_bound == true_bound
21
+
22
+ @lower, @upper = [false_bound, true_bound].sort
23
+ @flip_sign = [@lower, @upper].sort != [false_bound, true_bound]
24
+ end
25
+
26
+ # Evaluate a value against the given false and true bound.
27
+ #
28
+ # If the value is less than or equal to the false bound, the method will return `false`.
29
+ # If the value is larger than or equal to the true bound, the method will return 'true'.
30
+ #
31
+ # If the value is between the two bounds, the method will first determine the probability
32
+ # of the value being true, based on a linear interpolation.
33
+ # For example:
34
+ #
35
+ # d = Dimension.new(0, 1)
36
+ # d.choose(0) # => false
37
+ # d.choose(1) # => true
38
+ # d.choose(0.5) #=> will be `true` in 50% of the cases (probability of 0.5)
39
+ # d.choose(0.1) #=> will be `true` in 10% of the cases (probability of 0.1)
40
+ #
41
+ # Primary use is for things like load balancing. Imagine you have a server handling 14 connections,
42
+ # and you know that it can take about 20 maximum. When you decide whether to send the connection
43
+ # number 17 to it, you want to take a little margin and only send that connection sometimes, to
44
+ # balance the choices - so you want to use a softer bound (a bit of a fuzzy logic).
45
+ #
46
+ # will_accept_connection = Dimension.new(20, 10) # ten connections is always safe
47
+ # will_accept_connection.choose(server.current_connection_count + 1) # will give you a fuzzy choice
48
+ #
49
+ # @param value[#to_f, Comparable] a value to be evaluated (must be coercible to a Float and Comparable)
50
+ # @return [Boolean] the chosen value based on probability and randomness
51
+ def choose(value)
52
+ choice = if !fuzzy?(value)
53
+ value >= @upper
54
+ else
55
+ # Interpolate the probability of the value being true
56
+ delta = @upper.to_f - @lower.to_f
57
+ v = (value - @lower).to_f
58
+ t = (v / delta)
59
+
60
+ r = Random.new # To override in tests if needed
61
+ r.rand < t
62
+ end
63
+ choice ^ @flip_sign
64
+ end
65
+
66
+ # Tells whether the evaluation will use the probabilities or not
67
+ # (whether the given value is within the range where probability evaluation will take place).
68
+ #
69
+ # @param value[Comparable] a value to be evaluated (must be comparable)
70
+ # @return [Boolean] whether choosing on this value will use probabilities or not
71
+ def fuzzy?(value)
72
+ value > @lower && value < @upper
73
+ end
74
+ end
75
+
76
+ # Performs an evaluation based on multiple dimensions. The dimensions
77
+ # will be first coerced into one (number of truthy evaluations vs. number of falsey evaluations)
78
+ # and then a true/false value will be deduced from that.
79
+ class ManyDimensions
80
+ def initialize(*dimensions)
81
+ @dimensions = dimensions
82
+ raise CardinalityError, "%s has no dimensions to evaluate" % inspect if @dimensions.empty?
83
+ end
84
+
85
+ # Performs a weighted choice, by first collecting choice results from all the dimensions,
86
+ # and then by interpolating those results by the ratio of truthy values vs falsey values.
87
+ #
88
+ # x = Dimension.new(0,1)
89
+ # y = Dimension.new(0,1)
90
+ # z = Dimension.new(0,1)
91
+ #
92
+ # within_positive_3d_space = ManyDimensions.new(x, y, z)
93
+ # within_positive_3d_space.choose(-1, -1, -0.5) #=> false
94
+ # within_positive_3d_space.choose(1.1, 123, 1) #=> true
95
+ # within_positive_3d_space.choose(1, 0.5, 0.7) #=> true or false depending on 3 probabilities
96
+ #
97
+ # @param values[Array<Comparable,#to_f>] an array of values of the same size as the `dimensions` given to `initialize`
98
+ # @return [Boolean] true or false
99
+ def choose(*values)
100
+ if @dimensions.length != values.length
101
+ raise CardinalityError, "%s has %d dimensions but %d values were given" % [inspect, @dimensions.length, values.length]
102
+ end
103
+
104
+ evaluations = values.zip(@dimensions).map { |(v, d)| d.choose(v) }
105
+ num_truthy_choices = evaluations.select{|e| e}.length
106
+
107
+ Dimension.new(0, evaluations.length).choose(num_truthy_choices)
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,65 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+ # stub: rational_choice 1.0.0 ruby lib
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "rational_choice"
9
+ s.version = "1.0.0"
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib"]
13
+ s.authors = ["Julik Tarkhanov"]
14
+ s.date = "2015-12-06"
15
+ s.description = "Fuzzy logic gate"
16
+ s.email = "me@julik.nl"
17
+ s.extra_rdoc_files = [
18
+ "LICENSE.txt",
19
+ "README.md"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ ".rspec",
24
+ ".travis.yml",
25
+ "Gemfile",
26
+ "LICENSE.txt",
27
+ "README.md",
28
+ "Rakefile",
29
+ "lib/rational_choice.rb",
30
+ "rational_choice.gemspec",
31
+ "spec/rational_choice/dimension_spec.rb",
32
+ "spec/rational_choice/many_dimensions_spec.rb",
33
+ "spec/rational_choice_spec.rb",
34
+ "spec/spec_helper.rb"
35
+ ]
36
+ s.homepage = "https://github.com/wetransfer/rational_choice"
37
+ s.licenses = ["MIT"]
38
+ s.rubygems_version = "2.2.2"
39
+ s.summary = "Makes life-concerning choices based on an informed coin toss"
40
+
41
+ if s.respond_to? :specification_version then
42
+ s.specification_version = 4
43
+
44
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
45
+ s.add_development_dependency(%q<yard>, [">= 0"])
46
+ s.add_development_dependency(%q<rspec>, ["~> 3.4.0"])
47
+ s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
48
+ s.add_development_dependency(%q<bundler>, ["~> 1.0"])
49
+ s.add_development_dependency(%q<jeweler>, ["~> 2.0.1"])
50
+ else
51
+ s.add_dependency(%q<yard>, [">= 0"])
52
+ s.add_dependency(%q<rspec>, ["~> 3.4.0"])
53
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
54
+ s.add_dependency(%q<bundler>, ["~> 1.0"])
55
+ s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
56
+ end
57
+ else
58
+ s.add_dependency(%q<yard>, [">= 0"])
59
+ s.add_dependency(%q<rspec>, ["~> 3.4.0"])
60
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
61
+ s.add_dependency(%q<bundler>, ["~> 1.0"])
62
+ s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
63
+ end
64
+ end
65
+
@@ -0,0 +1,136 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe 'RationalChoice::Dimension' do
4
+ describe '.new' do
5
+ it 'raises a DomainError if the bounds are the same' do
6
+ expect {
7
+ RationalChoice::Dimension.new(2,2)
8
+ }.to raise_error(/Bounds were the same at 2/)
9
+ end
10
+ end
11
+
12
+ describe 'with values at or beyound the thresholds' do
13
+ it 'always evaluates to true for values above upper threshold' do
14
+ d = RationalChoice::Dimension.new(0, 1)
15
+ 10_000.times do
16
+ expect(d.choose(1.1)).to eq(true)
17
+ end
18
+ end
19
+
20
+ it 'always evaluates to true for values at upper threshold' do
21
+ d = RationalChoice::Dimension.new(0, 1)
22
+ 10_000.times do
23
+ expect(d.choose(1)).to eq(true)
24
+ end
25
+ end
26
+
27
+ it 'always evaluates to false for values at lower threshold' do
28
+ d = RationalChoice::Dimension.new(0, 1)
29
+ 10_000.times do
30
+ expect(d.choose(0)).to eq(false)
31
+ end
32
+ end
33
+
34
+ it 'always evaluates to false for values below lower threshold' do
35
+ d = RationalChoice::Dimension.new(0, 1)
36
+ 10_000.times do
37
+ expect(d.choose(-0.0001)).to eq(false)
38
+ end
39
+ end
40
+ end
41
+
42
+ describe 'with values between thresholds creates a sensible choice distribution' do
43
+ it 'for 0.5 on a continuum from 0 to 1' do
44
+ d = RationalChoice::Dimension.new(0, 1)
45
+ expect(d).to be_fuzzy(0.5)
46
+
47
+ trues = 0
48
+
49
+ n_evaluations = 100_000
50
+ n_evaluations.times { trues += 1 if d.choose(0.5) }
51
+
52
+ expect(trues).to be_within(1000).of(n_evaluations / 2)
53
+ end
54
+
55
+ it 'for 0.1 on a continuum from 0 to 1' do
56
+ d = RationalChoice::Dimension.new(0, 1)
57
+ expect(d).to be_fuzzy(0.1)
58
+
59
+ trues = 0
60
+
61
+ n_evaluations = 100_000
62
+ n_evaluations.times { trues += 1 if d.choose(0.1) }
63
+
64
+ expect(trues).to be_within(1000).of(n_evaluations / 10)
65
+ end
66
+
67
+ it 'for 0.5 on a continuum from 1 to 0' do
68
+ d = RationalChoice::Dimension.new(1, 0)
69
+ expect(d).to be_fuzzy(0.5)
70
+
71
+ falses = 0
72
+ n_evaluations = 100_000
73
+ n_evaluations.times { falses += 1 if !d.choose(0.5) }
74
+
75
+ expect(falses).to be_within(1000).of(n_evaluations / 2)
76
+ end
77
+
78
+ it 'for 0.1 on a continuum from 1 to 0' do
79
+ d = RationalChoice::Dimension.new(1, 0)
80
+ expect(d).to be_fuzzy(0.1)
81
+
82
+ trues = 0
83
+
84
+ n_evaluations = 100_000
85
+ n_evaluations.times { trues += 1 if d.choose(0.1) }
86
+
87
+ expect(trues).to be_within(1000).of(n_evaluations / 10 * 9) # Over 90% of the choices must be trues
88
+ end
89
+
90
+ it 'for -0.5 on a continuum from -1 to 0' do
91
+ d = RationalChoice::Dimension.new(-1, 0)
92
+ expect(d).to be_fuzzy(-0.5)
93
+
94
+ trues = 0
95
+ n_evaluations = 100_000
96
+ n_evaluations.times { trues += 1 if d.choose(-0.5) }
97
+
98
+ expect(trues).to be_within(1000).of(n_evaluations / 2)
99
+ end
100
+
101
+ it 'for -0.1 on a continuum from -1 to 0' do
102
+ d = RationalChoice::Dimension.new(-1, 0)
103
+ expect(d).to be_fuzzy(-0.1)
104
+
105
+ trues = 0
106
+
107
+ n_evaluations = 100_000
108
+ n_evaluations.times { trues += 1 if d.choose(-0.1) }
109
+
110
+ expect(trues).to be_within(1000).of(n_evaluations / 10 * 9) # Over 90% of the choices must be trues
111
+ end
112
+
113
+ it 'for -0.5 on a continuum from 0 to -1' do
114
+ d = RationalChoice::Dimension.new(0, -1)
115
+ expect(d).to be_fuzzy(-0.5)
116
+
117
+ trues = 0
118
+ n_evaluations = 100_000
119
+ n_evaluations.times { trues += 1 if d.choose(-0.5) }
120
+
121
+ expect(trues).to be_within(1000).of(n_evaluations / 2)
122
+ end
123
+
124
+ it 'for -0.1 on a continuum from 0 to -1' do
125
+ d = RationalChoice::Dimension.new(0, -1)
126
+ expect(d).to be_fuzzy(-0.1)
127
+
128
+ trues = 0
129
+
130
+ n_evaluations = 100_000
131
+ n_evaluations.times { trues += 1 if d.choose(-0.1) }
132
+
133
+ expect(trues).to be_within(1000).of(n_evaluations / 10) # Over 90% of the choices must be trues
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,54 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe 'RationalChoice::ManyDimensions' do
4
+ describe '.new' do
5
+ it 'raises a CardinalityError when no dimensions are given' do
6
+ expect {
7
+ RationalChoice::ManyDimensions.new
8
+ }.to raise_error(RationalChoice::CardinalityError)
9
+ end
10
+ end
11
+
12
+ describe '.choose' do
13
+ it 'raises a CardinalityError if the number of values does not match the number of dimensions' do
14
+ md = RationalChoice::ManyDimensions.new(nil, nil, nil)
15
+ expect {
16
+ md.choose(1, 2)
17
+ }.to raise_error(RationalChoice::CardinalityError)
18
+ end
19
+
20
+ let(:md) {
21
+ one_to_zero = RationalChoice::Dimension.new(0, 1)
22
+ RationalChoice::ManyDimensions.new(one_to_zero, one_to_zero, one_to_zero)
23
+ }
24
+
25
+ it 'returns "true" when all dimensions are at or above upper bound' do
26
+ 10_000.times { expect(md.choose(1,1,1)).to eq(true) }
27
+ 10_000.times { expect(md.choose(2,2,2)).to eq(true) }
28
+ end
29
+
30
+ it 'returns "false" when all dimensions are at or below lower bound' do
31
+ 10_000.times { expect(md.choose(0,0,0)).to eq(false) }
32
+ 10_000.times { expect(md.choose(-1,-1,-1)).to eq(false) }
33
+ end
34
+
35
+ it 'returns "true" in approximately 50% of the cases when all the values are at 0.5' do
36
+ truthy = 0
37
+ 10_000.times { truthy += 1 if md.choose(0.5, 0.5, 0.5) }
38
+ expect(truthy).to be_within(100).of(10_000 / 2)
39
+ end
40
+
41
+ it 'returns "true" in approximately 10% of the cases when all the values are at 0.1' do
42
+ truthy = 0
43
+ 10_000.times { truthy += 1 if md.choose(0.1, 0.1, 0.1) }
44
+ expect(truthy).to be_within(100).of(10_000 / 10)
45
+ end
46
+
47
+ it 'creates a very uniform distribution of values with random values across the board' do
48
+ truthy = 0
49
+ 10_000.times { truthy += 1 if md.choose(rand, rand, rand) } # default rand() is 0..1
50
+ expect(truthy).to be_within(100).of(10_000 / 2)
51
+ end
52
+ end
53
+
54
+ end
@@ -0,0 +1,9 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "RationalChoice" do
4
+ it "has the necessary constants" do
5
+ RationalChoice::CardinalityError
6
+ RationalChoice::Dimension
7
+ RationalChoice::ManyDimensions
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+
4
+ require 'rspec'
5
+ require 'rational_choice'
6
+
7
+ RSpec.configure {|c| c.order = 'random' }
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rational_choice
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Julik Tarkhanov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-12-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: yard
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 3.4.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 3.4.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: rdoc
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.12'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.12'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: jeweler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 2.0.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 2.0.1
83
+ description: Fuzzy logic gate
84
+ email: me@julik.nl
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files:
88
+ - LICENSE.txt
89
+ - README.md
90
+ files:
91
+ - ".document"
92
+ - ".rspec"
93
+ - ".travis.yml"
94
+ - Gemfile
95
+ - LICENSE.txt
96
+ - README.md
97
+ - Rakefile
98
+ - lib/rational_choice.rb
99
+ - rational_choice.gemspec
100
+ - spec/rational_choice/dimension_spec.rb
101
+ - spec/rational_choice/many_dimensions_spec.rb
102
+ - spec/rational_choice_spec.rb
103
+ - spec/spec_helper.rb
104
+ homepage: https://github.com/wetransfer/rational_choice
105
+ licenses:
106
+ - MIT
107
+ metadata: {}
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 2.2.2
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: Makes life-concerning choices based on an informed coin toss
128
+ test_files: []