#!/opt/bin/perl

use strict;

use Time::HiRes qw(time);

BEGIN {
   eval "sub THREADS (){ $ARGV[0] }";
}

my $N = $ARGV[1] || 50;
my $T = 4;

my ($qv, $qr);

BEGIN {
   if (THREADS) {
      require threads;
      require threads::shared;
      require Thread::Queue;

      $qv = new Thread::Queue;
      $qr = new Thread::Queue;

      eval 'sub async(&@) {
         threads->create (@_);
      }';

   } else {
      require Coro; import Coro;
      require Coro::Channel;

      *Coro::Channel::enqueue = \&Coro::Channel::put;
      *Coro::Channel::dequeue = \&Coro::Channel::get;

      $qv = new Coro::Channel 512;
      $qr = new Coro::Channel 512;
   }
}

for (1..$T) {
   async {
      while (my $v = $qv->dequeue) {
         my ($x, $y, $a, $b) = @$v;
         my $r;

         $a = [@$a];
         $b = [@$b];

         $r += $a->[$_] * $b->[$_] for 0 .. $#$a;

         if (THREADS) {
            my $r = [$x, $y, $r];
            threads::shared::share ($r);
            $qr->enqueue ($r);
         } else {
            $qr->enqueue ([$x, $y, $r]);
         }
      }
   };
}

for (1..$T) {
   async {
      while () {
         my @a = map [map rand, 1..$N], 1..$N;
         my @b = map [map rand, 1..$N], 1..$N;

         for my $x (0..$#a) {
            for my $y (0..$#b) {
               if (THREADS) {
                  my $a = []; threads::shared::share $a; push @$a, @{$a[$x]};
                  my $b = []; threads::shared::share $b; push @$b, @{$b[$y]};
                  my $v = []; threads::shared::share $v; push @$v, $x, $y, $a, $b;
                  $qv->enqueue ($v);
               } else {
                  $qv->enqueue ([$x, $y, $a[$x], $b[$y]]);
               }
            }
         }
      }
   };
}

my $t;
my $NN = $N * $N;
my $count;

while (my $r = $qr->dequeue) {
   ++$count;

   if ($count == 63) {
      $t = time;
   } elsif (($count & 63) == 0) {
      if (time > $t + 5) {
         printf "%f\n", $count / ($N * $N * (time - $t));
         last;
      }
   }
}

