{"id":17942627,"url":"https://github.com/petercamilleri/fibonacci_rng","last_synced_at":"2025-04-03T13:27:41.850Z","repository":{"id":23334349,"uuid":"26694734","full_name":"PeterCamilleri/fibonacci_rng","owner":"PeterCamilleri","description":"An experimental random number generator inspired by the famous Fibonacci number sequence.","archived":false,"fork":false,"pushed_at":"2021-05-19T15:49:47.000Z","size":2779,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-09T04:46:36.086Z","etag":null,"topics":["cyclic-fibonacci-generator","prng","ruby","rubygem"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PeterCamilleri.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-11-15T21:34:13.000Z","updated_at":"2023-03-05T04:20:47.000Z","dependencies_parsed_at":"2022-07-15T23:46:13.246Z","dependency_job_id":null,"html_url":"https://github.com/PeterCamilleri/fibonacci_rng","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PeterCamilleri%2Ffibonacci_rng","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PeterCamilleri%2Ffibonacci_rng/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PeterCamilleri%2Ffibonacci_rng/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PeterCamilleri%2Ffibonacci_rng/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PeterCamilleri","download_url":"https://codeload.github.com/PeterCamilleri/fibonacci_rng/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247008820,"owners_count":20868428,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cyclic-fibonacci-generator","prng","ruby","rubygem"],"created_at":"2024-10-29T03:06:31.925Z","updated_at":"2025-04-03T13:27:41.821Z","avatar_url":"https://github.com/PeterCamilleri.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FibonacciRng\n\nThis gem implements a random number generator inspired by the famous Fibonacci\nnumber sequence. To be specific, it is a normalized, cyclic Fibonacci pseudo\nrandom number generator. So far, this generator has performed quite well when\ncompared to the built-in Ruby random number generator when tested with:\n\n* A chi-squared test\n* An auto-correlation test\n* An X/Y scatter graph test\n\nAt this time I lack the mathematical skill to go beyond these rudimentary\nmeasures but they are enough to qualify this code for \"light\" duties.\n\nProving that this is indeed a good (or poor) RNG is left as an exercise for\nthe reader! Hopefully one with greater knowledge of statistics than I.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n    gem 'fibonacci_rng'\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install fibonacci_rng\n\nThe fibonacci_rng gem itself is found at: ( https://rubygems.org/gems/fibonacci_rng )\n\nA C++ version of this generator also exists and may be found at\n( https://github.com/PeterCamilleri/Fibonacci_CPP )\n\n\n## Usage\n\n```ruby\nrequire 'fibonacci_rng'\n```\n\nThen in an appropriate place in the code:\n\n```ruby\n@my_rng = FibonacciRng.new(seed, depth, init)\n```\n\nWhere:\n* depth is an optional integer value between 2 and 256. Defaults to 8.\n* seed is an optional number or string or other object that has a repeatable\nvalue and responds to the to_s method. Defaults to a value derived from system\nentropy.\n* init is the number of initial training cycles used to seed the generator. The\nallowed range is 1 to 1,000,000. By default the init value is 32*depth+768.\n\nHere is an overview of the available options.\n\n```ruby\n#Method #1\n@my_rng = FibonacciRng.new                            # Random seed, depth = 8\n\n#Method #2\n@my_rng = FibonacciRng.new('seed')                    # Specified seed = 'seed', depth = 8\n\n#Method #3\n@my_rng = FibonacciRng.new('seed', 12)                # Specified seed = 'seed', depth = 12\n\n#Method #4\n@my_rng = FibonacciRng.new(FibonacciRng.new_seed, 12) # Random seed, depth = 12\n\n#Method #5\n@my_rng = FibonacciRng.new(FibonacciRng.new_seed, 12, 100) # Random seed, depth = 12 with 100 passes.\n```\nIn addition, keyword arguments are emulated (as of Version 0.4.0) so these\nadditional options also are available:\n\n* depth: value -- an integer value between 2 and 256. Defaults to 8.\n* seed: value -- a number or string or other object that has a repeatable value and\nresponds to the to_s method. Defaults to a value derived from system entropy.\n* init: value -- the number of initial training cycles used to seed the\ngenerator. The allowed range is 1 to 1,000,000. By default the init value is\n32*depth+768.\n\nThe following are examples of constructors with keyword arguments.\n\n```ruby\n#Method #6\n@my_rng = FibonacciRng.new(seed: 'seed')              # Specified seed = 'seed', depth = 8\n\n#Method #7\n@my_rng = FibonacciRng.new(seed: 'seed', depth: 12)   # Specified seed = 'seed', depth = 12\n\n#Method #8\n@my_rng = FibonacciRng.new(depth: 12)                 # Random seed, depth = 12\n\n#Method #9\n@my_rng = FibonacciRng.new(seed: 'seed', init: 2048)  # Specified seed = 'seed' with 2048 passes.\n\n```\nNote: Mixing positional and keyword arguments will not, in general, work. So\ndon't do it!\n\n#### Generating Pseudo Random Data\n\nThe Fibonacci generator class supports the classical rand method that emulates\nthe behavior of the standard generator. It works like this:\n\n```ruby\n@my_rng.rand          # A \"random\" float between 0.0 and less than 1.\n@my_rng.rand(0)       # A \"random\" float between 0.0 and less than 1.\n@my_rng.rand(100)     # A \"random\" integer between 0 and 99\n@my_rng.rand(1..6)    # A \"random\" integer between 1 and 6\n```\n\nIn addition, here are some other options:\n\n```ruby\n@my_rng.dice(100)     # A \"random\" integer between 0 and 99\n@my_rng.byte          # A \"random\" integer between 0 and 255\n@my_rng.word          # A \"random\" integer between 0 and 65535\n@my_rng.float         # A quick \"random\" float between 0 and less than 1.\n@my_rng.double        # A better \"random\" float between 0 and less than 1.\n@my_rng.string(10)    # A \"random\" string of 10 characters in length.\n\n# A \"random\" string of 10 characters in length from the string 'abcdefg'.\n@my_rng.string(10, 'abcdefg')\n```\n\nand also available are these helpful methods:\n\n```ruby\n@my_rng.reseed(value) # Reseed the sequence with the new value.\n@my_rng.reseed        # Reseed the sequence with a \"random\" seed.\n@my_rng.spin(count)   # Spin the generator count times.\n@my_rng.spin          # Spin the generator once.\n```\n\nNote: reseed is an alias of the srand method. The reseed method is preferred\ndue to its clearer name.\n\nIf more than one stream of numbers is required, it is best to use multiple\ninstances of FibonacciRng objects rather than rely on one. This will help avoid\nthe two streams of data being correlated.\n\n#### Hashing\n\nAs more as an experiment than anything else, it is also possible to use\nthe generator as a primitive hash generator. To do so, create a new\ngenerator with a salt value, append data to it, and the retrieve the results\nas a (big) number or a string.\n\n```ruby\nfib = FibonacciRng.new('salt')\nfib \u003c\u003c \"The quick brown fox jumps over the lazy dog.\"\nputs fib.hash_string\n#displays: j5jqhk7ntrze02icv38gj28efa2qrctr6mi5ejbr2p4nj\n```\nNote that the length of the hash string is a function of the depth of the\ngenerator used to create it. This is about 5.5 characters per unit of depth.\n\n#### Salting\n\nAnother (more practical) use for the Fibonacci generator is the creation of\nsalting strings for use in more capable hashing schemes. Here are four possible\nways that this can be done:\n\n```ruby\n#Method #1\nsalt_string = FibonacciRng.new.hash_string  #Thread safe.\n\n#Method #2\nsalt_string = FibonacciRng.new(depth: 12).hash_string  #Thread safe.\n\n#Method #3\nThread.current[:salter] = FibonacciRng.new   #Need a separate generator for each thread.\n# Much intervening code omitted.\nsalter = Thread.current[:salter]\nsalter \u003c\u003c Time.now.to_s  # Note that unique time values are NOT needed.\nsalt_string = salter.hash_string\n\n#Method #4\nThread.current[:salter] = FibonacciRng.new   #Need a separate generator for each thread.\n# Much intervening code omitted.\nsalter = Thread.current[:salter]\nsalter.spin\nsalt_string = salter.hash_string\n```\n\nEach time any of these is run, a different salt string will be generated.\n\n#### Methods back in Good Standing\n\n* bytes - This method would seem to generate an array of random bytes. It does\nnot. However, it doesn't matter either. The bytes method was added to improve\ninteroperability with the standard Random class. Thus deprecating it was a\nmistake. This note serves to announce that the bytes method is not going away.\n\n\n## Theory of Operation\n\nThe random number generator used in this gem is based on a modified, cyclic\nFibonacci generator. This ring buffer design modifies the simple sequence so\nthat it feeds back onto itself, which in turn gives the appearance of chaos.\nThere is one further required modification however. Since the Fibonacci sequence\nuses additions, a mechanism for preventing zeros from \"swamping\" the data, is\nneeded. This is accomplished by rotating one of the arguments to the right by\none bit. The basic outline of the generational cycle operation, with depth of\nN is shown below:\n![The Cycle Operation](docs/cycle.png)\n\u003cbr\u003eNotes:\n* The last two elements are copies of the first two elements before\nthe array was transformed.\n* The notation ror(x) above signifies a 29 bit rotate right of the argument.\n* Not shown above for brevity, the result of each addition is masked with the\nvalue 0x1FFFFFFF before being stored. This masks off any data beyond the low\n29 bits.\n\n#### 29 bit Integers?\n\nThe random number generator masks the data in the ring buffer to 29 bit,\nunsigned values. To understand this, it is first necessary to under why\nmasking is needed at all, and secondly, why this was done at 29 bits.\n\nMasking at some level is required because it is needed to simulate the numeric\noverflow of more primitive systems of arithmetic. In most systems, when the\nlimit of an integer is reached, overflow occurs with no error indication. Ruby\ndoes not permit this to occur. To avoid overflow, Ruby transparently converts\nfrom a simple integer (FIXNUM class) to a multiple-precision number\n(BIGNUM class). The random number generator *requires* the computations to\noverflow, so masking the results forces the computation to act as if they had.\n\nSo why 29 bits? Why not 32? or 64? The issue here is performance. Arithmetic\nwith BIGNUM values is much slower than with FIXNUM values. Further, the\nconversion between FIXNUM and BIGNUM is also slow. The 29 bit size was\nchosen to ensure that all computations remain with FIXNUM values without\never reaching the threshold for the switch to BIGNUM values. In 32 bit Ruby\nsystems, 29 bits is the largest value that meets this requirement.\n\n#### Code\n\nEnough with all these words and pictures. The best way to gain insight into\nruby code is to study ruby code! What follows is the critical snippet of code\nthat makes this random number generator spin:\n\n```ruby\nprivate\n\n#Cycle through the PRNG once.\ndef do_spin\n  @buffer[-2] = @buffer[0]\n  @buffer[-1] = @buffer[1]\n\n  (0...@depth).each do |idx|\n    tmp = @buffer[idx+2]\n    @buffer[idx] = (@buffer[idx+1] + ((tmp \u003e\u003e 1)|(tmp.odd? ? TOP : 0))) \u0026 CHOP\n  end\nend\n\n```\nThe above is found in the spinner.rb file. For completeness of understanding,\nthe following constants are defined in the fibonacci_rng.rb file:\n\n```ruby\nCHOP = 0x1FFFFFFF\nTOP  = 0x10000000\n\n```\n\n## Error Detection\n\nLike all systems, the fibonacci random number generator has a failure mode. If\nit should happen that all the internal data registers have a value of zero, the\ngenerator will be trapped in an endless state of zero output. It will cease to\noperate as a pseudo random number generator.\n\nWhile it is possible to show that this cannot occur in small generators, the\ncase for larger ones is more difficult to analyze. To this end, the code now\nincorporates a test that at least one non-zero register is present. If this\ntest should fail an exception \"InvalidFibonacciRngState\" is raised so that\nthe application can take appropriate action.\n\nTo date, torture testing of the generator has yielded no failures, but this is\nnot a proof that one cannot happen.\n\nThe test scans the registers until it finds a non-zero value. This means that\nit almost always only needs to look at one register, saving a great deal of\nexecution time.\n\n\n## Contributing\n\nCreating a good pseudo random number generator is quite an undertaking. For\nthis reason, any input is most welcomed. There are two basic plans by which\nthis can be accomplished.\n\n#### Plan A\n\n1. Fork it ( https://github.com/PeterCamilleri/fibonacci_rng/fork )\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create new Pull Request\n\n#### Plan B\n\nGo to the GitHub repository and raise an issue calling attention to some\naspect that could use some TLC or a suggestion or an idea.\n\n## License\n\nThe gem is available as open source under the terms of the\n[MIT License](./LICENSE.txt).\n\n## Code of Conduct\n\nEveryone interacting in the fully_freeze project’s codebases, issue trackers,\nchat rooms and mailing lists is expected to follow the\n[code of conduct](./CODE_OF_CONDUCT.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpetercamilleri%2Ffibonacci_rng","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpetercamilleri%2Ffibonacci_rng","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpetercamilleri%2Ffibonacci_rng/lists"}