Math::BaseArith - Perl extension for mixed-base number representation (like APL encode/decode)


Math-BaseArith documentation Contained in the Math-BaseArith distribution.

Index


Code Index:

NAME

Top

Math::BaseArith - Perl extension for mixed-base number representation (like APL encode/decode)

SYNOPSIS

Top

  use Math::BaseArith;
  encode( value, base_list );
  decode( representation_list, base_list );

DESCRIPTION

Top

The inspiration for this module is a pair of functions in the APL programming language called encode (a.k.a. "representation") and decode (a.k.a. base-value). Their principal use is to convert numbers from one number base to another. Mixed number bases are permitted.

In this perl implementation, the representation of a number in a particular number base consists of a list whose elements are the digit values in that base. For example, the decimal number 31 would be expressed in binary as a list of five ones with any number of leading zeros: [0, 0, 0, 1, 1, 1, 1, 1]. The same number expressed as three hexadecimal (base 16) digits would be [0, 1, 15], while in base 10 it would be [0, 3, 1]. Fifty-one inches would be expressed in yards, feet, inches as [1, 1, 3], an example of a mixed number base.

In the following description of encode and decode, Q will mean an abstract value or quantity, R will be its representation and B will define the number base. Q will be a perl scalar; R and B are perl lists. The values in R correspond to the radix values in B.

In the examples below, assume the output of print has been altered by setting $, = ' ' and that => is your shell prompt.

&encode

Top

Encode is used to represent a number in one or more number bases. The first argument is the number to be converted, and the second argument defines the base (or bases) to be used for the representation. Consider first the representation of a scalar in a single uniform number base:

    print encode( 2, [2, 2, 2, 2] )
    => 0 0 1 0

    print encode( 5, [2, 2, 2, 2] )
    => 0 1 0 1

    print encode( 13, [2, 2, 2, 2] )
    => 1 1 0 1

    print encode( 62, [16, 16, 16] )
    => 0 3 14

The second argument is called the base list. The length of the base list determines the number of digits in the representation of the first argument. No error occurs if the length is insufficient to give a proper representation of the number. Exploring this situation will suggest other uses of encode, and may clarify the use of encode with mixed number bases.

    # The representation of 75 in base 4
    print encode( 75, [4, 4, 4, 4] )
    => 1 0 2 3

    # At least four digits are needed for the full representation
    print encode( 75, [4, 4, 4] )
    => 0 2 3

    # If fewer elements are in the second argument,
    # leading digits do not appear in the representation.
    print encode( 75, [4, 4] )
    => 2 3

    # If the second argument is a one-element list, encode is identical
    # to modulus (%)
    print encode( 75, [4] )
    => 3
    print encode( 76, [4] )
    => 0

    # The expression encode( Q, [0] ) always yields Q as the result
    print encode ( 75, [0] )
    => 75

    # This usage returns quotient and remainder
    print encode( 75, [0, 4] )
    => 18 3

    # The first quotient (18) is again divided by 4,
    # yielding a second quotient and remainder
    print encode( 75, [0, 4, 4] )
    => 4 2 3

    # The process is repeated again. Since the last quotient
    # is less than 4, the result is the same as encode(75,[4,4,4,4])
    print encode( 75, [0, 4, 4, 4] )
    => 1 0 2 3

Now consider a mixed number base: convert 175 inches into yards, feet, inches.

    # 175 inches is 14 feet, 7 inches (quotient and remainder). 
    print encode( 175, [0, 12] )
    => 14 7

    # 14 feet is 4 yards, 2 feet,
    print encode( 14, [0, 3] )
    => 4 2

    # so 175 inches is 4 yards, 2 feet, 7 inches.
    print encode( 175, [0, 3, 12] )
    => 4 2 7

&decode

Top

Decode (or base-value) is used to determine the value of the representation of a quantity in some number base. If R is a list representation (perhaps produced by the encode function described above) of some quantity Q in a number base defined by the radix list B (i.e., @R = encode($Q,@B), then the expression decode(@R,@B) yields $Q:

    print decode( [0, 0, 1, 0], [2, 2, 2, 2] )
    => 2

    print decode( [0, 1, 0, 1], [2, 2, 2, 2] )
    => 5

    print decode( [0, 3, 14], [16, 16, 16]
    => 62

The length of the representation list must be less than or equal to that of the base list.

    print decode( [1, 1, 1, 1], [2, 2, 2, 2] )
    => 15

    print decode( [1, 1, 1, 1], [2] )
    => 15

    print decode( [1], [2, 2, 2, 2] )
    => 15

    print decode( [1, 1, 1, 1], [2, 2, 2] )
    => (void) 
    raises a LENGTH ERROR

As with the encode function, mixed number bases can be used:

    # Convert 4 yards, 2 feet, 7 inches to inches.
    print decode( [4, 2, 7], [0, 3, 12] )
    => 175




    # Convert 2 days, 3 hours, 5 minutes, and 27 seconds to seconds 
    print decode( [2, 3, 5, 27], [0, 24, 60, 60] )
    => 183927

    # or to minutes.
    print decode( [2, 3, 5, 27], [0, 24, 60, 60] ) / 60
    => 3065.45

The first element of the radix list (second argument) is not used; it is required only to make the lengths match and so can be any value.

EXPORT

Top

  use Math::BaseArith;
   &encode
   &decode

  use Math::BaseArith ':all';
   &encode
   &decode
   BaseArith::debug

DEBUGGING

Top

Import the global $Math::BaseArith::debug to print debugging information to STDERR.

If set to 1, function names and parameters are printed.

If set to 2, intermediate results are also printed.

LIMITATIONS

Top

The APL encode function allows both arguments to be a list, in which case the function evaluates in dot-product fashion, generating a matrix whose columns are the representation vectors for each value in the value list; i.e. a call such as encode([15,31,32,33,75],[4,4,4,4]) would generate the following matrix;

	0 0 0 0 1
	0 1 2 2 0
	3 3 0 0 2
	3 3 0 1 3

This version of encode supports only a scalar value as the first argument.

The APL version of decode support non-integer values. This version doesn't.

AUTHOR

Top

Gary Puckering <gary.puckering@cognos.com>

SEE ALSO

Top

http://www.acm.org/sigapl/encode.htm.


Math-BaseArith documentation Contained in the Math-BaseArith distribution.

package Math::BaseArith;

use 5.006;
use strict;
use warnings;
use integer;
use Carp;

require Exporter;

our @ISA = qw(Exporter);

our($debug) = 0;

# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.

# This allows declaration	use Math::BaseArith ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = ( 'all' => [ qw(
	encode
	decode
	$Math::BaseArith::debug
) ] );

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

our @EXPORT = qw(
	encode
	decode	
);
our $VERSION = '1.00';

#######################################################################

sub encode {
	croak "Function called in void context" unless defined wantarray;
	
	my $value = shift; # value to be encoded
	my $b_listRef = shift; # list of base values

	my @b_list = @$b_listRef; # copy the base value list
	my @r_list;
	my @radix_list = (1);
	
	print STDERR "encode($value ,[@$b_listRef])" 
		if $Math::BaseArith::debug >= 1;

	my $r = 0;	
	my $b = 1;

	# Compute the radix divisors from the base list, and put in reverse order
	# [1760,3,12] miles/yards/feet/inches becomes [63360,5280,1760]
	# [2,2,2,2] becomes [16,8,4,2]
	do {
		$b *= pop @b_list;
		unshift @radix_list, $b;
	} while @b_list;

	my $i = 0;
	foreach $b (@radix_list) {
		$i++;
		if ($b > $value) {
			printf STDERR "%10d%10d%10d%10d\n", $b,$value,$r,$value%$b 
				if $Math::BaseArith::debug >= 2;
			push @r_list, 0 if $i > 1;
			next;
		}
		my $r = $b ? int($value/$b) : 0;
		printf STDERR "%10d%10d%10d%10d\n", $b,$value,$r,$b?$value%$b:0 if $Math::BaseArith::debug >= 2;
		push @r_list, $r;
		$value %= $b if $b;
	}
	
	shift @r_list while (scalar(@r_list) > scalar(@$b_listRef));
	
	return wantarray ? @r_list : \@r_list;	
}	

#######################################################################

sub decode {
	my $r_listRef = shift; # list of representation values
	my $b_listRef = shift; # list of base values

	print STDERR "decode([@$r_listRef],[@$b_listRef])" 
		if $Math::BaseArith::debug >= 1;

	if ( scalar(@$r_listRef) > scalar(@$b_listRef) && 
		 scalar(@$b_listRef) != 1 )
	{
		carp "length error";
		return; 
	}
	
	my $value = 0;
	my $b = 1;
	my $base = 1;
	my $r;

	do {
		$r = pop @$r_listRef;
		$value += $r * $base;
		printf STDERR "%10d%10d%10d%10d\n", $r,$b,$base,$value 
			if $Math::BaseArith::debug >= 2;
		$b = pop @$b_listRef || $b;
		$base *= $b;
	} while @$r_listRef;
	$value;
}

1;
__END__