#!/usr/bin/env perl
use strict;
use warnings;
use Benchmark;
use PDL::Lite;
use Getopt::Long;
use Algorithm::Simplex::Float;
use Algorithm::Simplex::PDL;
use Algorithm::Simplex::Rational;
use Data::Dumper;

=head1 Name

benchmark_random_LPs.pl - Benchmark the three models w/ random Linear Programs

=head1 Usage

perl benchmark_random_LPs.pl --rows 50 --columns 50 -n 50

=cut

my $rows          = 20;
my $columns       = 20;
my $number_of_LPs = 20;

GetOptions(
    'rows|r=i'          => \$rows,
    'columns|c=i'       => \$columns,
    'number_of_LPs|n=i' => \$number_of_LPs,
);

srand;
my $matrix = random_float_matrix( $rows, $columns, 1 );

# Get shell tableau object for access to EPSILON and MAXIMUM_PIVOTS
my $tableau_shell = Algorithm::Simplex->new( tableau => [ [] ] );

timethese(
    $number_of_LPs,
    {
        float    => 'solve_LP("float")',
        piddle   => 'solve_LP("piddle")',
        rational => 'solve_LP("rational")',
    }
);

=head2 solve_LP

Solver subroutine for a given model and initial tableau.

=cut

sub solve_LP {
    my $model   = shift;
    my $tableau = matrix_copy($matrix);

    my $tableau_object =
      $model eq 'float'
      ? Algorithm::Simplex::Float->new( tableau => $tableau )
      : $model eq 'piddle'
      ? Algorithm::Simplex::PDL->new( tableau => $tableau )
      : $model eq 'rational'
      ? Algorithm::Simplex::Rational->new( tableau => $tableau )
      : die "The model type: $model could not be found.";

    my $counter = 1;
    until ( $tableau_object->is_optimal ) {
        my ( $pivot_row_number, $pivot_column_number ) =
          $tableau_object->determine_bland_pivot_row_and_column_numbers;
        $tableau_object->pivot( $pivot_row_number, $pivot_column_number );
        $tableau_object->exchange_pivot_variables( $pivot_row_number,
            $pivot_column_number );
        $counter++;

        # Too many pivots?
        if ( $counter > $tableau_shell->MAXIMUM_PIVOTS ) {
            warn "HALT: Exceeded the maximum number of pivots allowed: "
              . $tableau_shell->MAXIMUM_PIVOTS . "\n";
            return 0;
        }
    }
    return $tableau_object;
}

sub random_float_matrix {

    # code to produce a matrix of random floats (or naturals)
    my $rows    = shift;
    my $columns = shift;
    my $natural_numbers;
    $natural_numbers = 0 unless $natural_numbers = shift;
    my $matrix;
    for my $i ( 0 .. $rows - 1 ) {
        for my $j ( 0 .. $columns - 1 ) {
            $matrix->[$i]->[$j] =
              $natural_numbers == 0 ? rand : int( 10 * rand );
        }
    }

    return $matrix;
}

sub matrix_copy {

    # code to copy matrix
    my $matrix = shift;
    my $matrix_copy;

    for my $i ( 0 .. $rows - 1 ) {
        for my $j ( 0 .. $columns - 1 ) {
            $matrix_copy->[$i]->[$j] = $matrix->[$i]->[$j];
        }
    }

    return $matrix_copy;
}
