nametrainer 0.2.1 → 0.2.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/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
|