I have replaced the 'TOTAL' with a more "speaking" label. Pls look at the 'all...' rows and how they represent the sums of the (random) data in each of the blocks:
use strict;
use warnings;
use Data::Dumper;
my %pivot;
# generate 100 random records for app(lications) being accessed thru s
+ome dev(ices) with some bro(wsers)
for(1..100){
my($app,$dev,$bro) = (qw(App1 App2 App3)[rand 3],qw(Dev1 Dev2 Dev3
+)[rand 3],qw(Bro1 Bro2 Bro3)[rand 3]);
++$pivot{$app}{$dev}{$bro};
++$pivot{'allapps'}{$dev}{$bro};
++$pivot{$app}{'alldevs'}{$bro};
++$pivot{$app}{$dev}{'allbros'};
++$pivot{'allapps'}{'alldevs'}{$bro};
++$pivot{$app}{'alldevs'}{'allbros'};
++$pivot{'allapps'}{$dev}{'allbros'};
++$pivot{'allapps'}{'alldevs'}{'allbros'};
}
# statistics for accesses
for my $app (sort keys %pivot) {
for my $dev (sort keys %{$pivot{$app}}) {
for my $bro (sort keys %{$pivot{$app}{$dev}}) {
print "$app\t$dev\t$bro\t$pivot{$app}{$dev}{$bro}\n";
}
print "------------------------------------------------\n";
}
}