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
         |