#!/usr/bin/perl -w

# Copyright (C) 2003 level
# License: GPL
# spamdump.pl v1.1 May/29/2003
# http://www5e.biglobe.ne.jp/~level0/mozilla/
# Mozillaのtraining.datをダンプする。
#
# Usage: spamdump [-s(1-4)] [-limit [+|-]p] [-v] [trainig.dat]
#
#  -s(1-4)
#    ソートのキーを指定する。
#    1: spam確率（デフォルト）
#    2: spamカウント
#    3: non-spamカウント
#    4: 単語
#  -limit [+|-]p
#    pは0以上1以下の実数
#    正の実数の場合スパム確率がp以上の単語のみをダンプする
#    負の実数の場合スパム確率がp以下の単語のみをダンプする
#    デフォルトではすべての単語をダンプする。
#  -v : verbose mode
#

use strict;

my $nGood;      # non-spamメッセージの数
my $nBad;       # spamメッセージの数
my @goodarray;  # non-spamでの単語数
my @badarray;   # spamでの単語数
my @spamprob;   # spam確率
my @wordarray;  # 単語。%hashの逆方向
my $index=0;    # goodarray/badarray/spamprobのindex
my %hash;       # 単語のハッシュ、keyは単語、値は$index

my $pmin   = 0.0; # これより大きなspam確率の単語をダンプする
my $pmax   = 1.0; # これより小さなspam確率の単語をダンプする
my $bmin   = 0;   # これより大きなspamカウントの単語をダンプする
my $verbose= 0;
my $sort_key = 1; # 1:spam確率、2:spamカウント、3:non-spamカウント、4:単語

&read_option();
printf("pmin=%f pmax=%f bmin=%d\n",$pmin,$pmax,$bmin);
&read_header();
&read_ham();
&read_spam();
&dump_info();

#####################################################
#ヘッダ部分
sub read_header{
  my $cookie;
  read(STDIN,$cookie,4);
  if ($cookie ne "\xfe\xed\xfa\xce") { die "Illegal file type"; }
  $nGood = &ReadInt();
  $nBad  = &ReadInt();
  printf("nGood %d\n",$nGood);
  printf("nBad  %d\n",$nBad);
  if ($verbose) {
    printf(STDERR "nGood %d\n",$nGood);
    printf(STDERR "nBad  %d\n",$nBad);
  }
}

#ham(non-spam)の情報
sub read_ham{
  my $total = &ReadInt();
  my $i;
  printf("total non-spam tokens %d\n",$total);
  printf(STDERR "total non-spam tokens %d\n",$total) if ($verbose);
  for ($i=0;$i<$total;$i++) {
    my $count = &ReadInt();
    my $word  = &ReadStr();
    #printf("%4d %s\n",$count,$word);
    $goodarray[$index] = $count;
    $hash{$word} = $index;
    $index++;
  }
}

#spamの情報
sub read_spam{
  my $total = &ReadInt();
  my $i;
  printf("total     spam tokens %d\n",$total);
  printf(STDERR "total     spam tokens %d\n",$total) if ($verbose);
  for ($i=0;$i<$total;$i++) {
    my $count = &ReadInt();
    my $word  = &ReadStr();
    #  printf("%4d %s\n",$count,$word);
    my $ii = $hash{$word};
    if (defined ($ii)) {
      $badarray[$ii] = $count;
    } else {
      $hash{$word} = $index;
      $badarray[$index] = $count;
      $index++;
    }
  }
}

#spam確率の計算、情報をダンプする
sub dump_info{
  my ($word,$p,$i,$g,$b);
  foreach $word (keys(%hash)) {
    $i = $hash{$word};               # index
    $g= $goodarray[$i];             # good count
    if (!defined($g)) { $g = 0; $goodarray[$i] = 0}
    $g *= 2.0;
    $b = $badarray[$i];             # badcount;
    if (!defined($b)) { $b = 0; $badarray[$i] = 0;}
    if ($g+$b>5 && $nBad && $nGood) {
      $p = max(0.01,
               min(0.99,
                   (min(1.0, ($b / $nBad)) /
                       (min(1.0, ($g / $nGood)) +
                        min(1.0, ($b / $nBad))))));
    } else {
      $p = 0.4;
    }
    $spamprob[$i] = $p;
    $wordarray[$i] = $word;
  }

  my @indexes;
  @indexes = sort by_prob  values(%hash) if ($sort_key==1);
  @indexes = sort by_nbad  values(%hash) if ($sort_key==2);
  @indexes = sort by_ngood values(%hash) if ($sort_key==3);
  @indexes = sort by_word  values(%hash) if ($sort_key==4);
  foreach $i (@indexes) {
    $g = $goodarray[$i]; if (!defined($g)) { $g = 0; }
    $b = $badarray [$i]; if (!defined($b)) { $b = 0; }
    $p = $spamprob [$i];
    $word = $wordarray[$i];
    if ($p>=$pmin && $p<=$pmax && $b>=$bmin) {
      printf("%5d %5d %5.3f %s\n",$g,$b,$p,$word);
    }
  }
}

# $sort_key=1:spam確率
sub by_prob{
  $spamprob [$b] <=> $spamprob [$a] ||
  $badarray [$b] <=> $badarray [$a] ||
  $goodarray[$b] <=> $goodarray[$a] ||
  $wordarray[$b] cmp $wordarray[$a];
}
# $sort_key=2:spamカウント
sub by_nbad{
  $badarray [$b] <=> $badarray [$a] ||
  $goodarray[$b] <=> $goodarray[$a] ||
  $spamprob [$b] <=> $spamprob [$a] ||
  $wordarray[$b] cmp $wordarray[$a];
}
# $sort_key=3:non-spamカウント
sub by_ngood{
  $goodarray[$b] <=> $goodarray[$a] ||
  $badarray [$b] <=> $badarray [$a] ||
  $spamprob [$b] <=> $spamprob [$a] ||
  $wordarray[$b] cmp $wordarray[$a];
}
# $sort_key=4:単語
sub by_word{
  $wordarray[$b] cmp $wordarray[$a];
}

sub usage{
print STDERR<<END_OF_DATA;
Usage: spamdump.pl [-limit [+|-]p] [trainig.dat]
    -limit [+|-]p : specify lower(+)/upper(-) limit of spam probability.
    -s1           : sort by spam probablility
    -v            : verbose mode
END_OF_DATA
exit;
}

sub read_option{
  while ( $#ARGV >= 0 && $ARGV[0]=~/^\-/) {
    my $v;
    if ($#ARGV >= 1 && $ARGV[0] eq "-limit") {
      $v = $ARGV[1];
      if ($v>0) { $pmin = $v; }
      else      { $pmax = -$v; }
      shift(@ARGV);
      shift(@ARGV);
    } elsif ($ARGV[0] =~ /^\-s([1-4])$/ ) {
      $sort_key = $1;
      shift(@ARGV);
    } elsif ($ARGV[0] eq "-v") {
      $verbose = 1;
      shift(@ARGV);
    } else {
      print(STDERR "ilegal option: $ARGV[0]\n");
      &usage();
    }
  }
  if ($#ARGV>=0) {
    open(STDIN,$ARGV[0]) || die "Can't open file: $ARGV[0]";
    shift(@ARGV);
  }
}

# <STDIN>からIntのデータを1個読み返す
sub ReadInt {
  my $val;
  read(STDIN,$val,4); 
  return unpack("N",$val);
}
sub ReadStr {
  my $len = &ReadInt();
  my $s;
  read(STDIN,$s,$len);
  return $s;
}

sub max {
  return ($_[0]>$_[1])?$_[0]:$_[1];
}
sub min {
  return ($_[0]<$_[1])?$_[0]:$_[1];
}
