lfsr 1.0.0

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