deacon 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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: