lfsr 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +21 -0
- data/README.markdown +96 -0
- data/Rakefile +67 -0
- data/examples/sample.rb +38 -0
- data/ext/lfsr/extconf.rb +6 -0
- data/ext/lfsr/lfsr.c +974 -0
- data/ext/lfsr/lfsr.h +6 -0
- data/lfsr.gemspec +38 -0
- data/lib/lfsr.rb +38 -0
- data/lib/lfsr/fast.rb +4 -0
- data/lib/lfsr/pure.rb +665 -0
- data/scripts/bench +21 -0
- data/scripts/genc.rb +55 -0
- data/scripts/genruby.rb +72 -0
- data/scripts/taps.rb +69 -0
- data/test/lfsr_test.rb +44 -0
- metadata +65 -0
data/scripts/bench
ADDED
@@ -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
|
data/scripts/genc.rb
ADDED
@@ -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 "}"
|
data/scripts/genruby.rb
ADDED
@@ -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
|
data/scripts/taps.rb
ADDED
@@ -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]
|
data/test/lfsr_test.rb
ADDED
@@ -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
|