How to convert bigint to byte array in perl similar as java - arrays

I am trying to replicate some java code in perl.
The operation, I need to perform is to convert Java BigInteger to byte array
Java Code:
public class Main {
public static void main(String[] args) {
BigInteger n = new BigInteger("1677259342285725925376");
System.out.println(Arrays.toString(n.toByteArray()));
}
}
Output: [90, -20, -90, 53, 78, -38, 2, -128, 0]
As, new in perl, tried to search and got help from https://www.perlmonks.org/?node_id=229290
Perl Code:
sub bigint_to_bytearray {
my $bigint = shift;
my #bytes;
while(1) {
my ($q,$r) = $bigint->brsft(8);
push(#bytes,$r+0);
last if $q == 0;
$bigint = Math::BigInt->new($q);
}
return #bytes;
}
$n = new Math::BigInt('1677259342285725925376');
my #array = bigint_to_bytearray($n);
print "\\tOUTPUT ARRAY = ", join ', ', #array, "\n";
I am getting error
Use of uninitialized value $r in addition (+) at path/test.pl line 11.
Use of uninitialized value $r in addition (+) at path/test.pl line 11.
Use of uninitialized value $r in addition (+) at path/test.pl line 11.
Use of uninitialized value $r in addition (+) at path/test.pl line 11.
Use of uninitialized value $r in addition (+) at path/test.pl line 11.
Use of uninitialized value $r in addition (+) at path/test.pl line 11.
Use of uninitialized value $r in addition (+) at path/test.pl line 11.
Use of uninitialized value $r in addition (+) at path/test.pl line 11.

This doesn't support negative numbers.
sub bigint_to_bytearray {
my $bigint = shift;
die "Negative numbers not supported" if $bigint->is_neg;
my #bytes = unpack('c*', $bigint->to_bytes);
unshift #bytes, 0x00 if $bytes[0] < 0; # Add sign bit if necessary.
return #bytes;
}
The above requires a relatively new version of Math::BigInt. The following is less efficient, but it works on far older versions:
sub bigint_to_bytearray {
my $bigint = shift;
die "Negative numbers not supported" if $bigint->is_neg;
my #bytes = unpack('c*', pack('H*', substr($bigint->as_hex, 2)));
unshift #bytes, 0x00 if $bytes[0] < 0; # Add sign bit if necessary.
return #bytes;
}
my #bytes = unpack('c*', $bigint->to_bytes);
can be replaced with
my #bytes =
map { $_ >= 0x80 ? 0x100 - $_ : $_ }
map ord,
split //,
$bigint->to_bytes;
and
my #bytes = unpack('c*', pack('H*', substr($bigint->as_hex, 2)));
can be replaced with
my $hex = substr($bigint->as_hex, 2);
$hex = "0$hex" if length($hex) % 2;
my #bytes =
map { $_ >= 0x80 ? 0x100 - $_ : $_ }
map hex,
$hex =~ /../g;

Related

Counting how many numbers greater than 5 in a given array

I am having an error saying that prototype not terminated at filename.txt line number 113 where as line number 113 belongs to a different program which is running successfully.
sub howmany(
my #H = #_;
my $m = 0;
foreach $x (#H) {
if ( $x > 5 ) {
$m +=1;
}
else {
$m +=0;
}
}
print "Number of elements greater than 5 is equal to: $m \n";
}
howmany(1,6,9);
The sub keyword should be followed by { } not ( ) (if you define a simple function), that's why the error
prototype not terminated
After this, always start with : use strict; use warnings;
Put this and debug your script, there's more errors.
Last but not least, indent your code properly, using an editor with syntax highlighting, you will save many time debugging
The error is due to parenthesis.
Never do $m += 0; As you actually load processor for nothing. Of course it's not gonna be visible on such a small function, but...
sub howmany {
my $m = 0;
foreach (#_) {
$m++ if ($_ > 5);
}
print "Number of elements greater than 5 is equal to: $m \n";
}
howmany(1,6,9);

Perl array not saving all variables

I have a Perl program that's parsing data from an external program and saving it to an array. However, some of the data isn't being saved and comes back blank when trying to retrieve it later.
Here's the code parsing the data, which comes through as #packetData:
if(#packetData[0] ne ""){
if(!$detectedClient{"#packetData[0]"}) {
my $rawSignal = average(#packetData[2]);
my $distance = 10**((27.55-(20*logten(2437))+abs($rawSignal))/20);
my #newClient = ($rawSignal, # Signal (dBm)
1, # Count
#packetData[0], # Source MAC
time(), # Last seen
$distance); # Distance (m)
$detectedClient{"#packetData[0]"} = [#newClient];
$uniqueClient++;
print "++ New probe request from #packetData[0] [$rawSignal dBm, $distance m]\n";
} else {
$detectedClient{"#packetData[0]"}[1]++;
$detectedClient{"#packetData[0]"}[3] = time();
}
}
The print statement shows the signal and distance fine, but trying to show it from the detectedClient array gives with the code below gives a blank space:
for $key2 ( keys %detectedClient) {
#Signal, Count, MAC, Time
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($detectedClient{$key2}[3]);
my $lastSeen = sprintf("%04d/%02d/%02d %02d:%02d:%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec);
print STDOUT sprintf("!! %-20s %10s %-20s\n", $detectedClient{$key2}[2], $detectedClient{$key2}[1], $lastSeen, $detectedClient{$key2}[0], $detectedClient{$key2}[4]);
}
Any ideas why this is happening?
The signal and the distance aren't printed because your pattern ("!! %-20s %10s %-20s\n") doesn't reference them (the 4th and 5th values). Fix:
printf("!! %-20s %5s %-20s %20s %20s\n",
$detectedClient{$key2}[2],
$detectedClient{$key2}[1],
$lastSeen,
$detectedClient{$key2}[0],
$detectedClient{$key2}[4],
);
Tested using the following: (Had to change for $key2 to for my $key2 too)
use strict;
use warnings qw( all );
use List::Util qw( sum );
sub average { sum(#{ $_[0] })/#{ $_[0] } }
sub logten { log($_[0])/log(10) }
my #packetData = ("foo", undef, [ 4, 5 ]);
my %detectedClient;
my $uniqueClient;
if(#packetData[0] ne ""){
if(!$detectedClient{"#packetData[0]"}) {
my $rawSignal = average(#packetData[2]);
my $distance = 10**((27.55-(20*logten(2437))+abs($rawSignal))/20);
my #newClient = ($rawSignal, # Signal (dBm)
1, # Count
#packetData[0], # Source MAC
time(), # Last seen
$distance); # Distance (m)
$detectedClient{"#packetData[0]"} = [#newClient];
$uniqueClient++;
print "++ New probe request from #packetData[0] [$rawSignal dBm, $distance m]\n";
} else {
$detectedClient{"#packetData[0]"}[1]++;
$detectedClient{"#packetData[0]"}[3] = time();
}
}
for my $key2 ( keys %detectedClient) {
#Signal, Count, MAC, Time
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($detectedClient{$key2}[3]);
my $lastSeen = sprintf("%04d/%02d/%02d %02d:%02d:%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec);
printf("!! %-20s %5s %-20s %20s %20s\n",
$detectedClient{$key2}[2],
$detectedClient{$key2}[1],
$lastSeen, $detectedClient{$key2}[0],
$detectedClient{$key2}[4],
);
}
Output:
Scalar value #packetData[0] better written as $packetData[0] at a.pl line 18.
Scalar value #packetData[0] better written as $packetData[0] at a.pl line 19.
Scalar value #packetData[2] better written as $packetData[2] at a.pl line 20.
Scalar value #packetData[0] better written as $packetData[0] at a.pl line 24.
Scalar value #packetData[0] better written as $packetData[0] at a.pl line 27.
Scalar value #packetData[0] better written as $packetData[0] at a.pl line 29.
Scalar value #packetData[0] better written as $packetData[0] at a.pl line 31.
Scalar value #packetData[0] better written as $packetData[0] at a.pl line 32.
++ New probe request from foo [4.5 dBm, 0.0164302613184467 m]
!! foo 1 2016/05/16 15:19:38 4.5 0.0164302613184467
Next time, please provide a minimal, runnable demonstration of the problem.
The syntax for an entire array is #array but to access individual elements, it is (e.g.) $array[0]
Also, there is a better way to write the inner if in your context.
Third, you're using an array reference for detectedClient, so the syntax could be changed from $detectedClient{$smac}[1]++ to $detectedClient{$smac}->[1]++ that would be clearer as to intent. I've changed it in the code below, and you'll could also change it when printing as well.
Here's your code modified to reflect all that and shortened to use a scalar:
my $smac = $packetData[0];
if ($smac ne "") {
if(! ref($detectedClient{$smac})) {
my $rawSignal = average($packetData[2]);
my $distance = 10**((27.55-(20*logten(2437))+abs($rawSignal))/20);
my #newClient = ($rawSignal, # Signal (dBm)
1, # Count
$smac, # Source MAC
time(), # Last seen
$distance); # Distance (m)
$detectedClient{$smac} = [#newClient];
$uniqueClient++;
print "++ New probe request from $smac [$rawSignal dBm, $distance m]\n";
} else {
my $ptr = $detectedClient{$smac};
$ptr->[1]++;
$ptr->[3] = time();
}
}
UPDATE:
Applying the use of a scalar and a bit of reindent, the print code looks like:
for $key2 ( keys %detectedClient) {
my $ptr = $detectedClient{$key2};
#Signal, Count, MAC, Time
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
localtime($ptr->[3]);
my $lastSeen = sprintf("%04d/%02d/%02d %02d:%02d:%02d",
$year+1900, $mon+1, $mday, $hour, $min, $sec);
print STDOUT sprintf("!! %-20s %10s %-20s\n",
$ptr->[2], $ptr->[1],
$lastSeen, $ptr->[0], $ptr->[4]);
}
Now, it's a little easier to see [for me, at least ;-)] that the format is short by two fields [so the last two arguments won't be printed].
So, something like this instead:
printf("!! %-20s %10s %-20s %10s %10s\n",

Perl: how to test if every element in an array is greater than a value?

I need to test a file with each line having the same number of columns and each entry is some value and I want to only select those lines with every values greater than, say 0.5.
I know I can loop through the array in each line by doing something like this:
open (IN, shift #ARGV);
while (<IN>){
chomp;
my $count = 0;
my #array = split/\t/;
foreach (#array){
if ($_ > 0.5) {
$count ++;
}
}
if ($count == scalar #array){
print $_,"\n";
}
}
close IN;
This is kinda long and I'm wondering if there is a better way to do it?
Thanks.
Use all from List::Util - it checks if passed code block (often with condition) returns true for all elements of list.
use List::Util qw(all);
if (all { $_ > 0.5 } #array) {
print "Pass!"
}
It will even short-circuit for you, terminating as soon as it finds first false value, producing most speed-effective result.
my #array = 10 .. 20;
# compare size of array with list size of grep
if (#array == grep { $_ > 0.5 } #array) {
print "All are greater than 0.5\n";
}

Perl Modification of non creatable array value attempted, subscript -1

I have a Perl-Script, which executes a recursive function. Within it compares two elements of a 2dimensional Array:
I call the routine with a 2D-Array "#data" and "0" as a starting value. First I load the parameters into a separate 2D-Array "#test"
Then I want to see, if the array contains only one Element --> Compare if the last Element == the first. And this is where the Error occurs: Modification of non creatable array value attempted, subscript -1.
You tried to make an array value spring into existence, and the subscript was probably negative, even counting from end of the array backwards.
This didn't help me much...I'm pretty sure it has to do with the if-clause "$counter-1". But I don't know what, hope you guys can help me!
routine(#data,0);
sub routine {
my #test #(2d-Array)
my $counter = $_[-1]
for(my $c=0; $_[$c] ne $_[-1]; $c++){
for (my $j=0; $j<13;$j++){ #Each element has 13 other elements
$test[$c][$j] = $_[$c][$j];
}
}
if ($test[$counter-1][1] eq $test[-1][1]{
$puffertime = $test[$counter][4];
}
else{
for (my $l=0; $l<=$counter;$l++){
$puffertime+= $test[$l][4]
}
}
}
#
#
#
if ($puffertime <90){
if($test[$counter][8]==0){
$counter++;
routine(#test,$counter);
}
else{ return (print"false");}
}
else{return (print "true");}
Weird thing is that I tried it out this morning, and it worked. After a short time of running he again came up with this error message. Might be that I didn't catch up a error constellation, which could happen by the dynamic database-entries.
Your routine() function would be easier to read if it starts off like this:
sub routine {
my #data = #_;
my $counter = pop(#data);
my #test;
for(my $c=0; $c <= $#data; $c++){
for (my $j=0; $j<13;$j++){ #Each element has 13 other elements
$test[$c][$j] = $data[$c][$j];
}
}
You can check to see if #data only has one element by doing scalar(#data) == 1 or $#data == 0. From your code snippet, I do not see why you need to copy the data to passed to routine() to #test. Seems superfluous. You can just as well skip all this copying if you are not going to modify any of the data passed to your routine.
Your next code might look like this:
if ($#test == 0) {
$puffertime = $test[0][4];
} else {
for (my $l=0; $l <= $counter; $l++) {
$puffertime += $test[$l][4];
}
}
But if your global variable $puffertime was initialized to zero then you can replace this code with:
for (my $l=0; $l <= $counter; $l++) {
$puffertime += $test[$l][4];
}

I can't access hash values

I have a program that creates an array of hashes while parsing a FASTA file. Here is my code
use strict;
use warnings;
my $docName = "A_gen.txt";
my $alleleCount = 0;
my $flag = 1;
my $tempSequence;
my #tempHeader;
my #arrayOfHashes = ();
my $fastaDoc = open(my $FH, '<', $docName);
my #fileArray = <$FH>;
for (my $i = 0; $i <= $#fileArray; $i++) {
if ($fileArray[$i] =~ m/>/) { # creates a header for the hashes
$flag = 0;
$fileArray[$i] =~ s/>//;
$alleleCount++;
#tempHeader = split / /, $fileArray[$i];
pop(#tempHeader); # removes the pointless bp
for (my $j = 0; $j <= scalar(#tempHeader)-1; $j++) {
print $tempHeader[$j];
if ($j < scalar(#tempHeader)-1) {
print " : "};
if ($j == scalar(#tempHeader) - 1) {
print "\n";
};
}
}
# push(#arrayOfHashes, "$i");
if ($fileArray[$i++] =~ m/>/) { # goes to next line
push(#arrayOfHashes, {
id => $tempHeader[0],
hla => $tempHeader[1],
bpCount => $tempHeader[2],
sequence => $tempSequence
});
print $arrayOfHashes[0]{id};
#tempHeader = ();
$tempSequence = "";
}
$i--; # puts i back to the current line
if ($flag == 1) {
$tempSequence = $tempSequence.$fileArray[$i];
}
}
print $arrayOfHashes[0]{id};
print "\n";
print $alleleCount."\n";
print $#fileArray +1;
My problem is when the line
print $arrayOfHashes[0]{id};
is called, I get an error that says
Use of uninitialized value in print at fasta_tie.pl line 47, line 6670.
You will see in the above code I commented out a line that says
push(#arrayOfHashes, "$i");
because I wanted to make sure that the hash works. Also the data prints correctly in the
desired formatting. Which looks like this
HLA:HLA00127 : A*74:01 : 2918
try to add
print "Array length:" . scalar(#arrayOfHashes) . "\n";
before
print $arrayOfHashes[0]{id};
So you can see, if you got some content in your variable. You can also use the module Data::Dumper to see the content.
use Data::Dumper;
print Dumper(\#arrayOfHashes);
Note the '\' before the array!
Output would be something like:
$VAR1 = [
{
'sequence' => 'tempSequence',
'hla' => 'hla',
'bpCount' => 'bpCount',
'id' => 'id'
}
];
But if there's a Module for Fasta, try to use this. You don't have to reinvent the wheel each time ;)
First you do this:
$fileArray[$i] =~ s/>//;
Then later you try to match like this:
$fileArray[$i++] =~ m/>/
You step through the file array, removing the first "greater than" sign in the line on each line. And then you want to match the current line by that same character. That would be okay if you only want to push the line if it has a second "greater than", but you will never push anything into the array if you only expect 1, or there turns out to be only one.
Your comment "puts i back to the current line" shows what you were trying to do, but if you only use it once, why not use the expression $i + 1?
Also, because you're incrementing it post-fix and not using it for anything, your increment has no effect. If $i==0 before, then $fileArray[$i++] still accesses $fileArray[0], only $i==1 after the expression has been evaluated--and to no effect--until being later decremented.
If you want to peek ahead, then it is better to use the pre-fix increment:
if ($fileArray[++$i] =~ m/>/) ...

Resources