The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
=head1 NAME

PDL::Filter::Linear - linear filtering for PDL

=head1 SYNOPSIS

	$a = new PDL::Filter::Linear(
		{Weights => $v,
		 Point => 10});

	$b = new PDL::Filter::Gaussian(15,2); # 15 points, 2 std devn.

	($pred,$corrslic) = $a->predict($dat);

=head1 DESCRIPTION

A wrapper for generic linear filters.
Just for convenience. This should in the future use DataPresenter.

Also, this class should at some point learn to do FFT whenever it is
useful.

=cut

package PDL::Filter::Linear;
use PDL;
use PDL::Basic;
use PDL::Slices;
use PDL::Primitive;
use strict;

sub new($$) {
	my($type,$pars) = @_;

	my $this = bless {},$type;
        barf("Must specify weights\n") unless defined $pars->{Weights};
	$this->{Weights} = delete $pars->{Weights};
	$this->{Point} = defined $pars->{Point} ? $pars->{Point} : 0;
	$this;
}

sub predict($$) {
	my($this,$data) = @_;
	my $ldata = $data->lags(0,1,$this->{Weights}->getdim(0));
	inner($ldata->xchg(0,1),$this->{Weights},
		(my $pred = PDL->null));
	return wantarray ?  ($pred,$ldata->slice(":,($this->{Point})")) :
		$pred ;
}

package PDL::Filter::Gaussian;
use PDL; use PDL::Basic; use PDL::Slices; use PDL::Primitive;
use strict;

@PDL::Filter::Gaussian::ISA = qw/PDL::Filter::Linear/;

sub new($$) {
	my($type,$npoints,$sigma) = @_;
	my $cent = int($npoints/2);
	my $x = ((PDL->zeroes($npoints )->xvals) - $cent)->float;
	my $y = exp(-($x**2)/(2*$sigma**2));
# Normalize to unit total
	$y /= sum($y);
	return PDL::Filter::Linear::new($type,{Weights => $y,
			Point => $cent});
}

# Savitzky-Golay (see Numerical Recipes)
package PDL::Filter::SavGol;
use PDL; use PDL::Basic; use PDL::Slices; use PDL::Primitive;
use strict;

@PDL::Filter::Gaussian::ISA = qw/PDL::Filter::Linear/;

# XXX Doesn't work
sub new($$) {
	my($type,$deg,$nleft,$nright) = @_;
	my $npoints = $nright + $nleft + 1;
	my $x = ((PDL->zeroes($npoints )->xvals) - $nleft)->float;
	my $mat1 = ((PDL->zeroes($npoints,$deg+1)->xvals))->float;
	for(0..$deg-1) {
		(my $tmp = $mat1->slice(":,($_)")) .= ($x ** $_);
	}
	my $y;
# Normalize to unit total
	return PDL::Filter::Linear::new($type,{Weights => $y,
			Point => $nleft});
}