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] (ie. 100**256 + 10**1) 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]