#!/usr/local/bin/perl --

##########################################################################
##
##   inndelay.pl: Delay between post and reception of usenet articles. 
##
##  version: 1.0.3
##
##  Date: 05-31-97
##
##  Copyright (c) 1997, Fabien TASSIN (tassin@eerie.fr).
##
##########################################################################
##
## ABSOLUTELY NO WARRANTY WITH THIS PACKAGE. USE IT AT YOUR OWN RISKS.
##
## Usage: send a part of your history file to inndelay.pl
##
##        tail -10000 /var/news/etc/history | inndelay.pl
##     or
##        inndelay.pl history_extract
##
## This will produce a GIF file (see $out below)
##
## To obtain good results, do not forget to synchronize your clock (with
## xntp for example) before using this tool. 
##
## Notes : You need the Perl graphic library GD.pm (1.14 or more)
##         GD is available on all good CPAN ftp sites :
##             [CPAN_DIR]/authors/id/LDS/GD-1.14.tar.gz
##           or directly to :
##             http://www-genome.wi.mit.edu/pub/software/WWW/GD.html
##
## Report : please report bugs directly to the author.
##          Be sure your are using the latest version of this script.
##          (check ftp://ftp.eerie.fr/pub/usenet/inndelay/)
##
##########################################################################

# Name of the graph
$out = "./inndelay.gif";

# Want articles posted in the future ?
$WANT_FUTURE = 1;

# Size of the graph
($xmax, $ymax) = (640, 240);

###############################################
## THERE'S NOTHING TO CHANGE AFTER THIS LINE ##
###############################################
 
$version = "1.0.3";

BEGIN
{
  eval "use GD;";
  $::HAVE_GD = ($@ eq "");
};
 
unless ($HAVE_GD)
{
  printf STDERR <<EOF;
ERROR: can't make graph.
 
You need the Perl graphic library GD.pm (1.14 or more)
GD is available on all CPAN mirrors, e.g.:
    ftp://ftp.eerie.fr/pub/perl/CPAN/authors/id/LDS/GD-1.14.tar.gz
or directly to :
    http://www-genome.wi.mit.edu/pub/software/WWW/GD.html
EOF
  #'
  exit (1);
}

$adj = 0.1;

$prec = 0.01;
$t = log (10);
while (<>)
{
  $max++;
  unless (/^(<[^>]+>)\s+(\d+)\~.*[^\~]+\~(\d+)\s+\S+/)
  {
    $cancel++;
    next;
  }
  # Compute the delay..
  $d = $2 - $3;

  # Articles posted and received at the same second...strange !
  if ($d == 0)
  {
    $future++; # count them as future
    $llf{0}++;
    next;
  }

  # Humm.. posted in the future ?
  if ($d < 0)
  {
    $future++;
    # my ($s) = $1 =~ /<.*\@(.*)>$/;
    # $bad_date_delay{$s} -= $d;
    # $bad_date{$s}++;
    if ($WANT_FUTURE)
    {
      if ($d > - 45)
      {
	$l = - $d;
      }
      else
      {
	$l = sprintf "%.2f", log (- $d) / $t;
	# trunc again
	$l = sprintf "%d", 10 ** $l;
      }
      # and finally store
      $llf{$l}++;
    }
    next;
  }
  
  # Seems to be a cancel... skip it.
  if (/\scontrol/)
  {
    $cancel++;
    next;
  }

  # Finnaly, a "positive" delay..
  $done++;
  if ($d < 45)
  {
    $l = $d;
  }
  else
  {
    $l = sprintf "%.2f", log ($d) / $t; # look at $prec to know the precision
    # trunc again
    $l = sprintf "%d", 10 ** $l;
  }
  # and finally store
  $ll{$l}++;
}

$min_x = 0;
$max_x = 0;
$max_y = 0;
foreach $v (sort (keys (%ll)))
{
  $max_x = $v if ($max_x < $v);
  $max_y = $ll{"$v"} if ($max_y < $ll{"$v"});
}

if ($WANT_FUTURE)
{
  foreach $v (sort (keys (%llf)))
  {
    $min_x = - $v if ($min_x > - $v);
    $max_y = $llf{"$v"} if ($max_y < $llf{"$v"});
  }
}

$max_x = log ($max_x) / log (10);
$min_x = - log (- $min_x) / log (10) unless ($min_x == 0);

$c = sprintf "%.1f%%", $max_y / $done * 100;

($ml, $mr, $mt, $mb) = (gdSmallFont->width * length ("$c") + 12, 30,
			gdSmallFont->height * 2 + 35, 45);

$image = new GD::Image ($xmax, $ymax);

$white = $image->colorAllocate (255, 255, 255);
$black = $image->colorAllocate (  0,   0,   0);
$blue  = $image->colorAllocate (  0,   0, 255);
$red   = $image->colorAllocate (255,   0,   0);
$col1  = $image->colorAllocate (255, 200,   0);
$image->setStyle ($black, $black, gdTransparent, gdTransparent, 
		  gdTransparent, gdTransparent, gdTransparent);

# title
$s = "Delay between post and reception";
$image->string (gdMediumBoldFont,
		($xmax - length ($s) * gdMediumBoldFont->width) / 2,
		$ymax - gdMediumBoldFont->height - 5, $s, $black);
# header
$image->string (gdSmallFont, $ml, 5, 
		sprintf ("Number of articles:   %10d", $max), $black);
$image->string (gdSmallFont, $xmax / 2, 5, 
		sprintf ("Processed:            %10d (%.1f%%)",
		$done, $done / $max * 100), $black);
$image->string (gdSmallFont, $xmax / 2, 7 + gdSmallFont->height,
		sprintf ("Posted in the future: %10d (%.1f%%)",
			 $future, $future / $max * 100), $black);
$image->string (gdSmallFont, $ml, 7 + gdSmallFont->height, 
		sprintf ("Cancelled or Expired: %10d (%.1f%%)",
		$cancel, $cancel / $max * 100), $black);

if ($WANT_FUTURE)
{
  $image->filledRectangle ($xmax / 2 - 15, 5, $xmax / 2 - 5,
			   3 + gdSmallFont->height,
			   $blue);
  $image->rectangle ($xmax / 2 - 15, 5, $xmax / 2 - 5, 3 + gdSmallFont->height,
		     $black);

  $image->filledRectangle ($xmax / 2 - 15,
			   7 + gdSmallFont->height, $xmax / 2 - 5,
			   5 + 2 * gdSmallFont->height, $red);
  $image->rectangle ($xmax / 2 - 15, 7 + gdSmallFont->height,
		     $xmax / 2 - 5, 5 + 2 * gdSmallFont->height, $black);

}

# border
$image->rectangle ($ml, $mt, $xmax - $mr, $ymax - $mb, $black);

# graduations
$image->line ($ml - 3, $ymax - $mb, $ml, $ymax - $mb, $black);
$image->string (gdSmallFont, $ml - length ("0%") * gdSmallFont->width - 5,
		$ymax - $mb - gdSmallFont->height / 2, "0%", $black);
$image->line ($ml - 3, $mt, $ml, $mt, $black);
for ($i = 1; $i < 4; $i++)
{
  $image->line ($ml - 3, $mt + ($ymax - $mt - $mb) * $i / 4,
		$xmax - $mr, $mt + ($ymax - $mt - $mb) * $i / 4, gdStyled);
}
$image->string (gdSmallFont, $ml - length ("$c") * gdSmallFont->width - 5,
		$mt - gdSmallFont->height / 2, "$c", $black);

# ratio
if ($WANT_FUTURE)
{
  $rx = ($xmax - $mr - $ml - 2 * $adj) / ($max_x - $min_x + $prec + 2 * $adj);
  $ry = ($ymax - $mt - $mb) / $max_y;
}
else
{
  $rx = ($xmax - $mr - $ml) / ($max_x - $min_x + $prec);
  $ry = ($ymax - $mt - $mb) / $max_y;
}
$i = 0;
$p = 0;
$pp = 1;

$l = 0;
foreach $v (sort {$a <=> $b} (keys (%ll)))
{
  my ($t1, $t2);
  if ($v < 60)
  {
    $t1 = log ($v) / log (10);
    $t2 = log ($v + 1) / log (10);
  }
  else
  {
    $t1 = log ($v) / log (10);
    $t2 = log ($v) / log (10) + $prec;
  }

  if ($WANT_FUTURE)
  {
    $t1 += 2 * $adj;
    $t2 += 2 * $adj;
  }

  $p += $ll{$v};
  if ($p / $done * 10 > $pp)
  {
    $image->line ($ml + ($t2 - $min_x) * $rx, $mt,
			$ml + ($t2 - $min_x) * $rx, $ymax - $mb, gdStyled);
    $image->line ($ml + ($t2 - $min_x) * $rx, $mt - 3,
		  $ml + ($t2 - $min_x) * $rx, $mt, $black);

    my $s = sprintf "%d%%", $pp++ * 10;
    my $ss = $ml + ($t2 - $min_x) * $rx - (gdSmallFont->width * length $s) / 2;
    if ($ss > $l + 3 * gdSmallFont->width)
    {
      $image->string (gdSmallFont, 
		      $ss, $mt - gdSmallFont->height - 5, $s, $black);
      $l = $ss;
    }
  }
  $image->filledRectangle ($ml + ($t1 - $min_x) * $rx,
			   $ymax - $mb - $ll{$v} * $ry,
			   $ml + ($t2 - $min_x) * $rx,
			   $ymax - $mb,
			   $blue);
}

if ($WANT_FUTURE)
{
  $pf = 0;
  $ppf = 1;
  $l = $xmax;
  foreach $v (sort {$a <=> $b} (keys (%llf)))
  {
    my ($t1, $t2);
    if ($v == 0)
    {
      $t1 = $adj;
      $t2 = -$adj;
    }
    elsif ($v < 60)
    {
      $t1 = - $adj - log ($v) / log (10);
      $t2 = - $adj - log ($v + 1) / log (10);
    }
    else
    {
      $t1 = - $adj - log ($v) / log (10);
      $t2 = - $adj - (log ($v) / log (10) + $prec);
    }
    $pf += $llf{$v};
    #printf "pf = $pf\t future = $future\t %d $ppf \n", $pf / $future * 10;
    if ($pf / $future * 10 > $ppf)
    {
      $image->line ($ml + ($t1 - $min_x + $adj) * $rx, $mt,
		    $ml + ($t1 - $min_x + $adj) * $rx, $ymax - $mb, gdStyled);
      $image->line ($ml + ($t1 - $min_x + $adj) * $rx, $mt - 3,
		    $ml + ($t1 - $min_x + $adj) * $rx, $mt, $black);
      my $s = sprintf "%d%%", $ppf++ * 10;
      my $ss = $ml + ($t1 - $min_x + $adj) * $rx -
	       (gdSmallFont->width * length $s) / 2;
      if ($ss < $l - 3 * gdSmallFont->width)
      {
	$image->string (gdSmallFont, 
		      $ss, $mt - gdSmallFont->height - 5, $s, $black);
	$l = $ss;
      }
    }
    if ($v == 0)
    {
      $image->filledRectangle ($ml + ($adj + $t2 - $min_x) * $rx,
			       $ymax - $mb - $llf{$v} * $ry,
			       $ml + ($adj + $t1 - $min_x) * $rx,
			       $ymax - $mb,
			       $col1);

      # Axe
      $image->line ($ml + ($adj + ($t1 + $t2) / 2 - $min_x) * $rx,
		    $mt - 3,
		    $ml + ($adj + ($t1 + $t2) / 2 - $min_x) * $rx,
		    $ymax - $mb,
		    $black);
      $image->string (gdSmallFont,
	   $ml + ($adj + ($t1 + $t2) / 2 - $min_x) * $rx - gdSmallFont->width,
	   $mt - gdSmallFont->height - 5, "0%", $black);
    }
    else
    {
      $image->filledRectangle ($ml + ($adj + $t2 - $min_x) * $rx,
			       $ymax - $mb - $llf{$v} * $ry,
			       $ml + ($adj + $t1 - $min_x) * $rx,
			       $ymax - $mb,
			       $red);
    }
  }
}
my $s = "100%";
$image->string (gdSmallFont,
	    $xmax - $mr - (gdSmallFont->width * length $s) / 2,
	    $mt - gdSmallFont->height - 5, $s, $black);
$image->line ($xmax - $mr, $mt -3, $xmax - $mr , $mt, $black);


# Graduations
@a = (1, 3, 7, 15, 30,		# seconds
      60, 180, 420, 1800,	# minutes
      3600, 10800, 43200,	# hours
      86400, 259200, 864000);	# days

# Labels
@b = ("1s", "3s", "7s", "15s", "30s",
      "1mn", "3mn", "7mn", "30mn",
      "1h", "3h", "12h",
      "1day", "3days", "10days");

($t1, $t2) = (1, 0);
for ($i = 0; $i <= $#a; $i++)
{
  $v = $a[$i];
  next if (log ($v) / log (10) > $max_x);
  $image->line ($ml + (2 * $adj + log ($v) / log (10) - $min_x) * $rx,
		$ymax - $mb,
		$ml + (2 * $adj + log ($v) / log (10) - $min_x) * $rx,
		$ymax - $mb + 5,
		$black);
  $image->string (gdSmallFont,
		  $ml + (2 * $adj + log ($v) / log (10) - $min_x) * $rx
		  - length ("$b[$i]") * gdSmallFont->width / 2,
		  $ymax - $mb + 7 + 9 * $t1, $b[$i], $black);
  $t1 = $t1 ? 0 : 1;

  if ($WANT_FUTURE)
  {
    my $t = log ($a[$i]) / log (10);
    if ($t <= - $min_x)
    {
      $image->line ($ml + (- log ($v) / log (10) - $min_x) * $rx,
		    $ymax - $mb,
		    $ml + (- log ($v) / log (10) - $min_x) * $rx,
		    $ymax - $mb + 5,
		    $black);
      $image->string (gdSmallFont,
		      $ml + (- log ($v) / log (10) - $min_x) * $rx
		      - length ("$b[$i]") * gdSmallFont->width / 2,
		      $ymax - $mb + 7 + 9 * $t2, $b[$i], $black);
    }
    $t2 = $t2 ? 0 : 1;
  }
}
open (IMG, "> $out") || die "can open \"$out\": $!\n";
print IMG $image->gif;
close (IMG);




#$bad_date_delay{$s} -= $d;
#$bad_date{$s}++;

#print "Sort by delay:\n";
#foreach $host (sort {$bad_date_delay{$b} / $bad_date{$b} <=>
#		     $bad_date_delay{$a} / $bad_date{$a}}
#	       (keys (%bad_date_delay)))
#{
#  printf "%10d  %10d %s\n", $bad_date_delay{$host} / $bad_date{$host},
#                            $bad_date{$host}, $host;
#}

