Synopsis
Radix provides the means of converting to and from any base. For example, a number in base 256 can be represented by the array 100, 10 and easily converted to base 10.
[100,10].b(256).to_i #=> 25610
We can get an Array representation as well.
[100,10].b(256).to_a(10) #=> [2,5,6,1,0]
[100,10].b(256).to_a(62) #=> [6,41,4]
[100,10].b(256).to_a(64) #=> [6,16,10]
To get a String representation for any base use #to_s.
[100,10].b(256).to_s(10) #=> "25610"
[100,10].b(256).to_s(62) #=> "6 41 4"
[100,10].b(256).to_s(64) #=> "6 16 10"
Notice that anything above base 10 is seperated by a space divider. The divider can be changed by providing a second argument.
[100,10].b(256).to_s(64, ':') #=> "6:16:10"
A string representation of a number can be converted upto base 62 (B62).
"10".b(62).to_s(10) #=> "62"
"zz".b(62).to_s(10) #=> "3843"
To encode a number with a base greater than 10, use an Array base. Radix
provides a built-in set of these, such as BASE::B62
.
[100,10].b(256).to_s(Radix::BASE::B62) #=> "6f4"
To use a custom character set, use an array of characters as the base rather than an integer. For example we can convert a base 10 number to another base 10 number but useing a different encoding.
base = %w[Q W E R T Y U I O U]
"10".b(10).to_a(base) #=> ["W", "Q"]
"10".b(10).to_s(base) #=> "WQ"
All of the above holds equally for floating point numbers.
Radix Integer
Radix provides an Integer class for working with integers in various bases.
require 'radix'
Initialization
Radix::Integer's initializer can accept either an Integer, String or Array as a value and an integer base.
Give an integer value, it will automatically be converted to the base specified.
check do |integer, base, digits|
r = Radix::Integer.new(integer, base)
r.digits.assert == digits
end
ok 8, 2, [1,0,0,0]
ok 4, 2, [1,0,0]
ok 8, 10, [8]
ok 10, 10, [1, 0]
ok 8, 16, [8]
ok 16, 16, [1, 0]
Where as a String value is taken to already be in the base given.
ok "1000", 2, [1,0,0,0]
ok "100", 2, [1,0,0]
ok "8", 10, [8]
ok "10", 10, [1, 0]
ok "8", 16, [8]
ok "10", 16, [1, 0]
And an Array is also taken to be in the base given.
ok %w[1 0 0 0], 2, [1,0,0,0]
ok %w[ 1 0 0], 2, [1,0,0]
ok %w[ 8], 10, [8]
ok %w[1 0], 10, [1, 0]
ok %w[ 8], 16, [8]
ok %w[1 0], 16, [1, 0]
Integers can also be negative, rather than positive. In each case just prepend the value with a minus sign.
check do |integer, base, digits|
r = Radix::Integer.new(integer, base)
r.digits.assert == digits
r.assert.negative?
end
ok -8, 2, ['-',1,0,0,0]
ok "-1000", 2, ['-',1,0,0,0]
ok %w[- 1 0 0 0], 2, ['-',1,0,0,0]
If a value has a digit outside of the range of the base an ArgumentError will be raised.
expect ArgumentError do
Radix::Integer.new('9', 2)
end
Radix provides a convenience extension method to Integer, String and Array
called #b, to more easily initialize a Radix numeric object. The method simply
passes the receiver on to Radix::Integer#new
.
check do |integer, base, digits|
r = integer.b(base)
r.assert.is_a?(Radix::Integer)
r.digits.assert == digits
end
ok 8, 2, [1,0,0,0]
ok 4, 2, [1,0,0]
ok "1000", 2, [1,0,0,0]
ok "100", 2, [1,0,0]
ok %w"1 0 0 0", 2, [1,0,0,0]
ok %w"1 0 0", 2, [1,0,0]
Conversion
Radix integers can ve converted to other bases with the #convert method.
b = "1000".b(2)
d = b.convert(10)
d.digits.assert == [8]
We can convert a Radix::Integer to a regular base-10 Integer with the #to_i method.
b = "1000".b(2)
d = b.to_i
d.assert == 8
Equality
Radix extend the Integer, String and Array classes with the #b method which simplifies the creation of Radix::Integer instances. The following return the equivalent instance of Radix::Integer.
a = 8.b(2)
b = "1000".b(2)
c = [1, 0, 0, 0].b(2)
a.assert == b
b.assert == c
c.assert == a
a.assert == 8
b.assert == 8
c.assert == 8
More stringent equality can be had from #eql?, in which the other integer must be a Radix::Integer too.
a.assert.eql?(b)
a.refute.eql?(8)
Operations
Radix::Integer supports all the usual mathematical operators.
Addition
check do |a, b, x|
(a + b).assert == x
end
ok "1000".b(2), "0010".b(2), "1010".b(2)
ok "1000".b(2), "2".b(8), "1010".b(2)
ok "1000".b(2), "2".b(8), "10".b(10)
A more complex example.
x = "AZ42".b(62) + "54".b(10)
x.assert == "2518124".b(10)
x.assert == 2518124
Adding negative integers will, of course, be akin to subtraction.
ok "1000".b(2), "-0010".b(2), "110".b(2)
ok "1000".b(2), "-2".b(8), "110".b(2)
ok "1000".b(2), "-2".b(8), "6".b(10)
ok "-1000".b(2), "0010".b(2), "-110".b(2)
ok "-1000".b(2), "2".b(8), "-110".b(2)
ok "-1000".b(2), "2".b(8), "-6".b(10)
ok "-1000".b(2), "-0010".b(2), "-1010".b(2)
ok "-1000".b(2), "-2".b(8), "-1010".b(2)
ok "-1000".b(2), "-2".b(8), "-10".b(10)
Subtraction
check do |a, b, x|
(a - b).assert == x
end
ok "1000".b(2), "10".b(2), "0110".b(2)
ok "1000".b(2), "2".b(8), "0110".b(2)
ok "1000".b(2), "2".b(8), "6".b(8)
ok "1000".b(2), "2".b(8), "6".b(10)
A more complex example.
x = "AZ42".b(62) - "54".b(10)
x.assert == "2518016".b(10)
x.assert == 2518016
Multiplication
check do |a, b, x|
(a * b).assert == x
end
ok "1000".b(2), "10".b(2), "10000".b(2)
ok "1000".b(2), "2".b(8), "10000".b(2)
ok "1000".b(2), "2".b(8), "20".b(8)
ok "1000".b(2), "2".b(8), "16".b(10)
A more complex example.
x = "Z42".b(62) * "4".b(10)
x.assert == "539160".b(10)
x.assert == 539160
Division
check do |a, b, x|
(a / b).assert == x
end
ok "1000".b(2), "10".b(2), "100".b(2)
ok "1000".b(2), "2".b(8), "100".b(2)
ok "1000".b(2), "2".b(8), "4".b(8)
ok "1000".b(2), "2".b(8), "4".b(10)
A more complex example.
x = "AZ42".b(62) / "54".b(10)
x.assert == "46630".b(10)
x.assert == 46630
Power
check do |a, b, x|
(a ** b).assert == x
end
ok "1000".b(2), "10".b(2), 64
Modulo
check do |a, b, x|
(a % b).assert == x
end
ok "1000".b(2), "10".b(2), 0
ok "1000".b(2), "11".b(2), 2
Bitwise Shift
check do |a, b, x|
(a << b).assert == x
end
ok "10".b(2), "10".b(2), "1000".b(2)
ok "10".b(2), 2, "1000".b(2)
ok "10".b(2), 2, 8
Bitwise AND
check do |a, b, x|
(a & b).assert == x
end
ok "1010".b(2), "10".b(2), "10".b(2)
ok "1010".b(2), "2".b(8), "10".b(2)
Coerce
When a Radix::Integer is the operand in an operation against a regular Ruby Integer, the calculation should still work via #coerce.
check do |a, b, x|
(a + b).assert == x
end
ok 10, "10".b(2), "12".b(10)
Radix Float
Radix provides a Float class for working with rational numbers in various bases. Actually Radix's implementation of Float is a fixed point, not a floating point.
require 'radix'
D = Radix::DOT
Initialization
Radix::Float's initializer can accept either an Integer, Float, String or Array as a value and an integer base.
Give a float value, it will automatically be converted to the base specified.
check do |float, base, digits|
r = Radix::Float.new(float, base)
r.digits.assert == digits
end
ok 8.5, 2, [1,0,0,0,D,1]
ok 4.5, 2, [ 1,0,0,D,1]
ok 8.1, 10, [ 8,D,1]
ok 10.2, 10, [1,0,D,2]
#ok 8.1, 16, [ 8,D,1]
#ok 16.1, 16, [1,0,D,1]
Give an integer value, it will automatically be converted to the base specified and given a fraction part set to zero.
check do |float, base, digits|
r = Radix::Float.new(float, base)
r.digits.assert == digits
end
ok 8, 2, [1,0,0,0,D,0]
ok 4, 2, [ 1,0,0,D,0]
ok 8, 10, [ 8,D,0]
ok 10, 10, [1,0,D,0]
ok 8, 16, [ 8,D,0]
ok 16, 16, [1,0,D,0]
Given a float, the same will occur.
ok 8.0, 2, [1,0,0,0,D,0]
ok 4.0, 2, [ 1,0,0,D,0]
ok 8.0, 10, [ 8,D,0]
ok 10.0, 10, [1,0,D,0]
ok 8.0, 16, [ 8,D,0]
ok 16.0, 16, [1,0,D,0]
Where as a String value is taken to already be in the base given.
ok "1000", 2, [1,0,0,0,D,0]
ok "100", 2, [ 1,0,0,D,0]
ok "8", 10, [ 8,D,0]
ok "10", 10, [1,0,D,0]
ok "8", 16, [ 8,D,0]
ok "10", 16, [1,0,D,0]
ok "1000.0", 2, [1,0,0,0,D,0]
ok "100.0", 2, [ 1,0,0,D,0]
ok "8.0", 10, [ 8,D,0]
ok "10.0", 10, [1,0,D,0]
ok "8.0", 16, [ 8,D,0]
ok "10.0", 16, [1,0,D,0]
And an Array is also taken to be in the base given.
ok %w[1 0 0 0], 2, [1,0,0,0,D,0]
ok %w[ 1 0 0], 2, [ 1,0,0,D,0]
ok %w[ 8], 10, [ 8,D,0]
ok %w[1 0], 10, [1,0,D,0]
ok %w[ 8], 16, [ 8,D,0]
ok %w[1 0], 16, [1,0,D,0]
Passing in an Array with a fraction part, either the DOT constant can be used, which is simply the symbol :'.', or the string '.' can be used.
ok %w[1 0 0 0 . 0], 2, [1,0,0,0,D,0]
ok %w[ 1 0 0 . 0], 2, [ 1,0,0,D,0]
ok %w[ 8 . 0], 10, [ 8,D,0]
ok %w[1 0 . 0], 10, [1,0,D,0]
ok %w[ 8 . 0], 16, [ 8,D,0]
ok %w[1 0 . 0], 16, [1,0,D,0]
Integers can also be negative, rather than positive. In each case just prepend the value with a minus sign.
check do |float, base, digits|
r = Radix::Float.new(float, base)
r.digits.assert = digits
r.assert.negative?
end
ok( -8, 2, ['-',1,0,0,0,D,0])
ok( "-1000", 2, ['-',1,0,0,0,D,0])
ok( %w[- 1 0 0 0], 2, ['-',1,0,0,0,D,0])
If a value has a digit outside of the range of the base an ArgumentError will be raised.
expect ArgumentError do
Radix::Float.new('9', 2)
end
Radix provides a convenience extension method to Integer, String and Array
called #b, to more easily initialize a Radix numeric object. The method simply
passes the receiver on to Radix::Integer#new
.
check do |float, base, digits|
r = float.b(base)
r.assert.is_a?(Radix::Float)
r.digits.assert = digits
end
ok 8.0, 2, [1,0,0,0,D,0]
ok 4.0, 2, [ 1,0,0,D,0]
ok "1000.0", 2, [1,0,0,0,D,0]
ok "100.0", 2, [ 1,0,0,D,0]
ok %w"1 0 0 0 . 0", 2, [1,0,0,0,D,0]
ok %w"1 0 0 . 0", 2, [ 1,0,0,D,0]
Conversion
Radix integers can ve converted to other bases with the #convert method.
b = "1000.0".b(2)
d = b.convert(10)
d.digits.assert == [8,D,0]
We can convert a Radix::Float to a regular base-10 Float with the #to_f method.
b = "1000.0".b(2)
d = b.to_f
d.assert == 8.0
We can convert a Radix::Float to a regular base-10 Integer with the #to_i method.
b = "1000.0".b(2)
d = b.to_i
d.assert == 8
Equality
Radix extend the Integer, String and Array classes with the #b method which simplifies the creation of Radix::Float instances. The following return the equivalent instance of Radix::Float.
a = 8.0.b(2)
b = "1000.0".b(2)
c = [1,0,0,0,'.',0].b(2)
a.assert = b
b.assert = c
c.assert = a
a.assert = 8.0
b.assert = 8.0
c.assert = 8.0
More stringent equality can be had from #eql?, in which the other integer must be a Radix::Integer too.
a.assert.eql?(b)
a.refute.eql?(8.0)
Operations
Radix::Float supports all the usual mathematical operators.
Addition
check do |a, b, x|
(a + b).assert = x
end
ok "1000.0".b(2), "0010.0".b(2), "1010.0".b(2)
ok "1000.0".b(2), "2.0".b(8), "1010.0".b(2)
ok "1000.0".b(2), "2.0".b(8), "10.0".b(10)
A more complex example.
x = "AZ42.0".b(62) + "54.0".b(10)
x.assert == "2518124.0".b(10)
x.assert == 2518124.0
Adding negative integers will, of course, be akin to subtraction.
ok "1000.0".b(2), "-0010".b(2), "110.0".b(2)
ok "1000.0".b(2), "-2".b(8), "110.0".b(2)
ok "1000.0".b(2), "-2".b(8), "6.0".b(10)
ok "-1000.0".b(2), "0010".b(2), "-110.0".b(2)
ok "-1000.0".b(2), "2".b(8), "-110.0".b(2)
ok "-1000.0".b(2), "2".b(8), "-6.0".b(10)
ok "-1000.0".b(2), "-0010".b(2), "-1010.0".b(2)
ok "-1000.0".b(2), "-2".b(8), "-1010.0".b(2)
ok "-1000.0".b(2), "-2".b(8), "-10.0".b(10)
Subtraction
check do |a, b, x|
(a - b).assert == x
end
ok "1000.0".b(2), "10".b(2), "110.0".b(2)
ok "1000.0".b(2), "2".b(8), "110.0".b(2)
ok "1000.0".b(2), "2".b(8), "6.0".b(8)
ok "1000.0".b(2), "2".b(8), "6.0".b(10)
A more complex example.
x = "AZ42.0".b(62) - "54".b(10)
x.assert == "2518016.0".b(10)
x.assert == 2518016.0
Multiplication
check do |a, b, x|
(a * b).assert = x
end
ok "1000.0".b(2), "10".b(2), "10000.0".b(2)
ok "1000.0".b(2), "2".b(8), "10000.0".b(2)
ok "1000.0".b(2), "2".b(8), "20.0".b(8)
ok "1000.0".b(2), "2".b(8), "16.0".b(10)
A more complex example.
x = "Z42.0".b(62) * "4.0".b(10)
x.assert == "539160.0".b(10)
x.assert == 539160.0
Division
check do |a, b, x|
(a / b).assert = x
end
ok "1000.0".b(2), "10".b(2), "100.0".b(2)
ok "1000.0".b(2), "2".b(8), "100.0".b(2)
ok "1000.0".b(2), "2".b(8), "4.0".b(8)
ok "1000.0".b(2), "2".b(8), "4.0".b(10)
A more complex example.
x = "AZ40.0".b(62) / "62.0".b(10)
x.assert == "40614.0".b(10)
x.assert == 40614.0
Power
check do |a, b, x|
(a ** b).assert == x
end
ok "1000.0".b(2), "10.0".b(2), 64.0
Modulo
check do |a, b, x|
(a % b).assert == x
end
ok "1000.0".b(2), "10".b(2), 0
ok "1000.0".b(2), "11".b(2), 2
Coerce
When a Radix::Integer is the operand in an operation against a regular Ruby Integer, the calculation should still work via #coerce.
check do |a, b, x|
(a + b).assert == x
end
ok 10.0, "10".b(2), "12".b(10)
Radix Rational
require 'radix'
Initialization
Radix::Rational's initializer takes a numerator and a denominator, either of which can be an Integer, Float, String or Array along witha an integer base.
Give a integer value, it will automatically be converted to the base specified.
check do |num, dem, base, eqf|
r = Radix::Rational.new(num, dem, base)
r.assert == eqf
end
ok 1, 2, 2, 0.5
ok 1, 1, 2, 1.0
ok 8, 1, 10, 8.0
ok 8, 5, 10, 1.6
ok 8, 8, 10, 1.0
ok 10, 1, 10, 10.0
ok 10, 2, 10, 5.0
ok 10, 5, 10, 2.0
ok 8, 1, 16, 8.0
ok 16, 1, 16, 16.0
Reduction
check do |a, x|
r = a.reduce
r.assert == x
end
ok [10,5].br(10), [2,1].br(10)
ok [30,3].br(10), [10,1].br(10)
Operations
Addition
check do |a, b, x|
r = a + b
r.assert == x
end
ok [8,5].br(10), [1,2].br(10), [21,10].br(10)
ok [8,5].br(10), 1, [13,5].br(10)
ok [8,5].br(10), 0.5, [21,10].br(10)
Subtraction
check do |a, b, x|
r = a - b
r.assert == x
end
ok [8,5].br(10), [1,2].br(10), [11,10].br(10)
Multiplication
check do |a, b, x|
r = a * b
r.assert == x
end
ok [8,5].br(10), [1,2].br(10), [8,10].br(10)
Division
check do |a, b, x|
r = a / b
r.assert == x
end
ok [8,5].br(10), [1,2].br(10), [16,5].br(10)
Radix::Base
The Radix::Base class is an encapsulatin of a numeric base. By creating an instance of Base one can convert numbers to and from other bases.
require 'radix/base'
Base Instance
First let's try something we all know, converting decimal to hexideciaml. To do this we setup the radix base objects for each base.
b10 = Radix::Base.new(Radix::BASE::B10)
b16 = Radix::Base.new(Radix::BASE::B16)
Now we can covert from one base to the other.
b16.convert("16" , b10).should == "10"
b16.convert("160", b10).should == "A0"
b16.convert("255", b10).should == "FF"
To confirm, lets convert from hexidecimal back to decimal.
b10.convert("10", b16).should == "16"
b10.convert("A0", b16).should == "160"
b10.convert("FF", b16).should == "255"
If we are happy with standard encodings then we can simply provide an integer base, rather than a Radix::Base object.
b10.convert("10", 16).should == "16"
b10.convert("A0", 16).should == "160"
b10.convert("FF", 16).should == "255"
Now let's try a more down to earth base, my favorite, senary, or base six.
b6 = Radix::Base.new(0..5)
b6.convert("39", 10).should == "103"
And the notations need not be in ASCII order. Odd alternate notations can be used as well.
b10 = Radix::Base.new([:Q, :W, :E, :R, :T, :Y, :U, :I, :O, :U])
b10.convert("FF", 16) #=> "EYY"
Encoding and Decoding
Radix::Base instances can also be used to encode and decode strings.
b16.encode("CHARLIE").should == "434841524C4945"
b16.decode("434841524C4945").should == "CHARLIE"
Module Methods
For further convenience, Radix::base provides functions to convert to and from standard notations upto 62 without creating an instance of Radix::Base.
Radix.convert("10", 16, 10).should == "16"
Radix.convert("A0", 16, 10).should == "160"
Radix.convert("FF", 16, 10).should == "255"
Let's try that again with the maximum base supported.
Radix.convert( "62", 10, 62).should == "10"
Radix.convert("8814542", 10, 62).should == "az42"
Radix.convert( "10", 62, 10).should == "62"
Radix.convert( "az42", 62, 10).should == "8814542"
Finally, we will demonstrate how to convert bases larger than 62. These can only be represented as arrays since there are not enough latin characters to represent them.
Radix.convert_base([100, 10], 256, 10).should == [2, 5, 6, 1, 0]
Radix.convert_base([2, 5, 6, 1, 0], 10, 256).should == [100, 10]
Radix.convert_base([1, 0, 1, 0, 1], 2, 10).should == [2, 1]
Zero becomes empty string (#4)
Example of the issue:
0.b(10).to_s #=> ""
I would expect "0" as a result.
0.b(10).to_s #=> "0"
Okay, lets make sure this works for Floats.
0.0.b(10).to_s #=> "0.0"
And Rationals too.
[0,1].br(10).to_s #=> "0/1"