#! ./perl 
#
# Use:
#
# Read a file with records on a line containing factor values and 
# the measured value as the one but last element on each line.
# The last element is considered a comment
# 
# use (note the quotes):
# CorrectedMeans.pl 'testfactors' data-file1 [data-file2] > destination-file
# 
# bv:
# 
# CorrectedMeans.pl '0 2 -4' KERdata.txt > KERresults.txt
# 
# In which testfactors is something like '0 2 -4' indicating
# the first and third and the fourth from the end, fields
# The test factors are grouped in the columns
# 
# More complex possibilities are for example:
# 
# CorrectedMeans.pl '&SplitPhoneme($1) -2 \!1' KER_data KBB_data > All_MOA-2.res
#
# The files  KER_data KBB_data are read and the result is written to
# All_MOA-2.res. Values from KER_data and KBB_data are given a new
# (virtual) factor: file of origin. That is, values from different files 
# keep their identity.
#
# &SplitPhoneme($1) indicates that the function SplitPhoneme() should be
# performed on the value of the second field and its result used. 
# \!1 indicates that the value itself of the second field should be 
# ignored. (\!) means 'remove value'.
# 
# CorrectedMeans.pl uses the program 'praat' to solve the necessary
# linear equations. A praat script file is needed called solveEquations.praat.
# The praat script uses the file whose name is stored in $ScratchFile.
# The full UNIX command is printed below. It can be replaced by a
# call of your own choice. In the code, it is executed with:
# eval($solveEquationsCMD);
# The 'awk' part gets the actual values from the relevant lines.
# Note, when using "-quotes, every $ must be 'escaped' 
# with one (perl variables), or three \-slashes, (UNIX variables).
# The Unix command is:

$solveEquationsCMD = 
"qx/praat solveEquation.praat \$ScratchFile|awk 'NF == 5 {print \\\$NF}'/";

# Note that the $ScratchFile is deleted with `rm $ScratchFile`
#
# CorrectedMeans.pl requires the file SummaryMatrix.pl which itself requires
# StatisticalTests.pl and Entropy.pl. All must reside in the same directory
# as CorrectedMeans.pl
#
#
###############################################################################
#
# Author and Copyright:
# Rob van Son, © 1997,1998,1999
# Institute of Phonetic Sciences & IFOTT
# University of Amsterdam
# Herengracht 338
# NL-1016CG Amsterdam, The Netherlands 
# Email: rob@fon.hum.uva.nl
#        Rob.van.Son@hum.uva.nl
#        rob.van.son@workmail.com
# WWW  : http://www.fon.hum.uva.nl/rob
# mail:  Institute of Phonetic Sciences
#        University of Amsterdam
#        Herengracht 338
#        NL-1016CG Amsterdam
#        The Netherlands
#        tel +31 205252183
#        fax +31 205252197
#
# License for use and disclaimers
#
# CorrectedMeans.pl calculates the corrected mean values of a list
# of records. 
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
#
#########################################################################
#

package Statistics;
require "./SummaryMatrix.pl"; # UNIX #
# MAC #require "StandardFile.pl";
# MAC #require "SummaryMatrix.pl";

# MAC #$Prompt = "Data file";
# MAC #@types = ();
# MAC #@FileList = ();
# MAC #while($DataFilename = &StandardFile::GetFile($Prompt, @types))
# MAC #{
# MAC #  push(@FileList, $DataFilename);
# MAC #};

# MAC #$TestFactors =  MacPerl::Ask(
# MAC #                 "Enter factor numbers", "3 4");


sub PlaceOA   # (Phoneme)->place
{
  my %PlaceList = (
              "fvpbmw",			"Labial",
              "DTsztdnlL",		"Coronal",
              "xXkgNGryCSZjJ",		"Body",
              "h\?",			"Glottal",
              "\#",			"Silence"
              );
  my $Phoneme = shift;
  $Phoneme =~ s/brst|aspr//g;
#  grep(/$Phoneme/, keys(%PlaceList));
  return $PlaceList{(grep(/$Phoneme/, keys(%PlaceList)))[0]} || $Phoneme;
}

sub MannerOA   # (Phoneme)->manner
{
  my %MannerList = (
              "fvDTszSZxXh",	"Fricative",
              "pbtdkg\?",	"PlosStop",
#              "brst",		"PlosBurst",
#              "aspr",		"PlosAspiration",
              "mnNG",		"Nasal",
              "C",		"Affricates",
              "wlLjJry",	"VowelLike",
              "\#",		"Silence"
              );
  my $Phoneme = shift;
  return "PlosBurst" if $Phoneme =~ /brst/;
  return "PlosAspiration" if $Phoneme =~ /aspr/;
#  grep(/$Phoneme/, keys(%MannerList));
  return $MannerList{(grep(/$Phoneme/, keys(%MannerList)))[0]} || $Phoneme;
}

sub Voicing   # (Phoneme) -> Voicing
{
   my %VoicingList = (
               "vDzZXbdgwlLjJrymnNG", "Voiced",
               "fTsSxhptk\?C\#", "Unvoiced"
                     );
  my $Phoneme = shift;
  $Phoneme =~ s/brst|aspr//g;
#  grep(/$Phoneme/, keys(%MannerList));
  return $VoicingList{(grep(/$Phoneme/, keys(%VoicingList)))[0]} || $Phoneme;
}

sub SplitPhoneme    # (Phoneme) -> (Place, Manner, Voicing)
{
   my $Phoneme = shift;
   return (&PlaceOA($Phoneme), &MannerOA($Phoneme), &Voicing($Phoneme));
}
###############################################################################
#
# Read values
#

$TestFactors = shift; # UNIX #
@InputList = @ARGV ? @ARGV : ("STDIN"); # UNIX #
# MAC #@InputList = @FileList;

#
###########################################################################
#
# Take in raw data, i.e., read and store records and collect statistics
#
# Initialize Matrices to contain the measurements. One for the 
# ADDITIVE model (i.e., Value = Phoneme + Factor1 + Factor2 +....)
# and one for the 
# MULTIPLICATIVE model (i.e., Value = Phoneme * Factor1 * Factor2 *....)
#
$AllRecords = new MeanMatrix;
$AllLogRecords = new MeanMatrix;
#
# Split input factors (only necessary if there are more than one)
#
$TestFactors =~ s/\$([0-9\+]+)/\$ItemValueList\{$1\}/g;
$TestFactors =~ s/\$\-([0-9]+)/\$ItemValueList\{\$RecordLength - $1 +1\}/g;
@SelectNumbers = split(' ', $TestFactors);

($RawSum, $RawSumSquare, $CountValues) = (0, 0, 0);
($AddRawSum, $AddRawSumSquare, $AddCountValues) = (0, 0, 0);
($MultRawSum, $MultRawSumSquare, $MultCountValues) = (0, 0, 0);
($AddMinRawSum, $AddMinRawSumSquare, $AddMinCountValues) = (0, 0, 0);
($MultMinRawSum, $MultMinRawSumSquare, $MultMinCountValues) = (0, 0, 0);
#
my $FileNumber = 0;
foreach $InputFile (@InputList)
{
  ++$FileNumber;
  #
  # Open input file
  open(Input, "<$InputFile") || die "<$InputFile not opened";
  print "# $FileNumber $InputFile\n";
  #
  # Read records into memory
  #
  @NonSelectNumbers = ();
  while(<Input>)
  {
    next unless /\S/;	# Skip empty lines
    next if(/^\!/);	# Skip empty comments
    my @Record = split;  
    #
    # The actual measured value
    pop(@Record);    # Remove last value
    my $Value = pop(@Record); # The actual value to use
    next unless $Value > 0;  # Only positive values
    # Add the File (speaker) Number to distinguish records from different files
    push(@Record, $FileNumber);
    #
    # Construct Row name as a concatenation of Selected Factor Values
    my $Row = "";
    my $RecordLength = scalar(@Record);
    my @ItemIndexNumbers = (1..$RecordLength);
    my %ItemValueList = ();
    # This creates a hash table of the input
    while(@ItemIndexNumbers)
    { $ItemValueList{shift(@ItemIndexNumbers)} = shift(@Record);
    };
    
    my $Item = 0;
    foreach $Item (@SelectNumbers)
    {
      if($Item =~ /^\!([0-9\-]+)$/)
      {  
      	delete($ItemValueList{$1}) if $1 > 0;
        delete($ItemValueList{$RecordLength - $1 + 1}) if $1 < 0;
      }
      elsif($Item > 0) 
      {
        $Row .= $ItemValueList{$Item} . "\.";
      	delete($ItemValueList{$Item});
      }
      elsif($Item < 0) 
      {
        $Row .= $ItemValueList{$RecordLength + $Item +1} . "\.";
      	delete($ItemValueList{$RecordLength + $Item + 1});
      }
      else 
      {
        my @NewList = ();
        push(@NewList, eval($Item));
        my $i = 0;
        for($i= 0; $i < scalar(@NewList); ++$i)
        {  
          $ItemValueList{$i + $RecordLength + 1} = $NewList[$i];
        };
        $RecordLength += scalar(@NewList);
      };
    };
    chop($Row);  # Remove last dot
    #
    # Construct Col name as a concatenation of Non-Selected Factor Values
    my $Col = "";
    foreach $Item (sort({$a <=> $b} keys(%ItemValueList)))
    {  
      $Col .= $ItemValueList{$Item} . "\.";
    };
    chop($Col);  # Remove last dot
    #
    # Add the current value to the table
    $AllRecords->add_value($Row, $Col, $Value);		# ADDITIVE model
    $AllLogRecords->add_value($Row, $Col, log($Value));	# MULTIPLICATIVE model
    #
    # Calculate raw variance
    $RawSum += $Value;
    $RawSumSquare += $Value*$Value;
    ++$CountValues;
  };
};

if($CountValues)
{
  $RawMean = $RawSum / $CountValues;
  $RawVariance = ($RawSumSquare - $RawSum**2/$CountValues) / ($CountValues - 1);
  $RawStandardDeviation = sqrt($RawVariance) unless $RawVariance < 0;
};
#
###########################################################################
#
# All data have been read, start processing and reporting
#
# Report which factors are in use, and the raw means and counts
#
# Print the factors used
print "#\n";
print "# The chosen factors\n";
print "# ", join(" ", @SelectNumbers), "\n";
print "#\n";
#
# Print the Raw numbers (i.e., Means and Counts)
$AllRecords->Totals;
$RawTable = new MeanMatrix;
$RawTable->PrintWidth(9);
print "#\n# The Raw Row Means\n";
#
# Construct a table of Raw chosen factor means (First versus the Rest)
foreach $Row ($AllRecords->get_rows)
{
   $Row =~ /^([^\.]+)(\.|$)/;
   $RawRow = $1;
   $RawCol = $' || "Col";
   $RawTable->{'Sum'}->add_value($RawRow, $RawCol, 
              $AllRecords->{'Sum'}->read_value($Row, "Total"));
   $RawTable->{'Count'}->add_value($RawRow, $RawCol, 
              $AllRecords->{'Count'}->read_value($Row, "Total"));
   #
   # Additive Variance
   $AddSum += $AllRecords->read_value($Row, "Total");
   $AddSumSquare += $AllRecords->read_value($Row, "Total")**2;
   ++$AddCountValues;
   #
   # Multiplicative Variance
   $MultValue = exp(($AllLogRecords->read_value($Row, "Total")));
   $MultSum += $MultValue;
   $MultSumSquare += $MultValue**2;
   ++$MultCountValues;
   #
   # Minimal Variance, i.e., the variance of all means.
   foreach $Col ($AllRecords->get_cols)
   {
      next unless $AllRecords->{'Count'}->read_value($Row, $Col);
      #
      # Minimal Additive Variance
      $AddMinSum += $AllRecords->read_value($Row, $Col);
      $AddMinSumSquare += ($AllRecords->read_value($Row, $Col))**2;
      ++$AddMinCountValues;
      #
      # Multiplicative Variance
      $MultMinValue = exp(($AllLogRecords->read_value($Row, $Col)));
      $MultMinSum += $MultMinValue;
      $MultMinSumSquare += $MultMinValue**2;
      ++$MultMinCountValues;
   };
};
$RawTable->print;

#
# Calculate the real varance values
if($AddCountValues)
{
  $AddMean = $AddSum / $AddCountValues;
  $AddVariance = ($AddSumSquare - $AddSum**2/$AddCountValues) / ($AddCountValues - 1);
  $AddStandardDeviation = sqrt($AddVariance) unless $AddVariance < 0;
};
if($AddMinCountValues)
{
  $AddMinMean = $AddMinSum / $AddMinCountValues;
  $AddMinVariance = ($AddMinSumSquare - $AddMinSum**2/$AddMinCountValues) 
                      / ($AddMinCountValues - 1);
  $AddMinStandardDeviation = sqrt($AddMinVariance) unless $AddMinVariance < 0;
};
if($MultCountValues)
{
  $MultMean = $MultSum / $MultCountValues;
  $MultVariance = ($MultSumSquare - $MultSum**2/$MultCountValues) / ($MultCountValues - 1);
  $MultStandardDeviation = sqrt($MultVariance) unless $MultVariance < 0;
};
if($MultMinCountValues)
{
  $MultMinMean = $MultSum / $MultCountValues;
  $MultMinVariance = ($MultMinSumSquare - $MultMinSum**2/$MultMinCountValues) 
                       / ($MultMinCountValues - 1);
  $MultMinStandardDeviation = sqrt($MultMinVariance) unless $MultMinVariance < 0;
};


# Print the Raw Counts
print "#\n# The Raw (total) Row Counts\n";
$RawTable->{'Count'}->print;
#
# The Total Means, Counts, and variance, needed because the tables have a 
# limited precision.
$OverallMean = $AllRecords->read_value("Total", "Total");
print "#\n# Raw mean = $RawMean, sd = $RawStandardDeviation ($RawVariance)\n";
print "# Additive model\n";
print "# Mean = $AddMean, sd = $AddStandardDeviation ($AddVariance)\n";
print "# All factors: Mean = $AddMinMean, sd = $AddMinStandardDeviation ($AddMinVariance)\n";
print "# Multiplicative model\n";
print "# Mean = $MultMean, sd = $MultStandardDeviation ($MultVariance)\n";
print "# All factors: Mean = $MultMinMean, sd = $MultMinStandardDeviation ($MultMinVariance)\n";
print "#\n# Weighting function: w = 1/sqrt(1/N1 + 1/N2)\n";

#
###########################################################################
#
# Start processing and reporting CORRECTED values, i.e., based on 
# differences between rows
#
# Construct a matrix with all the Row Differences AND the Statistical tests
#
# Initialize Matrices
$EquationDiffTable = new MeanMatrix;
$EquationDiffLogTable = new MeanMatrix;
$EquationWeightTable = new Matrix;
$SignTestTable = new SignTestMatrix;
$SignedRankTestTable = new SignedRankTestMatrix;
$MeanValue = new MeanMatrix; # The means of the values used for the differences
$MeanValue->PrintWidth(9);
$WeightedMeanValue = new MeanMatrix; # The weighted means of the values used for the differences
#
# Get rows and columns
#
@AllRows = $AllRecords->get_rows;
@AllCols = $AllRecords->get_cols;
#
#
####################################################################
#
# Determine the coverage of the factors by the data
#
$PerplexityMeasure = new Entropy;
($Cells, $EmptyCells, $SingleCells) = (0, 0, 0);
while($Row = shift(@AllRows))
{
  foreach $Col (@AllCols)
  { 
    my $N = $AllRecords->{'Count'}->read_value($Row, $Col);
    $PerplexityMeasure->write_value($Row, $Col, $N);
    ++$Cells;
    ++$EmptyCells unless $N;
    ++$SingleCells if $N == 1;
  };
};
print "# Out of a total of $Cells cells (".scalar(@AllCols).
        " columns), $EmptyCells are empty (",
      int(100*$EmptyCells/$Cells), "\%)\n";
print "# and $SingleCells contain only a single realization (",
      int(100*$SingleCells/$Cells), "\%)\n";
print "#\n# Entropy (H) and Perplexity (Px) of the Factor Table\n";
$PerplexityMeasure->print_entropies;
print "#\n";
#
########################################################################
#
# Determine mean differences between all pairs of rows
#
@AllRows = $AllRecords->get_rows;
@AllCols = $AllRecords->get_cols;
$SingleObservations = 0;
$DifferenceCount = 0;
while($Row = shift(@AllRows))
{
   foreach $OtherRow (@AllRows)
   {
      foreach $Col (@AllCols)
      { 
        $N1 = $AllRecords->{'Count'}->read_value($Row, $Col);
        $N2 = $AllRecords->{'Count'}->read_value($OtherRow, $Col);
        next unless $N1 && $N2;
        $Value1 = $AllRecords->read_value($Row, $Col);
        $Value2 = $AllRecords->read_value($OtherRow, $Col);
        $LogValue1 = $AllLogRecords->read_value($Row, $Col);
        $LogValue2 = $AllLogRecords->read_value($OtherRow, $Col);
        #
        ++$SingleObservations if $N1 == 1 || $N2 == 1;
        ++$DoubleSingleObservations if $N1 == 1 && $N2 == 1;
        ++$DifferenceCount;
        #
        # The weighting factor
#        $HypoN = sqrt($N1*$N1 + $N2*$N2);
        $HypoN = 1/sqrt(1/$N1 + 1/$N2);	# Standard Error Based
        # The "local", weighted  difference
        $Difference = $HypoN * ($Value1 - $Value2);
        $LogDifference = $HypoN * ($LogValue1 - $LogValue2);
        # Store results in the Mean table
        $EquationDiffTable->{'Sum'}->add_value($Row, $OtherRow, $Difference);
        $EquationDiffTable->{'Count'}->add_value($Row, $OtherRow, $HypoN);
        # Store results in the Log Mean table
        $EquationDiffLogTable->{'Sum'}->add_value($Row, $OtherRow, $LogDifference);
        $EquationDiffLogTable->{'Count'}->add_value($Row, $OtherRow, $HypoN);
        # Store values for the Sign test
        $SignTestTable->add_value($Row, $OtherRow, $Value1-$Value2);
        $SignedRankTestTable->add_value($Row, $OtherRow, $Value1-$Value2);
        #
        # Keep track of the number and mean of the realizations actually used
        # (this one contains double counts)
        $MeanValue->{'Sum'}->add_value($Row, $OtherRow, $N1*$Value1+$N2*$Value2);
        $MeanValue->{'Count'}->add_value($Row, $OtherRow, $N1+$N2);
        #
        # Keep track of the weights and weighted mean of the realizations 
        # actually used
        # (this one contains double counts)
        $WeightedMeanValue->{'Sum'}->add_value($Row, $OtherRow, $HypoN*($Value1+$Value2));
        $WeightedMeanValue->{'Count'}->add_value($Row, $OtherRow, 2*$HypoN);
     };
   };
};

#
#########################################################################
#
# Output
#
# The general information on the coverage of the difference data
#
# General Covering of the Matrix
print "# In total $SingleObservations out of $DifferenceCount differences had ",
      "only a single realization\n".
      "# in one or both groups " if $SingleObservations;
print "($DoubleSingleObservations in both)" if $DoubleSingleObservations;
print "\n#\n";
#
# Print the means, counts, and overall mean used for the equations
$MeanValue->Totals;
$OverallMean = $MeanValue->read_value("Total", "Total");
print "#\n# The Overall Mean = $OverallMean\n";
$WeightedMeanValue->Totals;
print "# The Weighted Mean = ", 
      $WeightedMeanValue->read_value("Total", "Total"), "\n";
#
#
#########################################################################
#
# The data is now collected, calculate the Corrected Means
# eval($::solveEquationsCMD) prints out a list of values. 
#
# First get a sorted list of all the values in the difference table
foreach $Item ($EquationDiffTable->get_rows, $EquationDiffTable->get_cols)
{ ++$List{$Item};};
@EquationSymbols = sort keys(%List);
#
# The Additive Model
#
# Write the Matrix equation (A.d) used in the SVD solving to
# a scratch file
#
# Print the coefficients
$ScratchFile = join('', @InputList)."temp".time.
               ".scratch".join('',@SelectNumbers);
$ScratchFile =~ s/[\W\s]//g;
open(SCRATCH, ">$ScratchFile") || die "Scratch file: $ScratchFile $!\n"; 
foreach $Item1 (@EquationSymbols)
{
  foreach $Item2 (@EquationSymbols)
  {
    next if $Item1 eq $Item2;
    $Weight = $EquationDiffTable->{'Count'}->read_value($Item1, $Item2);
    next unless $Weight > 0;
    foreach $PrintItem (@EquationSymbols)
    {
      print SCRATCH "  1" if $PrintItem eq $Item1;
      print SCRATCH " -1" if $PrintItem eq $Item2;
      print SCRATCH "  0" unless $PrintItem eq $Item1 || $PrintItem eq $Item2;
    };
    print SCRATCH " ", $EquationDiffTable->read_value($Item1, $Item2);
    print SCRATCH "\n";
  };
};
close(SCRATCH);
#
# Get the Corrected Mean Values by solving the difference equations in the 
# SCRATCH file
@CorrectedMeans = eval($::solveEquationsCMD);
#
$RelativeMean = new MeanMatrix;
$RelativeMean->PrintWidth(9);
$AbsoluteMean = new MeanMatrix;
$AbsoluteMean->PrintWidth(9);
#
# Construct two tables with the Corrected Mean values
foreach $Item1 (@EquationSymbols)
{
  $One = (split("\\.", $Item1))[0];
  $Item1 =~ /^$One[\.]/;
  $Two = $' || "Col";
  $Value = shift @CorrectedMeans;
  chomp($Value);
  $RelativeMean->write_value($One, $Two, $Value);
  $AbsoluteMean->write_value($One, $Two, $Value+$OverallMean);
};
#
# Remove the SCRATCH file
`rm $ScratchFile`;
#
#########################################################################
#
# The Multiplicative Model
#
# Print the Log Matrix equation A.d used in the SVD solving
# The equations (i.e., A.d) are written to a scratch file. The program
# eval($::solveEquationsCMD) prints out a list of values. 
#
# Print the list again (but now ALWAYS the complete list)
print "#\n";
#
#
# Print the coefficients 
$ScratchFile = join('', @InputList)."temp".time.
               "Log.scratch".join('',@SelectNumbers);
$ScratchFile =~ s/[\W\s]//g;
open(SCRATCH, ">$ScratchFile");
foreach $Item1 (@EquationSymbols)
{
  foreach $Item2 (@EquationSymbols)
  {
    next if $Item1 eq $Item2;
    $Weight = $EquationDiffLogTable->{'Count'}->read_value($Item1, $Item2);
    next unless $Weight > 0;
    foreach $PrintItem (@EquationSymbols)
    {
      print SCRATCH "  1" if $PrintItem eq $Item1;
      print SCRATCH " -1" if $PrintItem eq $Item2;
      print SCRATCH "  0" unless $PrintItem eq $Item1 || $PrintItem eq $Item2;
    };
    print SCRATCH " ", $EquationDiffLogTable->read_value($Item1, $Item2);
    print SCRATCH "\n";
  };
};
close(SCRATCH);
#
# Get the Corrected Mean Values by solving the difference equations in the 
# SCRATCH file
@CorrectedMeans = eval($::solveEquationsCMD);
#
# Remove the SCRATCH file
`rm $ScratchFile`;
#
$RelativeMultMean = new MeanMatrix;
$RelativeMultMean->PrintWidth(9);
$AbsoluteMultMean = new MeanMatrix;
$AbsoluteMultMean->PrintWidth(9);
#
# Construct two tables with the Corrected Mean values
foreach $Item1 (@EquationSymbols)
{
  $One = (split("\\.", $Item1))[0];
  $Item1 =~ /^$One[\.]/;
  $Two = $' || "Col";
  $Value = shift @CorrectedMeans;
  chomp($Value);
  $RelativeMultMean->add_value($One, $Two, exp($Value));
  $AbsoluteMultMean->add_value($One, $Two, exp($Value)*$OverallMean);
};
#
#
#########################################################################
#
# The data is now collected, calculate and print the Statistical 
# Significance of the differences
#
# Print the results for the ADDITIVE Model 
#
# Print the list again (but now ALWAYS the complete list)
print "#\n#\n# ", join(" ", @EquationSymbols), "\n";
#
# Print the Corrected means
print "#\n", "#"x70, "\n";
print "#\n# The Additive Model\n";
print "# The Corrected Means\n";
$AbsoluteMean->print;
#
# Print the Relative means
print "#\n# The Relative Corrected Means\n";
$RelativeMean->print;
#
#
#########################################################################
#
# Print the results for the MULTIPLICATIVE model 
#
# Print the Corrected means
print "#\n", "#"x70, "\n";
print "#\n# The Multiplicative Model\n";
print "# The Corrected Means\n";
$AbsoluteMultMean->print;
#
# Print the Relative Means (Factors)
print "#\n# The Relative Corrected Means (Factors)\n";
$RelativeMultMean->print;
#
#
#########################################################################
#
# Print the Statistical Significance of the differences
#
# Print out a list of statistical significance levels for each difference
#
# Print the Significance Levels
$ListLength = scalar(@EquationSymbols);
$ListLength *= ($ListLength-1)/2;
$MaxItemLength = 0;
foreach $Item1 (@EquationSymbols)
{ 
  $ItemLength = length($Item1);
  $MaxItemLength = $ItemLength if $ItemLength > $MaxItemLength;
};
#
print "#\n# The Significance levels of the differences\n";
print "# Sign Test\tSigned Rank test\tPair Counts\n";
print "# ", " "x($MaxItemLength*2+1);
printf(" %9s %9s %4s %4s %4s", "Sign Test", "W M-P S-R", "Prs#", "+#", "-#");
print "\n";
#
foreach $Item1 (@EquationSymbols)
{
  foreach $Item2 (@EquationSymbols)
  {
    next if $Item1 eq $Item2;
    # Do not print an endless list of difference significance levels
    unless($ListLength < 3000)
    {
       $One = (split("\\.", $Item1))[0];
       next unless $Item2 =~ /^$One[\.]/;
    };
    $Weight = $EquationDiffTable->{'Count'}->read_value($Item1, $Item2);
    next unless $Weight > 0;
    #
    printf("%${MaxItemLength}s - %${MaxItemLength}s ", $Item1, $Item2);
    printf("%9.6f ", $SignTestTable->read_value($Item1, $Item2));
    printf("%9.6f ", $SignedRankTestTable->read_value($Item1, $Item2));
    printf("%4d ", $SignTestTable->{'Plus'}->read_value($Item1, $Item2) +
                $SignTestTable->{'Min'}->read_value($Item1, $Item2));
    printf("%4d %4d ", $SignTestTable->{'Plus'}->read_value($Item1, $Item2),
                $SignTestTable->{'Min'}->read_value($Item1, $Item2));
    print "\n";
  };
};
#
#
#########################################################################
#
# Print the Difference sizes (additive) and factor sizes (multiplicative)
# 
print "#\n# The Corrected mean differences\n";
print "#\n# Difference sizes: Additive & Multiplicative\n";
print "# ", " "x($MaxItemLength*2+1);
printf(" %9s %9s ", "Additive", "Mult.");
print "\n";
#
$DifferenceList = new Matrix;
$DifferenceList->PrintWidth(13);
foreach $Item1 (@EquationSymbols)
{
  foreach $Item2 (@EquationSymbols)
  {
    next if $Item1 eq $Item2;
    # Do not print an endless list of difference significance levels
    @Factors1 = split("\\.", $Item1);
    $One1 = $Factors1[0];
    $Two1 = $Factors1[1];
    $Three1 = join(".", @Factors1[2..$#Factors1]);
    unless($ListLength < 300)
    {
       next unless $Item2 =~ /^$One1[\.]/;
       next unless scalar(@Factors1) <= 2 || $Item2 =~ /^$One1[\.]$Two1[\.]/;
    };
    $Weight = $EquationDiffTable->{'Count'}->read_value($Item1, $Item2);
    next unless $Weight > 0;

    @Factors2 = split("\\.", $Item2);
    $One2 = $Factors1[0];
    $Two2 = $Factors1[1];
    $Three2 = join(".", @Factors2[2..$#Factors2]);
    #
    printf("%${MaxItemLength}s - %${MaxItemLength}s ", $Item1, $Item2);

    #
    # Print the differences between the Corrected Mean "Cells"
    $Item1Row = (split("\\.", $Item1))[0];
    $Item1 =~ /^$Item1Row[\.]/;
    $Item1Column = $' || "Col";
    $Item2Row = (split("\\.", $Item2))[0];
    $Item2 =~ /^$Item2Row[\.]/;
    $Item2Column = $' || "Col";
    # Additive   
    $Diff =  $RelativeMean->read_value($Item1Row, $Item1Column) -
                     $RelativeMean->read_value($Item2Row, $Item2Column);
    $Decimals = $Diff < 10 ? 6 : 3;
    printf("%9.${Decimals}f ", $Diff);

    # Store the differences
    if("$One1.$Two1" eq "$One2.$Two2")
    {
      $Diff *= $Three2 cmp $Three1;
      $DifferenceList->write_value($One1, $Two1, $Diff);
    };
    # Multiplicative
    $Diff =  exp($RelativeMultMean->read_value($Item1Row, $Item1Column) -
                     $RelativeMultMean->read_value($Item2Row, $Item2Column));
    $Decimals = $Diff < 10 ? 6 : 3;
    printf("%9.${Decimals}f ", $Diff);

    print "\n";
  };
};

exit if $DifferenceList->empty;

print "#\n# The Corrected mean differences as a function of the second factor\n";
print "#\n# Difference sizes: Additive\n";
print "# ", $Three2 cmp $Three1, " * ($Three1 - $Three2)\n";
print "# ", " "x($MaxItemLength*2+1);
print "\n";
$DifferenceList->print;
