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.
- 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
|