├── index ├── name ├── title ├── version ├── created ├── organization ├── summary ├── authors ├── repositories ├── requirements ├── copyrights ├── resources └── description ├── lib ├── radix.yml ├── radix.rb └── radix │ ├── operator.rb │ ├── numeric.rb │ ├── base.rb │ ├── rational.rb │ ├── float.rb │ └── integer.rb ├── demo ├── applique │ ├── radix.rb │ ├── ae.rb │ └── check.rb ├── issues │ ├── 004_zero_empty_string.md │ └── 012_array_base.md ├── 01_synopsis.md ├── 04_rational.md ├── 05_base.md ├── 02_integer.md └── 03_float.md ├── Gemfile ├── .yardopts ├── .gitignore ├── work ├── deprecated │ ├── qed │ │ ├── applique │ │ │ └── env.rb │ │ ├── 04_rational.rdoc │ │ ├── 03_float.rdoc │ │ ├── 02_integer.rdoc │ │ └── 05_base.rdoc │ ├── task │ │ └── rubyforge.syckle │ ├── radix_operator.rdoc │ └── integer.rb └── consider │ ├── digits.rdoc │ └── digits.rb ├── .travis.yml ├── MANIFEST ├── Assembly ├── LICENSE.txt ├── .index ├── README.md ├── HISTORY.md └── .gemspec /index/name: -------------------------------------------------------------------------------- 1 | radix 2 | -------------------------------------------------------------------------------- /index/title: -------------------------------------------------------------------------------- 1 | Radix 2 | -------------------------------------------------------------------------------- /index/version: -------------------------------------------------------------------------------- 1 | 2.2.1 2 | -------------------------------------------------------------------------------- /lib/radix.yml: -------------------------------------------------------------------------------- 1 | ../.index -------------------------------------------------------------------------------- /index/created: -------------------------------------------------------------------------------- 1 | 2009-07-01 2 | -------------------------------------------------------------------------------- /index/organization: -------------------------------------------------------------------------------- 1 | Rubyworks 2 | -------------------------------------------------------------------------------- /demo/applique/radix.rb: -------------------------------------------------------------------------------- 1 | require 'radix' 2 | -------------------------------------------------------------------------------- /index/summary: -------------------------------------------------------------------------------- 1 | Convert to and from any base. 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | gemspec 3 | -------------------------------------------------------------------------------- /index/authors: -------------------------------------------------------------------------------- 1 | --- 2 | - Thomas Sawyer 3 | -------------------------------------------------------------------------------- /demo/applique/ae.rb: -------------------------------------------------------------------------------- 1 | require 'ae' 2 | require 'ae/should' 3 | 4 | -------------------------------------------------------------------------------- /index/repositories: -------------------------------------------------------------------------------- 1 | --- 2 | upstream: git://github.com/rubyworks/radix.git 3 | -------------------------------------------------------------------------------- /index/requirements: -------------------------------------------------------------------------------- 1 | --- 2 | - ergo (build) 3 | - qed (test) 4 | - ae (test) 5 | -------------------------------------------------------------------------------- /index/copyrights: -------------------------------------------------------------------------------- 1 | --- 2 | - (c) Thomas Sawyer, Rubyworks 2009 (BSD-2-Clause) 3 | 4 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --title Radix 2 | --readme README.md 3 | --protected 4 | --private 5 | lib 6 | - 7 | *.md 8 | *.txt 9 | -------------------------------------------------------------------------------- /demo/applique/check.rb: -------------------------------------------------------------------------------- 1 | def check(&block) 2 | @check = block 3 | end 4 | 5 | def ok(*args) 6 | @check.call(*args) 7 | end 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.lock 3 | DEMO.md 4 | .ergo/digest 5 | .yardoc 6 | doc/ 7 | log/ 8 | pkg/ 9 | tmp/ 10 | web/ 11 | 12 | -------------------------------------------------------------------------------- /index/resources: -------------------------------------------------------------------------------- 1 | --- 2 | home: http://rubyworks.github.com/radix 3 | code: http://github.com/rubyworks/radix 4 | bugs: http://github.com/rubyworks/radix/issues 5 | 6 | -------------------------------------------------------------------------------- /work/deprecated/qed/applique/env.rb: -------------------------------------------------------------------------------- 1 | require 'ae/should' 2 | 3 | require 'qed/extensions/check' 4 | 5 | $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib') 6 | 7 | require 'radix' 8 | 9 | -------------------------------------------------------------------------------- /index/description: -------------------------------------------------------------------------------- 1 | Radix is a very easy to use Ruby library for converting numbers to and from 2 | any base. It supports both Integer, Float and Rational numbers, as well as 3 | representational string-notations that need not be in ASCII order. 4 | -------------------------------------------------------------------------------- /work/deprecated/task/rubyforge.syckle: -------------------------------------------------------------------------------- 1 | --- 2 | rubyforge: 3 | service : Rubyforge 4 | unixname: <%= project %> 5 | groupid : ~ 6 | package : <%= package %> 7 | sitemap: 8 | doc/rdoc: <%= package %> 9 | active : false 10 | 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: ruby 3 | script: "bundle exec qed" 4 | rvm: 5 | - 2.1.0 6 | - 1.9.3 7 | - 1.9.2 8 | - 1.8.7 9 | - rbx 10 | - jruby 11 | matrix: 12 | allow_failures: 13 | - rvm: rbx 14 | - rvm: rbx-2 15 | cache: bundler 16 | -------------------------------------------------------------------------------- /demo/issues/004_zero_empty_string.md: -------------------------------------------------------------------------------- 1 | # Zero becomes empty string (#4) 2 | 3 | Example of the issue: 4 | 5 | 0.b(10).to_s #=> "" 6 | 7 | I would expect "0" as a result. 8 | 9 | 0.b(10).to_s #=> "0" 10 | 11 | Okay, lets make sure this works for Floats. 12 | 13 | 0.0.b(10).to_s #=> "0.0" 14 | 15 | And Rationals too. 16 | 17 | [0,1].br(10).to_s #=> "0/1" 18 | 19 | -------------------------------------------------------------------------------- /demo/issues/012_array_base.md: -------------------------------------------------------------------------------- 1 | # Encoding/decoding with custom character sets (#12) 2 | 3 | There is not problem converting a numeric value into a custom base. 4 | 5 | c62 = (0..9).map{ |x| x.to_s } + ('a'..'z').to_a + ('A'..'Z').to_a 6 | 7 | 809145531659995.b(c62).to_s #=> "3HLszsQsP" 8 | 9 | But there was a problem with decoding custom number back. 10 | 11 | "3HLszsQsP".b(c62).to_i #=> 809145531659995 12 | 13 | 14 | -------------------------------------------------------------------------------- /work/consider/digits.rdoc: -------------------------------------------------------------------------------- 1 | Digits 2 | 3 | require 'radix/digits' 4 | 5 | == #digits 6 | 7 | Integer 8 | 9 | 8.digits(8) #=> [1,0] 10 | 11 | Float 12 | 13 | 8.0.digits(8) #=> [1,0,'.',0] 14 | 15 | Negative integer 16 | 17 | -8.digits(8) #=> ['-',1,0] 18 | 19 | Negative float 20 | 21 | -8.0.digits(8) #=> ['-',1,0,'.',0] 22 | 23 | 24 | Conversion fun! 25 | 26 | 40.digits(6).join #=> "104" 27 | 28 | Using Arrays 29 | 30 | 37.digits(%w{O L T W F S}).join #=> "LOL" 31 | 32 | Using BASE proc 33 | 34 | 254.digits(BASE[16]).join #=> "FE" 35 | 36 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | #!mast .index .yardopts bin demo lib man spec test *.md *.txt 2 | .index 3 | .yardopts 4 | demo/01_synopsis.md 5 | demo/02_integer.md 6 | demo/03_float.md 7 | demo/04_rational.md 8 | demo/05_base.md 9 | demo/applique/ae.rb 10 | demo/applique/check.rb 11 | demo/applique/radix.rb 12 | demo/issues/004_zero_empty_string.md 13 | demo/issues/012_array_base.md 14 | lib/radix/base.rb 15 | lib/radix/float.rb 16 | lib/radix/integer.rb 17 | lib/radix/numeric.rb 18 | lib/radix/operator.rb 19 | lib/radix/rational.rb 20 | lib/radix.rb 21 | lib/radix.yml 22 | README.md 23 | HISTORY.md 24 | DEMO.md 25 | LICENSE.txt 26 | -------------------------------------------------------------------------------- /Assembly: -------------------------------------------------------------------------------- 1 | --- 2 | github: 3 | gh_pages: web 4 | 5 | gem: 6 | active: true 7 | 8 | dnote: 9 | title: Source Notes 10 | output: log/notes.html 11 | 12 | qed: 13 | files: demo/ 14 | 15 | qedoc: 16 | files: demo/ 17 | title: Radix Demonstrations 18 | output: 19 | - DEMO.md 20 | - web/demo.html 21 | 22 | locat: 23 | active: false 24 | 25 | email: 26 | service : Email 27 | file : ~ 28 | subject : ~ 29 | mailto : 30 | - ruby-talk@ruby-lang.org 31 | - rubyworks-mailinglist@googlegroups.com 32 | 33 | vclog: 34 | active: false 35 | output: 36 | - log/changes.html 37 | - log/history.html 38 | 39 | -------------------------------------------------------------------------------- /lib/radix.rb: -------------------------------------------------------------------------------- 1 | require 'radix/base' 2 | require 'radix/integer' 3 | require 'radix/float' 4 | require 'radix/rational' 5 | require 'radix/operator' 6 | 7 | module Radix 8 | 9 | ## 10 | # Returns the metadata contained in Radix.yml 11 | # 12 | # @return [Hash{String=>String}] 13 | def self.metadata 14 | @metadata ||= ( 15 | require 'yaml' 16 | YAML.load(File.new(File.dirname(__FILE__) + '/radix.yml')) 17 | ) 18 | end 19 | 20 | ## 21 | # Gets value of name in metadata or goes up ancestry. 22 | # 23 | # @param [Symbol] name 24 | # 25 | # @return [String] 26 | def self.const_missing(name) 27 | key = name.to_s.downcase 28 | metadata.key?(key) ? metadata[key] : super(name) 29 | end 30 | 31 | end 32 | 33 | -------------------------------------------------------------------------------- /work/deprecated/radix_operator.rdoc: -------------------------------------------------------------------------------- 1 | = Radix Base Operator 2 | 3 | There first version of Radix worked well enough, 4 | but its API was very functional and thus a bit difficult 5 | to read at a glance. 6 | 7 | To improve upon this Radix now offers an option operater 8 | API. 9 | 10 | require 'radix' 11 | 12 | With it, the #b method extends String and Array classes to 13 | simplify all mulit-base operations. 14 | 15 | r = "1000".b(2) + "2".b(8) 16 | r.assert == "1010".b(2) 17 | r.assert == "12".b(8) 18 | r.assert == "10".b(10) 19 | 20 | r = "AZ42".b(62) + "54".b(10) 21 | r.assert == "8814596".b(10) 22 | 23 | Working with arrays for bases greater than 62. 24 | 25 | r = [100,10].b(256) + "54".b(10) 26 | r.assert == "25664".b(10) 27 | 28 | -------------------------------------------------------------------------------- /work/deprecated/qed/04_rational.rdoc: -------------------------------------------------------------------------------- 1 | = Radix Rational 2 | 3 | Radix also provides a Rational class. Like the Integer and Float classes 4 | Radix::Rational delegates to an underlying instance of Ruby standard 5 | Rational class. 6 | 7 | require 'radix' 8 | 9 | b = ["100","10"].br(2) 10 | b.assert = [2,1].br(10) 11 | 12 | When convert to Array or String Radix::Rational uses `/` to separate 13 | the numerator from the denominator. 14 | 15 | b.to_a #=> [1,0,0,'/',1,0] 16 | b.to_s #=> "100/10" 17 | 18 | To use a custom character set, use an array of characters as the base 19 | rather than an integer. For example we can convert a base 10 number 20 | to another base 10 number but useing a different encoding. 21 | 22 | base = %w[Q W E R T Y U I O U] 23 | 24 | ["10","1"].br(10).to_a(base) #=> ["W", "Q", '/', 'W'] 25 | 26 | ["10","1"].br(10).to_s(base) #=> "WQ/W" 27 | 28 | -------------------------------------------------------------------------------- /work/deprecated/qed/03_float.rdoc: -------------------------------------------------------------------------------- 1 | = Radix Float 2 | 3 | Radix provides a Float class for working with floating point numbers in 4 | any base. 5 | 6 | require 'radix' 7 | 8 | With it, the #b method extends String and Array classes to 9 | simplify all mulit-base operations. 10 | 11 | b = "100.01".b(2) 12 | b.to_a.assert == [1,0,0,'.',0,1] 13 | 14 | Convert to base 10. 15 | 16 | t = b.convert(10) 17 | t.to_a.assert == [4,'.',2,5] 18 | 19 | Like a Numeric class, Radix::Float's can be added, subtracted, multipled, etc. 20 | 21 | r = "1000.01".b(2) + "2".b(8) 22 | r.assert == "1010.01".b(2) 23 | r.assert == "12.2".b(8) 24 | r.assert == "10.25".b(10) 25 | 26 | Even complex conversions are supported. 27 | 28 | r = "AZ42".b(62) + "54".b(10) 29 | r.assert == "2518124".b(10) 30 | 31 | To work with bases greater than 62, use arrays. A '.' entry in the array 32 | can be used to separate the whole from the fractional part of the number. 33 | 34 | r = [100,10,'.',64].b(256) + "54".b(10) 35 | r.assert == "25664.25".b(10) 36 | 37 | -------------------------------------------------------------------------------- /work/deprecated/integer.rb: -------------------------------------------------------------------------------- 1 | require 'radix' 2 | 3 | feature "Integer Instantiation" do 4 | 5 | scenario "given an integer value and base" do 6 | to do |int, base| 7 | Radix::Integer.new(int, base) 8 | end 9 | 10 | valid do |result, compare| 11 | result.digits == compare 12 | end 13 | 14 | ok [ 8, 2] => [1,0,0,0] 15 | ok [ 4, 2] => [1,0,0] 16 | ok [ 8, 10] => [8] 17 | ok [10, 10] => [1, 0] 18 | ok [ 8, 16] => [8] 19 | ok [16, 16] => [1, 0] 20 | end 21 | 22 | # notice in the string case that the string is expected 23 | # to be in the given base already. 24 | scenario "given an string value and base" do 25 | to do |str, base| 26 | Radix::Integer.new(str, base) 27 | end 28 | 29 | valid do |result, compare| 30 | result.digits == compare 31 | end 32 | 33 | ok [ "1000", 2] => [1,0,0,0] 34 | ok [ "100", 2] => [1,0,0] 35 | ok [ "8", 10] => [8] 36 | ok [ "10", 10] => [1, 0] 37 | ok [ "8", 16] => [8] 38 | ok [ "10", 16] => [1, 0] 39 | end 40 | 41 | end 42 | 43 | -------------------------------------------------------------------------------- /work/deprecated/qed/02_integer.rdoc: -------------------------------------------------------------------------------- 1 | = Radix Integer 2 | 3 | Radix provides an Integer class for working with integers in various bases. 4 | 5 | require 'radix' 6 | 7 | Radix extend the Integer, String and Array classes with the #b method 8 | which simplifies the creation of Radix::Integer instances. The following 9 | return the equivalent instance of Radix::Integer. 10 | 11 | a = 8.b(2) 12 | 13 | b = "1000".b(2) 14 | 15 | c = [1, 0, 0, 0].b(2) 16 | 17 | a.assert == b 18 | b.assert == c 19 | c.assert == a 20 | 21 | a.assert == 8 22 | b.assert == 8 23 | c.assert == 8 24 | 25 | Radix integers can ve converted to other bases with the #convert method. 26 | 27 | b = "1000".b(2) 28 | d = b.convert(10) 29 | d.digits.assert == [8] 30 | 31 | Radix::Integer supports all the usual mathematical operators. 32 | 33 | r = "1000".b(2) + "2".b(8) 34 | r.assert == "1010".b(2) 35 | r.assert == "12".b(8) 36 | r.assert == "10".b(10) 37 | 38 | A more complex example with a much higher base. 39 | 40 | r = "AZ42".b(62) + "54".b(10) 41 | r.assert == "2518124".b(10) 42 | r.assert == 2518124 43 | 44 | Work with arrays for bases greater than 62. 45 | 46 | r = [100,10].b(256) + "54".b(10) 47 | r.assert == "25664".b(10) 48 | 49 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | BSD-2-Clause License 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 14 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 15 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 16 | COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 20 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 22 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | -------------------------------------------------------------------------------- /.index: -------------------------------------------------------------------------------- 1 | --- 2 | revision: 2013 3 | type: ruby 4 | sources: 5 | - index 6 | authors: 7 | - name: Thomas Sawyer 8 | email: transfire@gmail.com 9 | organizations: [] 10 | requirements: 11 | - groups: 12 | - build 13 | development: true 14 | name: ergo 15 | - groups: 16 | - test 17 | development: true 18 | name: qed 19 | - groups: 20 | - test 21 | development: true 22 | name: ae 23 | conflicts: [] 24 | alternatives: [] 25 | resources: 26 | - type: home 27 | uri: http://rubyworks.github.com/radix 28 | label: Homepage 29 | - type: code 30 | uri: http://github.com/rubyworks/radix 31 | label: Source Code 32 | - type: bugs 33 | uri: http://github.com/rubyworks/radix/issues 34 | label: Issue Tracker 35 | repositories: 36 | - name: upstream 37 | scm: git 38 | uri: git://github.com/rubyworks/radix.git 39 | categories: [] 40 | copyrights: 41 | - holder: '' 42 | year: '2009' 43 | license: BSD-2-Clause 44 | customs: [] 45 | paths: 46 | lib: 47 | - lib 48 | name: radix 49 | title: Radix 50 | summary: Convert to and from any base. 51 | created: '2009-07-01' 52 | description: "Radix is a very easy to use Ruby library for converting numbers to and 53 | from\nany base. It supports both Integer, Float and Rational numbers, as well as 54 | \nrepresentational string-notations that need not be in ASCII order." 55 | version: 2.2.1 56 | date: '2015-03-02' 57 | -------------------------------------------------------------------------------- /demo/01_synopsis.md: -------------------------------------------------------------------------------- 1 | # Synopsis 2 | 3 | Radix provides the means of converting to and from any base. 4 | For example, a number in base 256 can be represented by the array [100, 10] 5 | (ie. 100**256 + 10**1) and easily converted to base 10. 6 | 7 | [100,10].b(256).to_i #=> 25610 8 | 9 | We can get an Array representation as well. 10 | 11 | [100,10].b(256).to_a(10) #=> [2,5,6,1,0] 12 | [100,10].b(256).to_a(62) #=> [6,41,4] 13 | [100,10].b(256).to_a(64) #=> [6,16,10] 14 | 15 | To get a String representation for any base use #to_s. 16 | 17 | [100,10].b(256).to_s(10) #=> "25610" 18 | [100,10].b(256).to_s(62) #=> "6 41 4" 19 | [100,10].b(256).to_s(64) #=> "6 16 10" 20 | 21 | Notice that anything above base 10 is seperated by a space divider. The divider 22 | can be changed by providing a second argument. 23 | 24 | [100,10].b(256).to_s(64, ':') #=> "6:16:10" 25 | 26 | A string representation of a number can be converted upto base 62 (B62). 27 | 28 | "10".b(62).to_s(10) #=> "62" 29 | "zz".b(62).to_s(10) #=> "3843" 30 | 31 | To encode a number with a base greater than 10, use an Array base. Radix 32 | provides a built-in set of these, such as `BASE::B62`. 33 | 34 | [100,10].b(256).to_s(Radix::BASE::B62) #=> "6f4" 35 | 36 | To use a custom character set, use an array of characters as the base 37 | rather than an integer. For example we can convert a base 10 number 38 | to another base 10 number but useing a different encoding. 39 | 40 | base = %w[Q W E R T Y U I O U] 41 | 42 | "10".b(10).to_a(base) #=> ["W", "Q"] 43 | 44 | "10".b(10).to_s(base) #=> "WQ" 45 | 46 | All of the above holds equally for floating point numbers. 47 | -------------------------------------------------------------------------------- /demo/04_rational.md: -------------------------------------------------------------------------------- 1 | # Radix Rational 2 | 3 | require 'radix' 4 | 5 | ## Initialization 6 | 7 | Radix::Rational's initializer takes a numerator and a denominator, 8 | either of which can be an Integer, Float, String or Array along witha 9 | an integer base. 10 | 11 | Give a integer value, it will automatically be converted to the base 12 | specified. 13 | 14 | check do |num, dem, base, eqf| 15 | r = Radix::Rational.new(num, dem, base) 16 | r.assert == eqf 17 | end 18 | 19 | ok 1, 2, 2, 0.5 20 | ok 1, 1, 2, 1.0 21 | 22 | ok 8, 1, 10, 8.0 23 | ok 8, 5, 10, 1.6 24 | ok 8, 8, 10, 1.0 25 | 26 | ok 10, 1, 10, 10.0 27 | ok 10, 2, 10, 5.0 28 | ok 10, 5, 10, 2.0 29 | 30 | ok 8, 1, 16, 8.0 31 | ok 16, 1, 16, 16.0 32 | 33 | ## Reduction 34 | 35 | check do |a, x| 36 | r = a.reduce 37 | r.assert == x 38 | end 39 | 40 | ok [10,5].br(10), [2,1].br(10) 41 | ok [30,3].br(10), [10,1].br(10) 42 | 43 | ## Operations 44 | 45 | ### Addition 46 | 47 | check do |a, b, x| 48 | r = a + b 49 | r.assert == x 50 | end 51 | 52 | ok [8,5].br(10), [1,2].br(10), [21,10].br(10) 53 | 54 | ok [8,5].br(10), 1, [13,5].br(10) 55 | 56 | ok [8,5].br(10), 0.5, [21,10].br(10) 57 | 58 | ### Subtraction 59 | 60 | check do |a, b, x| 61 | r = a - b 62 | r.assert == x 63 | end 64 | 65 | ok [8,5].br(10), [1,2].br(10), [11,10].br(10) 66 | 67 | ### Multiplication 68 | 69 | check do |a, b, x| 70 | r = a * b 71 | r.assert == x 72 | end 73 | 74 | ok [8,5].br(10), [1,2].br(10), [8,10].br(10) 75 | 76 | ### Division 77 | 78 | check do |a, b, x| 79 | r = a / b 80 | r.assert == x 81 | end 82 | 83 | ok [8,5].br(10), [1,2].br(10), [16,5].br(10) 84 | 85 | -------------------------------------------------------------------------------- /work/consider/digits.rb: -------------------------------------------------------------------------------- 1 | # Maximum single-charcter encoded base supported. 2 | BASE62 = ["0".."9", "A".."Z", "a".."z"].map{ |r| r.to_a }.flatten 3 | 4 | # Lamda for producing any subset of BASE62. 5 | BASE = lambda{ |n| BASE62[0...n] } 6 | 7 | #BASE10 = ["0".."9"].map { |r| r.to_a }.flatten 8 | #BASE12 = ["0".."9", ["X", "E"]].map { |r| r.to_a }.flatten 9 | #BASE16 = ["0".."9", "A".."F"].map { |r| r.to_a }.flatten 10 | #BASE36 = ["0".."9", "A".."Z"].map { |r| r.to_a }.flatten 11 | #BASE60 = ["0".."9", "a".."z", "A".."X"].map { |r| r.to_a }.flatten 12 | 13 | class Integer 14 | 15 | # 16 | def digits(base=10) 17 | case base 18 | when Array 19 | table = base 20 | base = base.size 21 | else 22 | table = (0...base).to_a 23 | end 24 | 25 | raise ArgumentError unless base > 1 26 | 27 | i = abs 28 | a = [] 29 | while i > 0 30 | i, r = i.divmod(base) 31 | a << table[r] #a << r 32 | end 33 | d = a.reverse 34 | d.unshift('-') if self < 0 35 | d 36 | end 37 | 38 | end 39 | 40 | class Float 41 | 42 | #-- 43 | # TODO: Doe Float have another way to handle precision? 44 | #++ 45 | def digits(base=10, prec=10) 46 | case base 47 | when Array 48 | table = base 49 | base = base.size 50 | else 51 | table = (0...base).to_a 52 | end 53 | 54 | raise ArgumentError unless base > 1 55 | 56 | value = to_f.abs 57 | 58 | i, f = value.split 59 | 60 | a = [] 61 | while i > 0 62 | i, r = i.divmod(base) 63 | a << table[r] #a << r 64 | end 65 | 66 | p = prec 67 | b = [] 68 | while !f.zero? 69 | k = (f * base) 70 | r, f = k.split 71 | break if p == 0; p -= 1 72 | b << table[r] #b << r 73 | end 74 | 75 | b << 0 if b.empty? 76 | 77 | d = a.reverse + ['.'] + b 78 | d.unshift('-') if self < 0 79 | d 80 | end 81 | 82 | # 83 | def split 84 | i, f = to_s.split('.') 85 | return i.to_i, ('0.'+f).to_f 86 | end 87 | 88 | end 89 | 90 | -------------------------------------------------------------------------------- /lib/radix/operator.rb: -------------------------------------------------------------------------------- 1 | # This script defines the `#b` core extension method for Ruby's built-in 2 | # Numeric classes to make it easy to convert to Radix base classes. 3 | 4 | ## 5 | # Adds the b(base) method to this ruby class for quickly creating Radix 6 | # instances. 7 | class ::Float 8 | 9 | ## 10 | # Takes a Ruby Float and makes it into a Radix::Float as given base. 11 | # 12 | # @param [Fixnum, Array] base 13 | # The desired base. 14 | # 15 | # @return [Radix::Float] 16 | def b(base) 17 | Radix::Float.new(self, base) 18 | end 19 | end 20 | 21 | ## 22 | # Adds the b(base) method to this ruby class for quickly creating Radix 23 | # instances. 24 | class ::Integer 25 | 26 | ## 27 | # Takes a Ruby Integer and makes it into a Radix::Integer as given base. 28 | # 29 | # @param [Fixnum, Array] base 30 | # The desired base. 31 | # 32 | # @return [Radix::Integer] 33 | def b(base) 34 | Radix::Integer.new(self, base) 35 | end 36 | end 37 | 38 | ## 39 | # Adds the b(base) method to this ruby class for quickly creating Radix 40 | # instances. 41 | class ::String 42 | 43 | # Ruby 2.x defines it's own `String#b` method for converting to ASCII 8-bit. 44 | # That breaks Radix (of course), but it a terrbile name. `String#ascii` is 45 | # much better (duh!). So that's what we are doing. 46 | if method_defined?(:b) 47 | alias :ascii :b 48 | else 49 | def ascii 50 | force_encoding('ASCII') 51 | end 52 | end 53 | 54 | ## 55 | # Takes a String and makes it into a Radix::Integer or Radix::Float as given 56 | # base. Float is determined by a "." character in string instance 57 | # 58 | # @param [Fixnum, Array] base 59 | # The desired base. 60 | # 61 | # @return [Radix::Integer, Radix::Float] 62 | def b(base=nil) 63 | return ascii unless base 64 | 65 | if index('.') 66 | Radix::Float.new(self, base) 67 | else 68 | Radix::Integer.new(self, base) 69 | end 70 | end 71 | end 72 | 73 | ## 74 | # Adds the b(base) method to this ruby class for quickly creating Radix 75 | # instances. 76 | class ::Array 77 | 78 | ## 79 | # Takes array and makes it into a Radix::Integer or Radix::Float as given 80 | # base. Float is determined by a "." character in array instance. 81 | # 82 | # @param [Fixnum, Array] base 83 | # The desired base. 84 | # 85 | # @return [Radix::Integer, Radix::Float] 86 | def b(base) 87 | if index('.') 88 | Radix::Float.new(self, base) 89 | else 90 | Radix::Integer.new(self, base) 91 | end 92 | end 93 | 94 | end 95 | 96 | -------------------------------------------------------------------------------- /demo/05_base.md: -------------------------------------------------------------------------------- 1 | # Radix::Base 2 | 3 | The Radix::Base class is an encapsulatin of a numeric base. By creating 4 | an instance of Base one can convert numbers to and from other bases. 5 | 6 | require 'radix/base' 7 | 8 | ## Base Instance 9 | 10 | First let's try something we all know, converting decimal to hexideciaml. 11 | To do this we setup the radix base objects for each base. 12 | 13 | b10 = Radix::Base.new(Radix::BASE::B10) 14 | b16 = Radix::Base.new(Radix::BASE::B16) 15 | 16 | Now we can covert from one base to the other. 17 | 18 | b16.convert("16" , b10).should == "10" 19 | b16.convert("160", b10).should == "A0" 20 | b16.convert("255", b10).should == "FF" 21 | 22 | To confirm, lets convert from hexidecimal back to decimal. 23 | 24 | b10.convert("10", b16).should == "16" 25 | b10.convert("A0", b16).should == "160" 26 | b10.convert("FF", b16).should == "255" 27 | 28 | If we are happy with standard encodings then we can simply provide an 29 | integer base, rather than a Radix::Base object. 30 | 31 | b10.convert("10", 16).should == "16" 32 | b10.convert("A0", 16).should == "160" 33 | b10.convert("FF", 16).should == "255" 34 | 35 | Now let's try a more down to earth base, my favorite, 36 | senary, or base six. 37 | 38 | b6 = Radix::Base.new(0..5) 39 | b6.convert("39", 10).should == "103" 40 | 41 | And the notations need not be in ASCII order. Odd alternate notations 42 | can be used as well. 43 | 44 | b10 = Radix::Base.new([:Q, :W, :E, :R, :T, :Y, :U, :I, :O, :U]) 45 | b10.convert("FF", 16) #=> "EYY" 46 | 47 | ## Encoding and Decoding 48 | 49 | Radix::Base instances can also be used to encode and decode strings. 50 | 51 | b16.encode("CHARLIE").should == "434841524C4945" 52 | b16.decode("434841524C4945").should == "CHARLIE" 53 | 54 | ## Module Methods 55 | 56 | For further convenience, Radix::base provides functions to convert to and from 57 | standard notations upto 62 without creating an instance of Radix::Base. 58 | 59 | Radix.convert("10", 16, 10).should == "16" 60 | Radix.convert("A0", 16, 10).should == "160" 61 | Radix.convert("FF", 16, 10).should == "255" 62 | 63 | Let's try that again with the maximum base supported. 64 | 65 | Radix.convert( "62", 10, 62).should == "10" 66 | Radix.convert("8814542", 10, 62).should == "az42" 67 | 68 | Radix.convert( "10", 62, 10).should == "62" 69 | Radix.convert( "az42", 62, 10).should == "8814542" 70 | 71 | Finally, we will demonstrate how to convert bases larger than 62. 72 | These can only be represented as arrays since there are not enough 73 | latin characters to represent them. 74 | 75 | Radix.convert_base([100, 10], 256, 10).should == [2, 5, 6, 1, 0] 76 | Radix.convert_base([2, 5, 6, 1, 0], 10, 256).should == [100, 10] 77 | Radix.convert_base([1, 0, 1, 0, 1], 2, 10).should == [2, 1] 78 | 79 | -------------------------------------------------------------------------------- /work/deprecated/qed/05_base.rdoc: -------------------------------------------------------------------------------- 1 | = Radix Base 2 | 3 | Radix::Base encapsulates a base which can then be used to perform conversions 4 | to and form that base. 5 | 6 | NOTE: Radix::Base is the original Radix API. But with the advent of v2.0 7 | and the new Integer and Float classes, it is outmoded. For now it is here 8 | for backward compatibility. In a future version it may be deprecated, or 9 | reworked to serve as the backbone of the other classes. 10 | 11 | require 'radix/base' 12 | 13 | First let's try something we all know, hexideciaml. 14 | First we setup the radix for each. 15 | 16 | b10 = Radix::Base.new(Radix::BASE::B10) 17 | b16 = Radix::Base.new(Radix::BASE::B16) 18 | 19 | Now we can covert from one base to the other. 20 | 21 | b16.convert("16" , b10).should == "10" 22 | b16.convert("160", b10).should == "A0" 23 | b16.convert("255", b10).should == "FF" 24 | 25 | To confirm, lets convert from deciaml to hexidecimal. 26 | 27 | b10.convert("10", b16).should == "16" 28 | b10.convert("A0", b16).should == "160" 29 | b10.convert("FF", b16).should == "255" 30 | 31 | If we we want to use standard encodings then we 32 | can simply provide an integer base, rather than 33 | a Radix object. 34 | 35 | b10.convert("10", 16).should == "16" 36 | b10.convert("A0", 16).should == "160" 37 | b10.convert("FF", 16).should == "255" 38 | 39 | Now let's try a more down to earth base, my favorite, 40 | senary, or base six. 41 | 42 | b6 = Radix::Base.new(0..5) 43 | b6.convert("39", 10).should == "103" 44 | 45 | And the notations need not be in ASCII order. Odd alternate notations 46 | can be used as well. 47 | 48 | b10 = Radix::Base.new([:Q, :W, :E, :R, :T, :Y, :U, :I, :O, :U]) 49 | b10.convert("FF", 16) #=> "EYY" 50 | 51 | == Encoding and Decoding 52 | 53 | Radix can also be used to encode and decode strings. 54 | 55 | b16.encode("CHARLIE").should == "434841524C4945" 56 | b16.decode("434841524C4945").should == "CHARLIE" 57 | 58 | == Module Methods 59 | 60 | We can also use the module function to convert to and from standard 61 | notations upto 62 without creating an instance of Radix::Base. 62 | 63 | Radix.convert("10", 16, 10).should == "16" 64 | Radix.convert("A0", 16, 10).should == "160" 65 | Radix.convert("FF", 16, 10).should == "255" 66 | 67 | Let's try that again with the maximum base supported. 68 | 69 | Radix.convert( "62", 10, 62).should == "10" 70 | Radix.convert("8814542", 10, 62).should == "az42" 71 | 72 | Radix.convert( "10", 62, 10).should == "62" 73 | Radix.convert( "az42", 62, 10).should == "8814542" 74 | 75 | Finally, we will demonstrate how to convert bases larger than 62. 76 | These can only be represented as arrays since there are not enough 77 | latin characters to represent them. 78 | 79 | Radix.convert_base([100, 10], 256, 10).should == [2, 5, 6, 1, 0] 80 | Radix.convert_base([2, 5, 6, 1, 0], 10, 256).should == [100, 10] 81 | Radix.convert_base([1, 0, 1, 0, 1], 2, 10).should == [2, 1] 82 | 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Radix 2 | 3 | [![Gem Version](http://img.shields.io/gem/v/radix.svg?style=flat)](https://rubygems.org/gems/radix) 4 |     5 | [![Fork Me](http://img.shields.io/badge/scm-github-blue.svg?style=flat)](http://github.com/rubyworks/radix) 6 | [![Report Issue](http://img.shields.io/github/issues/rubyworks/radix.svg?style=flat)](http://github.com/rubyworks/radix/issues) 7 | [![Build Status](http://img.shields.io/travis/rubyworks/radix.svg?style=flat)](http://travis-ci.org/rubyworks/radix) 8 |     9 | [![Gittip](http://img.shields.io/badge/gittip-$1/wk-green.svg?style=flat)](https://www.gittip.com/on/github/rubyworks/) 10 | [![Flattr Me](http://api.flattr.com/button/flattr-badge-large.png)](http://flattr.com/thing/324911/Rubyworks-Ruby-Development-Fund) 11 | 12 | 13 | [Radix]((http://rubyworks.github.com/radix)) is a very easy to use Ruby library 14 | for converting numbers to and from any base. It supports Integer, Float and 15 | Rational numbers, as well as representational string-notations that need not be 16 | in ASCII order. 17 | 18 | 19 | ## Features 20 | 21 | * Convert to and from any base. 22 | * Convert Integer, Float and Rational numbers. 23 | * Define custom encodings and character sets. 24 | * Can be used to encode/decode bytecode strings. 25 | * Very intuitive API. 26 | 27 | 28 | ## Usage 29 | 30 | Base conversions with ASCII ordered notations are easy in Ruby. 31 | 32 | ```ruby 33 | 255.to_s(16) #=> "FF" 34 | 35 | "FF".to_i(16) #=> 255 36 | ``` 37 | 38 | But Ruby reaches it's limit at base 36. 39 | 40 | ```ruby 41 | 255.to_s(37) #=> Error 42 | ``` 43 | 44 | Radix provides the means of converting to and from any base. 45 | 46 | For example, a number in base 256 can be represented by the array `[100, 10]` 47 | (`100**256 + 10**1`) and can be convert to base 10. 48 | 49 | ```ruby 50 | [100,10].b(256).to_a(10) #=> [2,5,6,1,0] 51 | ``` 52 | 53 | Or, to get a string representation for any base up to 62. 54 | 55 | ```ruby 56 | [100,10].b(256).to_s(10) #=> "25610" 57 | ``` 58 | 59 | A string representation of a number can be converted too, again, 60 | up to base 62. 61 | 62 | ```ruby 63 | "10".b(62).to_s(10) #=> "62" 64 | ``` 65 | 66 | To use a custom character set, use an array of characters as the base 67 | rather than an integer. For example we can convert a base 10 number 68 | to another base 10 number using a different encoding. 69 | 70 | ```ruby 71 | base = [:Q, :W, :E, :R, :T, :Y, :U, :I, :O, :U] 72 | 73 | "10".b(10).to_a(base) #=> [:W, :Q] 74 | ``` 75 | 76 | To learn more have a look at the [QED Demo](http://rubydoc.info/gems/radix/file/DEMO.md). 77 | 78 | 79 | ## Installing 80 | 81 | If using Bundler, then add the ususal gem entry to your project's Gemfile. 82 | 83 | gem 'radix' 84 | 85 | To install with RubyGems simply open a console and type: 86 | 87 | $ gem install radix 88 | 89 | Radix follows [Ruby Setup](http://rubyworks.github.com/setup) package standard 90 | so it can also be installed in an FHS compliant manner using setup.rb (very 91 | old-school and no longer recommeded). 92 | 93 | 94 | ## Special Thanks 95 | 96 | Special thanks to **douglascodes** for taking the time to fully document 97 | Radix's API. Documentation is an under-addressed and time-consuming affair, 98 | so your contribution is greatly appreciated. Thank you, Douglas! 99 | 100 | 101 | ## Copyrights 102 | 103 | Copyright (c) 2009 [Rubyworks](https://rubyworks.github.io) 104 | 105 | This program is distributable in accordance with the *BSD-2-Clause* license. 106 | 107 | See LICENSE.txt for details. 108 | 109 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # RELEASE HISTORY 2 | 3 | ## 2.2.1 / 2014-03-02 4 | 5 | This release fixes a couple of issues. One unsettling issue in particular, 6 | somewhere around Ruby 2.0, `String#b` became an offical core method 7 | that force-encodes a string to ASCII 8-bit. This, of course, broke Radix :( 8 | That aside, it seems like a rather weak name for such a method. `String#ascii` 9 | seems a much better choice (IOHO). Thankfully, there's just enough wiggle-room 10 | to implement a work around, so the Radix API remains the same. In addition, 11 | there was a bug when trying to decode custom character sets, which has been 12 | fixed. 13 | 14 | Changes: 15 | 16 | * Work around Ruby's `String#b` method. 17 | * Fix custom base decoding. 18 | 19 | 20 | ## 2.2.0 / 2013-03-20 21 | 22 | Good documentation is so under-addressed by most developers that we really 23 | have to roll out the bright lights when someone comes along and takes 24 | up the time consuming mantle of documenting an API in complete detail. 25 | This is exactly what happened with Radix this last month. A coder going 26 | by the unassuming handle, **douglascodes**, announced his desire to 27 | document Radix and proceeded to do so until YARD pronounced 100% 28 | completeness. Hey, we think that is worth of a whole version bump and 29 | a big shout-out for Douglas! 30 | 31 | Changes: 32 | 33 | * 100% API documentation coverage. (douglascodes) 34 | 35 | 36 | ## 2.1.1 / 2013-02-06 37 | 38 | Minor release to fix gemspec. Which, alas, Bundler cannot do without. 39 | 40 | Changes: 41 | 42 | * Update .gemspec file for latest indexer. 43 | 44 | 45 | ## 2.1.0 / 2013-01-31 46 | 47 | This release fixes two bugs. The first caused base conversions of `0` 48 | to return an empty string. Second, the `Float#to_r` extension was 49 | using multiplication when it should have been using power. 50 | Ruby 1.9 includes `Float#to_r` though, so the use of Radix's *fixed* 51 | core extension is conditioned on Ruby 1.8 now. This release also 52 | updates the build a bit, primarily converting RDoc documents to 53 | Markdown. 54 | 55 | Changes: 56 | 57 | * Fix issue #5, Float#to_r using times instead of power. (simonratner) 58 | * Fix issue #4, zero converts to empty string. (bbasata) 59 | * Convert RDoc documents to Markdown. 60 | 61 | 62 | ## 2.0.1 / 2011-10-23 63 | 64 | This release is simply an administrative release to update the project 65 | build configuration. The functionality of the library itself has not 66 | changed. This release also transitions the project to the BSD-2-Clause 67 | license. 68 | 69 | Changes: 70 | 71 | * Modernize build configuration. 72 | * Switch to BSD-2-Clause license. 73 | 74 | 75 | ## 2.0.0 / 2010-10-31 76 | 77 | Radix::Number has been deprecate in favor of three separate classes, 78 | Radix::Integer, Radix::Float and Radix::Rational. In addition 79 | implementation of these classes has been reworked. 80 | 81 | Changes: 82 | 83 | * Deprecate Radix::Number 84 | * Add Radix::Integer 85 | * Add Radix::Float 86 | * Add Radix::Rational 87 | 88 | 89 | ## 1.1.0 / 2010-09-03 90 | 91 | Radix now provides an actual Numeric subclass, Radix::Number, that stores 92 | the base and can be used like any other Numeric object. This makes it very 93 | easy to convert and manipulate numbers in any base. The implementation is still 94 | a bit nascent. For the moment, it only supports the most basic math operators 95 | and only handles integer values, but future releases will continue to expand 96 | on it's capabilities. 97 | 98 | Changes: 99 | 100 | * Add Radix::Number to handle bases like any other numeric. 101 | * Rename Radix class to Radix::Base. 102 | 103 | 104 | ## 1.0.0 / 2009-07-01 105 | 106 | This is the initial stand-alone release of Radix, 107 | ported from the basex.rb library of Ruby Facets. 108 | 109 | Changes: 110 | 111 | * Happy Birthday! 112 | 113 | -------------------------------------------------------------------------------- /lib/radix/numeric.rb: -------------------------------------------------------------------------------- 1 | require 'radix/base' 2 | 3 | module Radix 4 | 5 | # Radix separator used in string and array representations. 6 | DOT = '.' 7 | 8 | # Division character for rational numbers 9 | DIV = '/' 10 | 11 | # Default seperator character. 12 | DIVIDER = " " 13 | 14 | ## 15 | # Radix::Numeric class inherits from Ruby's Numeric class. It is then 16 | # subclassed by Radix::Integer and Radix::Float. 17 | # 18 | # @todo Make immutable, but best way to do it? 19 | # @example First suggestion 20 | # class << self 21 | # alias_method :_new, :new 22 | # private :_new 23 | # end 24 | # @example Second suggestion 25 | # def self.new(value, base=10) 26 | # @cache ||= {} 27 | # @cache[[value, base]] ||= _new(value, base) 28 | # end 29 | class Numeric < ::Numeric 30 | 31 | ## 32 | # Addition, binary operation. 33 | # 34 | # @param [Radix::Numeric] other 35 | # 36 | # @return [Radix::Numeric] Result of arithmetic operation. 37 | def +(other) 38 | operation(:+, other) 39 | end 40 | 41 | ## 42 | # Subtraction, binary operation. 43 | # 44 | # @param [Radix::Numeric] other 45 | # 46 | # @return [Radix::Numeric] Result of arithmetic operation. 47 | def -(other) 48 | operation(:-, other) 49 | end 50 | 51 | ## 52 | # Multiplication, binary operation. 53 | # 54 | # @param [Radix::Numeric] other 55 | # 56 | # @return [Radix::Numeric] Result of arithmetic operation. 57 | def *(other) 58 | operation(:*, other) 59 | end 60 | 61 | ## 62 | # Division, binary operation. 63 | # 64 | # @param [Radix::Numeric] other 65 | # 66 | # @return [Radix::Numeric] Result of arithmetic operation. 67 | def /(other) 68 | operation(:/, other) 69 | end 70 | 71 | private 72 | 73 | ## 74 | # Parses the value of the base and character set to use. 75 | # 76 | # @param [Fixnum, Array] base 77 | # The value of the base, or a set of characters to use as 78 | # representation of the base. 79 | # 80 | # @note If an array of String characters is passed, its length is the 81 | # value of the base level. 82 | # 83 | # @return [Array<(Fixnum, [Array, nil])>] Two part array: 84 | # 0 - Fixnum value of the base. 85 | # 1 - Nil, or Array of characters representing the base values. 86 | def parse_base(base) 87 | case base 88 | when Array 89 | code = base 90 | base = base.size 91 | else 92 | code = nil 93 | base = base 94 | end 95 | return base, code 96 | end 97 | 98 | ## 99 | # Simply returns the passed value. Used for simplifying creation of 100 | # Radix::Numeric instances. 101 | # 102 | # @param [Radix::Float, Radix::Integer] value Given value. 103 | # @param [Fixnum, Array] base Desired base. 104 | # 105 | # @return [Radix::Float, Radix::Integer] The passed value. 106 | def parse_numeric(value, base) 107 | value 108 | end 109 | 110 | ## 111 | # If a float style string is passed in for +value+, e.g. "9.5", the 112 | # decimal will simply be truncated. So "9.x" would become "9". 113 | # 114 | # @param [String] value 115 | # Given value. 116 | # 117 | # @param [Fixnum, Array] base 118 | # Desired base. 119 | # 120 | # @return [Radix::Float, Radix::Integer] The passed value. 121 | def parse_string(value, base) 122 | digits = value.split(//) 123 | parse_array(digits, base) 124 | end 125 | 126 | ## 127 | # Take an Array in the form of [d1, d2, ..., DOT, d-1, d-2, ...] 128 | # and convert it to base ten, and store in @value. 129 | # 130 | # @param [Array] value Given value. 131 | # @param [Fixnum, Array] base Desired base. 132 | # 133 | # @return [Fixnum] Decimal version of passed array in base context. 134 | def parse_array(value, base) 135 | value = value.dup 136 | 137 | if value.first == '-' 138 | neg = true 139 | value.shift 140 | else 141 | neg = false 142 | end 143 | 144 | value = base_decode(value) 145 | 146 | ## raise an error if any digit is not less than base 147 | raise ArgumentError if value.any?{ |e| ::Numeric === e && base < e } 148 | 149 | v = decimal(value, base) 150 | 151 | neg ? -v : v # Returns negated v if value array.first == "-" 152 | end 153 | 154 | ## 155 | # Convert array of values of a different base to decimal. This handles 156 | # integer values. The method for Radix::Float is slighly different. 157 | # 158 | # @param [Array] digits 159 | # Representation of base values. 160 | # 161 | # @param [Fixnum, Array] base 162 | # The base to convert from. 163 | # 164 | # @return [Integer] The digits of base converted to decimal. 165 | def decimal(digits, base) 166 | e = digits.size - 1 167 | v = 0 168 | digits.each do |n| 169 | v += n.to_i * base**e 170 | e -= 1 171 | end 172 | v 173 | end 174 | 175 | ## 176 | # Map array of values to base encoding. If no encoding is defined 177 | # this simply returns the +digits+ unchanged. 178 | # 179 | # @param [Array] digits 180 | # 181 | # @return [Array] Encoded digits, or digits if @code is nil 182 | def base_encode(digits) 183 | return digits unless @code 184 | digits.map do |i| 185 | case i 186 | when '-', DOT, DIV 187 | i 188 | else 189 | code[i] 190 | end 191 | end 192 | end 193 | 194 | ## 195 | # Decode an encoded array. Defaults to BASE::B62 if @code is not set. 196 | # 197 | # @param [Array] digits The encoded characters. 198 | # 199 | # @return [Array] Decoded array. 200 | def base_decode(digits) 201 | #return digits unless code 202 | code = self.code || BASE::B62 203 | digits.map do |c| 204 | case c 205 | when '-', DOT, DIV 206 | c 207 | when ::Numeric 208 | c 209 | else 210 | code.index(c) # TODO: Could speed up with an reverse index. 211 | end 212 | end 213 | end 214 | 215 | end 216 | 217 | end 218 | -------------------------------------------------------------------------------- /demo/02_integer.md: -------------------------------------------------------------------------------- 1 | # Radix Integer 2 | 3 | Radix provides an Integer class for working with integers in various bases. 4 | 5 | require 'radix' 6 | 7 | ## Initialization 8 | 9 | Radix::Integer's initializer can accept either an Integer, String or 10 | Array as a value and an integer base. 11 | 12 | Give an integer value, it will automatically be converted to the base 13 | specified. 14 | 15 | check do |integer, base, digits| 16 | r = Radix::Integer.new(integer, base) 17 | r.digits.assert == digits 18 | end 19 | 20 | ok 8, 2, [1,0,0,0] 21 | ok 4, 2, [1,0,0] 22 | ok 8, 10, [8] 23 | ok 10, 10, [1, 0] 24 | ok 8, 16, [8] 25 | ok 16, 16, [1, 0] 26 | 27 | Where as a String value is taken to already be in the base given. 28 | 29 | ok "1000", 2, [1,0,0,0] 30 | ok "100", 2, [1,0,0] 31 | 32 | ok "8", 10, [8] 33 | ok "10", 10, [1, 0] 34 | ok "8", 16, [8] 35 | ok "10", 16, [1, 0] 36 | 37 | And an Array is also taken to be in the base given. 38 | 39 | ok %w[1 0 0 0], 2, [1,0,0,0] 40 | ok %w[ 1 0 0], 2, [1,0,0] 41 | 42 | ok %w[ 8], 10, [8] 43 | ok %w[1 0], 10, [1, 0] 44 | ok %w[ 8], 16, [8] 45 | ok %w[1 0], 16, [1, 0] 46 | 47 | Integers can also be negative, rather than positive. In each case 48 | just prepend the value with a minus sign. 49 | 50 | check do |integer, base, digits| 51 | r = Radix::Integer.new(integer, base) 52 | r.digits.assert == digits 53 | r.assert.negative? 54 | end 55 | 56 | ok -8, 2, ['-',1,0,0,0] 57 | ok "-1000", 2, ['-',1,0,0,0] 58 | ok %w[- 1 0 0 0], 2, ['-',1,0,0,0] 59 | 60 | If a value has a digit outside of the range of the base an ArgumentError 61 | will be raised. 62 | 63 | expect ArgumentError do 64 | Radix::Integer.new('9', 2) 65 | end 66 | 67 | Radix provides a convenience extension method to Integer, String and Array 68 | called #b, to more easily initialize a Radix numeric object. The method simply 69 | passes the receiver on to `Radix::Integer#new`. 70 | 71 | check do |integer, base, digits| 72 | r = integer.b(base) 73 | r.assert.is_a?(Radix::Integer) 74 | r.digits.assert == digits 75 | end 76 | 77 | ok 8, 2, [1,0,0,0] 78 | ok 4, 2, [1,0,0] 79 | 80 | ok "1000", 2, [1,0,0,0] 81 | ok "100", 2, [1,0,0] 82 | 83 | ok %w"1 0 0 0", 2, [1,0,0,0] 84 | ok %w"1 0 0", 2, [1,0,0] 85 | 86 | ## Conversion 87 | 88 | Radix integers can ve converted to other bases with the #convert method. 89 | 90 | b = "1000".b(2) 91 | d = b.convert(10) 92 | d.digits.assert == [8] 93 | 94 | We can convert a Radix::Integer to a regular base-10 Integer with the #to_i 95 | method. 96 | 97 | b = "1000".b(2) 98 | d = b.to_i 99 | d.assert == 8 100 | 101 | ## Equality 102 | 103 | Radix extend the Integer, String and Array classes with the #b method 104 | which simplifies the creation of Radix::Integer instances. The following 105 | return the equivalent instance of Radix::Integer. 106 | 107 | a = 8.b(2) 108 | b = "1000".b(2) 109 | c = [1, 0, 0, 0].b(2) 110 | 111 | a.assert == b 112 | b.assert == c 113 | c.assert == a 114 | 115 | a.assert == 8 116 | b.assert == 8 117 | c.assert == 8 118 | 119 | More stringent equality can be had from #eql?, in which the other integer 120 | must be a Radix::Integer too. 121 | 122 | a.assert.eql?(b) 123 | a.refute.eql?(8) 124 | 125 | ## Operations 126 | 127 | Radix::Integer supports all the usual mathematical operators. 128 | 129 | ### Addition 130 | 131 | check do |a, b, x| 132 | (a + b).assert == x 133 | end 134 | 135 | ok "1000".b(2), "0010".b(2), "1010".b(2) 136 | ok "1000".b(2), "2".b(8), "1010".b(2) 137 | ok "1000".b(2), "2".b(8), "10".b(10) 138 | 139 | A more complex example. 140 | 141 | x = "AZ42".b(62) + "54".b(10) 142 | x.assert == "2518124".b(10) 143 | x.assert == 2518124 144 | 145 | Adding negative integers will, of course, be akin to subtraction. 146 | 147 | ok "1000".b(2), "-0010".b(2), "110".b(2) 148 | ok "1000".b(2), "-2".b(8), "110".b(2) 149 | ok "1000".b(2), "-2".b(8), "6".b(10) 150 | 151 | ok "-1000".b(2), "0010".b(2), "-110".b(2) 152 | ok "-1000".b(2), "2".b(8), "-110".b(2) 153 | ok "-1000".b(2), "2".b(8), "-6".b(10) 154 | 155 | ok "-1000".b(2), "-0010".b(2), "-1010".b(2) 156 | ok "-1000".b(2), "-2".b(8), "-1010".b(2) 157 | ok "-1000".b(2), "-2".b(8), "-10".b(10) 158 | 159 | ### Subtraction 160 | 161 | check do |a, b, x| 162 | (a - b).assert == x 163 | end 164 | 165 | ok "1000".b(2), "10".b(2), "0110".b(2) 166 | ok "1000".b(2), "2".b(8), "0110".b(2) 167 | ok "1000".b(2), "2".b(8), "6".b(8) 168 | ok "1000".b(2), "2".b(8), "6".b(10) 169 | 170 | A more complex example. 171 | 172 | x = "AZ42".b(62) - "54".b(10) 173 | x.assert == "2518016".b(10) 174 | x.assert == 2518016 175 | 176 | ### Multiplication 177 | 178 | check do |a, b, x| 179 | (a * b).assert == x 180 | end 181 | 182 | ok "1000".b(2), "10".b(2), "10000".b(2) 183 | ok "1000".b(2), "2".b(8), "10000".b(2) 184 | ok "1000".b(2), "2".b(8), "20".b(8) 185 | ok "1000".b(2), "2".b(8), "16".b(10) 186 | 187 | A more complex example. 188 | 189 | x = "Z42".b(62) * "4".b(10) 190 | x.assert == "539160".b(10) 191 | x.assert == 539160 192 | 193 | ### Division 194 | 195 | check do |a, b, x| 196 | (a / b).assert == x 197 | end 198 | 199 | ok "1000".b(2), "10".b(2), "100".b(2) 200 | ok "1000".b(2), "2".b(8), "100".b(2) 201 | ok "1000".b(2), "2".b(8), "4".b(8) 202 | ok "1000".b(2), "2".b(8), "4".b(10) 203 | 204 | A more complex example. 205 | 206 | x = "AZ42".b(62) / "54".b(10) 207 | x.assert == "46630".b(10) 208 | x.assert == 46630 209 | 210 | ### Power 211 | 212 | check do |a, b, x| 213 | (a ** b).assert == x 214 | end 215 | 216 | ok "1000".b(2), "10".b(2), 64 217 | 218 | ### Modulo 219 | 220 | check do |a, b, x| 221 | (a % b).assert == x 222 | end 223 | 224 | ok "1000".b(2), "10".b(2), 0 225 | ok "1000".b(2), "11".b(2), 2 226 | 227 | ### Bitwise Shift 228 | 229 | check do |a, b, x| 230 | (a << b).assert == x 231 | end 232 | 233 | ok "10".b(2), "10".b(2), "1000".b(2) 234 | ok "10".b(2), 2, "1000".b(2) 235 | ok "10".b(2), 2, 8 236 | 237 | ### Bitwise AND 238 | 239 | check do |a, b, x| 240 | (a & b).assert == x 241 | end 242 | 243 | ok "1010".b(2), "10".b(2), "10".b(2) 244 | ok "1010".b(2), "2".b(8), "10".b(2) 245 | 246 | ## Coerce 247 | 248 | When a Radix::Integer is the operand in an operation against a regular 249 | Ruby Integer, the calculation should still work via #coerce. 250 | 251 | check do |a, b, x| 252 | (a + b).assert == x 253 | end 254 | 255 | ok 10, "10".b(2), "12".b(10) 256 | 257 | -------------------------------------------------------------------------------- /lib/radix/base.rb: -------------------------------------------------------------------------------- 1 | module Radix 2 | 3 | ## 4 | # Namespace for common bases defined as reusable constants. 5 | # 6 | module BASE 7 | # Array of chars 0 - 9 8 | B10 = ('0'..'9').to_a 9 | # Array of chars 0 - 9 + X + E 10 | B12 = B10 + ['X', 'E'] 11 | # Array of chars 0 - 9 + A - F 12 | B16 = B10 + ('A'..'F').to_a 13 | # Array of chars 0 - 9 + A - Z 14 | B36 = B10 + ('A'..'Z').to_a 15 | # Array of chars 0 - 9 + A - Z + a - x 16 | B60 = B36 + ('a'..'x').to_a 17 | # Array of chars 0 - 9 + A - Z + a - z 18 | B62 = B36 + ('a'..'z').to_a 19 | # Array of chars 0 - 9 + a - f 20 | HEX = B10 + ('a'..'f').to_a 21 | end 22 | 23 | ## 24 | # Radix::Base is a functional model of a base system that can be used for 25 | # number conversions. 26 | # 27 | # Radix::Base is the original Radix API. But with the advent of v2.0 28 | # and the new Integer and Float classes, it is essentially deprecated. 29 | # 30 | # @example Convert any base 31 | # b10 = Radix::Base.new(10) 32 | # b10.convert_base([100, 10], 256) 33 | # #=> [2,5,6,1,0] 34 | # 35 | # @example Convert to string notation upto base 62 36 | # b10.convert("10", 62) #=> "62" 37 | # 38 | # @example Odd notations 39 | # b10 = Radix::Base.new(%w{Q W E R T Y U I O U}) 40 | # b10.convert("FF", 16) #=> "EYY" 41 | # 42 | # @!attribute [r] chars 43 | # @return [Array] The ASCII character set in use. 44 | # @!attribute [r] base 45 | # @return [Fixnum] The base level in use. 46 | # @!attribute [r] values 47 | # @example Testing base hash default values. 48 | # > test = Radix::Base.new(36) 49 | # > test.values["F"] 50 | # 15 51 | # > test.values["5"] 52 | # 5 53 | # > test.values["Y"] 54 | # 34 55 | # > test.values["YY"] 56 | # nil # Fails because "YY" is not a key in the +values+ hash. 57 | # @return [Hash{String=>Fixnum}] 58 | # A hash of characters and their respective value. 59 | class Base 60 | 61 | ## 62 | # The characters for this base level. 63 | # 64 | # @return [Array] The ASCII character set in use. 65 | attr :chars 66 | 67 | ## 68 | # The base of this instance. 69 | # 70 | # @return [Fixnum] The base level in use. 71 | attr :base 72 | 73 | ## 74 | # Hash of characters and values. 75 | # 76 | # @return [Hash{String=>Fixnum}] 77 | # A hash of characters and their respective value. 78 | attr :values 79 | 80 | ## 81 | # New Radix using +chars+ representation. 82 | # 83 | # @param [Array, Numeric] chars 84 | # Array of string representation of number values of the base 85 | # or a Numeric of the Base level. 86 | # 87 | # @return [void] 88 | def initialize(chars=BASE::B62) 89 | if ::Numeric === chars 90 | chars = BASE::B62[0...chars] 91 | end 92 | @chars = chars.map{ |c| c.to_s } 93 | @base = @chars.size 94 | @values = Hash[*(0...@base).map { |i| [ @chars[i], i ] }.flatten] 95 | end 96 | 97 | ## 98 | # Convert a value of given radix_base to that of the base instance. 99 | # 100 | # @param [String, Numeric, Array] number 101 | # The value in "radix_base" context. 102 | # 103 | # @param [Radix::Base, Numeric] radix_base 104 | # Numeric for the radix or instance of Radix::Base. 105 | # 106 | # @example Convert Testing (Binary, Decimal, Hex) 107 | # > b = Radix::Base.new(2) 108 | # > d = Radix::Base.new(10) 109 | # > h = Radix::Base.new(16) 110 | # > d.convert("A", h) 111 | # "10" 112 | # > h.convert("A", d) 113 | # TypeError 114 | # > h.convert(10, d) 115 | # "A" 116 | # > h.convert(10, 10) 117 | # "A" 118 | # > b.convert(10, d) 119 | # "1010" 120 | # > b.convert(10, h) 121 | # "10000" 122 | # 123 | # @return [String] representation of "number" in self.base level. 124 | def convert(number, radix_base) 125 | radix_base = Radix::Base.new(radix_base) unless Radix::Base === radix_base 126 | case number 127 | when ::String, ::Numeric 128 | digits = number.to_s.split(//) 129 | else 130 | digits = number 131 | end 132 | 133 | # decode the digits 134 | digits = digits.map{ |digit| radix_base.values[digit] } 135 | 136 | # THINK: Is best way to check for base out of bounds? 137 | raise TypeError if digits.any?{ |digit| digit.nil? } 138 | 139 | digits = Radix.convert_base(digits, radix_base.base, base) 140 | digits = digits.map{ |digit| chars[digit] } 141 | digits.join 142 | end 143 | 144 | ## 145 | # Convert any base to any other base, using array of Fixnum's. Indexes of 146 | # the array correspond to values for each column of the number in from_base 147 | # 148 | # @param [Array] digits 149 | # Array of values for each digit of source base. 150 | # 151 | # @param [Fixnum] from_base 152 | # Source Base 153 | # 154 | # @param [Fixnum] to_base 155 | # Destination Base 156 | # 157 | # @return [String] The value of digits in from_base converted as to_base. 158 | def convert_base(digits, from_base, to_base) 159 | bignum = 0 160 | digits.each { |digit| bignum = bignum * from_base + digit } 161 | converted = [] 162 | until bignum.zero? 163 | bignum, digit = bignum.divmod(to_base) 164 | converted.push(digit) 165 | end 166 | converted << 0 if converted.empty? # THINK: correct? 167 | converted.reverse 168 | end 169 | 170 | ## 171 | # Encode a string in the radix. 172 | # 173 | # @param [String] byte_string 174 | # String value in this base. 175 | # 176 | # @return [String] Encoded string from this Base. 177 | def encode(byte_string) 178 | digits = byte_string.unpack("C*") 179 | digits = Radix.convert_base(digits, 256, base) 180 | digits.map{ |d| @chars[d] }.join 181 | end 182 | 183 | ## 184 | # Decode a string that was previously encoded in the radix. 185 | # 186 | # @param [String] encoded 187 | # Encoded string from this Base. 188 | # 189 | # @return [String] Decoded string of value from this base. 190 | def decode(encoded) 191 | digits = encoded.split(//).map{ |c| @values[c] } 192 | Radix.convert_base(digits, base, 256).pack("C*") 193 | end 194 | 195 | end 196 | 197 | ## 198 | # Convert a number of from_base as to_base. 199 | # 200 | # @param [String, Numeric, Array] number 201 | # The value in context of "radix_base". 202 | # 203 | # @param [Fixnum, Radix::Base] from_base 204 | # Source Base 205 | # 206 | # @param [Fixnum, Radix::Base] to_base 207 | # Destination Base 208 | # 209 | # @return [String] 210 | # The value of `digits` in `from_base` converted into `to_base`. 211 | def self.convert(number, from_base, to_base) 212 | from_base = Radix::Base.new(from_base) unless Radix::Base === from_base 213 | to_base = Radix::Base.new(to_base) unless Radix::Base === to_base 214 | to_base.convert(number, from_base) 215 | end 216 | 217 | ## 218 | # Convert any base to any other base, using array of Fixnum's. Indexes of 219 | # the array correspond to values for each column of the number in from_base 220 | # 221 | # @param [Array] digits 222 | # Array of values for each digit of source base. 223 | # 224 | # @param [Fixnum] from_base 225 | # Source Base 226 | # 227 | # @param [Fixnum] to_base 228 | # Destination Base 229 | # 230 | # @return [String] The value of digits in from_base converted as to_base. 231 | def self.convert_base(digits, from_base, to_base) 232 | bignum = 0 233 | digits.each { |digit| bignum = bignum * from_base + digit } 234 | converted = [] 235 | until bignum.zero? 236 | bignum, digit = bignum.divmod(to_base) 237 | converted.push(digit) 238 | end 239 | converted << 0 if converted.empty? # THINK: correct? 240 | converted.reverse 241 | end 242 | 243 | end 244 | 245 | -------------------------------------------------------------------------------- /demo/03_float.md: -------------------------------------------------------------------------------- 1 | # Radix Float 2 | 3 | Radix provides a Float class for working with rational numbers in various bases. 4 | Actually Radix's implementation of Float is a fixed point, not a 5 | *floating point*. 6 | 7 | require 'radix' 8 | 9 | D = Radix::DOT 10 | 11 | ## Initialization 12 | 13 | Radix::Float's initializer can accept either an Integer, Float, String or 14 | Array as a value and an integer base. 15 | 16 | Give a float value, it will automatically be converted to the base 17 | specified. 18 | 19 | check do |float, base, digits| 20 | r = Radix::Float.new(float, base) 21 | r.digits.assert == digits 22 | end 23 | 24 | ok 8.5, 2, [1,0,0,0,D,1] 25 | ok 4.5, 2, [ 1,0,0,D,1] 26 | 27 | ok 8.1, 10, [ 8,D,1] 28 | ok 10.2, 10, [1,0,D,2] 29 | #ok 8.1, 16, [ 8,D,1] 30 | #ok 16.1, 16, [1,0,D,1] 31 | 32 | Give an integer value, it will automatically be converted to the base 33 | specified and given a fraction part set to zero. 34 | 35 | check do |float, base, digits| 36 | r = Radix::Float.new(float, base) 37 | r.digits.assert == digits 38 | end 39 | 40 | ok 8, 2, [1,0,0,0,D,0] 41 | ok 4, 2, [ 1,0,0,D,0] 42 | 43 | ok 8, 10, [ 8,D,0] 44 | ok 10, 10, [1,0,D,0] 45 | ok 8, 16, [ 8,D,0] 46 | ok 16, 16, [1,0,D,0] 47 | 48 | Given a float, the same will occur. 49 | 50 | ok 8.0, 2, [1,0,0,0,D,0] 51 | ok 4.0, 2, [ 1,0,0,D,0] 52 | 53 | ok 8.0, 10, [ 8,D,0] 54 | ok 10.0, 10, [1,0,D,0] 55 | ok 8.0, 16, [ 8,D,0] 56 | ok 16.0, 16, [1,0,D,0] 57 | 58 | Where as a String value is taken to already be in the base given. 59 | 60 | ok "1000", 2, [1,0,0,0,D,0] 61 | ok "100", 2, [ 1,0,0,D,0] 62 | 63 | ok "8", 10, [ 8,D,0] 64 | ok "10", 10, [1,0,D,0] 65 | ok "8", 16, [ 8,D,0] 66 | ok "10", 16, [1,0,D,0] 67 | 68 | ok "1000.0", 2, [1,0,0,0,D,0] 69 | ok "100.0", 2, [ 1,0,0,D,0] 70 | 71 | ok "8.0", 10, [ 8,D,0] 72 | ok "10.0", 10, [1,0,D,0] 73 | ok "8.0", 16, [ 8,D,0] 74 | ok "10.0", 16, [1,0,D,0] 75 | 76 | And an Array is also taken to be in the base given. 77 | 78 | ok %w[1 0 0 0], 2, [1,0,0,0,D,0] 79 | ok %w[ 1 0 0], 2, [ 1,0,0,D,0] 80 | 81 | ok %w[ 8], 10, [ 8,D,0] 82 | ok %w[1 0], 10, [1,0,D,0] 83 | ok %w[ 8], 16, [ 8,D,0] 84 | ok %w[1 0], 16, [1,0,D,0] 85 | 86 | Passing in an Array with a fraction part, either the DOT constant can be used, 87 | which is simply the symbol :'.', or the string '.' can be used. 88 | 89 | ok %w[1 0 0 0 . 0], 2, [1,0,0,0,D,0] 90 | ok %w[ 1 0 0 . 0], 2, [ 1,0,0,D,0] 91 | 92 | ok %w[ 8 . 0], 10, [ 8,D,0] 93 | ok %w[1 0 . 0], 10, [1,0,D,0] 94 | ok %w[ 8 . 0], 16, [ 8,D,0] 95 | ok %w[1 0 . 0], 16, [1,0,D,0] 96 | 97 | Integers can also be negative, rather than positive. In each case 98 | just prepend the value with a minus sign. 99 | 100 | check do |float, base, digits| 101 | r = Radix::Float.new(float, base) 102 | r.digits.assert = digits 103 | r.assert.negative? 104 | end 105 | 106 | ok( -8, 2, ['-',1,0,0,0,D,0]) 107 | ok( "-1000", 2, ['-',1,0,0,0,D,0]) 108 | ok( %w[- 1 0 0 0], 2, ['-',1,0,0,0,D,0]) 109 | 110 | If a value has a digit outside of the range of the base an ArgumentError 111 | will be raised. 112 | 113 | expect ArgumentError do 114 | Radix::Float.new('9', 2) 115 | end 116 | 117 | Radix provides a convenience extension method to Integer, String and Array 118 | called #b, to more easily initialize a Radix numeric object. The method simply 119 | passes the receiver on to `Radix::Integer#new`. 120 | 121 | check do |float, base, digits| 122 | r = float.b(base) 123 | r.assert.is_a?(Radix::Float) 124 | r.digits.assert = digits 125 | end 126 | 127 | ok 8.0, 2, [1,0,0,0,D,0] 128 | ok 4.0, 2, [ 1,0,0,D,0] 129 | 130 | ok "1000.0", 2, [1,0,0,0,D,0] 131 | ok "100.0", 2, [ 1,0,0,D,0] 132 | 133 | ok %w"1 0 0 0 . 0", 2, [1,0,0,0,D,0] 134 | ok %w"1 0 0 . 0", 2, [ 1,0,0,D,0] 135 | 136 | ## Conversion 137 | 138 | Radix integers can ve converted to other bases with the #convert method. 139 | 140 | b = "1000.0".b(2) 141 | d = b.convert(10) 142 | d.digits.assert == [8,D,0] 143 | 144 | We can convert a Radix::Float to a regular base-10 Float with the #to_f 145 | method. 146 | 147 | b = "1000.0".b(2) 148 | d = b.to_f 149 | d.assert == 8.0 150 | 151 | We can convert a Radix::Float to a regular base-10 Integer with the #to_i 152 | method. 153 | 154 | b = "1000.0".b(2) 155 | d = b.to_i 156 | d.assert == 8 157 | 158 | ### Equality 159 | 160 | Radix extend the Integer, String and Array classes with the #b method 161 | which simplifies the creation of Radix::Float instances. The following 162 | return the equivalent instance of Radix::Float. 163 | 164 | a = 8.0.b(2) 165 | b = "1000.0".b(2) 166 | c = [1,0,0,0,'.',0].b(2) 167 | 168 | a.assert = b 169 | b.assert = c 170 | c.assert = a 171 | 172 | a.assert = 8.0 173 | b.assert = 8.0 174 | c.assert = 8.0 175 | 176 | More stringent equality can be had from #eql?, in which the other integer 177 | must be a Radix::Integer too. 178 | 179 | a.assert.eql?(b) 180 | a.refute.eql?(8.0) 181 | 182 | ## Operations 183 | 184 | Radix::Float supports all the usual mathematical operators. 185 | 186 | ### Addition 187 | 188 | check do |a, b, x| 189 | (a + b).assert = x 190 | end 191 | 192 | ok "1000.0".b(2), "0010.0".b(2), "1010.0".b(2) 193 | ok "1000.0".b(2), "2.0".b(8), "1010.0".b(2) 194 | ok "1000.0".b(2), "2.0".b(8), "10.0".b(10) 195 | 196 | A more complex example. 197 | 198 | x = "AZ42.0".b(62) + "54.0".b(10) 199 | x.assert == "2518124.0".b(10) 200 | x.assert == 2518124.0 201 | 202 | Adding negative integers will, of course, be akin to subtraction. 203 | 204 | ok "1000.0".b(2), "-0010".b(2), "110.0".b(2) 205 | ok "1000.0".b(2), "-2".b(8), "110.0".b(2) 206 | ok "1000.0".b(2), "-2".b(8), "6.0".b(10) 207 | 208 | ok "-1000.0".b(2), "0010".b(2), "-110.0".b(2) 209 | ok "-1000.0".b(2), "2".b(8), "-110.0".b(2) 210 | ok "-1000.0".b(2), "2".b(8), "-6.0".b(10) 211 | 212 | ok "-1000.0".b(2), "-0010".b(2), "-1010.0".b(2) 213 | ok "-1000.0".b(2), "-2".b(8), "-1010.0".b(2) 214 | ok "-1000.0".b(2), "-2".b(8), "-10.0".b(10) 215 | 216 | ### Subtraction 217 | 218 | check do |a, b, x| 219 | (a - b).assert == x 220 | end 221 | 222 | ok "1000.0".b(2), "10".b(2), "110.0".b(2) 223 | ok "1000.0".b(2), "2".b(8), "110.0".b(2) 224 | ok "1000.0".b(2), "2".b(8), "6.0".b(8) 225 | ok "1000.0".b(2), "2".b(8), "6.0".b(10) 226 | 227 | A more complex example. 228 | 229 | x = "AZ42.0".b(62) - "54".b(10) 230 | x.assert == "2518016.0".b(10) 231 | x.assert == 2518016.0 232 | 233 | ### Multiplication 234 | 235 | check do |a, b, x| 236 | (a * b).assert = x 237 | end 238 | 239 | ok "1000.0".b(2), "10".b(2), "10000.0".b(2) 240 | ok "1000.0".b(2), "2".b(8), "10000.0".b(2) 241 | ok "1000.0".b(2), "2".b(8), "20.0".b(8) 242 | ok "1000.0".b(2), "2".b(8), "16.0".b(10) 243 | 244 | A more complex example. 245 | 246 | x = "Z42.0".b(62) * "4.0".b(10) 247 | x.assert == "539160.0".b(10) 248 | x.assert == 539160.0 249 | 250 | ### Division 251 | 252 | check do |a, b, x| 253 | (a / b).assert = x 254 | end 255 | 256 | ok "1000.0".b(2), "10".b(2), "100.0".b(2) 257 | ok "1000.0".b(2), "2".b(8), "100.0".b(2) 258 | ok "1000.0".b(2), "2".b(8), "4.0".b(8) 259 | ok "1000.0".b(2), "2".b(8), "4.0".b(10) 260 | 261 | A more complex example. 262 | 263 | x = "AZ40.0".b(62) / "62.0".b(10) 264 | x.assert == "40614.0".b(10) 265 | x.assert == 40614.0 266 | 267 | ### Power 268 | 269 | check do |a, b, x| 270 | (a ** b).assert == x 271 | end 272 | 273 | ok "1000.0".b(2), "10.0".b(2), 64.0 274 | 275 | ### Modulo 276 | 277 | check do |a, b, x| 278 | (a % b).assert == x 279 | end 280 | 281 | ok "1000.0".b(2), "10".b(2), 0 282 | ok "1000.0".b(2), "11".b(2), 2 283 | 284 | ## Coerce 285 | 286 | When a Radix::Integer is the operand in an operation against a regular 287 | Ruby Integer, the calculation should still work via #coerce. 288 | 289 | check do |a, b, x| 290 | (a + b).assert == x 291 | end 292 | 293 | ok 10.0, "10".b(2), "12".b(10) 294 | 295 | -------------------------------------------------------------------------------- /lib/radix/rational.rb: -------------------------------------------------------------------------------- 1 | require 'rational' 2 | require 'radix/numeric' 3 | 4 | module Radix 5 | 6 | ## 7 | # Represents rational numbers. 8 | # 9 | # @!attribute [r] value 10 | # @return [Rational] Ruby Rational representation of self in Base-10. 11 | # @!attribute [r] base 12 | # @return [Fixnum] The base level of Rational instance. 13 | # @!attribute [r] code 14 | # @return [Array, nil] Substitution chars or nil if default. 15 | class Rational < Numeric 16 | 17 | ## 18 | # Alternative to #new. 19 | # 20 | # @return [Radix::Rational] 21 | def self.[](n,d=nil,b=10) 22 | new(n,d,b) 23 | end 24 | 25 | ## 26 | # Stores the Rational value in Base-10. 27 | # 28 | # @return [Rational] 29 | attr :value 30 | 31 | ## 32 | # Base of the number. 33 | # 34 | # @return [Fixnum] The base level of Rational instance. 35 | attr :base 36 | 37 | ## 38 | # Base encoding table. 39 | # 40 | # @return [Array, nil] Substitution chars or nil if default. 41 | attr :code 42 | 43 | private 44 | 45 | ## 46 | # Create a new Radix::Rational instance. 47 | # @example 48 | # Rational.new(, , ) 49 | # Rational.new(, ) 50 | # @param [Radix::Rational, ::Rational, Fixnum] numerator A rational number 51 | # or a fixnum for the numerator of a new Rational. 52 | # @param [Fixnum] denominator Denominator for new Rational. 53 | # @param [Fixnum] base Base level for new Rational. 54 | # @return [void] 55 | def initialize(numerator, denominator=nil, base=10) 56 | case numerator 57 | when ::Rational, Rational 58 | ratn = numerator 59 | base = denominator 60 | @value = Rational(ratn.numerator, ratn.denominator) 61 | else 62 | n = parse_value(numerator, base) 63 | d = parse_value(denominator, base) 64 | @value = Rational(n, d) 65 | end 66 | @base, @code = parse_base(base) 67 | end 68 | 69 | ## 70 | # Parses String, Array, Radix::Float, Radix::Integer or Ruby numerics and 71 | # returns the decimal value from base context for storage in @value. 72 | # 73 | # @param [Fixnum] base 74 | def parse_value(value, base) 75 | case value 76 | when Float, Integer # Radix 77 | parse_numeric(value.to_i, base) 78 | when ::Array 79 | parse_array(value, base) 80 | when ::String 81 | parse_string(value, base) 82 | when ::Numeric 83 | parse_numeric(value.to_i, base) 84 | end 85 | end 86 | 87 | public 88 | 89 | ## 90 | # The numerator. 91 | # 92 | # @return [Fixnum] The numerator of Radix::Rational 93 | def numerator 94 | @value.numerator 95 | end 96 | 97 | ## 98 | # The denominator. 99 | # 100 | # @return [Fixnum] The denominator of Radix::Rational 101 | def denominator 102 | @value.denominator 103 | end 104 | 105 | ## 106 | # Is the value negative? 107 | # 108 | # @return [Boolean] Returns true if value < 0. 109 | def negative? 110 | value < 0 111 | end 112 | 113 | ## 114 | # Convert rational to new base. 115 | # 116 | # @param [Fixnum] base Desired base. 117 | # @return [Radix::Rational] Returns new Radix::Rational in passed base. 118 | def convert(base) 119 | self.class.new(numerator, denominator, base) 120 | end 121 | 122 | ## 123 | # Convert to rational. 124 | # 125 | # @return [Rational] Returns the value. 126 | def to_r 127 | value 128 | end 129 | 130 | ## 131 | # Convert to Float by dividing the numerator by the denominator. 132 | # 133 | # Returns the converted value. [Float] 134 | def to_f 135 | numerator.to_f / denominator.to_f 136 | end 137 | 138 | ## 139 | # Convert to Integer by converting to Float first then 140 | # appling #to_i to the float. 141 | # 142 | # Returns the converted value. [Integer] 143 | def to_i 144 | to_f.to_i 145 | end 146 | 147 | ## 148 | # Translate value into an array of places. Uses current base unless 149 | # specified. 150 | # 151 | # @param [Fixnum] base Desired base. 152 | # @return [Array] Array of place values. 153 | def to_a(base=nil) 154 | if base 155 | convert(base).digits_encoded 156 | else 157 | digits_encoded 158 | end 159 | end 160 | 161 | ## 162 | # Convert the value into a string representation of the given base. 163 | # 164 | # @param [Fixnum] base The base to convert. 165 | # @param [String] divider The string char(s) to divided with. 166 | # @return [String] Translated value. 167 | def to_s(base=nil, divider=nil) 168 | divider = divider.to_s if divider 169 | if base 170 | convert(base).to_s(nil, divider) 171 | else 172 | if code 173 | digits_encoded.join(divider) 174 | else 175 | if @base > 10 176 | digits.join(divider || DIVIDER) 177 | else 178 | digits.join(divider) 179 | end 180 | end 181 | end 182 | end 183 | 184 | ## 185 | # Simple equality requires equal values only. 186 | # 187 | # @todo This may need improvement to be more percise. 188 | # @param [#to_f] other The value to compare to. 189 | # @return [Boolean] True if equal values. 190 | def ==(other) 191 | a, b = self.to_f, other.to_f 192 | a == b 193 | end 194 | 195 | ## 196 | # Returns an irreducible version of self in current base. 197 | # 198 | # @todo Is this method neccesary since @value is a Ruby Rational and 199 | # therefore already irreducible? 200 | # @return [Radix::Rational] 201 | def reduce 202 | self.class.new(Rational(numerator, denominator), base) 203 | end 204 | 205 | ## 206 | # Returns an array representation of the numerator and denominator with 207 | # each column's value. 208 | # 209 | # @return [Array] Values per column of @base as array. 210 | # Prepended with "-" if negative. 211 | def digits 212 | n = base_conversion(numerator, base) 213 | d = base_conversion(denominator, base) 214 | i = n + ['/'] + d 215 | i.unshift('-') if negative? 216 | i 217 | end 218 | 219 | ## 220 | # Returns digits, or coded version of digits if @code. 221 | # 222 | # @return [Array] Values per column of @base as array. 223 | # Prepended with "-" if negative. Or encoded version if @code is 224 | # defined. 225 | def digits_encoded 226 | base_encode(digits) 227 | end 228 | 229 | ## 230 | # Creates a string representation of self. 231 | # 232 | # @return [String] String rep of self.digits and @base. 233 | def inspect 234 | "#{digits.join(' ')} (#{base})" 235 | end 236 | 237 | ## 238 | # Returns new Radix::Rational of passed value in base-10 and self as an 239 | # Array. 240 | # 241 | # @return [Array<(Radix::Rational, Radix::Rational)>] 242 | def coerce(value) 243 | [Radix::Rational.new(value), self] 244 | end 245 | 246 | private 247 | 248 | ## 249 | # Perform passed arithmetic operation. 250 | # 251 | # @param [#to_r] other 252 | # @return [Radix::Rational] Returns the result of the operation in @base. 253 | def operation(op, other) 254 | x = value.__send__(op, other.to_r) 255 | self.class.new(x, base) 256 | end 257 | 258 | ## 259 | # Perform base conversion. 260 | # 261 | # @return [Array] Array of places. 262 | def base_conversion(value, base) 263 | #if value < 0 264 | # @negative, value = true, value.abs 265 | #end 266 | i = value.abs 267 | 268 | a = [] 269 | while i > 0 270 | i, r = i.divmod(base) 271 | a << r 272 | end 273 | 274 | a << 0 if a.empty? 275 | 276 | a.reverse 277 | end 278 | 279 | end 280 | 281 | end 282 | 283 | class ::Array 284 | ## 285 | # Adds convenience method for creating a Radix::Rational. 286 | # 287 | # @todo Keep #br? Or find another way? 288 | # @return [Radix::Rational] 289 | def br(base=nil) 290 | args = dup 291 | args << base if base 292 | Radix::Rational.new(*args) 293 | end 294 | end 295 | 296 | if RUBY_VERSION < '1.9' 297 | class ::Float 298 | 299 | ## 300 | # Adds a #to_r method to pre-1.9 ruby Rationals. 301 | # 302 | # @return [Rational] 303 | def to_r 304 | n, f = to_s.split('.') 305 | d = (10 ** f.size).to_i 306 | n = (n.to_i * d) + f.to_i 307 | Rational(n, d) 308 | end 309 | end 310 | end 311 | 312 | -------------------------------------------------------------------------------- /.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'yaml' 4 | require 'pathname' 5 | 6 | module Indexer 7 | 8 | # Convert index data into a gemspec. 9 | # 10 | # Notes: 11 | # * Assumes all executables are in bin/. 12 | # * Does not yet handle default_executable setting. 13 | # * Does not yet handle platform setting. 14 | # * Does not yet handle required_ruby_version. 15 | # * Support for rdoc entries is weak. 16 | # 17 | class GemspecExporter 18 | 19 | # File globs to include in package --unless a manifest file exists. 20 | FILES = ".index .yardopts alt bin data demo ext features lib man spec test try* [A-Z]*.*" unless defined?(FILES) 21 | 22 | # File globs to omit from FILES. 23 | OMIT = "Config.rb" unless defined?(OMIT) 24 | 25 | # Standard file patterns. 26 | PATTERNS = { 27 | :root => '{.index,Gemfile}', 28 | :bin => 'bin/*', 29 | :lib => 'lib/{**/}*', #.rb', 30 | :ext => 'ext/{**/}extconf.rb', 31 | :doc => '*.{txt,rdoc,md,markdown,tt,textile}', 32 | :test => '{test,spec}/{**/}*.rb' 33 | } unless defined?(PATTERNS) 34 | 35 | # For which revision of indexer spec is this converter intended? 36 | REVISION = 2013 unless defined?(REVISION) 37 | 38 | # 39 | def self.gemspec 40 | new.to_gemspec 41 | end 42 | 43 | # 44 | attr :metadata 45 | 46 | # 47 | def initialize(metadata=nil) 48 | @root_check = false 49 | 50 | if metadata 51 | root_dir = metadata.delete(:root) 52 | if root_dir 53 | @root = root_dir 54 | @root_check = true 55 | end 56 | metadata = nil if metadata.empty? 57 | end 58 | 59 | @metadata = metadata || YAML.load_file(root + '.index') 60 | 61 | if @metadata['revision'].to_i != REVISION 62 | warn "This gemspec exporter was not designed for this revision of index metadata." 63 | end 64 | end 65 | 66 | # 67 | def has_root? 68 | root ? true : false 69 | end 70 | 71 | # 72 | def root 73 | return @root if @root || @root_check 74 | @root_check = true 75 | @root = find_root 76 | end 77 | 78 | # 79 | def manifest 80 | return nil unless root 81 | @manifest ||= Dir.glob(root + 'manifest{,.txt}', File::FNM_CASEFOLD).first 82 | end 83 | 84 | # 85 | def scm 86 | return nil unless root 87 | @scm ||= %w{git hg}.find{ |m| (root + ".#{m}").directory? }.to_sym 88 | end 89 | 90 | # 91 | def files 92 | return [] unless root 93 | @files ||= \ 94 | if manifest 95 | File.readlines(manifest). 96 | map{ |line| line.strip }. 97 | reject{ |line| line.empty? || line[0,1] == '#' } 98 | else 99 | list = [] 100 | Dir.chdir(root) do 101 | FILES.split(/\s+/).each do |pattern| 102 | list.concat(glob(pattern)) 103 | end 104 | OMIT.split(/\s+/).each do |pattern| 105 | list = list - glob(pattern) 106 | end 107 | end 108 | list 109 | end.select{ |path| File.file?(path) }.uniq 110 | end 111 | 112 | # 113 | def glob_files(pattern) 114 | return [] unless root 115 | Dir.chdir(root) do 116 | Dir.glob(pattern).select do |path| 117 | File.file?(path) && files.include?(path) 118 | end 119 | end 120 | end 121 | 122 | def patterns 123 | PATTERNS 124 | end 125 | 126 | def executables 127 | @executables ||= \ 128 | glob_files(patterns[:bin]).map do |path| 129 | File.basename(path) 130 | end 131 | end 132 | 133 | def extensions 134 | @extensions ||= \ 135 | glob_files(patterns[:ext]).map do |path| 136 | File.basename(path) 137 | end 138 | end 139 | 140 | def name 141 | metadata['name'] || metadata['title'].downcase.gsub(/\W+/,'_') 142 | end 143 | 144 | def homepage 145 | page = ( 146 | metadata['resources'].find{ |r| r['type'] =~ /^home/i } || 147 | metadata['resources'].find{ |r| r['name'] =~ /^home/i } || 148 | metadata['resources'].find{ |r| r['name'] =~ /^web/i } 149 | ) 150 | page ? page['uri'] : false 151 | end 152 | 153 | def licenses 154 | metadata['copyrights'].map{ |c| c['license'] }.compact 155 | end 156 | 157 | def require_paths 158 | paths = metadata['paths'] || {} 159 | paths['load'] || ['lib'] 160 | end 161 | 162 | # 163 | # Convert to gemnspec. 164 | # 165 | def to_gemspec 166 | if has_root? 167 | Gem::Specification.new do |gemspec| 168 | to_gemspec_data(gemspec) 169 | to_gemspec_paths(gemspec) 170 | end 171 | else 172 | Gem::Specification.new do |gemspec| 173 | to_gemspec_data(gemspec) 174 | to_gemspec_paths(gemspec) 175 | end 176 | end 177 | end 178 | 179 | # 180 | # Convert pure data settings. 181 | # 182 | def to_gemspec_data(gemspec) 183 | gemspec.name = name 184 | gemspec.version = metadata['version'] 185 | gemspec.summary = metadata['summary'] 186 | gemspec.description = metadata['description'] 187 | 188 | metadata['authors'].each do |author| 189 | gemspec.authors << author['name'] 190 | 191 | if author.has_key?('email') 192 | if gemspec.email 193 | gemspec.email << author['email'] 194 | else 195 | gemspec.email = [author['email']] 196 | end 197 | end 198 | end 199 | 200 | gemspec.licenses = licenses 201 | 202 | requirements = metadata['requirements'] || [] 203 | requirements.each do |req| 204 | next if req['optional'] 205 | next if req['external'] 206 | 207 | name = req['name'] 208 | groups = req['groups'] || [] 209 | 210 | version = gemify_version(req['version']) 211 | 212 | if groups.empty? or groups.include?('runtime') 213 | # populate runtime dependencies 214 | if gemspec.respond_to?(:add_runtime_dependency) 215 | gemspec.add_runtime_dependency(name,*version) 216 | else 217 | gemspec.add_dependency(name,*version) 218 | end 219 | else 220 | # populate development dependencies 221 | if gemspec.respond_to?(:add_development_dependency) 222 | gemspec.add_development_dependency(name,*version) 223 | else 224 | gemspec.add_dependency(name,*version) 225 | end 226 | end 227 | end 228 | 229 | # convert external dependencies into gemspec requirements 230 | requirements.each do |req| 231 | next unless req['external'] 232 | gemspec.requirements << ("%s-%s" % req.values_at('name', 'version')) 233 | end 234 | 235 | gemspec.homepage = homepage 236 | gemspec.require_paths = require_paths 237 | gemspec.post_install_message = metadata['install_message'] 238 | end 239 | 240 | # 241 | # Set gemspec settings that require a root directory path. 242 | # 243 | def to_gemspec_paths(gemspec) 244 | gemspec.files = files 245 | gemspec.extensions = extensions 246 | gemspec.executables = executables 247 | 248 | if Gem::VERSION < '1.7.' 249 | gemspec.default_executable = gemspec.executables.first 250 | end 251 | 252 | gemspec.test_files = glob_files(patterns[:test]) 253 | 254 | unless gemspec.files.include?('.document') 255 | gemspec.extra_rdoc_files = glob_files(patterns[:doc]) 256 | end 257 | end 258 | 259 | # 260 | # Return a copy of this file. This is used to generate a local 261 | # .gemspec file that can automatically read the index file. 262 | # 263 | def self.source_code 264 | File.read(__FILE__) 265 | end 266 | 267 | private 268 | 269 | def find_root 270 | root_files = patterns[:root] 271 | if Dir.glob(root_files).first 272 | Pathname.new(Dir.pwd) 273 | elsif Dir.glob("../#{root_files}").first 274 | Pathname.new(Dir.pwd).parent 275 | else 276 | #raise "Can't find root of project containing `#{root_files}'." 277 | warn "Can't find root of project containing `#{root_files}'." 278 | nil 279 | end 280 | end 281 | 282 | def glob(pattern) 283 | if File.directory?(pattern) 284 | Dir.glob(File.join(pattern, '**', '*')) 285 | else 286 | Dir.glob(pattern) 287 | end 288 | end 289 | 290 | def gemify_version(version) 291 | case version 292 | when /^(.*?)\+$/ 293 | ">= #{$1}" 294 | when /^(.*?)\-$/ 295 | "< #{$1}" 296 | when /^(.*?)\~$/ 297 | "~> #{$1}" 298 | else 299 | version 300 | end 301 | end 302 | 303 | end 304 | 305 | end 306 | 307 | Indexer::GemspecExporter.gemspec -------------------------------------------------------------------------------- /lib/radix/float.rb: -------------------------------------------------------------------------------- 1 | require 'radix/numeric' 2 | 3 | module Radix 4 | 5 | ## 6 | # Advanced float class for Radix conversions and mathematical operations 7 | # with other bases. 8 | # 9 | # @todo Make fully immutable. After that we can catch @digits and 10 | # the library should be a good bit faster. 11 | # 12 | # @!attribute [r] value 13 | # @return [Float] Float's decimal value. 14 | # @!attribute [r] base 15 | # @return [Fixnum] The base level of Float instance. 16 | # @!attribute [r] code 17 | # @return [Array, nil] Substitution chars or nil if default. 18 | class Float < Numeric 19 | 20 | ## 21 | # Internal floating point value. 22 | # 23 | # @return [Float] Float's decimal value. 24 | attr :value 25 | 26 | ## 27 | # Base of the number. 28 | # 29 | # @return [Fixnum] The base level of Float instance. 30 | attr :base 31 | 32 | ## 33 | # Base encoding table. 34 | # 35 | # @return [Array, nil] Substitution chars or nil if default. 36 | attr :code 37 | 38 | private 39 | 40 | ## 41 | # Starts a new instance of the Radix::Float class. 42 | # 43 | # @param [Radix::Numeric, Numeric, Array, String] value 44 | # The value of the new integer in context of base. 45 | # 46 | # @param [Fixnum, Array] base 47 | # The base context in which value is determined. Can be an array 48 | # of characters to use in place of default. 49 | # 50 | # @return [void] 51 | def initialize(value, base=10) 52 | @base, @code = parse_base(base) 53 | @value = parse_value(value, @base) 54 | end 55 | 56 | ## 57 | # Takes a Radix::Numeric, String or array and returns the decimal float 58 | # value for storage in @value. 59 | # 60 | # @param [Radix::Numeric, Numeric, String, Array] value 61 | # The value of the integer in base context. 62 | # 63 | # @param [Fixnum, Array] base 64 | # The context base of value. 65 | # 66 | # @return [Float] Float value of Integer. 67 | def parse_value(value, base) 68 | case value 69 | when Float, Integer # Radix 70 | parse_numeric(value.to_f, base) 71 | when ::Array 72 | parse_array(value, base) 73 | when ::String 74 | parse_string(value, base) 75 | when ::Numeric 76 | parse_numeric(value.to_f, base) 77 | end 78 | end 79 | 80 | public 81 | 82 | ## 83 | # Convert the Radix::Float a Ruby Integer. 84 | # 85 | # @return [Integer] Base(10) value as Integer. 86 | def to_i 87 | to_f.to_i 88 | end 89 | 90 | alias_method :to_int, :to_i 91 | 92 | ## 93 | # Convert Radix::Float to a Ruby float. 94 | # 95 | # @return [Float] Base(10) value as Float. 96 | def to_f 97 | value.to_f 98 | end 99 | 100 | ## 101 | # Makes this Radix::Float an array using code if defined. Returns an 102 | # array using default chars otherwise. 103 | # 104 | # @param [Fixnum] base 105 | # Desired base. 106 | # 107 | # @return [Array] Current base encoded array. 108 | def to_a(base=nil) 109 | if base 110 | convert(base).digits_encoded 111 | else 112 | digits_encoded 113 | end 114 | end 115 | 116 | ## 117 | # Creates an encoded string in passed base, with passed digit divider. 118 | # 119 | # @note For base 10 or less does not use a divider unless specified. 120 | # 121 | # @param [Fixnum, Array] base 122 | # Desired base. 123 | # 124 | # @param [String] divider 125 | # Desired divider character(s). 126 | # 127 | # @return [String] Encoded string with specified divider. 128 | def to_s(base=nil, divider=nil) 129 | divider = divider.to_s if divider 130 | if base 131 | convert(base).to_s(nil, divider) 132 | else 133 | if code 134 | digits_encoded.join(divider) 135 | else 136 | if @base > 10 137 | digits.join(divider || DIVIDER) 138 | else 139 | digits.join(divider) 140 | end 141 | end 142 | end 143 | end 144 | 145 | ## 146 | # Creates a string representation of self. 147 | # 148 | # @return [String] String rep of self.digits and @base. 149 | def inspect 150 | "#{digits.join(' ')} (#{base})" 151 | end 152 | 153 | ## 154 | # Returns an array representation of each column's value in decimal chars. 155 | # 156 | # @return [Array] 157 | # Values per column of @base as array. Prepended with "-" if negative. 158 | def digits 159 | i, f = base_conversion(value, base) 160 | if negative? 161 | ['-'] + i + [DOT] + f 162 | else 163 | i + [DOT] + f 164 | end 165 | end 166 | 167 | ## 168 | # Returns digits, or coded version of digits if @code. 169 | # 170 | # @return [Array] 171 | # Values per column of @base as array. Prepended with "-" if negative. 172 | # Or encoded version if @code is defined. 173 | def digits_encoded 174 | base_encode(digits) 175 | end 176 | 177 | ## 178 | # Returns true if the number is negative? 179 | # 180 | # @return [Boolean] True if float value is < 0. 181 | def negative? 182 | value < 0 183 | end 184 | 185 | ## 186 | # Creates a new Radix::Float of same value in different base. 187 | # 188 | # @return [Radix::Float] New float of same value in different base. 189 | def convert(new_base) 190 | self.class.new(value, new_base) 191 | end 192 | 193 | ## 194 | # Power exponentional operation. 195 | # 196 | # @param [#to_f] other 197 | # The exponent by which to raise Float. 198 | # 199 | # @return [Radix::Float] Result of exponential operation. 200 | def **(other) 201 | operation(:**, other) 202 | end 203 | 204 | ## 205 | # Modulo binary operation. 206 | # 207 | # @param [#to_f] other 208 | # 209 | # @return [Radix::Float] Modulo result of division operation. 210 | def %(other) 211 | operation(:%, other) 212 | end 213 | 214 | alias_method :modulo, :% 215 | 216 | ## 217 | # Returns the absolute value of self in @base. 218 | # 219 | # @return [Radix::Float] Absolute of @value. 220 | def abs 221 | self.class.new(value.abs, base) 222 | end 223 | 224 | ## 225 | # Returns the largest integer greater than or equal to self as a 226 | # Radix::Float. 227 | # 228 | # @return [Radix::Float] 229 | def ceil 230 | self.class.new(value.ceil, base) 231 | end 232 | 233 | ## 234 | # Returns the smallest integer less than or equal to self as a 235 | # Radix::Float. 236 | # 237 | # @return [Radix::Float] 238 | def floor 239 | self.class.new(value.floor, base) 240 | end 241 | 242 | ## 243 | # Returns a new Radix::Float instance of same base, rounded to the nearest 244 | # whole integer. 245 | # 246 | # @example Rounding Radix Float 247 | # > round_test = Radix::Float.new(123.03, 16) 248 | # 7 11 . 0 7 10 14 1 4 7 10 14 1 (16) 249 | # > round_test.value 250 | # 123.03 251 | # > round_test.round 252 | # 7 11 . 0 (16) 253 | # > round_test.round.value 254 | # 123.0 255 | # > round_test += 0.5 256 | # 7 11 . 8 7 10 14 1 4 7 10 14 1 (16) 257 | # > round_test.value 258 | # 123.53 259 | # > round_test.round 260 | # 7 12 . 0 (16) 261 | # > round_test.round.value 262 | # 124.0 263 | # 264 | # @return [Radix::Float] New Instance 265 | def round 266 | return self.class.new((value + 0.5).floor, base) if self > 0.0 267 | return self.class.new((value - 0.5).ceil, base) if self < 0.0 268 | return self.class.new(0, base) 269 | end 270 | 271 | ## 272 | # Strict equality requires same class as well as value. 273 | # 274 | # @param [Object] num 275 | # Object to compare. 276 | # 277 | # @return [Boolean] True if class and value are equal. 278 | def eql?(num) 279 | self.class.equal?(num.class) && self == num 280 | end 281 | 282 | ## 283 | # Simple equality requires equal values only. 284 | # 285 | # @param [Numeric] other 286 | # Any Numeric instance. 287 | # 288 | # @return [Boolean] True if values are equal. 289 | def ==(other) 290 | case other 291 | when Float, Integer # Radix 292 | value == other.value 293 | else 294 | value == other 295 | end 296 | end 297 | 298 | ## 299 | # Comparitive binary operation. Very useful for sorting methods. 300 | # 301 | # @param [#to_f] other 302 | # The object to compare value against. 303 | # 304 | # @example Comparison testing 305 | # > lower = Radix::Float.new(123.00,10) 306 | # 1 2 3 . 0 (10) 307 | # > higher = Radix::Float.new(456.00,16) 308 | # 1 12 8 . 0 (16) 309 | # > lower <=> higher 310 | # -1 311 | # > lower <=> 123 312 | # 0 313 | # > lower <=> "123" 314 | # 0 315 | # > higher <=> lower 316 | # 1 317 | # 318 | # @return [Fixnum] Returns -1 for less than, 0 for equal or 1 for more than. 319 | def <=>(other) 320 | to_f <=> other.to_f 321 | end 322 | 323 | # 324 | #def infinite? 325 | # digits[0,2] == [0, DOT] 326 | #end 327 | 328 | # 329 | #def finite? 330 | # !infinite 331 | #end 332 | 333 | # 334 | #def nan? 335 | # digits[-2,2] == [DOT, 0] 336 | #end 337 | 338 | ## 339 | # Create a new Radix::Float from value in Base-10. 340 | # 341 | # @param [Numeric, Array, String] other 342 | # The value of the new integer in base-10. 343 | # 344 | # @return [Array] An array of the new Float object and self. 345 | def coerce(other) 346 | [Radix::Float.new(other), self] 347 | end 348 | 349 | private 350 | 351 | ## 352 | # Perform passed arithmetic operation. 353 | # 354 | # @param [#to_f] other 355 | # 356 | # @return [Radix::Float] Result of binary operation in @base. 357 | def operation(op, other) 358 | a = self.to_f 359 | b = other.to_f 360 | x = a.__send__(op, b) 361 | Radix::Float.new(x, base) 362 | 363 | end 364 | 365 | ## 366 | # Returns two arrays. The integer part and the fractional part of the Float 367 | # value in param base. 368 | # 369 | # @param [Float] value Float's decimal value. 370 | # @param [Fixnum] base The base level of Float instance. 371 | # @param [Fixnum] prec The # of places to extend F-part. 372 | # 373 | # @return [Array<(Array[Fixnum], Array[Fixnum])>] 374 | def base_conversion(value, base, prec=10) 375 | #if value < 0 376 | # @negative, value = true, value.abs 377 | #end 378 | value = value.to_f.abs 379 | 380 | i, f = split_float(value) 381 | 382 | a = [] 383 | while i > 0 384 | i, r = i.divmod(base) 385 | a << r 386 | end 387 | 388 | #c = [] # f-cache 389 | p = prec 390 | b = [] 391 | while !f.zero? 392 | k = (f * base) 393 | r, f = split_float(k) 394 | #c.include?(f) ? break : c << f 395 | break if p == 0; p -= 1 396 | b << r 397 | end 398 | 399 | a << 0 if a.empty? 400 | b << 0 if b.empty? 401 | 402 | [a.reverse, b] 403 | end 404 | 405 | ## 406 | # Convert array of values of a different base to decimal as called by 407 | # parse_array. 408 | # 409 | # @param [Array] digits Representation of Base values. 410 | # @param [Fixnum, Array] base The base to convert from. 411 | # 412 | # @return [Float] The digits of base converted to decimal. 413 | def decimal(digits, base) 414 | i, f = split_digits(digits) 415 | e = i.size - 1 416 | v = 0 417 | (i + f).each do |n| 418 | v += n * base**e 419 | e -= 1 420 | end 421 | v 422 | end 423 | 424 | ## 425 | # Returns the I-Part and F-Part of the passed value as arrays of fixnums. 426 | # 427 | # @param [Array] value 428 | # The array of decimal values per column of @base. 429 | # 430 | # @return [Array<(Array, Array)>] 431 | def split_digits(value) 432 | if d = value.index(DOT) || value.index('.') 433 | i, f = value[0...d], value[d+1..-1] 434 | else 435 | i, f = value, [0] 436 | end 437 | i.map!{ |x| x.to_i } 438 | f.map!{ |x| x.to_i } 439 | return i, f 440 | end 441 | 442 | ## 443 | # Returns an array of Integer and Float portions of the Radix::Float 444 | # 445 | # @param [Radix::Float] float Float value to split 446 | # 447 | # @return [Array<(Integer, Float)>] 448 | def split_float(float) 449 | i, f = float.to_s.split('.') 450 | return i.to_i, ('0.'+f).to_f 451 | end 452 | 453 | end 454 | 455 | end 456 | 457 | -------------------------------------------------------------------------------- /lib/radix/integer.rb: -------------------------------------------------------------------------------- 1 | require 'radix/numeric' 2 | 3 | module Radix 4 | 5 | ## 6 | # Advanced integer class for Radix conversions and mathematical operations 7 | # with other bases. 8 | # 9 | # @!attribute [r] value 10 | # @return [Fixnum] Integer's decimal value. 11 | # @!attribute [r] base 12 | # @return [Fixnum] The base level of Integer instance. 13 | # @!attribute [r] code 14 | # @return [Array, nil] Substitution chars or nil if default. 15 | class Integer < Numeric 16 | 17 | ## 18 | # Stores the numeric value as normal number. 19 | # 20 | # @return [Fixnum] Integer's decimal value. 21 | attr :value 22 | 23 | ## 24 | # Base of the number. 25 | # 26 | # @return [Fixnum] The base level of Integer instance. 27 | attr :base 28 | 29 | ## 30 | # Base encoding table. 31 | # 32 | # @return [Array, nil] Substitution chars or nil if default. 33 | attr :code 34 | 35 | private 36 | 37 | ## 38 | # Starts a new instance of the Radix::Integer class 39 | # 40 | # @param [Radix::Numeric, Numeric, Array, String] value 41 | # The value of the new integer in context of base. 42 | # 43 | # @param [Fixnum, Array] base The base context in which value is 44 | # determined. Can be an array of characters to use in place of default. 45 | # 46 | # @return [void] 47 | def initialize(value, base=10) 48 | @base, @code = parse_base(base) 49 | @value = parse_value(value, @base) 50 | end 51 | 52 | ## 53 | # Takes a Radix::Numeric, String or array and returns the decimal value for 54 | # storage in @value. 55 | # 56 | # @param [Radix::Numeric, Numeric, String, Array] value 57 | # The value of the integer in base context. 58 | # 59 | # @param [Fixnum, Array] base 60 | # The context base of value. 61 | # 62 | # @return [Fixnum] Decimal value of Integer. 63 | def parse_value(value, base) 64 | case value 65 | when Integer, Float # Radix 66 | parse_numeric(value.to_i, base) 67 | when ::Array 68 | parse_array(value, base) 69 | when ::String 70 | parse_string(value, base) 71 | when ::Numeric 72 | parse_numeric(value, base) 73 | end 74 | end 75 | 76 | ## 77 | # Take an Array in the form of [..., d2, d1, d0] and convert it to 78 | # base ten, and store in @value. 79 | # 80 | # @note If a float style array is passed in for +value+, e.g. [9, '.', 5], 81 | # the fractional part will simply be truncated. 82 | # 83 | # @param [Array] value Given value. 84 | # 85 | # @param [Fixnum, Array] base Desired base. 86 | # 87 | # @return [Fixnum] Decimal version of array value in base context. 88 | def parse_array(value, base) 89 | if i = value.index(DOT) 90 | value = [0...i] 91 | end 92 | super(value, base) 93 | end 94 | 95 | ## digits << #Radix.convert(d, base, 10).to_i 96 | 97 | public 98 | 99 | ## 100 | # Makes this Radix::Integer a ruby integer. 101 | # 102 | # @return [Fixnum] Base(10) value. 103 | def to_i 104 | value.to_i #(sign + convert(10).digits.join('')).to_i 105 | end 106 | 107 | alias :to_int :to_i 108 | 109 | ## 110 | # Makes this Radix::Integer a ruby float. 111 | # 112 | # @return [Float] Base(10) value as float. 113 | def to_f 114 | value.to_f #(sign + convert(10).digits.join('')).to_f 115 | end 116 | 117 | ## 118 | # Makes this Radix::Integer an array using code if defined. Returns an 119 | # array using default chars otherwise. 120 | # 121 | # @param [Fixnum] base Desired base. 122 | # 123 | # @return [Array] Current base encoded array. 124 | def to_a(base=nil) 125 | if base 126 | convert(base).digits_encoded 127 | else 128 | digits_encoded 129 | end 130 | end 131 | 132 | ## 133 | # Creates an encoded string in desired base, with desired digit divider. 134 | # 135 | # @note For base 10 or less does not use a divider unless specified. 136 | # 137 | # @param [Fixnum, Array] base 138 | # Desired base. 139 | # 140 | # @param [String] divider 141 | # Desired divider character(s). 142 | # 143 | # @return [String] Encoded string with specified divider. 144 | def to_s(base=nil, divider=nil) 145 | divider = divider.to_s if divider 146 | if base 147 | convert(base).to_s(nil, divider) 148 | else 149 | if code 150 | digits_encoded.join(divider) 151 | else 152 | if @base > 10 153 | digits.join(divider || DIVIDER) 154 | else 155 | digits.join(divider) 156 | end 157 | end 158 | end 159 | end 160 | 161 | ## 162 | # Creates a string representation of self. 163 | # 164 | # @return [String] String rep of self.digits and @base. 165 | def inspect 166 | "#{digits.join(' ')} (#{base})" 167 | end 168 | 169 | ## 170 | # Returns an array representation of each column's value in decimal chars. 171 | # 172 | # @return [Array] Values per column of @base as array. 173 | # Prepended with "-" if negative. 174 | def digits 175 | i = base_conversion(value, base) 176 | i.unshift('-') if negative? 177 | i 178 | end 179 | 180 | ## 181 | # Returns the encoded version of digits. 182 | # 183 | # @return [Array] The encoded digits. Or digits if @code exists. 184 | def digits_encoded 185 | base_encode(digits) 186 | end 187 | 188 | ## 189 | # Returns true if the number is negative. 190 | # 191 | # @return [Boolean] True if negative. 192 | def negative? 193 | value < 0 194 | end 195 | 196 | ## 197 | # Converts Integer to a new base. 198 | # 199 | # @param [Fixnum, Array] base 200 | # The base context in which value is determined. Can be an array 201 | # of characters to use in place of default. 202 | # 203 | # @return [Radix:Integer] New Integer of same value, different base. 204 | def convert(base) 205 | self.class.new(value, base) 206 | #new_digits = Radix::Base.convert_base(digits, base, new_base) 207 | #self.class.new(new_digits, new_base) 208 | end 209 | 210 | ## 211 | # Addition binary operation. 212 | # 213 | # @param [#to_i] other 214 | # 215 | # @example Which operand determines the base? 216 | # > i = Radix::Integer.new(123,16) 217 | # 7 11 (16) 218 | # > i2 = Radix::Integer.new(456,10) 219 | # 4 5 6 (10) 220 | # > i + i2 # i is base 16 and is first operand 221 | # 2 4 3 (16) # so base of return is 16 222 | # > i2 + i # i2 is base 10 and is first operand 223 | # 5 7 9 (10) # so base of return is 10 224 | # 225 | # @return [Radix::Integer] Result of arithmetic operation. 226 | def +(other) 227 | operation(:+, other) 228 | end 229 | 230 | ## 231 | # Subtraction binary operation. 232 | # 233 | # @param [#to_i] other 234 | # 235 | # @return [Radix::Integer] Result of arithmetic operation. 236 | def -(other) 237 | operation(:-, other) 238 | end 239 | 240 | ## 241 | # Multiplication binary operation. 242 | # 243 | # @param [#to_i] other 244 | # 245 | # @return [Radix::Integer] Result of arithmetic operation. 246 | def *(other) 247 | operation(:*, other) 248 | end 249 | 250 | ## 251 | # Division binary operation. 252 | # 253 | # @param [#to_i] other 254 | # 255 | # @return [Radix::Integer] Result of arithmetic operation. 256 | def /(other) 257 | operation(:/, other) 258 | end 259 | 260 | ## 261 | # Power, exponentional operation. 262 | # 263 | # @param [#to_i] other 264 | # The exponent by which to raise Integer. 265 | # 266 | # @return [Radix::Integer] Result of exponential operation. 267 | def **(other) 268 | operation(:**, other) 269 | end 270 | 271 | ## 272 | # Modulo binary operation. 273 | # 274 | # @param [#to_i] other 275 | # 276 | # @return [Radix::Integer] Modulo result of division operation. 277 | def %(other) 278 | operation(:%, other) 279 | end 280 | 281 | ## 282 | # Leftwise bit shift operator. 283 | # 284 | # @note Negative numbers will shift rightward. This will truncate bytes 285 | # that get carried past zero. 286 | # 287 | # @param [#to_int] integer 288 | # The number of places to shift the bits of self. 289 | # 290 | # @return [Radix::Integer] The new Radix::Integer with shifted bits. 291 | def <<(integer) 292 | Radix::Integer.new(to_int << integer.to_int, base) 293 | end 294 | 295 | ## 296 | # AND bitwise operator 297 | # 298 | # @param [#to_int] integer 299 | # 300 | # @return [Radix::Integer] The logical AND. 301 | def &(integer) 302 | Radix::Integer.new(to_int & integer.to_int, base) 303 | end 304 | 305 | ## 306 | # Returns the absolute value of self in @base. 307 | # 308 | # @return [Radix::Integer] Absolute of @value. 309 | def abs 310 | self.class.new(value.abs, base) 311 | end 312 | 313 | ## 314 | # Strict equality requires same class as well as value. 315 | # 316 | # @param [Object] num 317 | # Object to compare. 318 | # 319 | # @return [Boolean] True if class and value are equal. 320 | def eql?(num) 321 | self.class.equal?(num.class) && self == num 322 | end 323 | 324 | ## 325 | # Simple equality requires equal values only. 326 | # @todo Handle Float and Radix::Float. 327 | # 328 | # @param [#value] other 329 | # Any object that responds to value. 330 | # 331 | # @return [Boolean] True if values are equal. 332 | def ==(other) 333 | case other 334 | when Float, Integer # Radix 335 | value == other.value 336 | else 337 | value == other 338 | end 339 | end 340 | 341 | ## 342 | # Comparitive binary operation. Very useful for sorting methods. 343 | # 344 | # @param [#to_f] other The object to compare value against. 345 | # 346 | # @example Comparison testing 347 | # > lower = Radix::Integer.new(123,10) 348 | # 1 2 3 (10) 349 | # > higher = Radix::Integer.new(456, 16) 350 | # 1 12 8 (16) 351 | # > lower <=> higher 352 | # -1 353 | # > lower <=> 123 354 | # 0 355 | # > higher <=> lower 356 | # 1 357 | # 358 | # @return [Fixnum] Returns -1 for less than, 0 for equal or 1 for more than. 359 | def <=>(other) 360 | value <=> other.to_f # to_num 361 | end 362 | 363 | ## 364 | # Create a new Radix::Integer from value in Base-10 365 | # 366 | # @return [Array] An array of the new Integer object and 367 | # self. 368 | def coerce(value) 369 | [Radix::Integer.new(value), self] 370 | end 371 | 372 | private 373 | 374 | ## 375 | # Perform passed arithmetic operation. 376 | # 377 | # @param [#to_i] other 378 | # 379 | # @example Which operand determines the base? 380 | # > i = Radix::Integer.new(123,16) 381 | # 7 11 (16) 382 | # > i2 = Radix::Integer.new(456,10) 383 | # 4 5 6 (10) 384 | # > i + i2 # i is base 16 and is first operand 385 | # 2 4 3 (16) # so base of return is 16 386 | # > i2 + i # i2 is base 10 and is first operand 387 | # 5 7 9 (10) # so base of return is 10 388 | # 389 | # @return [Radix::Integer] Result of binary operation in @base. 390 | def operation(op, other) 391 | a = self.to_i 392 | b = other.to_i 393 | x = a.__send__(op, b) 394 | self.class.new(x, base) 395 | end 396 | 397 | ## 398 | # Returns the value as an array of decimal values where each column is a 399 | # place of @base. 400 | # 401 | # @param (see #Radix::Integer.value) 402 | # @param (see #Radix::Integer.base) 403 | # 404 | # @return [Array] 405 | def base_conversion(value, base) 406 | #if value < 0 407 | # @negative, value = true, value.abs 408 | #end 409 | i = value.abs 410 | 411 | a = [] 412 | while i > 0 413 | i, r = i.divmod(base) 414 | a << r 415 | end 416 | 417 | # if nothing add zero 418 | a << 0 if a.empty? 419 | 420 | a.reverse 421 | end 422 | 423 | end 424 | 425 | end 426 | 427 | ### Want to see the hard way to do addition? 428 | ## 429 | ## def +(other) 430 | ## other = convert_other(other) 431 | ## 432 | ## result = [] 433 | ## index = -1 434 | ## carry = 0 435 | ## 436 | ## x1 = self.digits[index] 437 | ## x2 = other.digits[index] 438 | ## while x1 or x2 439 | ## r = (x1||0) + (x2||0) + carry 440 | ## if r >= base 441 | ## result.unshift(r - base) 442 | ## carry = 1 443 | ## else 444 | ## result.unshift(r) 445 | ## carry = 0 446 | ## end 447 | ## index -= 1 448 | ## x1 = self.digits[index] 449 | ## x2 = other.digits[index] 450 | ## end 451 | ## result << carry if carry != 0 452 | ## Radix::Integer.new(result, base) 453 | ## end 454 | ## 455 | ## def convert_other(other) 456 | ## case other 457 | ## when Radix::Integer 458 | ## other.convert(base) 459 | ## when Integer 460 | ## Radix::Integer.new(other, base) 461 | ## when String, Array 462 | ## Radix::Integer.new(other, base) 463 | ## end 464 | ## end 465 | ## 466 | --------------------------------------------------------------------------------