nametrainer 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +3 -5
- data/Rakefile +1 -1
- data/bin/nametrainer +2 -2
- data/lib/nametrainer.rb +4 -2
- data/lib/nametrainer/collection.rb +39 -99
- data/lib/nametrainer/collectionloader.rb +55 -0
- data/lib/nametrainer/gui.rb +9 -6
- data/lib/nametrainer/rng.rb +63 -0
- data/lib/nametrainer/version.rb +3 -3
- data/test/test_collection.rb +10 -29
- data/test/test_collectionloader.rb +26 -0
- data/test/test_rng.rb +37 -0
- metadata +23 -8
- data/test/collection/nametrainer.dat +0 -4
data/README.md
CHANGED
@@ -65,15 +65,13 @@ Install with `gem install nametrainer`.
|
|
65
65
|
|
66
66
|
You will also have to install Qt bindings for Ruby:
|
67
67
|
|
68
|
-
- On Windows, the `qtbindings` gem should get you up and running.
|
69
|
-
|
70
68
|
- On Linux, install the appropriate package for your distribution,
|
71
|
-
|
72
|
-
|
73
|
-
You can also compile the bindings using the `qtbindings` gem
|
69
|
+
or compile the bindings using the `qtbindings` gem
|
74
70
|
(see the [qtbindings README](https://github.com/ryanmelt/qtbindings#readme)
|
75
71
|
for instructions).
|
76
72
|
|
73
|
+
- On Windows, the `qtbindings` gem should get you up and running.
|
74
|
+
|
77
75
|
Acknowledgements
|
78
76
|
----------------
|
79
77
|
|
data/Rakefile
CHANGED
data/bin/nametrainer
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#!/usr/bin/ruby
|
1
|
+
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'nametrainer'
|
4
4
|
|
@@ -10,7 +10,7 @@ module Nametrainer
|
|
10
10
|
|
11
11
|
# Prints an error message, then exits with +code+
|
12
12
|
# (<tt>:general</tt> or <tt>:usage</tt>).
|
13
|
-
def self.fail(message, code = :general)
|
13
|
+
def self.fail(message, code = :general)
|
14
14
|
warn "#{PROGNAME}: #{message}"
|
15
15
|
exit ERRORCODE[code]
|
16
16
|
end
|
data/lib/nametrainer.rb
CHANGED
@@ -16,7 +16,7 @@
|
|
16
16
|
#
|
17
17
|
# == Author
|
18
18
|
#
|
19
|
-
# Copyright (C) 2012 Marcus Stollsteimer
|
19
|
+
# Copyright (C) 2012-2013 Marcus Stollsteimer
|
20
20
|
#
|
21
21
|
# License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
|
22
22
|
|
@@ -30,9 +30,11 @@ rescue LoadError => e
|
|
30
30
|
end
|
31
31
|
|
32
32
|
require 'nametrainer/gui' unless Nametrainer::QT_LOAD_ERROR
|
33
|
-
require 'nametrainer/person'
|
34
33
|
require 'nametrainer/collection'
|
34
|
+
require 'nametrainer/collectionloader'
|
35
35
|
require 'nametrainer/optionparser'
|
36
|
+
require 'nametrainer/person'
|
37
|
+
require 'nametrainer/rng'
|
36
38
|
require 'nametrainer/statistics'
|
37
39
|
require 'nametrainer/version'
|
38
40
|
|
@@ -1,69 +1,61 @@
|
|
1
|
-
require 'set'
|
2
1
|
require 'yaml'
|
2
|
+
require 'forwardable'
|
3
3
|
|
4
|
-
require 'nametrainer/
|
4
|
+
require 'nametrainer/rng'
|
5
5
|
|
6
6
|
module Nametrainer
|
7
7
|
|
8
|
-
# A
|
8
|
+
# A class for a collection of persons, each with a name,
|
9
9
|
# a corresponding (image) file, and a score.
|
10
|
-
# A collection is an array of instances of the Person class.
|
11
10
|
#
|
12
|
-
#
|
13
|
-
# collection =
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# (extension is removed, underscore is converted to space)
|
18
|
-
# or the content of a corresponding `txt' file, and the file name as
|
19
|
-
# the +image+ attribute.
|
11
|
+
# Create a Collection instance with
|
12
|
+
# collection = CollectionLoader.load(
|
13
|
+
# :directory => directory,
|
14
|
+
# :extensions => extensions
|
15
|
+
# )
|
20
16
|
#
|
21
17
|
# You can get a random person from a collection with
|
22
18
|
# person = collection.sample
|
23
19
|
#
|
24
20
|
# Persons with a lower score are chosen more often
|
25
21
|
# than persons with a higher score.
|
26
|
-
class Collection
|
22
|
+
class Collection
|
23
|
+
|
24
|
+
extend Forwardable
|
25
|
+
def_delegators :@collection,
|
26
|
+
:size, :empty?, :[], :last, :index, :shuffle, :map, :each
|
27
|
+
|
28
|
+
include Enumerable
|
27
29
|
|
28
30
|
SCORE_FILE = 'nametrainer.dat'
|
29
31
|
|
30
|
-
# Creates a Collection instance.
|
31
|
-
# It searches in +directory+ for files with the given
|
32
|
-
# file extensions (also in upper case) and populates
|
33
|
-
# the collection with corresponding Person instances.
|
32
|
+
# Creates a Collection instance. Expects an argument hash with:
|
34
33
|
#
|
35
|
-
# +
|
36
|
-
# +
|
37
|
-
|
38
|
-
|
39
|
-
@
|
40
|
-
@
|
41
|
-
@
|
42
|
-
self.concat self.class.load(@directory, @extensions.to_a)
|
34
|
+
# +:persons+ - array of Person instances
|
35
|
+
# +:directory+ - collection directory
|
36
|
+
# +:rng+ - random number generator, defaults to RNG
|
37
|
+
def initialize(args)
|
38
|
+
@collection = args[:persons]
|
39
|
+
@directory = args[:directory]
|
40
|
+
@rng = args[:rng] || RNG.new(:size => @collection.size, :weighting_factor => 6)
|
43
41
|
end
|
44
42
|
|
45
43
|
# Returns an array of all names.
|
46
44
|
def names
|
47
|
-
|
48
|
-
self.each {|person| all_names << person.name }
|
49
|
-
|
50
|
-
all_names
|
45
|
+
map {|person| person.name }
|
51
46
|
end
|
52
47
|
|
53
48
|
# Returns a hash with the score of all persons (name => score).
|
54
49
|
def scores
|
55
|
-
|
56
|
-
self.each {|person| all_scores[person.name] = person.score }
|
57
|
-
|
58
|
-
all_scores
|
50
|
+
Hash[map {|person| [person.name, person.score] }]
|
59
51
|
end
|
60
52
|
|
61
53
|
# Sets the score of some or all persons.
|
62
54
|
#
|
63
55
|
# +new_scores+ - hash with (name => score) values
|
64
56
|
def set_scores(new_scores)
|
65
|
-
|
66
|
-
person.score = new_scores[person.name]
|
57
|
+
each do |person|
|
58
|
+
person.score = new_scores[person.name] if new_scores[person.name]
|
67
59
|
end
|
68
60
|
end
|
69
61
|
|
@@ -80,78 +72,26 @@ module Nametrainer
|
|
80
72
|
File.open(filename, 'w') {|f| f.write(scores.to_yaml) }
|
81
73
|
end
|
82
74
|
|
75
|
+
# Delete score file.
|
76
|
+
def delete_scores
|
77
|
+
filename = File.expand_path("#{@directory}/#{SCORE_FILE}")
|
78
|
+
File.delete(filename)
|
79
|
+
end
|
80
|
+
|
83
81
|
# Returns a random element, preferring persons with a smaller score.
|
84
82
|
def sample
|
85
|
-
|
83
|
+
shuffle.sort[@rng.rand] # shuffle first to mix up elements with equal scores
|
86
84
|
end
|
87
85
|
|
88
|
-
# Returns the successor of the specified element
|
89
|
-
#
|
86
|
+
# Returns the successor of the specified element, if possible,
|
87
|
+
# or the first element.
|
90
88
|
#
|
91
89
|
# +element+ - element whose successor should be returned
|
92
90
|
def successor(element)
|
93
|
-
|
94
|
-
return
|
95
|
-
|
96
|
-
(index == self.size - 1) ? self[0] : self[index + 1]
|
97
|
-
end
|
98
|
-
|
99
|
-
private
|
100
|
-
|
101
|
-
# Returns a random index (obsolete).
|
102
|
-
def random_index
|
103
|
-
rand(self.size)
|
104
|
-
end
|
91
|
+
element_index = index(element)
|
92
|
+
return first unless element_index
|
105
93
|
|
106
|
-
|
107
|
-
def weighted_random_index
|
108
|
-
indices = indices_urn(:size => self.size, :weighting_factor => 6)
|
109
|
-
|
110
|
-
indices[rand(indices.size)]
|
111
|
-
end
|
112
|
-
|
113
|
-
# Returns an array of all indices from 0 to :size - 1, where lower indices are
|
114
|
-
# more frequent than higher indices. Index 0 will be about :weighting_factor
|
115
|
-
# times more probable then the highest index.
|
116
|
-
def indices_urn(options)
|
117
|
-
size = options[:size]
|
118
|
-
weighting_factor = options[:weighting_factor]
|
119
|
-
urn = Array.new
|
120
|
-
# repeatedly add partial ranges of indices to urn
|
121
|
-
1.upto(weighting_factor) do |i|
|
122
|
-
count = (i.to_f/weighting_factor * size).ceil
|
123
|
-
urn += (0...count).to_a
|
124
|
-
end
|
125
|
-
|
126
|
-
urn
|
127
|
-
end
|
128
|
-
|
129
|
-
# class methods
|
130
|
-
|
131
|
-
# Load a collection. Returns an array of Person instances.
|
132
|
-
def self.load(directory, extensions)
|
133
|
-
extension_list = extensions.join(',')
|
134
|
-
files = Dir.glob("#{directory}/*.{#{extension_list}}").sort
|
135
|
-
result = Array.new
|
136
|
-
files.each do |file|
|
137
|
-
name = get_name(file, extensions)
|
138
|
-
result << Person.new(name, file)
|
139
|
-
end
|
140
|
-
|
141
|
-
result
|
142
|
-
end
|
143
|
-
|
144
|
-
# Get name from corresponding `txt' file or
|
145
|
-
# from file name (remove extension, convert underscores).
|
146
|
-
def self.get_name(file, extensions)
|
147
|
-
name = file.dup
|
148
|
-
extensions.each {|ext| name.gsub!(/\.#{ext}\Z/, '') } # remove file extension
|
149
|
-
begin
|
150
|
-
info_file = "#{name}.txt"
|
151
|
-
File.read(info_file).chomp
|
152
|
-
rescue
|
153
|
-
File.basename(name).tr('_', ' ')
|
154
|
-
end
|
94
|
+
self[element_index + 1] || first
|
155
95
|
end
|
156
96
|
end
|
157
97
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
require 'nametrainer/person'
|
4
|
+
require 'nametrainer/collection'
|
5
|
+
|
6
|
+
module Nametrainer
|
7
|
+
|
8
|
+
# CollectionLoader.load loads a collection from a directory
|
9
|
+
# and returns a Collection instance.
|
10
|
+
#
|
11
|
+
# It searches in the directory for files with the given
|
12
|
+
# file extensions (ignoring case) and for each file creates
|
13
|
+
# a Person instance, using the file name as the person's +name+
|
14
|
+
# (extension is removed, underscore is converted to space)
|
15
|
+
# or the content of a corresponding `txt' file,
|
16
|
+
# and the file name as the +image+ attribute.
|
17
|
+
module CollectionLoader
|
18
|
+
|
19
|
+
# Loads a collection. Expects an argument hash with:
|
20
|
+
#
|
21
|
+
# +:directory+ - collection directory
|
22
|
+
# +:extensions+ - array of file extensions
|
23
|
+
# +:collection_class+ - defaults to Collection
|
24
|
+
# +:person_class+ - defaults to Person
|
25
|
+
#
|
26
|
+
# Returns a Collection instance.
|
27
|
+
def self.load(args)
|
28
|
+
directory = args[:directory]
|
29
|
+
extensions = args[:extensions]
|
30
|
+
collection_class = args[:collection_class] || Collection
|
31
|
+
person_class = args[:person_class] || Person
|
32
|
+
|
33
|
+
pattern = extensions.map {|ext| ext.downcase }.uniq.join('|')
|
34
|
+
valid_extensions = /\.(#{pattern})\Z/i
|
35
|
+
files = Dir.glob("#{directory}/*").grep(valid_extensions)
|
36
|
+
persons = files.sort.map {|file| person_class.new(get_name(file), file) }
|
37
|
+
|
38
|
+
collection_class.new(:persons => persons, :directory => directory)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# Get name from corresponding `txt' file or
|
44
|
+
# from file name (remove extension, convert underscores).
|
45
|
+
def self.get_name(file)
|
46
|
+
name = file.gsub(/#{File.extname(file)}\Z/, '')
|
47
|
+
begin
|
48
|
+
info_file = "#{name}.txt"
|
49
|
+
File.read(info_file).chomp
|
50
|
+
rescue
|
51
|
+
File.basename(name).tr('_', ' ')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/nametrainer/gui.rb
CHANGED
@@ -39,7 +39,7 @@ module Nametrainer
|
|
39
39
|
show
|
40
40
|
|
41
41
|
collection_dir = options[:collection]
|
42
|
-
init_collection File.expand_path(collection_dir)
|
42
|
+
init_collection File.expand_path(collection_dir) if collection_dir
|
43
43
|
end
|
44
44
|
|
45
45
|
# Initializes the GUI layout and functions.
|
@@ -127,15 +127,18 @@ module Nametrainer
|
|
127
127
|
# Opens a file dialog and tries to load a collection.
|
128
128
|
def load_collection
|
129
129
|
collection_dir = Qt::FileDialog.get_existing_directory self, 'Load Collection'
|
130
|
-
|
131
|
-
init_collection File.expand_path(collection_dir)
|
130
|
+
init_collection File.expand_path(collection_dir) if collection_dir
|
132
131
|
end
|
133
132
|
|
134
133
|
# Tries to load a collection (does not change anything if load fails).
|
135
134
|
#
|
136
135
|
# +collection_dir+ - path to collection
|
137
136
|
def init_collection(collection_dir)
|
138
|
-
|
137
|
+
args = {
|
138
|
+
:directory => collection_dir,
|
139
|
+
:extensions => Nametrainer::FILE_EXTENSIONS
|
140
|
+
}
|
141
|
+
collection = Nametrainer::CollectionLoader.load(args)
|
139
142
|
if collection.nil? or collection.empty?
|
140
143
|
Qt::MessageBox.warning self, 'Error', Nametrainer.collection_empty_message
|
141
144
|
return
|
@@ -236,7 +239,7 @@ module Nametrainer
|
|
236
239
|
def choose_person
|
237
240
|
@name_label.set_text ''
|
238
241
|
if @ordered_checkbox.is_checked
|
239
|
-
@person = @
|
242
|
+
@person = @collection.successor(@person)
|
240
243
|
else
|
241
244
|
@person = @collection.sample
|
242
245
|
end
|
@@ -248,7 +251,7 @@ module Nametrainer
|
|
248
251
|
|
249
252
|
# Repaints the image when the application window is resized.
|
250
253
|
def resizeEvent(event)
|
251
|
-
return if @image.
|
254
|
+
return if (@image.nil? || @image.null?)
|
252
255
|
scaledSize = @image.size.scale(@image_label.size, Qt::KeepAspectRatio)
|
253
256
|
show_image if scaledSize != @image_label.pixmap.size
|
254
257
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Nametrainer
|
2
|
+
|
3
|
+
# A random number generator.
|
4
|
+
#
|
5
|
+
# Get a random index from 0 to (size - 1), where smaller indices
|
6
|
+
# are preferred, with
|
7
|
+
#
|
8
|
+
# rng = RNG.new(:size => size, :weighting_factor => 6)
|
9
|
+
# rng.rand
|
10
|
+
#
|
11
|
+
# Index 0 will be :weighting_factor times more probable
|
12
|
+
# than the highest index.
|
13
|
+
class RNG
|
14
|
+
|
15
|
+
def initialize(args)
|
16
|
+
@size = args[:size]
|
17
|
+
@weighting_factor = args[:weighting_factor]
|
18
|
+
|
19
|
+
@limits = normalized_limits
|
20
|
+
end
|
21
|
+
|
22
|
+
#--
|
23
|
+
# Weighted random numbers in the range from 0 to (size - 1).
|
24
|
+
#
|
25
|
+
# Divide the range [0;1) into intervals with size depending
|
26
|
+
# on the desired weight. Choose a (evenly distributed)
|
27
|
+
# random number and return the number that corresponds to
|
28
|
+
# the interval within it lies.
|
29
|
+
#++
|
30
|
+
def rand
|
31
|
+
x = Kernel.rand
|
32
|
+
interval = @limits.find_index {|limit| x < limit }
|
33
|
+
|
34
|
+
interval || (@size - 1)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# Returns a list of weighting factors from @weighting_factor to 1.
|
40
|
+
def weighting_factors
|
41
|
+
delta = (@weighting_factor - 1).to_f / (@size - 1)
|
42
|
+
|
43
|
+
Array.new(@size) {|index| @weighting_factor - index * delta}
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns a list of upper limits for all intervals.
|
47
|
+
def raw_limits
|
48
|
+
limits = weighting_factors.dup
|
49
|
+
1.upto(@size - 1) do |index|
|
50
|
+
limits[index] = limits[index-1] + limits[index]
|
51
|
+
end
|
52
|
+
|
53
|
+
limits
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns a list of upper limits normalized to the range [0;1).
|
57
|
+
def normalized_limits
|
58
|
+
max = raw_limits.last
|
59
|
+
|
60
|
+
raw_limits.map {|limit| limit.to_f / max }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/nametrainer/version.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
module Nametrainer
|
2
2
|
|
3
3
|
PROGNAME = 'nametrainer'
|
4
|
-
VERSION = '0.2.
|
5
|
-
DATE = '
|
4
|
+
VERSION = '0.2.2'
|
5
|
+
DATE = '2013-01-04'
|
6
6
|
HOMEPAGE = 'https://github.com/stomar/nametrainer/'
|
7
7
|
|
8
|
-
COPYRIGHT = "Copyright (C) 2012 Marcus Stollsteimer.\n" +
|
8
|
+
COPYRIGHT = "Copyright (C) 2012-2013 Marcus Stollsteimer.\n" +
|
9
9
|
"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n" +
|
10
10
|
"This is free software: you are free to change and redistribute it.\n" +
|
11
11
|
"There is NO WARRANTY, to the extent permitted by law."
|
data/test/test_collection.rb
CHANGED
@@ -2,14 +2,18 @@ require 'minitest/spec'
|
|
2
2
|
require 'minitest/autorun'
|
3
3
|
|
4
4
|
require 'nametrainer/collection'
|
5
|
+
require 'nametrainer/collectionloader'
|
5
6
|
|
6
|
-
SRCPATH = File.dirname(__FILE__)
|
7
|
+
SRCPATH = File.dirname(__FILE__) unless defined?(SRCPATH)
|
7
8
|
|
8
9
|
describe Nametrainer::Collection do
|
9
10
|
|
10
11
|
before do
|
11
|
-
|
12
|
-
|
12
|
+
args = {
|
13
|
+
:directory => "#{SRCPATH}/collection",
|
14
|
+
:extensions => %w{png jpg}
|
15
|
+
}
|
16
|
+
@collection = Nametrainer::CollectionLoader.load(args)
|
13
17
|
@sample_scores = {
|
14
18
|
'Albert Einstein' => 3,
|
15
19
|
'Paul Dirac' => 1,
|
@@ -41,12 +45,13 @@ describe Nametrainer::Collection do
|
|
41
45
|
@collection.scores.must_equal @sample_scores
|
42
46
|
end
|
43
47
|
|
44
|
-
it 'can save the scores to a file and load them again' do
|
48
|
+
it 'can save the scores to a file and load them again and delete them' do
|
45
49
|
@collection.set_scores(@sample_scores)
|
46
50
|
@collection.export_scores
|
47
51
|
@collection.first.score = nil
|
48
52
|
@collection.import_scores
|
49
53
|
@collection.scores.must_equal @sample_scores
|
54
|
+
@collection.delete_scores
|
50
55
|
end
|
51
56
|
|
52
57
|
it 'can get sorted by the scores' do
|
@@ -66,30 +71,6 @@ describe Nametrainer::Collection do
|
|
66
71
|
@collection.successor(p1).name.must_equal p2.name
|
67
72
|
@collection.successor(p2).name.must_equal p3.name
|
68
73
|
@collection.successor(p3).name.must_equal p1.name
|
69
|
-
|
70
|
-
|
71
|
-
it 'can increase the score for a specified person' do
|
72
|
-
person = @collection.last
|
73
|
-
person.score.must_equal 0
|
74
|
-
person.increase_score
|
75
|
-
@collection.last.score.must_equal 1
|
76
|
-
end
|
77
|
-
|
78
|
-
# private methods
|
79
|
-
it 'can return a weighted index list' do
|
80
|
-
expected = [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
|
81
|
-
3, 3, 3, 4, 4, 4, 5, 5, 5,
|
82
|
-
6, 6, 7, 7, 8, 8,
|
83
|
-
9, 10, 11]
|
84
|
-
indices = @collection.send(:indices_urn, :size => 12, :weighting_factor => 4)
|
85
|
-
indices.sort.must_equal expected
|
86
|
-
end
|
87
|
-
|
88
|
-
it 'returns random indices in the correct range' do
|
89
|
-
samples = 100
|
90
|
-
indices = Array.new(samples) { @collection.send(:weighted_random_index) }
|
91
|
-
indices.size.must_equal samples
|
92
|
-
indices.sort.first.must_be :>=, 0
|
93
|
-
indices.sort.last.must_be :<, @collection.size
|
74
|
+
@collection.successor(nil).name.must_equal p1.name
|
94
75
|
end
|
95
76
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'minitest/spec'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
|
4
|
+
require 'nametrainer/collectionloader'
|
5
|
+
|
6
|
+
SRCPATH = File.dirname(__FILE__) unless defined?(SRCPATH)
|
7
|
+
|
8
|
+
describe Nametrainer::CollectionLoader do
|
9
|
+
|
10
|
+
before do
|
11
|
+
args = {
|
12
|
+
:directory => "#{SRCPATH}/collection",
|
13
|
+
:extensions => %w{png JPG}
|
14
|
+
}
|
15
|
+
@collection = Nametrainer::CollectionLoader.load(args)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'returns a Collection instance' do
|
19
|
+
@collection.class.must_equal Nametrainer::Collection
|
20
|
+
@collection.first.class.must_equal Nametrainer::Person
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'returns a collection with the correct size' do
|
24
|
+
@collection.size.must_equal 3
|
25
|
+
end
|
26
|
+
end
|
data/test/test_rng.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'minitest/spec'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
|
4
|
+
require 'nametrainer/rng'
|
5
|
+
|
6
|
+
describe Nametrainer::RNG do
|
7
|
+
|
8
|
+
before do
|
9
|
+
@size = 7
|
10
|
+
@rng = Nametrainer::RNG.new(:size => @size, :weighting_factor => 4)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'returns random indices in the correct range' do
|
14
|
+
samples = 100
|
15
|
+
indices = Array.new(samples) { @rng.rand }
|
16
|
+
indices.size.must_equal samples
|
17
|
+
indices.sort.first.must_be :>=, 0
|
18
|
+
indices.sort.last.must_be :<, @size
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'returns random indices with the expected frequency' do
|
22
|
+
samples = 100000
|
23
|
+
indices = Array.new(samples) { @rng.rand }
|
24
|
+
|
25
|
+
frequency = Hash.new {|h, k| h[k] = 0 }
|
26
|
+
indices.each do |index|
|
27
|
+
frequency[index] += 1
|
28
|
+
end
|
29
|
+
relative_frequency = frequency.sort.map {|ind, freq| freq.to_f/samples }
|
30
|
+
|
31
|
+
expected = [4.0/17.5, 3.5/17.5, 3.0/17.5, 2.5/17.5,
|
32
|
+
2.0/17.5, 1.5/17.5, 1.0/17.5]
|
33
|
+
relative_frequency.each_with_index do |freq, index|
|
34
|
+
freq.must_be_within_epsilon expected[index], 0.05
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nametrainer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-01-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: minitest
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ! '>='
|
@@ -32,7 +37,12 @@ dependencies:
|
|
32
37
|
version: '0'
|
33
38
|
type: :development
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
36
46
|
description: nametrainer is a name learning trainer using the Qt GUI toolkit. It will
|
37
47
|
assist you in learning people's names from a collection of images.
|
38
48
|
email: sto.mar@web.de
|
@@ -55,12 +65,16 @@ files:
|
|
55
65
|
- demo_collection/Werner_Heisenberg.png
|
56
66
|
- demo_collection/Albert_Einstein.png
|
57
67
|
- lib/nametrainer/optionparser.rb
|
68
|
+
- lib/nametrainer/collectionloader.rb
|
58
69
|
- lib/nametrainer/gui.rb
|
59
70
|
- lib/nametrainer/version.rb
|
71
|
+
- lib/nametrainer/rng.rb
|
60
72
|
- lib/nametrainer/person.rb
|
61
73
|
- lib/nametrainer/collection.rb
|
62
74
|
- lib/nametrainer/statistics.rb
|
63
75
|
- lib/nametrainer.rb
|
76
|
+
- test/test_rng.rb
|
77
|
+
- test/test_collectionloader.rb
|
64
78
|
- test/test_person.rb
|
65
79
|
- test/test_statistics.rb
|
66
80
|
- test/test_collection.rb
|
@@ -70,7 +84,6 @@ files:
|
|
70
84
|
- test/collection/001.txt
|
71
85
|
- test/collection/Max_Born.png
|
72
86
|
- test/collection/002.JPG
|
73
|
-
- test/collection/nametrainer.dat
|
74
87
|
homepage: https://github.com/stomar/nametrainer/
|
75
88
|
licenses:
|
76
89
|
- GPL-3
|
@@ -94,11 +107,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
107
|
requirements:
|
95
108
|
- the Qt toolkit and Qt bindings for Ruby
|
96
109
|
rubyforge_project: nametrainer
|
97
|
-
rubygems_version: 1.8.
|
110
|
+
rubygems_version: 1.8.24
|
98
111
|
signing_key:
|
99
112
|
specification_version: 3
|
100
113
|
summary: nametrainer is a name learning trainer using the Qt GUI toolkit.
|
101
114
|
test_files:
|
115
|
+
- test/test_rng.rb
|
116
|
+
- test/test_collectionloader.rb
|
102
117
|
- test/test_person.rb
|
103
118
|
- test/test_statistics.rb
|
104
119
|
- test/test_collection.rb
|