=head1 NAME

iPE::Smoother::Gaussian - A basic Gaussian smoother

=head1 DESCRIPTION

This locally smooths all counts for each member of the distribution.  A stddev and a stddev_factor are passed in to indicate the shape of the distribution and the locality of the smoothing.  For each member of the distribution, a the Gaussian distribution is applied to the stddev*stddev_factor members on either side of the distribution.

=head1 FUNCTIONS

=over 8

=cut

package iPE::Smoother::Gaussian;
use iPE;
use base("iPE::Smoother");
use strict;

our $PI = 3.1415926535897932384626433832795028841971693993751058;

=item init ()

Initialize the constants used in this smoother.

=cut
sub init {
    my ($this) = @_;

    if($this->data =~ m/^\s*\d+\s+\d+\s*$/) {
        ($this->{sigma_}, $this->{radiusFactor_}) = split ' ', $this->data;
    }
    elsif(length($this->data) && $this->data =~ m/\S/) {
        die "Invalidly formatted attribute smoothing_data $this->{data_}\n ".
        "for Gaussian smoother.  Expecting format \"<sigma> <radiusFactor>\"\n";
    }
    else {
        $this->{sigma_} = 2;
        $this->{radiusFactor_} = 5;
    }

    $this->{radius_} = $this->{sigma_}*$this->{radiusFactor_};
}

=item smoothArray (aref)

Smooths the data given according to the kernel-Gaussian smoother.

=cut
sub smoothAref {
    my ($this, $aref, $oldN) = @_;
    # get the old density for renormalization at the end.
    if(!defined($oldN)) { $oldN = $this->getArefTotal($aref); }

    #create an array of lengths and a parallel array of counts.
    my @dist;
    my $n = 0;
    for(my $i = 0; $i < scalar(@$aref); $i++) {
        push @dist, 0;
        $n += $aref->[$i] if(defined($aref->[$i]));
    }

    my $radius = $this->{radius_};
    my $sigmasquared = $this->{sigma_}**2;

    # precompute the denominator constant in the gaussian density function.
    my $denom = sqrt(2*$PI)*$this->{sigma_};
    for(my $i = 0; $i < scalar(@$aref); $i++) {
        my $tot = 0;
        for (my $j = $i-$radius; $j < $i+$radius; $j++) {
            # calculate the gaussian density function
            # at the distance from this point to the length
            # we're examining.
            if($j > 0 && $j < scalar(@$aref)) {
                my $gauss = exp(-0.5*($i-$j)**2/$sigmasquared)/$denom;
                # add the kernel function to the total
                $tot += $aref->[$j]*$gauss;
            }
        }
        $dist[$i] = $tot;
    }

    for my $i (0 .. scalar(@$aref)-1) { $aref->[$i] = $dist[$i] }

    #renormalize the aref to have the original density
    $this->renormalizeAref($aref, $oldN);
}

=back

=head1 SEE ALSO

L<iPE::Smoother>

=head1 AUTHOR

Bob Zimmermann (rpz@cse.wustl.edu)

=cut


1;
