use strict; use warnings; use feature 'say'; use Math::Complex; use List::AllUtils qw(sum mesh); use ntheory qw; sub zip { my($a,$b) = @_; my($la, $lb) = (length $a, length $b); my $l = '0' x abs $la - $lb; $a .= $l if $la < $lb; $b .= $l if $lb < $la; (join('', mesh(@{[split('',$a),]}, @{[split('',$b),]})) =~ s/0+$//r) or 0; } sub base_i { my($num,$radix,$precision) = @_; die unless $radix > -37 and $radix < -1; return '0' unless $num; my $value = $num; my $result = ''; my $place = 0; my $upper_bound = 1 / (-$radix + 1); my $lower_bound = $radix * $upper_bound; $value = $num / $radix ** ++$place until $lower_bound <= $value and $value < $upper_bound; while (($value or $place > 0) and $place > $precision) { my $digit = int $radix * $value - $lower_bound; $value = $radix * $value - $digit; $result .= '.' unless $place or not index($result, '.'); $result .= $digit == -$radix ? todigitstring($digit-1, -$radix) . '0' : (todigitstring($digit, -$radix) or '0'); $place--; } $result } sub base_c { my($num, $radix, $precision) = @_; die "Base $radix out of range" unless (-6 <= $radix->Im or $radix->Im <= -2) or (2 <= $radix->Im or $radix->Im <= 6); my ($re,$im); defined $num->Im ? ($re, $im) = ($num->Re, $num->Im) : $re = $num; my ($re_wh, $re_fr) = split /\./, base_i( $re, -1 * int($radix->Im**2), $precision); my ($im_wh, $im_fr) = split /\./, base_i( ($im/($radix->Im)), -1 * int($radix->Im**2), $precision); $_ //= '' for $re_fr, $im_fr; my $whole = reverse zip scalar reverse($re_wh), scalar reverse($im_wh); my $fraction = zip $im_fr, $re_fr; $fraction eq 0 ? "$whole" : "$whole.$fraction" } sub parse_base { my($str, $radix) = @_; return -1 * parse_base( substr($str,1), $radix) if substr($str,0,1) eq '-'; my($whole, $frac) = split /\./, $str; my $fraction = 0; my $k = 0; $fraction = sum map { (fromdigits($_, int $radix->Im**2) || 0) * $radix ** -($k++ +1) } split '', $frac if $frac; $k = 0; $fraction + sum map { (fromdigits($_, int $radix->Im**2) || 0) * $radix ** $k++ } reverse split '', $whole; } for ( [ 0*i, 2*i], [1+0*i, 2*i], [5+0*i, 2*i], [ -13+0*i, 2*i], [ 9*i, 2*i], [ -3*i, 2*i], [7.75-7.5*i, 2*i], [0.25+0*i, 2*i], [5+5*i, 2*i], [5+5*i, 3*i], [5+5*i, 4*i], [5+5*i, 5*i], [5+5*i, 6*i], [5+5*i, -2*i], [5+5*i, -3*i], [5+5*i, -4*i], [5+5*i, -5*i], [5+5*i, -6*i] ) { my($v,$r) = @$_; my $ibase = base_c($v, $r, -6); my $rt = cplx parse_base($ibase, $r); $rt->display_format('format' => '%.2f'); printf "base(%3s): %10s => %9s => %13s\n", $r, $v, $ibase, $rt; } say ''; say 'base( 6i): 31432.6219135802-2898.5266203704*i => ' . base_c(31432.6219135802-2898.5266203704*i, 0+6*i, -3);