(repost) Math problem - converting between arbitrary bases in perl - help!

A

! aaa

I Posted earlier to Newsgroups: comp.lang.perl,alt.perl,alt.math,
but this looks like the more appropriate place in hindsight...


Hi all - I've been trying to write a small sub to convert form an input base
(eg: 16) to an output base (eg:64) and back.

The reason is that I need to communicate tristate (base 3) data efficiently
via DNS (base 37 a-z0-9 and '-').

I *could* do it by converting into a "bigmath binary" structure I guess, but
I thought there might be a more elegant way...

but such a solution eludes me.

Can anyone think how to approach the problem?

Below is my NON-working attempt. I thought of creating a "buffer" (I called
it $remainder) through which I can
convert incoming stuff to the outgoing stuff, but (A) I'm not sure if this
is even possible, and (B) if so, I'm lost
near the end, where I'm "nibbling off" parts of the $remainder variable as I
output results. In short. I'm wrong
there someplace :)

# Sorry I forgot to comment it :))




#!perl

use strict;

# my($from_base)=10; my($to_base)=94; my($data)='92356234';
# my($from_base)=10; my($to_base)=16; my($data)='283036414'; # 283036414
should be 10DECAFE in hex
my($from_base)=16; my($to_base)=10; my($data)='C0DECAFE'; # 283036414 should
be 10DECAFE in hex

my $b2 = &baseconv($from_base,$to_base,$data);
print "Base$from_base($data)=Base$to_base($b2)\n";

sub baseconv {
my($from_base,$to_base,$data)=@_;
my
$collseq='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!"#$
%&\'()*+,-./:;<=>?@[\]^_`{|}~'; # 94 "digits"
my $remainder=0; my $pow=0; my $ret=''; my $tmp; my $mop=0;
while(($data ne '')||($remainder)) {
$remainder+=index($collseq,chop $data)*($from_base**($pow++))
while(($data ne '')&&($remainder<(($to_base*$from_base)**2)));
print "remainder=$remainder\n";

my($tgt)=($to_base*$from_base)**2; $mop=0;
while((($tgt % $from_base)==0)||(($data eq '')&&($remainder>0))) {
$pow--; $tgt=$tgt/$from_base;
$tmp=$remainder % $to_base;
$ret.=substr($collseq,$tmp,1); $remainder=($remainder-$tmp)/$to_base;
print "ret=$ret data=$data remainder=$remainder tgt=$tgt\n";
exit(0) if($pow<-10);
}

# while(($remainder>=$to_base)||($data eq '')) { $tmp=$remainder %
$to_base; $ret.=substr($collseq,$tmp,1); $remainder=($remaind
er-$tmp)/$to_base; }


print "ret=$ret\n";
}
return reverse($ret);
}
 
T

thundergnat

! aaa said:
I Posted earlier to Newsgroups: comp.lang.perl,alt.perl,alt.math,
but this looks like the more appropriate place in hindsight...


Hi all - I've been trying to write a small sub to convert form an input base
(eg: 16) to an output base (eg:64) and back.

The reason is that I need to communicate tristate (base 3) data efficiently
via DNS (base 37 a-z0-9 and '-').

I *could* do it by converting into a "bigmath binary" structure I guess, but
I thought there might be a more elegant way...

but such a solution eludes me.

Can anyone think how to approach the problem?

Hmmm. I was intrigued and whomped this subroutine together to do base
conversions. I used your "digit" sequence but was having a hard time
following your logic, so I wrote the rest from scratch. It isn't as
efficient as it could be, but it works.

One comment on your script, whitespace is cheap, don't be afraid to use it.


Any lines in the subroutine that aren't indented at least 4 spaces are
part of the previous line. You may need to rejoin them for the script to
work. (News reader wrapping) I broke a few of the lines up to fit in
without wrapping.



#!/usr/bin/perl

use strict;
use warnings;

# Convert any arbitrary base number (up to base 94) to any other
# arbitrary base. Feed the subroutine the base of the existing number,
# the desired base and the the number to be converted. This will
# probably be inaccurate for anthing larger than 32 bit (or whatever
# your native integer is.) If you need larger numbers, you can probably
# adapt this to use Math::BigInt without too much pain.


print baseconvert( 16, 10, 'C0DECAFE' ), "\n";

sub baseconvert{
my ( $from_base, $to_base, $data) = @_;
my $digit_sequence = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
'abcdefghijklmnopqrstuvwxyz!"#$%&\'()*+,-./:;<=>?@[\]^'.
'_`{|}~'; # 94 "digits"
my $from_digits = substr( $digit_sequence, 0, $from_base );
my $to_digits = substr( $digit_sequence, 0, $to_base );
my $number = '';

# convert to base 10 integer
my $integer = 0;
my @digit_array = reverse split //, $data;
for ( 0 .. $#digit_array ){
die "$data is not a valid base $from_base number."
if ( index( $from_digits, $digit_array[$_]) < 0 );
$integer +=
index( $from_digits, $digit_array[$_] ) * $from_base ** $_;
}

# then convert to arbitrary base
while ( $integer > 0 ){
my $digit = $integer % $to_base;
$number = substr( $to_digits, $digit, 1 ) . $number;
$integer = int( $integer / $to_base );
}
return $number;
}
 
J

Jay Tilton

: Hi all - I've been trying to write a small sub to convert form an input base
: (eg: 16) to an output base (eg:64) and back.
:
: The reason is that I need to communicate tristate (base 3) data efficiently
: via DNS (base 37 a-z0-9 and '-').

Look at Math::BaseCalc.

use Math::BaseCalc;
my $b03 = Math::BaseCalc ->new(digits => [0..2]);
my $b37 = Math::BaseCalc ->new(digits => ['a'..'z',0..9,'-']);
print $b37 ->to_base( $b03 ->from_base( $some_trinary_number ) );
 
A

! aaa

Thanks!... but... I forgot to specifically mention that I want arbitrary
precision - oops!!! (though I did mention bigmath - but I'd prefer
to exclude that giant library if possible - I'm trying to build something
"lightweight").

I'm wanting at least 40 binary bits precision initially, probably upto 128
in the
very near term, then more later (to convert ascii (base 128 or base 256) to
efficient transfer characters: base94, 74-digit lines)- hence my "strange"
code
in the middle that worked through the input string scalar and attempted to
build
the output into a string scalar using nothing more than integers. Note
"attempted"
- the output building fails probably because I'm not even sure if this is
possible to
do.

Math::BaseCalc only functions on CPU datatypes, so doesn't seem useful :-(


thundergnat said:
! aaa said:
I Posted earlier to Newsgroups: comp.lang.perl,alt.perl,alt.math,
but this looks like the more appropriate place in hindsight...


Hi all - I've been trying to write a small sub to convert form an input base
(eg: 16) to an output base (eg:64) and back.

The reason is that I need to communicate tristate (base 3) data efficiently
via DNS (base 37 a-z0-9 and '-').

I *could* do it by converting into a "bigmath binary" structure I guess, but
I thought there might be a more elegant way...

but such a solution eludes me.

Can anyone think how to approach the problem?

Hmmm. I was intrigued and whomped this subroutine together to do base
conversions. I used your "digit" sequence but was having a hard time
following your logic, so I wrote the rest from scratch. It isn't as
efficient as it could be, but it works.

One comment on your script, whitespace is cheap, don't be afraid to use it.


Any lines in the subroutine that aren't indented at least 4 spaces are
part of the previous line. You may need to rejoin them for the script to
work. (News reader wrapping) I broke a few of the lines up to fit in
without wrapping.



#!/usr/bin/perl

use strict;
use warnings;

# Convert any arbitrary base number (up to base 94) to any other
# arbitrary base. Feed the subroutine the base of the existing number,
# the desired base and the the number to be converted. This will
# probably be inaccurate for anthing larger than 32 bit (or whatever
# your native integer is.) If you need larger numbers, you can probably
# adapt this to use Math::BigInt without too much pain.


print baseconvert( 16, 10, 'C0DECAFE' ), "\n";

sub baseconvert{
my ( $from_base, $to_base, $data) = @_;
my $digit_sequence = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
'abcdefghijklmnopqrstuvwxyz!"#$%&\'()*+,-./:;<=>?@[\]^'.
'_`{|}~'; # 94 "digits"
my $from_digits = substr( $digit_sequence, 0, $from_base );
my $to_digits = substr( $digit_sequence, 0, $to_base );
my $number = '';

# convert to base 10 integer
my $integer = 0;
my @digit_array = reverse split //, $data;
for ( 0 .. $#digit_array ){
die "$data is not a valid base $from_base number."
if ( index( $from_digits, $digit_array[$_]) < 0 );
$integer +=
index( $from_digits, $digit_array[$_] ) * $from_base ** $_;
}

# then convert to arbitrary base
while ( $integer > 0 ){
my $digit = $integer % $to_base;
$number = substr( $to_digits, $digit, 1 ) . $number;
$integer = int( $integer / $to_base );
}
return $number;
}
 
J

Jay Tilton

[Please don't top-post replies. Please trim reply quoted material to the
minimum necessary to establish context. See the clpm posting guidelines
for other useful advice.]


: Thanks!... but... I forgot to specifically mention that I want arbitrary
: precision - oops!!! (though I did mention bigmath - but I'd prefer
: to exclude that giant library if possible - I'm trying to build something
: "lightweight").
:
: I'm wanting at least 40 binary bits precision initially, probably upto 128
: in the very near term, then more later

: Math::BaseCalc only functions on CPU datatypes, so doesn't seem useful :-(

But the code in Math::BaseCalc provides a nice template (superclass, even)
for creating a module that handles arbitrary precision. Just alter one
Math::BaseCalc method to handle bigints and let the overloaded math
operators in Math::BigInt do the Right Thing.

#!perl
use warnings;
use strict;

{{
package Math::BaseCalc::BigInt;
use base 'Math::BaseCalc';
use Math::BigInt;

sub from_base {
my $self = shift;
my $dignum = @{$self ->{digits}};
my $result = Math::BigInt ->new(0);
my $str = reverse shift;
$result = $result * $dignum + $self ->{trans}{chop $str}
while length($str);
return $result;
}

}}

my $b03 = Math::BaseCalc::BigInt ->new(digits => [0..2]);
my $b37 = Math::BaseCalc::BigInt ->new(digits => ['a'..'z',0..9,'-']);

my $i = '1' x 2000; # huge number, even in trinary.
my $j = $b37 -> to_base( $b03 ->from_base( $i ) );
__END__
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top