#!/usr/bin/perl -w

use strict;
use Getopt::Std;
use Math::MatrixReal;
my (%opts, $i, $j, $A, $x, $c, $v);

%opts = (
    f => "%+5.1f",	# format string
    m =>  3,	# height
    n =>  3,	# width
    r =>  undef,# rank
    l => -9,	# lower bound
    u =>  9,	# upper bound
    v => undef,	# variable names
);

print "# options: @ARGV\n";
getopts("f:m:n:r:l:u:v:", \%opts);
unless (defined $opts{v} and length($opts{v})==$opts{n}) {
    if ($opts{n} <= 3) {
	$opts{v} = substr("xyz", 0, $opts{n});
    } elsif ($opts{n} <= 6) {
	$opts{v} = substr("uvw", 0, $opts{n}-3) . "xyz";
    } else {
	$opts{v} = map { chr(ord('a')+$_) } 0..$opts{n}-1
    }
}
@$v = (undef, split //, $opts{v});

$A = rand_mat($opts{m}, $opts{m}) *  lower_rank($opts{m}, $opts{n}, $opts{r});
$x = rand_mat($opts{n}, 1);
$c = $A * $x;
print "#";
for ($j=1; $j<=$opts{n}; ++$j) {
    printf "  $opts{f} ", $x->element($j, 1);
}
print "\n";
for ($i=1; $i<=$opts{m}; ++$i) {
    for ($j=1; $j<=$opts{n}; ++$j) {
	printf " $opts{f} $v->[$j]", $A->element($i, $j);
    }
    printf " = $opts{f}\n", $c->element($i, 1);
}

sub rndint {
    return int(rand($opts{u}-$opts{l}+1))+$opts{l};
}

sub lower_rank {
    my ($m, $n, $r) = @_;
    my ($i, $j, $LC);
    $r = $m > $n ? $n : $m unless defined $r;
    die if $r > $m or $r > $n;
    $r = [ sort @{ rand_permute($m) }[0..$r-1] ];
    $i = 0;
    for ($j=0; $j<$n; ++$j) {
	my ($k);
	for ($k=0; $k<$i; ++$k) {
	    $LC->[$k][$j] = int(rand(5))-2;
	}
	for ($k=$i; $k<$m; ++$k) {
	    $LC->[$k][$j] = 0;
	}
	$LC->[$i++][$j] = 1 if ($i<=$#$r and $j == $r->[$i]);
    }
    $LC = Math::MatrixReal->new_from_rows($LC);
    return $LC;
}

sub rand_mat {
    my ($m, $n) = @_;
    my ($i, $j, $mat);
    for ($j=0; $j<$m; ++$j) {
	for ($i=0; $i<$m; ++$i) {
	    $mat->[$i][$j] = rndint();
	}
    }
    return Math::MatrixReal->new_from_rows($mat);
}

sub rand_permute {
    my ($n) = @_;
    my ($i, $r);
    [ map { $_->[0] } sort { $a->[1]<=>$b->[1] } map { [$_, rand()] } 0..$n-1 ];
}

