The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
$VERSION = '0.003';

pp_addpm({ At => 'Top'}, << 'EOD');

=head1 NAME

PDL::Lib::Linear::Solve -- Linear Equations Set Solution or Matrix Inversion
using Hard-Coded Routines

=head1 SYNOPSIS

    use PDL;
    use PDL::Lib::Linear::Solve;

    my $coeffs = pdl([[1,1],[0,1]]);
    my $values = pdl([[5],[2]]);
    my ($new_coeffs, $new_values) = linear_solve($coeffs, $values);

=head1 DESCRIPTION

This package provides routines for solving a set of linear equations and
inverting a matrix using hard-coded routines written in C.

=cut
EOD

pp_addpm({At => 'Bot'}, << 'EOD');

=head1 AUTHOR

Copyright (C) 2003 Shlomi Fish. There is no warranty. You are allowed
to redistribute this software / documentation under certain
conditions. For details, see the file COPYING in the PDL
distribution. If this file is separated from the PDL distribution,
the copyright notice should be included in the file.

=cut

EOD

pp_def(
    'linear_solve',
    'Pars' => 'in_coeffs(incols,inrows); in_values(valcols,inrows); [o]coeffs(incols,inrows) ; [o]values(valcols,inrows);',
    Code => << 'EOF' ,
int mr, mc, i,j,col,try, valcols_size;
$GENERIC() temp;
$GENERIC() f;
mr = $SIZE(inrows);
mc = $SIZE(incols);
valcols_size = $SIZE(valcols);
for(i=0;i<mr;i++)
{
    for(col=0;col<mc;col++)
    {
        $coeffs(inrows => i,incols => col) =
            $in_coeffs(inrows => i,incols => col);
    }
    for(col=0;col<valcols_size;col++)
    {
        $values(inrows => i,valcols => col) =
            $in_values(inrows => i,valcols => col);
    }
}
if (mr != mc)
{
    return;
}
for(i=0;i<mr;i++)
{
    try=i;
    while($coeffs(incols => i, inrows => try) == 0)
    {
        try++;
        if (try >= mr)
        {
            break;
        }
    }
    if (try >= mr)
    {
        break;
    }
    /* Swap i and try */
    for(col=0;col<mc;col++)
    {
        temp = $coeffs(inrows => i, incols => col);
        $coeffs(inrows => i, incols => col) = $coeffs(inrows => try, incols => col);
        $coeffs(inrows => try, incols => col) = temp;
    }
    for(col=0;col<valcols_size;col++)
    {
        temp = $values(inrows => i, valcols => col);
        $values(inrows => i, valcols => col) = $values(inrows => try, valcols => col);
        $values(inrows => try, valcols => col) = temp;        
    }

    f = 1 / $coeffs(inrows => i, incols => i);
    for(col=0;col<mc;col++)
    {
        $coeffs(inrows => i, incols => col) *= f;
    }
    for(col=0;col<valcols_size;col++)
    {
        $values(inrows => i, valcols => col) *= f;
    }

    for(j=0;j<mr;j++)
    {
        if (i == j)
        {
            continue;
        }
        f = $coeffs(incols => i, inrows => j);
        for(col=0;col<mc;col++)
        {
            $coeffs(incols => col, inrows => j) -= 
                $coeffs(incols => col, inrows => i) * f;
        }
        for(col=0;col<valcols_size;col++)
        {
            $values(valcols => col, inrows => j) -= 
                $values(valcols => col, inrows => i) * f;
        }
    }
}
EOF
    Doc => <<'EOF',
=for ref

C<linear_solve> provides a solving algorithm for a set of linear equations.

=for example

    ($result_coeffs, $result_values) = linear_solve($coeffs, $values);
    
C<linear_solve> accepts two arguments - a square matrix representing the linear
equation coefficients, and a tensors that contains their values. As a result
it produces a matrix with the final coefficients (an identity matrix if 
the coefficients form a regular matrix), and the final values.

=cut

EOF
);

my $rejects =  <<'EOF';
loop(inrows) %{
    loop(incols) %{
        $coeffs() = $in_coeffs();
    %}
    loop(valcols) %{
        $values() = $in_values();
    %}
%}
EOF

pp_addpm(<<'EOF');

sub eye
{
    my $n = shift;
    my $pdl = zeroes($n,$n);
    $pdl->diagonal(0,1)++;
    return $pdl;
}

sub myinv
{
    my $matrix = shift;
    my @dims = dims($matrix);    
    my $I = eye($dims[0]);
    my ($out_coeffs, $out_values) = linear_solve($matrix, $I);
    return $out_values;
}

=head2 myinv

=for ref

Inverts a matrix

=for usage

    $inverted_matrix = inv($matrix);

=for example

    $inverted_matrix = inv($matrix);

=cut
EOF

pp_add_exported('', 'myinv');

pp_done();