lfsr 1.0.0

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.
@@ -0,0 +1,21 @@
1
+ require 'benchmark'
2
+ require 'lfsr/pure'
3
+ require 'lfsr/fast'
4
+
5
+ TIMES = 500_000
6
+
7
+ puts "Bits\t| Pure\t| C ext\t| rand\t| Vs P.\t| Vs rand"
8
+
9
+ 2.upto(32) do |bits|
10
+ max = (1 << (bits - 1))+1
11
+
12
+ l = LFSR::Fast.const_get("Size#{bits}").new(max)
13
+ ext = Benchmark.realtime do TIMES.times {l.next_i} end
14
+
15
+ l = LFSR::Pure.const_get("Size#{bits}").new(max)
16
+ pure = Benchmark.realtime do TIMES.times {l.next_i} end
17
+
18
+ randt = Benchmark.realtime do TIMES.times {rand(max)} end
19
+
20
+ puts sprintf(" #{bits}\t| %.2fs\t| %.2fs\t| %.2fs\t| %.1fX\t| %.1fX", pure, ext, randt, pure/ext, randt/ext)
21
+ end
@@ -0,0 +1,55 @@
1
+ require 'taps'
2
+
3
+ code = %$
4
+ #include <stdio.h>
5
+ #include "ruby.h"
6
+
7
+ #define bit(n) ((reg & (1 << (n))) >> (n))
8
+ #define RUBY_EXPORT __attribute__ ((visibility ("default")))
9
+
10
+ $
11
+ puts code.strip
12
+ puts
13
+
14
+ 2.upto(MAXBITS) do |size|
15
+ taps = ([size] + TAPS[size])
16
+ bits_select = taps.uniq.map {|bit| "bit(#{size - bit})" }.join('^')
17
+
18
+ code = %$
19
+ VALUE rb_lfsr_next_i_#{size}(VALUE self) {
20
+ unsigned long long reg = NUM2ULL(ROBJECT(self)->as.ary[0]), max = NUM2ULL(ROBJECT(self)->as.ary[1]);
21
+
22
+ label_shift#{size}:
23
+ reg = (reg >> 1) | ((#{bits_select}) << 0x#{ sprintf "%x", size - 1 });
24
+ if (reg > max) goto label_shift#{size};
25
+
26
+ ROBJECT(self)->as.ary[0] = ULL2NUM(reg);
27
+
28
+ return ULL2NUM(reg - 1);
29
+ }
30
+ $
31
+ puts code.strip
32
+ puts
33
+ end
34
+
35
+ code = %$
36
+ void RUBY_EXPORT Init_lfsr() {
37
+ VALUE rb_mLFSR = rb_const_get(rb_cObject, rb_intern("LFSR"));
38
+ VALUE rb_cLFSR_Base = rb_const_get(rb_mLFSR, rb_intern("Base"));
39
+ VALUE rb_mLFSR_Fast = rb_define_module_under(rb_mLFSR, "Fast");
40
+
41
+ VALUE rb_cLFSR_SubClass;
42
+ $
43
+ puts code
44
+
45
+ 2.upto(MAXBITS) do |size|
46
+ code = %$
47
+ rb_cLFSR_SubClass = rb_define_class_under(rb_mLFSR_Fast, "Size#{size}", rb_cLFSR_Base);
48
+ rb_define_method(rb_cLFSR_SubClass, "next_i", rb_lfsr_next_i_#{size}, 0);
49
+ rb_define_const(rb_cLFSR_SubClass, "MASK", ULL2NUM(0x#{sprintf("%x", (2**size)-1)}LLU));
50
+ $
51
+ puts code
52
+ end
53
+
54
+
55
+ puts "}"
@@ -0,0 +1,72 @@
1
+ require 'taps'
2
+
3
+ if ARGV.last == "base"
4
+ code = %Q$
5
+ module LFSR
6
+ VERSION = "1.0.0"
7
+
8
+ class Base
9
+ attr_reader :reg
10
+ attr_accessor :max
11
+ attr_reader :seed
12
+
13
+ def initialize(max = nil, seed = nil)
14
+ # The C code depends on this being the first ivar to work properly.
15
+ @reg = 1
16
+
17
+ # This should be the second variable.
18
+ @max = (max || self.class.const_get(:MASK) - 1) + 1
19
+
20
+ reset seed
21
+ end
22
+
23
+ def reset(new_seed)
24
+ @reg = @seed = (new_seed || Time.now.utc.to_i) & self.class.const_get(:MASK)
25
+ raise ArgumentError, "The seed \#{new_seed} is unacceptable for this register size" if new_seed && @reg == 0
26
+ @reg = @seed = (new_seed || Time.now.utc.to_i) & self.class.const_get(:MASK) while @reg == 0
27
+ end
28
+
29
+ alias peek reg
30
+ end
31
+
32
+ # Get a LFSR with the appropriate number of bits to reach the maximum needed.
33
+ def self.gen(max, namespace = LFSR)
34
+ bits = Math.log2(max + 2).ceil
35
+ namespace.const_get("Size\#{bits < 3 ? 3 : bits}").new(max)
36
+ end
37
+
38
+ def self.default_to(namespace)
39
+ 2.upto(61) { |n| remove_const("Size\#{n}")} if defined? LFSR::Size2
40
+ 2.upto(61) { |n| const_set("Size\#{n}", namespace.const_get("Size\#{n}")) }
41
+ end
42
+ end
43
+ $
44
+ puts code.strip
45
+
46
+ else
47
+ puts "require 'lfsr'"
48
+ puts ""
49
+ puts "module LFSR::Pure"
50
+ puts "end"
51
+ puts
52
+
53
+ code = 2.upto(MAXBITS).map do |size|
54
+ bits_select = ([size] + TAPS[size]).uniq.map {|bit| "@reg[#{size - bit}]" }.join('^')
55
+
56
+ shift = %Q{@reg = (@reg >> 1) | ((#{bits_select}) << 0x#{ sprintf "%x", size - 1 })}
57
+ code = %Q$
58
+ class LFSR::Pure::Size#{size} < LFSR::Base
59
+ MASK = 0x#{ sprintf("%x", (2**size) - 1) }
60
+
61
+ def next_i
62
+ #{shift}
63
+ #{shift} while @reg > @max
64
+ @reg - 1
65
+ end
66
+ end
67
+ $
68
+ end
69
+ puts code.join("\n").strip
70
+ puts
71
+ puts "LFSR.default_to(LFSR::Pure)"
72
+ end
@@ -0,0 +1,69 @@
1
+ MAXBITS = 61 # (1<<62) in ruby 1.9.3 becomes a Bignum, we want to deal only with Fixnums
2
+
3
+ # http://en.wikipedia.org/wiki/LFSR
4
+ TAPS = []
5
+
6
+ TAPS[0] = []
7
+ TAPS[1] = []
8
+
9
+ TAPS << [ 2, 1]
10
+ TAPS << [ 3, 2]
11
+ TAPS << [ 4, 3]
12
+ TAPS << [ 4, 3, 2]
13
+ TAPS << [ 5, 3, 2]
14
+ TAPS << [ 6, 5, 4]
15
+ TAPS << [ 6, 5, 4]
16
+ TAPS << [ 8, 6, 5]
17
+ TAPS << [ 9, 7, 6]
18
+ TAPS << [10, 9, 7]
19
+ TAPS << [11, 8, 6]
20
+ TAPS << [12, 10, 9]
21
+ TAPS << [13, 11, 9]
22
+ TAPS << [14, 13, 11]
23
+ TAPS << [14, 13, 11]
24
+ TAPS << [16, 15, 14]
25
+ TAPS << [17, 16, 13]
26
+ TAPS << [18, 17, 14]
27
+ TAPS << [19, 16, 14]
28
+ TAPS << [20, 19, 16]
29
+ TAPS << [19, 18, 17]
30
+ TAPS << [22, 20, 18]
31
+ TAPS << [23, 21, 20]
32
+ TAPS << [24, 23, 22]
33
+ TAPS << [25, 24, 20]
34
+ TAPS << [26, 25, 22]
35
+ TAPS << [27, 24, 22]
36
+ TAPS << [28, 27, 25]
37
+ TAPS << [29, 26, 24]
38
+ TAPS << [30, 29, 28]
39
+ TAPS << [30, 26, 25]
40
+ TAPS << [32, 29, 27]
41
+ TAPS << [31, 30, 26]
42
+ TAPS << [34, 28, 27]
43
+ TAPS << [35, 29, 28]
44
+ TAPS << [36, 33, 31]
45
+ TAPS << [37, 33, 32]
46
+ TAPS << [38, 35, 32]
47
+ TAPS << [37, 36, 35]
48
+ TAPS << [40, 39, 38]
49
+ TAPS << [40, 37, 35]
50
+ TAPS << [42, 38, 37]
51
+ TAPS << [42, 39, 38]
52
+ TAPS << [44, 42, 41]
53
+ TAPS << [40, 39, 38]
54
+ TAPS << [46, 43, 42]
55
+ TAPS << [44, 41, 39]
56
+ TAPS << [45, 44, 43]
57
+ TAPS << [48, 47, 46]
58
+ TAPS << [50, 48, 45]
59
+ TAPS << [51, 49, 46]
60
+ TAPS << [52, 51, 47]
61
+ TAPS << [51, 48, 46]
62
+ TAPS << [54, 53, 49]
63
+ TAPS << [54, 52, 49]
64
+ TAPS << [55, 54, 52]
65
+ TAPS << [57, 53, 52]
66
+ TAPS << [57, 55, 52]
67
+ TAPS << [58, 56, 55]
68
+ TAPS << [60, 59, 56]
69
+ TAPS << [59, 57, 56]
@@ -0,0 +1,44 @@
1
+ require 'test/unit'
2
+
3
+ require 'lfsr/pure'
4
+ require 'lfsr/fast'
5
+ require 'set'
6
+
7
+ class LFSRTests < Test::Unit::TestCase
8
+ MAX_SEQUENCE_LENGTH = 2**16 # Keep tests at a runnable size... even if we don't test ALL the numbers generated for evey bit size.
9
+
10
+ def test_gen
11
+ assert_equal LFSR.gen(2).class, LFSR::Fast::Size3
12
+ assert_equal LFSR.gen(3).class, LFSR::Fast::Size3
13
+ assert_equal LFSR.gen(4).class, LFSR::Fast::Size3
14
+ assert_equal LFSR.gen(5).class, LFSR::Fast::Size3
15
+ assert_equal LFSR.gen(6).class, LFSR::Fast::Size3
16
+ assert_equal LFSR.gen(7).class, LFSR::Fast::Size4
17
+ assert_equal LFSR.gen(8).class, LFSR::Fast::Size4
18
+ assert_equal LFSR.gen(9).class, LFSR::Fast::Size4
19
+
20
+ assert_equal LFSR.gen(14).class, LFSR::Fast::Size4
21
+ assert_equal LFSR.gen(15).class, LFSR::Fast::Size5
22
+ assert_equal LFSR.gen(16).class, LFSR::Fast::Size5
23
+ end
24
+
25
+ 2.upto(61) do |bits|
26
+ ["Pure", "Fast"].each do |namespace|
27
+
28
+ define_method("test_#{namespace}_#{bits}_bits") do
29
+ gen = LFSR.const_get(namespace).const_get("Size#{bits}").new
30
+ gen.reset(1)
31
+
32
+ sequence = []
33
+
34
+ (2**bits - 1).times do
35
+ sequence << gen.next_i
36
+ break if sequence.length > MAX_SEQUENCE_LENGTH
37
+ end
38
+
39
+ assert sequence.uniq!.nil?
40
+ end
41
+
42
+ end
43
+ end
44
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lfsr
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Emmanuel Oga
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-05-02 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ! " The generation of random numbers is too important to be left to
15
+ chance.\n A Linear Feedback Shift Register is an automaton that\n can be used
16
+ to generate a long sequence of different pseudo-random numbers.\n"
17
+ email: emmanueloga@gmail.com
18
+ executables: []
19
+ extensions:
20
+ - ext/lfsr/extconf.rb
21
+ extra_rdoc_files:
22
+ - LICENSE
23
+ files:
24
+ - scripts/genc.rb
25
+ - scripts/bench
26
+ - scripts/genruby.rb
27
+ - scripts/taps.rb
28
+ - examples/sample.rb
29
+ - test/lfsr_test.rb
30
+ - LICENSE
31
+ - lfsr.gemspec
32
+ - ext/lfsr/extconf.rb
33
+ - ext/lfsr/lfsr.c
34
+ - ext/lfsr/lfsr.h
35
+ - Rakefile
36
+ - lib/lfsr.rb
37
+ - lib/lfsr/fast.rb
38
+ - lib/lfsr/pure.rb
39
+ - README.markdown
40
+ homepage: http://github.com/EmmanuelOga/lfsr
41
+ licenses: []
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project:
60
+ rubygems_version: 1.8.25
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: Generate sequences of pseudo random numbers
64
+ test_files:
65
+ - test/lfsr_test.rb