deacon 0.0.3

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/lib/deacon.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'deacon/version'
2
+ require 'deacon/generator'
3
+ require 'deacon/mac_generator'
4
+ require 'deacon/random_generator'
5
+
6
+ module Deacon
7
+ end
@@ -0,0 +1,37 @@
1
+ module Deacon
2
+ class Generator
3
+ DEFAULT_DATA_DIR = File.expand_path('../../../data', __FILE__)
4
+ GIVEN_MALE_NAMES_FILE = 'gmnames.txt'
5
+ GIVEN_FEMALE_NAMES_FILE = 'gfnames.txt'
6
+ SURNAMES_FILE = 'srnames.txt'
7
+ RECORD_LENGTH_GIVEN = 6
8
+ RECORD_LENGTH_SURNAME = 9
9
+
10
+ def initialize(data_dir = DEFAULT_DATA_DIR)
11
+ @data_dir = data_dir
12
+ end
13
+
14
+ private
15
+
16
+ def data_file(filename)
17
+ File.join(@data_dir, filename)
18
+ end
19
+
20
+ def find_name(index, filename, length)
21
+ File.open(filename, 'r') do |f|
22
+ f.seek(index * length)
23
+ f.readline.chomp.strip
24
+ end
25
+ rescue Exception => e
26
+ raise "Error when seeking to #{index} in #{filename}: #{e}"
27
+ end
28
+
29
+ def mac_to_bytes(mac)
30
+ mac.split(/[:-]/).collect{|x| x.to_i(16)}
31
+ end
32
+
33
+ def mac_to_shorts(mac)
34
+ mac_to_bytes(mac).each_slice(2).collect { |a, b| (a << 8) + b }
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,17 @@
1
+ module Deacon
2
+ class MacGenerator < Generator
3
+ def generate(mac, female = false)
4
+ return [] if mac.nil? || mac.empty?
5
+ # B0 B1 B2 B3 B4 B5
6
+ # SHRT0 SHRT1 SHRT2
7
+ bytes = mac_to_bytes(mac)
8
+ shorts = mac_to_shorts(mac)
9
+ filename = (shorts[0] & 0x1) == 0 ? GIVEN_MALE_NAMES_FILE : GIVEN_FEMALE_NAMES_FILE
10
+ firstname1 = find_name(bytes[2], data_file(filename), RECORD_LENGTH_GIVEN)
11
+ firstname2 = find_name(bytes[3], data_file(filename), RECORD_LENGTH_GIVEN)
12
+ surname1 = find_name(shorts[0], data_file(SURNAMES_FILE), RECORD_LENGTH_SURNAME)
13
+ surname2 = find_name(shorts[2], data_file(SURNAMES_FILE), RECORD_LENGTH_SURNAME)
14
+ [firstname2, firstname1, surname1, surname2]
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,42 @@
1
+ module Deacon
2
+ class RandomGenerator < Generator
3
+ MASK = 0x1ffffff
4
+
5
+ # Fibonacci linear feedback shift register with x^25 + x^24 + x^23 + x^22 + 1 poly
6
+ def next_lfsr25(seed)
7
+ i = 1
8
+ i = (seed + 1) & MASK
9
+ raise ArgumentError, "Seed #{seed} out of bounds" if seed && i == 0
10
+ i = (seed + 1) & MASK while i == 0
11
+ i = (i >> 1) | ((i[0]^i[1]^i[2]^i[3]) << 0x18)
12
+ i = (i >> 1) | ((i[0]^i[1]^i[2]^i[3]) << 0x18) while i > MASK
13
+ i - 1
14
+ end
15
+
16
+ def generate(seed = Time.now.utc.to_i)
17
+ return [] if seed.nil? || seed == 0
18
+ index = seed
19
+ loop do
20
+ index = next_lfsr25(index)
21
+ break if unique?(seed, index)
22
+ end
23
+ given_file = (index & 0x1000000) == 0 ? GIVEN_MALE_NAMES_FILE : GIVEN_FEMALE_NAMES_FILE
24
+ givenname_ix = (index & 0xff0000) >> 16
25
+ surnname_ix = index & 0xffff
26
+ firstname = find_name(givenname_ix, data_file(given_file), RECORD_LENGTH_GIVEN)
27
+ surname = find_name(surnname_ix, data_file(SURNAMES_FILE), RECORD_LENGTH_SURNAME)
28
+ [index, firstname, surname]
29
+ end
30
+
31
+ def self.random_initial_seed
32
+ rand(MASK - 2) + 1
33
+ end
34
+
35
+ private
36
+
37
+ # is first (or gender) and last name different from the old value?
38
+ def unique?(ix1, ix2)
39
+ (((ix1 & 0xff0000) != (ix2 & 0xff0000)) || ((ix1 & 0x1000000) != (ix2 & 0x1000000))) && ((ix1 & 0xffff) != (ix2 & 0xffff))
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,3 @@
1
+ module Deacon
2
+ VERSION = '0.0.3'
3
+ end
@@ -0,0 +1,2 @@
1
+ require 'minitest/autorun'
2
+ require "deacon"
@@ -0,0 +1,33 @@
1
+ require "test_helper"
2
+
3
+ class MacGeneratorTest < MiniTest::Unit::TestCase
4
+ def setup
5
+ dir = File.expand_path('../../../data', __FILE__)
6
+ @generator = Deacon::MacGenerator.new(dir)
7
+ end
8
+
9
+ def test_generates_empty_array_for_nil
10
+ assert_equal [], @generator.generate(nil)
11
+ end
12
+
13
+ def test_generates_empty_array_for_empty_string
14
+ assert_equal [], @generator.generate("")
15
+ end
16
+
17
+ def test_generates_a_male_name
18
+ assert_equal ["DEREK", "LEVI", "PRATICO", "CEDILLO"], @generator.generate("00:00:ca:fe:01:01")
19
+ end
20
+
21
+ def test_generates_a_female_name
22
+ assert_equal ["KATHY", "ALTA", "ROMEO", "CEDILLO"], @generator.generate("00:01:ca:fe:01:01")
23
+ end
24
+
25
+ def test_generates_same_middle_names_for_single_oid
26
+ name1 = @generator.generate("24:a4:3c:ec:76:06")
27
+ name2 = @generator.generate("24:a4:3c:e3:d3:92")
28
+ #puts name1.inspect
29
+ #puts name2.inspect
30
+ assert_equal name1[1], name2[1]
31
+ assert_equal name1[2], name2[2]
32
+ end
33
+ end
@@ -0,0 +1,121 @@
1
+ require "test_helper"
2
+
3
+ class RandomGeneratorTest < MiniTest::Unit::TestCase
4
+ # number of test iterations for some tests
5
+ TEST_ITERATIONS=10_000
6
+
7
+ def setup
8
+ @dir = File.expand_path('../../../data', __FILE__)
9
+ @generator = Deacon::RandomGenerator.new(@dir)
10
+ @random = rand((2 ** 25) - (TEST_ITERATIONS+3))
11
+ end
12
+
13
+ def test_generates_empty_array_for_nil
14
+ assert_equal [], @generator.generate(nil)
15
+ end
16
+
17
+ def test_generates_a_deterministic_sequence
18
+ expected = [
19
+ [0x1000000, 'VELMA', 'PRATICO'],
20
+ [0x17fffff, 'ANGIE', 'WARMBROD'],
21
+ [0x0017fff, 'GRANT', 'GOODGINE'],
22
+ [0x000bfff, 'ALTON', 'SIEBER'],
23
+ [0x100000b, 'VELMA', 'VANBEEK'],
24
+ [0x0800005, 'DON', 'OTERO'],
25
+ [0x0400002, 'SAM', 'HULAN'],
26
+ [0x0200000, 'AARON', 'PRATICO'],
27
+ [0x10fffff, 'SALLY', 'WARMBROD'],
28
+ [0x0087fff, 'TED', 'GOODGINE'],
29
+ [0x0043fff, 'CORY', 'THURSBY'],
30
+ [0x0021fff, 'GARRY', 'HANDREN'],
31
+ [0x0010fff, 'GRANT', 'STELMAN'],
32
+ [0x00087ff, 'ALTON', 'KAWSKI'],
33
+ [0x1000043, 'VELMA', 'MIRRA'],
34
+ [0x1800021, 'SUSAN', 'DARBY'],
35
+ [0x1c00010, 'MARIA', 'BOURDON'],
36
+ [0x1e00007, 'ANNIE', 'SCHMANDT'],
37
+ [0x1f00003, 'MAYRA', 'BURNAUGH'],
38
+ [0x1f80001, 'FAYE', 'ROMEO'],
39
+ [0x1fc0000, 'OPAL', 'PRATICO'],
40
+ [0x1fdffff, 'CLARA', 'WARMBROD'],
41
+ [0x07f7fff, 'DUANE', 'GOODGINE'],
42
+ [0x03fbfff, 'ERVIN', 'SIEBER'],
43
+ [0x01fdfff, 'ELDON', 'LEYUA'],
44
+ [0x00fefff, 'GARY', 'TRASHER'],
45
+ [0x007f7ff, 'BRET', 'ONDRICK'],
46
+ [0x003fbff, 'LANCE', 'BISTER'],
47
+ [0x001fdff, 'GRANT', 'KUZIEL'],
48
+ [0x000feff, 'ALTON', 'THOMPON'],
49
+ [0x10003fb, 'VELMA', 'SWILLE'],
50
+ [0x08001fd, 'DON', 'DEBRECHT'],
51
+ [0x14000fe, 'TARA', 'VAUGHNS'],
52
+ [0x0a0007e, 'HEATH', 'RUSHTON'],
53
+ [0x050003e, 'BRUCE', 'PHEONIX'],
54
+ [0x028001e, 'KURT', 'VEAZEY'],
55
+ [0x014000e, 'DEWEY', 'MOHSENI'],
56
+ [0x00a0006, 'ELI', 'DEWEY'],
57
+ [0x1050002, 'ROSA', 'HULAN'],
58
+ [0x0828000, 'JESUS', 'PONTON'],
59
+ [0x1413fff, 'MABEL', 'THURSBY'],
60
+ [0x0a09fff, 'HEATH', 'ZEHNDER'],
61
+ [0x0504fff, 'BRUCE', 'BARTOLO'],
62
+ [0x02827ff, 'KURT', 'MCCLEE'],
63
+ [0x01413ff, 'DEWEY', 'PASKELL'],
64
+ [0x00a09ff, 'ELI', 'HASFJORD'],
65
+ [0x00504ff, 'KARL', 'ANIBAL'],
66
+ [0x002827f, 'GARRY', 'SHALA'],
67
+ [0x001413f, 'GRANT', 'HIEBER'],
68
+ [0x000a09f, 'ALTON', 'PERIGO'],
69
+ ]
70
+ seq = []
71
+ ix = 1
72
+ (1..50).each do |_|
73
+ ix, first, last = tuple = @generator.generate(ix)
74
+ #puts "[0x#{sprintf("%07x", ix)}, '#{first}', '#{last}'],"
75
+ seq << tuple
76
+ end
77
+ assert_equal expected, seq
78
+ end
79
+
80
+ def test_generates_a_name_for_the_initial_round
81
+ assert_equal [16777216, "VELMA", "PRATICO"], @generator.generate(1)
82
+ end
83
+
84
+ def test_generates_a_name_for_the_last_round
85
+ assert_equal [32766, "ALTON", "MCCORY"], @generator.generate((2**25) - 2)
86
+ end
87
+
88
+ def test_generates_a_name_for_each_value_in_a_sequence
89
+ @random.step(@random + TEST_ITERATIONS, 1) do |i|
90
+ @generator.generate(i)
91
+ end
92
+ end
93
+
94
+ def test_generates_non_repeating_index_sequence
95
+ test_hash = {}
96
+ ix = 1
97
+ @random.step(@random + TEST_ITERATIONS, 1) do |i|
98
+ ix = @generator.next_lfsr25(ix)
99
+ if test_hash.include? ix
100
+ fail("Found duplicated index #{ix} (iteration #{i})")
101
+ else
102
+ test_hash[ix] = 1
103
+ end
104
+ end
105
+ end
106
+
107
+ def test_never_generates_two_same_names
108
+ test_hash = {}
109
+ ix = old_ix = 1
110
+ @random.step(@random + TEST_ITERATIONS, 1) do |i|
111
+ ix, first, last = @generator.generate(ix)
112
+ hash = first + last
113
+ if test_hash.include? hash
114
+ fail("Found duplicated name #{fn} #{sn} index #{old_ix}->#{ix} (iteration #{i})")
115
+ else
116
+ test_hash[hash] = 1
117
+ end
118
+ old_ix = ix
119
+ end
120
+ end
121
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: deacon
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Lukas Zapletal
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-03-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rdoc
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
+ description: Provides human readable name using continious LFSR
28
+ email:
29
+ - lukas-x@zapletalovi.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/deacon/random_generator.rb
35
+ - lib/deacon/mac_generator.rb
36
+ - lib/deacon/generator.rb
37
+ - lib/deacon/version.rb
38
+ - lib/deacon.rb
39
+ - data/gmnames.txt
40
+ - data/srnames.txt
41
+ - data/gfnames.txt
42
+ - LICENSE
43
+ - Rakefile
44
+ - README.md
45
+ - test/unit/mac_generator_test.rb
46
+ - test/unit/random_generator_test.rb
47
+ - test/test_helper.rb
48
+ homepage: https://github.com/lzap/deacon
49
+ licenses:
50
+ - GPLv3
51
+ - Public domain
52
+ metadata: {}
53
+ post_install_message:
54
+ rdoc_options: []
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubyforge_project:
69
+ rubygems_version: 2.0.14
70
+ signing_key:
71
+ specification_version: 4
72
+ summary: Human readable random name generator
73
+ test_files:
74
+ - test/unit/mac_generator_test.rb
75
+ - test/unit/random_generator_test.rb
76
+ - test/test_helper.rb
77
+ has_rdoc: