Truth Table Generation for the given input - perl

I want to generate a truth table for the given input.Suppose if i give input 2 the output will be
10 01 11 00
if the input is 3 the output will be
111 000 110 101 011 100 001 010
i have a code snippet
#!/usr/bin/perl
#print "a|b|c\n";
for $a (1, 0){
for $b (1, 0){
for $c (1,0) {
for $d ( 1,0)
{
print "$a $b $c $d";
#print $x = ($a & $b & $c);
print "\n";
}
}
}
}
print "\n";
above code is for 4.
i don't know how to do this without writing multiple for loops. Here for value 2 i need to write two for loops and so on.
can any body tell me how to tweak this code for several input values.
Any help would be great appreciated

Recursion
Here is a simple solution using recursion:
#!/usr/bin/perl -w
my $variables=$ARGV[0]||0;
show_combinations($variables);
sub show_combinations { my($n,#prefix)=#_;
if($n > 0) {
show_combinations( $n-1, #prefix, 0);
show_combinations( $n-1, #prefix, 1);
} else {
print "#prefix\n";
}
}
Here are some sample cases:
> script.pl 1
0
1
> script.pl 2
0 0
0 1
1 0
1 1
> script.pl 3
0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1

I'm no Perl expert, so you might need to clean this up, but if forced to use Perl I'd probably do something like this:
#!/usr/bin/perl
my ($n) = #ARGV;
printf("%0*b\n", $n, $_) for 0 .. (1 << $n) - 1;

This is simple one line Perl code using module Math::Cartesian::Product.
use Math::Cartesian::Product;
cartesian {print "#_\n"} ([0..1]) x $ARGV[0];
Output
./sample.pl 2
0 0
0 1
1 0
1 1
./sample.pl 3
0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1

I don't know Perl so this may not work:
-- loop from 0 to (2^n)-1, where n is the number of digits in your cases;
-- convert each number to its n-digit binary representation;

Here is a generalization of my previous solution using Math::BigInt. This is an iterative solution:
#!/usr/bin/perl
use strict;
use warnings;
use Math::BigInt try => 'GMP';
my $n_bits = $ARGV[0] || 0;
my $it = make_it($n_bits);
while ( defined(my $bits = $it->()) ) {
print "$bits\n";
}
sub make_it {
my ($n_bits) = #_;
my $limit = Math::BigInt->new('2');
$limit->blsft($n_bits - 1);
my $next = Math::BigInt->new('-1');
return sub {
$next->binc;
return unless $next->bcmp($limit) < 0;
my $bits = $next->as_bin;
$bits =~ s/^0b//;
if ( (my $x = length $bits) < $n_bits ) {
$bits = '0' x ($n_bits - $x) . $bits;
}
return $bits;
}
}
You can use the %b format specifier for printf:
use strict;
use warnings;
my ($count) = #ARGV;
my $fmt = "%0${count}b";
my $n = 2**$count - 1;
for my $c (0 .. $n) {
my #bits = split //, sprintf $fmt, $c;
print "#bits\n";
}
This will only work for $count values less than 32.
Output:
C:\Temp> y 3
0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1

I am surprised no one has mentioned glob as a solution here:
perl -e 'print join "\n", glob("{0,1}" x shift || 1 )' -- 3
This prints:
000
001
010
011
100
101
110
111
glob is very handy for computing string permutations.
Here is the above, in a cleaner, non-one-liner form:
use strict;
use warnings;
my $symbol_count = shift || 1;
my #permutations = glob( '{0,1}' x $symbol_count );
print join "\n", #permutations;

Related

File parsing using perl

I am stuck in middle , i need help .
i have two files :
file1:
Total X :
Total y :
Total z :
Total t :
file 2:
4790351 4786929 3422 0
84860 84860 0 0
206626 206626 0 0
93902 93823 79 0
now i want output like this in third file
Total X : 4790351 4786929 3422 0
Total y : 84860 84860 0 0
Total z : 206626 206626 0 0
Total t : 93902 93823 79 0
This is my code below to try the parsing :Please help me getting the required output
while ( not eof $tata and not eof $outfh )
{
my #vals1 = split /":"/,<$tata>;
my #vals2 = split /\s+/, <$outfh>;
my #sum = join "\t", map { $vals1,$vals2[$_]} 0 .. $#vals2;
printf $_ for #sum,"\n";
}
use strict;
use warnings;
use 5.020;
use autodie;
use Data::Dumper;
open my $FILE1, "<", "file1.txt";
open my $FILE2, "<", "file2.txt";
open my $OUTFILE, ">", "results.txt";
my $first_line = <$FILE1>;
close $FILE1;
my #line_prefixes = split /\s*:\s*/, $first_line;
while (my $line = <$FILE2>) {
print {$OUTFILE} "$line_prefixes[$. - 1]: $line";
}
close $FILE2;
close $OUTFILE;
$. is the current line number in the file ($. equals 1 for the first line).
A sample run:
/pperl_programs$ cat file1.txt
Total X : Total y : Total z : Total t :
~/pperl_programs$ cat file2.txt
4790351 4786929 3422 0
84860 84860 0 0
206626 206626 0 0
93902 93823 79 0
~/pperl_programs$ cat results.txt
~/pperl_programs$ perl myprog.pl
~/pperl_programs$ cat results.txt
Total X: 4790351 4786929 3422 0
Total y: 84860 84860 0 0
Total z: 206626 206626 0 0
Total t: 93902 93823 79 0
~/pperl_programs$
For your altered files:
use strict;
use warnings;
use 5.020;
use autodie;
use Data::Dumper;
open my $FILE1, "<", "file1.txt";
open my $FILE2, "<", "file2.txt";
open my $OUTFILE, ">", "results.txt";
chomp(my #line_prefixes = <$FILE1>);
close $FILE1;
while (my $line = <$FILE2>) {
print {$OUTFILE} "$line_prefixes[$.-1] $line";
}
close $FILE2;
close $OUTFILE;
Sample output:
~/pperl_programs$ cat file1.txt
Total X :
Total y :
Total z :
Total t :
~/pperl_programs$ cat file2.txt
4790351 4786929 3422 0
84860 84860 0 0
206626 206626 0 0
93902 93823 79 0
~/pperl_programs$ cat results.txt
~/pperl_programs$ perl 1.pl
~/pperl_programs$ cat results.txt
Total X : 4790351 4786929 3422 0
Total y : 84860 84860 0 0
Total z : 206626 206626 0 0
Total t : 93902 93823 79 0
If your files are big, you probably don't want to read the whole first file into memory. If that's the case, you can read each file line by line:
use strict;
use warnings;
use 5.020;
use autodie;
use Data::Dumper;
open my $FILE1, "<", "file1.txt";
open my $FILE2, "<", "file2.txt";
open my $OUTFILE, ">", "results.txt";
while (!eof($FILE1) and !eof($FILE2) ) {
my $line_prefix = <$FILE1>;
chomp $line_prefix;
my $numbers_line = <$FILE2>;
chomp $numbers_line;
my #numbers = split /\s+/, $numbers_line;
my $fifth_column = $numbers[1] / $numbers[0];
say {$OUTFILE} "$line_prefix $numbers_line $fifth_column";
}
close $FILE1;
close $FILE2;
close $OUTFILE;
Your specification has a few loose ends; for instance - what if there are more lines in file2 then there are total labels in file1? Do you want the spaces in the first input file ignored? Do you want the spaces in the output file specifically as shown? ... and you really don't want any totals calculated??
I've presumed "yes" to most of these questions. My solution is driven by the second data file - which means that if there are more total labels then there are lines of data, they are going to be ignored. It also means that if there are more data lines in file2 then there are labels in file1, the program will simply make up the label - No Label?.
Finally, just in case you want to add these numbers up at some point, I've included but commented the sum function from List::Util.
use v5.12;
use File::Slurp;
# use File::Util qw( sum );
my $file1 = "file1.txt";
my $file2 = "file2.txt";
my $file3 = "file3.txt";
open(my $outfh, '>', $file3) or die "$file3: $!";
my #vals1 = split /\s*:\s*\n/ , read_file($file1);
my #vals2 = read_file($file2);
while (my $line = shift #vals2) {
chomp $line;
# my $total = sum split(" ", $line);
printf $outfh "%s : %s\n" , shift #vals1 // "No Label?" , $line ;
}
#
# $ cat file3.txt
Total X : 4790351 4786929 3422 0
Total y : 84860 84860 0 0
Total z : 206626 206626 0 0
Total t : 93902 93823 79 0

merge multiple files with similar column

I have 30 files where column 1 is similar in each file. I would like to join the files based on column 1 so that the output file contains column 2 from each of the input files. I know how to join two files, but struggle with multiple files.
join -1 1 -2 1 File1 File2
The files are tab-separated with no header like this
File1
5S_rRNA 1324
5_8S_rRNA 32
7SK 15
ACA59 0
ACA64 0
BC040587 0
CDKN2B-AS 0
CDKN2B-AS_2 0
CDKN2B-AS_3 0
CLRN1-AS1 0
File2
5S_rRNA 571
5_8S_rRNA 11
7SK 5
ACA59 0
ACA64 0
BC040587 0
CDKN2B-AS 0
CDKN2B-AS_2 0
CDKN2B-AS_3 0
CLRN1-AS1 0
Output
5S_rRNA 1324 571
5_8S_rRNA 32 11
7SK 15 5
ACA59 0 0
ACA64 0 0
BC040587 0 0
CDKN2B-AS 0 0
CDKN2B-AS_2 0 0
CDKN2B-AS_3 0 0
CLRN1-AS1 0 0
First memory is the problem as the file size increases.Second if the ordering of the content is not important this will work good.
#!/usr/bin/perl
use strict;
use warnings;
my %hash;
my ($key,$value);
my #files=<files/*>;
foreach(#files){
open my $fh, '<', $_ or die "unable to open file: $! \n";
while(<$fh>){
chomp;
($key,$value)=split;
push(#{$hash{$key}},$value);
}
close($fh);
}
for(keys %hash){
print "$_ #{$hash{$_}} \n";
}
Below code will give your desire output but it will take more memory when number of files will increase (as you said there are 30 files). By using sort it sort the hash in alphabetical order of its keys (will give the output in same order as you mentioned in question).
#!/usr/bin/perl
use strict;
use warnings;
my #files = qw| input.log input1.log |; #you can give here path of files, or use #ARGV if you wish to pass files from command line
my %data;
foreach my $filename (#files)
{
open my $fh, '<', $filename or die "Cannot open $filename for reading: $!";
while (my $line = <$fh>)
{
chomp $line;
my ($col1, $col2) = split /\s+/, $line;
push #{ $data{$col1} }, $col2; #create an hash of array
}
}
foreach my $col1 (sort keys %data)
{
print join("\t", $col1, #{ $data{$col1} }), "\n";
}
Output:
5S_rRNA 1324 571
5_8S_rRNA 32 11
7SK 15 5
ACA59 0 0
ACA64 0 0
BC040587 0 0
CDKN2B-AS 0 0
CDKN2B-AS_2 0 0
CDKN2B-AS_3 0 0
CLRN1-AS1 0 0

Combining duplicated lines in txt file with perl

I am trying to combine duplicate lines using Perl with little luck. My tab-delimited text file is structured as follows (spaces added for readability):
Pentamer Probability Observed Length
ATGCA 0.008 1 16
TGTAC 0.021 1 16
GGCAT 0.008 1 16
CAGTG 0.004 1 16
ATGCA 0.016 2 23
TGTAC 0.007 1 23
I would like to be combine duplicated lines by adding the three numeric columns, therefor the line containing "ATGCA" would now look like this:
ATGCA 0.024 3 39
Any ideas/help/suggestions would be greatly appreciated! Thanks!
#!/usr/bin/perl
use warnings;
use strict;
my %hash;
while(<>) {
my #v = split(/\s+/);
if (defined $hash{$v[0]}) {
my $arr = $hash{$v[0]};
$hash{$v[0]} = [$v[0], $arr->[1] + $v[1],
$arr->[2] + $v[2], $arr->[3] + $v[3]];
} else {
$hash{$v[0]} = [#v];
}
}
foreach my $key (keys %hash) {
print join(" ", #{$hash{$key}}), "\n";
}
Here's another option:
use Modern::Perl;
my %hash;
while ( my $line = <DATA> ) {
my #vals = split /\s+/, $line;
$hash{ $vals[0] }->[$_] += $vals[ $_ + 1 ] for 0 .. 2;
}
say join "\t", $_, #{ $hash{$_} } for sort keys %hash;
__DATA__
ATGCA 0.008 1 16
TGTAC 0.021 1 16
GGCAT 0.008 1 16
CAGTG 0.004 1 16
ATGCA 0.016 2 23
TGTAC 0.007 1 23
Output:
ATGCA 0.024 3 39
CAGTG 0.004 1 16
GGCAT 0.008 1 16
TGTAC 0.028 2 39

count no.of occurrences per unique id

I am new to command line. I have long text file (samp.txt) with following columns with space delimited. Awk/sed/perl help appreciated.
Id Pos Re Va Cn SF:R1 SR He Ho NC
c|371443199 22 G A R Pass:8 0 1 0 0
c|371443199 25 C A M Pass:13 0 0 1 0
c|371443199 22 G A R Pass:8 0 1 0 0
c|367079424 17 C G S Pass:19 0 0 1 0
c|371443198 17 G A R Pass:18 0 1 0 0
c|367079424 17 G A R Pass:18 0 0 1 0
I want count for each unique id (count unique id how many occurrences), count 6th column (6th column =pass), count how many He (from 8th column) and how many Ho (9 th column). I would like to get result like this
Id CountId Countpass CountHe CountHO
cm|371443199 3 3 2 1
cm|367079424 2 2 0 2
awk '{ids[$1]++; pass[$1] = "?"; he[$1] += $8; ho[$1] += $9} END {OFS = "\t"; print "Id", "CountId", "Countpass", "CountHe", "CountHO"; for (id in ids) {print id, ids[id], pass[id], he[id], ho[id]}' inputfile
Broken out onto multiple lines:
awk '{
ids[$1]++;
pass[$1] = "?"; # I'm not sure what you want here
he[$1] += $8;
ho[$1] += $9
}
END {
OFS = "\t";
print "Id", "CountId", "Countpass", "CountHe", "CountHO";
for (id in ids) {
print id, ids[id], pass[id], he[id], ho[id]
}' inputfile
You seem to have a typo in your input, where you put ...98 instead of ...99. Assuming this is the case, your other information and expected output makes sense.
Using an array to store the ids to preserve the original order of the ids.
use strict;
use warnings;
use feature 'say'; # to enable say()
my $hdr = <DATA>; # remove header
my %hash;
my #keys;
while (<DATA>) {
my ($id,$pos,$re,$va,$cn,$sf,$sr,$he,$ho,$nc) = split;
$id =~ s/^c\K/m/;
$hash{$id}{he} += $he;
$hash{$id}{ho} += $ho;
$hash{$id}{pass}{$sf}++;
$hash{$id}{count}++;
push #keys, $id if $hash{$id}{count} == 1;
}
say join "\t", qw(Id CountId Countpass CountHe CountHO);
for my $id (#keys) {
say join "\t", $id,
$hash{$id}{count}, # occurences of id
scalar keys $hash{$id}{pass}, # the number of unique passes
#{$hash{$id}}{qw(he ho)};
}
__DATA__
Id Pos Re Va Cn SF:R1 SR He Ho NC
c|371443199 22 G A R Pass:8 0 1 0 0
c|371443199 25 C A M Pass:13 0 0 1 0
c|371443199 22 G A R Pass:8 0 1 0 0
c|367079424 17 C G S Pass:19 0 0 1 0
c|371443198 17 G A R Pass:18 0 1 0 0
c|367079424 17 G A R Pass:18 0 0 1 0
Output:
Id CountId Countpass CountHe CountHO
cm|371443199 3 2 2 1
cm|367079424 2 2 0 2
cm|371443198 1 1 1 0
Note: I made the output tab-delimited for easier post-processing. If you want it pretty instead, use printf to get some fixed width fields.

Use perl script to fill in values in data set

I have a series of numbers (in a text file) from 90,000 to 1,000,000 that correspond to files that I am missing. I would like to take this set and mark these files as "1" as a second column next to a complete series of numbers 90,000 to 1,000,000. For example for series 1to13 (which would correspond to the "missing" files):
3
7
10
12
I would like to create a data set:
1 0
2 0
3 1
4 0
5 0
6 0
7 1
8 0
9 0
10 1
11 0
12 1
13 0
I would like to be able to execute this in perl.
Assuming they are sorted:
use strict;
use warnings;
my $last = 89999;
while (my $next = <>) {
chomp($next);
print $last, " 0\n" while ++$last < $next;
print "$next 1\n";
}
print $last, " 0\n" while ++$last <= 1000000;