package Matrix;
#
# $RCSfile: Matrix.pm,v $
# $Date: 2015/02/28 20:47:17 $
# $Revision: 1.16 $
#
# Author: Manish Sud <msud@san.rr.com>
#
# Copyright (C) 2015 Manish Sud. All rights reserved.
#
# This file is part of MayaChemTools.
#
# MayaChemTools is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 3 of the License, or (at your option) any
# later version.
#
# MayaChemTools is distributed in the hope that it will be useful, but without
# any warranty; without even the implied warranty of merchantability of fitness
# for a particular purpose.  See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or
# write to the Free Software Foundation Inc., 59 Temple Place, Suite 330,
# Boston, MA, 02111-1307, USA.
#

use strict;
use Carp;
use Exporter;
use Scalar::Util ();
use Vector;

use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);

@ISA = qw(Exporter);
@EXPORT = qw(IsMatrix IdentityMatrix NewFromRows NewFromColumns NewFromDiagonal UnitMatrix ZeroMatrix);
@EXPORT_OK = qw(SetValuePrintFormat);

%EXPORT_TAGS = (
		all  => [@EXPORT, @EXPORT_OK]
	       );

# Setup class variables...
my($ClassName, $ValueFormat, $MatrixPrintStyle);
_InitializeClass();

#
# Using the following explicity overloaded operators, Perl automatically generates methods
# for operations with no explicitly defined methods. Autogenerated methods are possible for
# these operators:
#
#    o Arithmetic operators: += -= *= /= **= %= ++ -- x= .=
#    o Increment and decrement: ++ --
#
# 'fallback' is set to 'false' to raise exception for all other operators.
#
use overload '""' => 'StringifyMatrix',

  '@{}' => '_MatrixToArrayOperator',

  '+' => '_MatrixAdditionOperator',
  '-' => '_MatrixSubtractionOperator',
  '*' => '_MatrixMultiplicationOperator',
  '/' => '_MatrixDivisionOperator',
  '**' => '_MatrixExponentiationOperator',
  '%' => '_MatrixModulusOperator',

  'bool' => '_MatrixBooleanOperator',
  '!' => '_MatrixNotBooleanOperator',

  '==' => '_MatrixEqualOperator',
  '!=' => '_MatrixNotEqualOperator',
  '<' => '_MatrixLessThanOperator',
  '<=' => '_MatrixLessThanEqualOperator',
  '>' => '_MatrixGreatarThanOperator',
  '>=' => '_MatrixGreatarThanEqualOperator',

  'neg' => '_MatrixNegativeValueOperator',

  'abs' => '_MatrixAbsoluteValueOperator',
  'exp' => '_MatrixExpNaturalBaseOperator',
  'log' => '_MatrixLogNaturalBaseOperator',
  'sqrt' => '_MatrixSquareRootOperator',
  'cos' => '_MatrixCosineOperator',
  'sin' => '_MatrixSineOperator',

  'fallback' => undef;

# Class constructor...
sub new {
  my($Class, $NumOfRows, $NumOfCols) = @_;

  # Initialize object...
  my $This = {};
  bless $This, ref($Class) || $Class;
  $This->_InitializeMatrix($NumOfRows, $NumOfCols);

  return $This;
}

# Initialize object data...
#
sub _InitializeMatrix {
  my($This, $NumOfRows, $NumOfCols) = @_;

  if (!(defined($NumOfRows) && defined($NumOfCols))) {
    croak "Error: ${ClassName}->_InitializeMatrix: NumOfRows and NumOfCols must be specified...";
  }
  if (!(($NumOfRows > 0) && ($NumOfRows > 0))) {
    croak "Error: ${ClassName}->_InitializeMatrix: NumOfRows and NumOfCols must be a positive number...";
  }
  # Initialize matrix elements to zero...
  @{$This->{Values}} = ();

  my($RowIndex, @EmptyRow);

  @EmptyRow = ();
  @EmptyRow = ('0') x $NumOfCols;

  for $RowIndex (0 .. ($NumOfRows - 1)) {
    @{$This->{Values}[$RowIndex]} = ();
    @{$This->{Values}[$RowIndex]} = @EmptyRow;
  }
}

# Initialize class ...
sub _InitializeClass {
  #Class name...
  $ClassName = __PACKAGE__;

  # Print style for matrix rows during StringifyMatrix operation.
  # Possible values: AllRowsInOneLine, OneRowPerLine
  #
  $MatrixPrintStyle = "AllRowsInOneLine";

  # Print format for matrix values...
  $ValueFormat = "%g";
}

# Get matrix size...
#
sub GetSize {
  my($This) = @_;

  return ($This->GetNumOfRows(), $This->GetNumOfColumns());
}

# Get matrix dimensions...
#
sub GetDimension {
  my($This) = @_;

  return $This->GetSize();
}

# Get number of rows in matrix
#
sub GetNumOfRows {
  my($This) = @_;
  my($NumOfRows);

  # Size of row array...
  $NumOfRows = $#{$This->{Values}} + 1;

  return $NumOfRows;
}

# Get number of columns in matrix
#
sub GetNumOfColumns {
  my($This) = @_;
  my($NumOfCols);

  # Size of column array for first row assuming sizes of columns are same...
  $NumOfCols = $#{$This->{Values}[0]} + 1;

  return $NumOfCols;
}

# Get reference to array holding matrix values in order to directly manipulate these values...
#
sub GetMatrixValuesReference {
  my($This) = @_;

  return \@{$This->{Values}};
}

# Copy matrix...
#
sub Copy {
  my($This) = @_;
  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex, $Matrix);

  # Create a new matrix...
  ($NumOfRows, $NumOfCols) = $This->GetSize();
  $Matrix = new Matrix($NumOfRows, $NumOfCols);

  # Set matrix values...
  for $RowIndex (0 .. ($NumOfRows -1)) {
    for $ColIndex (0 .. ($NumOfCols -1)) {
      $Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex];
    }
  }
  return $Matrix;
}

# Create a new matrix using rows specified in one of the following formats:
#   o List of vector objects
#   o References to list of values
#   o List of strings containing row values delimited by space
#
# Each row must contain the same number of values.
#
# This functionality can be either invoked as a class function or an
# object method.
#
sub NewFromRows {
  my($FirstParameter, @OtherParamaters) = @_;

  if (IsMatrix($FirstParameter)) {
    return _NewFromRowsOrColumns('FromRows', @OtherParamaters);
  }
  else {
    return _NewFromRowsOrColumns('FromRows', @_);
  }
}

# Create a new matrix using columns specified in one of the following formats:
#   o List of vector objects
#   o References to list of values
#   o List of strings containing columns values delimited by space
#
# Each columns must contain the same number of values.
#
# This functionality can be either invoked as a class function or an
# object method.
#
sub NewFromColumns {
  my($FirstParameter, @OtherParamaters) = @_;

  if (IsMatrix($FirstParameter)) {
    return _NewFromRowsOrColumns('FromColumns', @OtherParamaters);
  }
  else {
    return _NewFromRowsOrColumns('FromColumns', @_);
  }
}

# Create a new matrix using diagonal values specified in one of the following formats:
#   o A vector object
#   o Reference to list of values
#   o Strings containing diagonal values delimited by space
#
# This functionality can be either invoked as a class function or an
# object method.
#
sub NewFromDiagonal {
  my($FirstParameter, @OtherParamaters) = @_;

  if (IsMatrix($FirstParameter)) {
    return _NewFromDiagonal(@OtherParamaters);
  }
  else {
    return _NewFromDiagonal(@_);
  }
}

# Create a new matrix using diagonal values specified in one of the following formats:
#   o A vector object
#   o Reference to list of values
#   o Strings containing diagonal values delimited by space
#
sub _NewFromDiagonal {
  my(@SpecifiedDiagonalValues) = @_;
  my($ErrorMsgPrefix, $CheckSizes, $CombineValues, $ValuesRefs, $DiagonalValuesRef);

  $ErrorMsgPrefix = "Error: ${ClassName}::_NewFromDiagonal";
  if (!@SpecifiedDiagonalValues) {
    croak "$ErrorMsgPrefix: No diagonal values specified...";
  }

  # Collect specified diagonal values...
  $CheckSizes = 0; $CombineValues = 1;
  $ValuesRefs = _ProcessSpecifiedMatrixValues($ErrorMsgPrefix, $CheckSizes, $CombineValues, @SpecifiedDiagonalValues);
  $DiagonalValuesRef = $ValuesRefs->[0];

  # Create a new matrix...
  my($Matrix, $NumOfRows, $NumOfCols, $RowIndex);

  $NumOfRows = @{$DiagonalValuesRef};
  $NumOfCols = $NumOfRows;

  $Matrix = new Matrix($NumOfRows, $NumOfCols);

  # Set diagonal values...
  for $RowIndex (0 .. ($NumOfRows - 1)) {
    $Matrix->{Values}[$RowIndex][$RowIndex] = $DiagonalValuesRef->[$RowIndex];
  }

  return $Matrix;
}

# Create a new matrix using rows or columns specified in one of the following formats:
#   o List of vector objects
#   o References to list of values
#   o List of strings containing row values delimited by space
#
# Each row or column must contain the same number of values.
#
sub _NewFromRowsOrColumns {
  my($Mode, @SpecifiedValues) = @_;

  if ($Mode !~ /^(FromRows|FromColumns)$/i) {
    croak "Error: ${ClassName}::_NewFromRowsOrColumns: Unknown mode: $Mode...";
  }
  my($ErrorMsgPrefix, $CheckSizes, $CombineValues, $ValuesRefs);

  # Retrieve information about specified values and make sure similar number of values
  # are specified for each row or column...
  if ($Mode =~ /^FromRows$/i) {
    $ErrorMsgPrefix = "Error: ${ClassName}::_NewFromRows";
  }
  else {
    $ErrorMsgPrefix = "Error: ${ClassName}::_NewFromColumns";
  }
  $CheckSizes = 1; $CombineValues = 0;
  $ValuesRefs = _ProcessSpecifiedMatrixValues($ErrorMsgPrefix, $CheckSizes, $CombineValues, @SpecifiedValues);

  # Create a new matrix...
  my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex, $RowMode, $Value);

  if ($Mode =~ /^FromRows$/i) {
    $NumOfRows = scalar @{$ValuesRefs};
    $NumOfCols = scalar @{$ValuesRefs->[0]};
    $RowMode = 1;
  }
  elsif ($Mode =~ /^FromColumns$/i) {
    $NumOfRows = scalar @{$ValuesRefs->[0]};
    $NumOfCols = scalar @{$ValuesRefs};
    $RowMode = 0;
  }
  $Matrix = new Matrix($NumOfRows, $NumOfCols);

  # Setup matrix values...
  for $RowIndex (0 .. ($NumOfRows - 1)) {
    for $ColIndex (0 .. ($NumOfCols - 1)) {
      $Value = $RowMode ? $ValuesRefs->[$RowIndex]->[$ColIndex]: $ValuesRefs->[$ColIndex]->[$RowIndex];
      $Matrix->{Values}[$RowIndex][$ColIndex] = $Value;
    }
  }

  return $Matrix;
}

# Process specified matrix values in any of the following supported formats:
#
#   o List of vector objects
#   o References to list of values
#   o List of strings containing row values delimited by space
#
# And return a reference to an array containing references to arrays with specified values.
#
# Value of CombineValuesStatus determines whether all the values specified are combined
# into one array and return its reference as the only entry in the array being returned.
#
sub _ProcessSpecifiedMatrixValues {
  my($ErrorMsgPrefix, $CheckSizesStatus, $CombineValuesStatus, @SpecifiedValues) = @_;
  my($Value, $TypeOfValue, @ValuesRefs);

  @ValuesRefs = ();
  if (!@SpecifiedValues) {
    croak "$ErrorMsgPrefix: No values specified...";
  }

  # Collect values...
  for $Value (@SpecifiedValues) {
    $TypeOfValue = ref $Value;

    if (Vector::IsVector($Value)) {
      # Feference to vector object...
      my($ValuesRef);
      $ValuesRef = $Value->GetValues();
      if (!@{$ValuesRef}) {
	croak "$ErrorMsgPrefix: Specified vector object must contain some values...";
      }
      push @ValuesRefs, $ValuesRef;
    }
    elsif ($TypeOfValue =~ /^ARRAY/) {
      # Refernece to an array...
      if (!@{$Value}) {
	croak "$ErrorMsgPrefix: Specified array reference must contain some values...";
      }
      push @ValuesRefs, $Value;
    }
    elsif ($TypeOfValue eq '') {
      # String value...
      my(@Values);
      @Values = split(' ', $Value);
      if (!@Values) {
	croak "$ErrorMsgPrefix: Specified string must contain some values...";
      }
      push @ValuesRefs, \@Values;
    }
    else {
      croak "$ErrorMsgPrefix: Value format, $TypeOfValue, of a specified value to be added to matrix object is not supported...";
    }
  }

  # Combine all specified values into one array...
  if ($CombineValuesStatus) {
    my($ValuesRef, @Values);

    @Values = ();
    for $ValuesRef (@ValuesRefs) {
      push @Values, @{$ValuesRef};
    }
    @ValuesRefs = ();
    push @ValuesRefs, \@Values;
  }

  # Make sure reference to all specified value arrays contain the same number of values...
  if ($CheckSizesStatus) {
    my($Index, $FirstValueSize);
    $FirstValueSize = $#{$ValuesRefs[0]};
    for $Index (1 .. $#ValuesRefs) {
      if ($FirstValueSize != $#{$ValuesRefs[$Index]}) {
	croak "$ErrorMsgPrefix: Number of values in each specified value type to be added to matrix object must be same...";
      }
    }
  }

  return \@ValuesRefs;
}

# Create a new zero matrix of specified size or default size of 3 x 3.
#
# This functionality can be either invoked as a class function or an
# object method.
#
sub ZeroMatrix (;$$$) {
  my($FirstParameter, $SecondParameter, $ThirdParameter) = @_;
  my($This, $NumOfRows, $NumOfCols, $Matrix);

  $This = undef;
  if (defined($FirstParameter) && IsMatrix($FirstParameter)) {
    ($This, $NumOfRows, $NumOfCols) = ($FirstParameter, $SecondParameter, $ThirdParameter);
  }
  else {
    ($This, $NumOfRows, $NumOfCols) = (undef, $FirstParameter, $SecondParameter);
  }
  ($NumOfRows, $NumOfCols) = (defined($NumOfRows) && defined($NumOfCols)) ? ($NumOfRows, $NumOfCols) : (3, 3);

  # Set up a new zero matrix
  $Matrix = new Matrix($NumOfRows, $NumOfCols);

  return $Matrix;
}

# Create a new unit matrix of specified size or default size of 3 x 3.
#
# This functionality can be either invoked as a class function or an
# object method.
#
sub UnitMatrix (;$$$) {
  my($FirstParameter, $SecondParameter, $ThirdParameter) = @_;
  my($This, $NumOfRows, $NumOfCols, $Matrix, $RowIndex);

  $This = undef;
  if (defined($FirstParameter) && IsMatrix($FirstParameter)) {
    ($This, $NumOfRows, $NumOfCols) = ($FirstParameter, $SecondParameter, $ThirdParameter);
  }
  else {
    ($This, $NumOfRows, $NumOfCols) = (undef, $FirstParameter, $SecondParameter);
  }
  ($NumOfRows, $NumOfCols) = (defined($NumOfRows) && defined($NumOfCols)) ? ($NumOfRows, $NumOfCols) : (3, 3);

  # Set up a new zero matrix
  $Matrix = new Matrix($NumOfRows, $NumOfCols);

  if ($NumOfRows != $NumOfCols) {
    carp "Warning: ${ClassName}::UnitMatrix: Specified matrix, $NumOfRows x $NumOfCols, is not a square matrix...";
  }

  # Initialize diagonal elements to 1...
  for $RowIndex (0 .. ($NumOfRows - 1)) {
    $Matrix->{Values}[$RowIndex][$RowIndex] = 1.0;
  }

  return $Matrix;
}

# Identity matrix of specified size or size 3 x 3...
#
sub IdentityMatrix (;$$$) {
  my($FirstParameter, $SecondParameter, $ThirdParameter) = @_;

  return UnitMatrix($FirstParameter, $SecondParameter, $ThirdParameter);
}

# Set all matrix values to 0s...
#
sub Zero {
  my($This) = @_;

  return $This->SetAllValues(0.0);
}

# Set all matrix values to 1s...
#
sub One {
  my($This) = @_;

  return $This->SetAllValues(1.0);
}

# Get a matrix value with row and column indicies starting from 0...
#
sub GetValue {
  my($This, $RowIndex, $ColIndex, $SkipIndexCheck) = @_;

  if ($SkipIndexCheck) {
    $This->_GetValue($RowIndex, $ColIndex);
  }

  $This->_ValidateRowAndColumnIndicies("Error: ${ClassName}::GetValue", $RowIndex, $ColIndex);

  return $This->_GetValue($RowIndex, $ColIndex);
}

# Get a matrix value...
#
sub _GetValue {
  my($This, $RowIndex, $ColIndex) = @_;

  return $This->{Values}[$RowIndex][$ColIndex];
}

# Set a matrix value with row and column indicies starting from 0...
#
sub SetValue {
  my($This, $RowIndex, $ColIndex, $Value, $SkipIndexCheck) = @_;

  if ($SkipIndexCheck) {
    $This->_SetValue($RowIndex, $ColIndex, $Value);
  }

  $This->_ValidateRowAndColumnIndicies("Error: ${ClassName}::SetValue", $RowIndex, $ColIndex);

  return $This->_SetValue($RowIndex, $ColIndex, $Value);
}

# Set a matrix value...
#
sub _SetValue {
  my($This, $RowIndex, $ColIndex, $Value) = @_;

  $This->{Values}[$RowIndex][$ColIndex] = $Value;

  return $This;
}

# Set all matrix values to a specified value...
#
sub SetAllValues {
  my($This, $Value) = @_;
  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();
  for $RowIndex (0 .. ($NumOfRows - 1)) {
    for $ColIndex (0 .. ($NumOfCols - 1)) {
      $This->{Values}[$RowIndex][$ColIndex] = $Value;
    }
  }
  return $This;
}

# Set values of a row in a matrix value with row index starting from 0...
#
sub SetRowValues {
  my($This, $RowIndex, @SpecifiedValues) = @_;
  my($NumOfRows, $NumOfCols, $ColIndex, $ErrorMsgPrefix, $CheckSizes, $CombineValues, $ValuesRefs, $RowValuesRef, $NumOfRowValues);

  $ErrorMsgPrefix = "Error: ${ClassName}->SetRowValues";

  ($NumOfRows, $NumOfCols) = $This->GetSize();
  $This->_ValidateRowIndex($ErrorMsgPrefix, $RowIndex);

  # Collect specified row values...
  $CheckSizes = 0; $CombineValues = 1;
  $ValuesRefs = _ProcessSpecifiedMatrixValues($ErrorMsgPrefix, $CheckSizes, $CombineValues, @SpecifiedValues);
  $RowValuesRef = $ValuesRefs->[0];

  # Check number of specified row values...
  $NumOfRowValues = @{$RowValuesRef};
  if ($NumOfRowValues != $NumOfCols) {
    croak "$ErrorMsgPrefix: Number of specified row values, $NumOfRowValues, must be equal to number of row values, $NumOfCols, in matrix...";
  }

  # Set row values...
  for $ColIndex (0 .. ($NumOfRowValues - 1)) {
    $This->{Values}[$RowIndex][$ColIndex] = $RowValuesRef->[$ColIndex];
  }
  return $This;
}

# Add new row values to a matrix...
#
sub AddRowValues {
  my($This, @SpecifiedValues) = @_;
  my($NumOfRows, $NumOfCols, $RowIndex, $ErrorMsgPrefix, $CheckSizes, $CombineValues, $RowValueRef, $RowValuesRefs, $NumOfNewRows, $NumOfNewCols);

  $ErrorMsgPrefix = "Error: ${ClassName}->AddRowValues";

  ($NumOfRows, $NumOfCols) = $This->GetSize();

  # Collect specified row values...
  $CheckSizes = 1; $CombineValues = 0;
  $RowValuesRefs = _ProcessSpecifiedMatrixValues($ErrorMsgPrefix, $CheckSizes, $CombineValues, @SpecifiedValues);

  # Check number of specified row values...
  $NumOfNewRows = scalar @{$RowValuesRefs};
  $NumOfNewCols = scalar @{$RowValuesRefs->[0]};

  if ($NumOfNewCols != $NumOfCols) {
    croak "$ErrorMsgPrefix: Number of values in each specified row, $NumOfNewCols, must be equal to number of row values, $NumOfCols, in matrix...";
  }

  # Add each row to the matrix...
  $RowIndex = $NumOfRows - 1;
  for $RowValueRef (@{$RowValuesRefs}) {
    $RowIndex++;
    @{$This->{Values}[$RowIndex]} = @{$RowValueRef};
  }

  return $This;
}

# Get values of a row in matrix as an array. In scalar context, number of row
# values is returned...
#
sub GetRowValues {
  my($This, $RowIndex) = @_;

  return $This->_GetRowOrColumnValues('AsArray', 'FromRow', $RowIndex);
}

# Get values of a row in matrix as a vector object...
#
sub GetRowValuesAsVector {
  my($This, $RowIndex) = @_;

  return $This->_GetRowOrColumnValues('AsVector', 'FromRow', $RowIndex);
}

# Get values of a row as row matrix object...
#
sub GetRowValuesAsRowMatrix {
  my($This, $RowIndex) = @_;

  return $This->_GetRowOrColumnValues('AsRowMatrix', 'FromRow', $RowIndex);
}

# Get values of a row as column matrix object...
#
sub GetRowValuesAsColumnMatrix {
  my($This, $RowIndex) = @_;

  return $This->_GetRowOrColumnValues('AsColumnMatrix', 'FromRow', $RowIndex);
}

# Get values of a row in matrix as a space delimited string...
#
sub GetRowValuesAsString {
  my($This, $RowIndex) = @_;

  return $This->_GetRowOrColumnValues('AsString', 'FromRow', $RowIndex);
}

# Set values of a column in a matrix value with row index starting from 0...
#
sub SetColumnValues {
  my($This, $ColIndex, @SpecifiedValues) = @_;
  my($NumOfRows, $NumOfCols, $RowIndex, $ErrorMsgPrefix, $CheckSizes, $CombineValues, $ValuesRefs, $ColValuesRef, $NumOfColValues);

  $ErrorMsgPrefix = "Error: ${ClassName}->SetColumnValues";

  ($NumOfRows, $NumOfCols) = $This->GetSize();
  $This->_ValidateColumnIndex($ErrorMsgPrefix, $ColIndex);

  # Collect specified row values...
  $CheckSizes = 0; $CombineValues = 1;
  $ValuesRefs = _ProcessSpecifiedMatrixValues($ErrorMsgPrefix, $CheckSizes, $CombineValues, @SpecifiedValues);
  $ColValuesRef = $ValuesRefs->[0];

  # Check number of specified col values...
  $NumOfColValues = @{$ColValuesRef};
  if ($NumOfColValues != $NumOfRows) {
    croak "$ErrorMsgPrefix: Number of specified col values, $NumOfColValues, must be equal to number of column values, $NumOfRows, in matrix...";
  }

  # Set col values...
  for $RowIndex (0 .. ($NumOfColValues - 1)) {
    $This->{Values}[$RowIndex][$ColIndex] = $ColValuesRef->[$RowIndex];
  }
  return $This;
}

# Add new column values to a matrix...
#
sub AddColumnValues {
  my($This, @SpecifiedValues) = @_;
  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex, $ErrorMsgPrefix, $CheckSizes, $CombineValues, $ColValueRef, $ColValuesRefs, $NumOfNewRows, $NumOfNewCols);

  $ErrorMsgPrefix = "Error: ${ClassName}->AddColumnValues";

  ($NumOfRows, $NumOfCols) = $This->GetSize();

  # Collect specified column values...
  $CheckSizes = 1; $CombineValues = 0;
  $ColValuesRefs = _ProcessSpecifiedMatrixValues($ErrorMsgPrefix, $CheckSizes, $CombineValues, @SpecifiedValues);

  # Check number of specified column values...
  $NumOfNewCols = scalar @{$ColValuesRefs};
  $NumOfNewRows = scalar @{$ColValuesRefs->[0]};

  if ($NumOfNewRows != $NumOfRows) {
    croak "$ErrorMsgPrefix: Number of values in each specified column, $NumOfNewRows, must be equal to number of column values, $NumOfRows, in matrix...";
  }

  # Add each column to the matrix...
  $ColIndex = $NumOfCols - 1;
  for $ColValueRef (@{$ColValuesRefs}) {
    $ColIndex++;
    for $RowIndex (0 .. ($NumOfCols - 1)) {
      $This->{Values}[$RowIndex][$ColIndex] = $ColValueRef->[$RowIndex];
    }
  }

  return $This;
}

# Get values of a column in matrix as an array. In scalar context, number of column
# values is returned...
#
sub GetColumnValues {
  my($This, $ColIndex) = @_;

  return $This->_GetRowOrColumnValues('AsArray', 'FromColumn', $ColIndex);
}

# Get values of a column in matrix as a vector object...
#
sub GetColumnValuesAsVector {
  my($This, $ColIndex) = @_;

  return $This->_GetRowOrColumnValues('AsVector', 'FromColumn', $ColIndex);
}

# Get values of a column as row matrix object...
#
sub GetColumnValuesAsRowMatrix {
  my($This, $ColIndex) = @_;

  return $This->_GetRowOrColumnValues('AsRowMatrix', 'FromColumn', $ColIndex);
}

# Get values of a column as column matrix object...
#
sub GetColumnValuesAsColumnMatrix {
  my($This, $ColIndex) = @_;

  return $This->_GetRowOrColumnValues('AsColumnMatrix', 'FromColumn', $ColIndex);
}

# Get values of a column in matrix as a space delimited string...
#
sub GetColumnValuesAsString {
  my($This, $ColIndex) = @_;

  return $This->_GetRowOrColumnValues('AsString', 'FromColumn', $ColIndex);
}

# Get row or column values...
#
sub _GetRowOrColumnValues {
  my($This, $Mode, $ValueMode, $ValueModeIndex) = @_;

  if ($Mode !~ /^(AsArray|AsVector|AsRowMatrix|AsColumnMatrix|AsString)$/i) {
    croak "Error: ${ClassName}->_GetRowOrColumnValues: Unknown mode, $Mode, specified...";
  }
  if ($ValueMode !~ /^(FromRow|FromColumn)$/i) {
    croak "Error: ${ClassName}->_GetRowOrColumnValues: Unknown value mode, $ValueMode, specified...";
  }

  # Setup error message prefix...
  my($ErrorMsgPrefix);

  $ErrorMsgPrefix = "${ClassName}->_GetRowOrColumnValues";
  if ($ValueMode =~ /^FromRow$/i) {
    $ErrorMsgPrefix = "Error: ${ClassName}->GetRowValues${Mode}";
  }
  elsif ($ValueMode =~ /^FromColumn$/i) {
    $ErrorMsgPrefix = "Error: ${ClassName}->GetColumnValues${Mode}";
  }

  # Validate specified index and collect values...
  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex, @Values);

  @Values  = ();
  ($NumOfRows, $NumOfCols) = $This->GetSize();

  if ($ValueMode =~ /^FromRow$/i) {
    $RowIndex = $ValueModeIndex;
    $This->_ValidateRowIndex($ErrorMsgPrefix, $RowIndex);

    for $ColIndex (0 .. ($NumOfCols - 1)) {
      push @Values, $This->{Values}[$RowIndex][$ColIndex];
    }
  }
  elsif ($ValueMode =~ /^FromColumn$/i) {
    $ColIndex = $ValueModeIndex;
    $This->_ValidateColumnIndex($ErrorMsgPrefix, $ColIndex);

    for $RowIndex (0 .. ($NumOfRows - 1)) {
      push @Values, $This->{Values}[$RowIndex][$ColIndex];
    }
  }

  # Return values...
  if ($Mode =~ /^AsRowMatrix$/i) {
    return NewFromRows(\@Values);
  }
  elsif ($Mode =~ /^AsColumnMatrix$/i) {
    return NewFromColumns(\@Values);
  }
  elsif ($Mode =~ /^AsVector$/i) {
    return new Vector(@Values);
  }
  elsif ($Mode =~ /^AsString$/i) {
    return join(' ', @Values);
  }
  else {
    return wantarray ? @Values : scalar @Values;
  }
}

# Set values of the diagonal in a square matrix...
#
sub SetDiagonalValues {
  my($This, @SpecifiedDiagonalValues) = @_;
  my($ErrorMsgPrefix, $NumOfRows, $NumOfCols, $RowIndex, $CheckSizes, $CombineValues, $ValuesRefs, $NumOfDiagonalValues, $DiagonalValuesRef);

  $ErrorMsgPrefix = "Error: ${ClassName}->SetDiagonalValues";
  if (!@SpecifiedDiagonalValues) {
    croak "$ErrorMsgPrefix: No diagonal values specified...";
  }

  ($NumOfRows, $NumOfCols) = $This->GetSize();
  if ($NumOfRows != $NumOfCols) {
    croak "Error: $ErrorMsgPrefix: Specified matrix, $NumOfRows x $NumOfCols, is not a square matrix...";
  }

  # Collect specified diagonal values...
  $CheckSizes = 0; $CombineValues = 1;
  $ValuesRefs = _ProcessSpecifiedMatrixValues($ErrorMsgPrefix, $CheckSizes, $CombineValues, @SpecifiedDiagonalValues);
  $DiagonalValuesRef = $ValuesRefs->[0];
  $NumOfDiagonalValues = @{$DiagonalValuesRef};

  if ($NumOfDiagonalValues != $NumOfRows) {
    croak "Error: $ErrorMsgPrefix: Number of specified diagonal values, $NumOfDiagonalValues, must be equal to number of rows, $NumOfRows, in square matrix...";
  }

  # Set diagonal values...
  for $RowIndex (0 .. ($NumOfRows - 1)) {
    $This->{Values}[$RowIndex][$RowIndex] = $DiagonalValuesRef->[$RowIndex];
  }

  return $This;
}

# Get values of the diagonal in a square matrix as an array. In scalar context, number of
# diagonal values is returned...
#
sub GetDiagonalValues {
  my($This) = @_;

  return $This->_GetDiagonalValues('AsArray');
}

# Get values of the diagonal in a square matrix as vector object...
#
sub GetDiagonalValuesAsVector {
  my($This) = @_;

  return $This->_GetDiagonalValues('AsVector');
}

# Get values of the diagonal in a square matrix as row matrix object
#
sub GetDiagonalValuesAsRowMatrix {
  my($This) = @_;

  return $This->_GetDiagonalValues('AsRowMatrix');
}

# Get values of the diagonal in a square matrix as column matrix object
#
sub GetDiagonalValuesAsColumnMatrix {
  my($This) = @_;

  return $This->_GetDiagonalValues('AsColumnMatrix');
}

# Get values of the diagonal in a square matrix as a space delimited string...
#
sub GetDiagonalValuesAsString {
  my($This) = @_;

  return $This->_GetDiagonalValues('AsString');
}

# Get diagonal values...
sub _GetDiagonalValues {
  my($This, $Mode) = @_;

  if ($Mode !~ /^(AsArray|AsVector|AsRowMatrix|AsColumnMatrix|AsString)$/i) {
    croak "Error: ${ClassName}->_GetDiagonalValues: Unknown mode, $Mode, specified...";
  }

  # Make sure it's a square matrix...
  my($NumOfRows, $NumOfCols, $ErrorMsgPrefix);

  $ErrorMsgPrefix = "${ClassName}->_GetDiagonalValues${Mode}";
  ($NumOfRows, $NumOfCols) = $This->GetSize();
  if ($NumOfRows != $NumOfCols) {
    croak "Error: $ErrorMsgPrefix: Specified matrix, $NumOfRows x $NumOfCols, is not a square matrix...";
  }

  # Collect values...
  my($RowIndex, @Values);
  @Values = ();

  for $RowIndex (0 .. ($NumOfRows - 1)) {
    push @Values, $This->{Values}[$RowIndex][$RowIndex];
  }

  # Return values...
  if ($Mode =~ /^AsRowMatrix$/i) {
    return NewFromRows(\@Values);
  }
  elsif ($Mode =~ /^AsColumnMatrix$/i) {
    return NewFromColumns(\@Values);
  }
  elsif ($Mode =~ /^AsVector$/i) {
    return new Vector(@Values);
  }
  elsif ($Mode =~ /^AsString$/i) {
    return join(' ', @Values);
  }
  else {
    return wantarray ? @Values : scalar @Values;
  }
}

# Is it a square matrix?
#
sub IsSquare {
  my($This) = @_;
  my($NumOfRows, $NumOfCols) = $This->GetSize();

  return ($NumOfRows == $NumOfCols) ? 1 : 0;
}

# Is it a unit matrix?
#
# A matrix is a unit matrix:
#   o It's a square matrix
#   o All its diagonal elements are ones and its off-diagonal elements are zeros
#
sub IsUnit {
  my($This) = @_;

  # Is is a square matrix?
  if (!$This->IsSquare()) {
    return 0;
  }

  # Check matrix values...
  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex, $ExpectedValue);
  ($NumOfRows, $NumOfCols) = $This->GetSize();

  for $RowIndex (0 .. ($NumOfRows - 1)) {
    for $ColIndex (0 .. ($NumOfCols - 1)) {
      $ExpectedValue = ($RowIndex == $ColIndex) ? 1.0 : 0.0;
      if ($This->{Values}[$RowIndex][$ColIndex] != $ExpectedValue) {
	return 0;
      }
    }
  }
  return 1;
}

# Is it an identity matrix?
#
sub IsIdentity {
  my($This) = @_;

  return $This->IsUnit();
}

# Is it a diagonal matrix?
#
# A matrix is a diagonal matrix:
#   o It's a square matrix
#   o All its off-diagonal elements are zeros and its diagonal elements may or may not
#     be zeros
#
#
sub IsDiagonal {
  my($This) = @_;

  # Is is a square matrix?
  if (!$This->IsSquare()) {
    return 0;
  }

  # Check off-diagonal matrix values...
  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
  ($NumOfRows, $NumOfCols) = $This->GetSize();

  for $RowIndex (0 .. ($NumOfRows - 1)) {
    COLINDEX: for $ColIndex (0 .. ($NumOfCols - 1)) {
      if ($RowIndex == $ColIndex) {
	next COLINDEX;
      }
      if ($This->{Values}[$RowIndex][$ColIndex] != 0.0) {
	return 0;
      }
    }
  }
  return 1;
}

# Is it a lower bidiagonal matrix?
#
# A matrix is a lower bidiagonal matrix:
#   o It's a square matrix
#   o All its main diagonal and lower diagonal elements are non-zeros and all its
#     other elements are zeros
#
sub IsLowerBiDiagonal {
  my($This) = @_;

  # Is is a square matrix?
  if (!$This->IsSquare()) {
    return 0;
  }

  # Check matrix values...
  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex, $Value);
  ($NumOfRows, $NumOfCols) = $This->GetSize();

  for $RowIndex (0 .. ($NumOfRows - 1)) {
    for $ColIndex (0 .. ($NumOfCols - 1)) {
      $Value = $This->{Values}[$RowIndex][$ColIndex];
      if ($RowIndex == $ColIndex) {
	# Main diagonal...
	if ($Value == 0.0) {
	  return 0;
	}
      }
      elsif ($RowIndex == ($ColIndex + 1)) {
	# Lower diagonal...
	if ($Value == 0.0) {
	  return 0;
	}
      }
      else {
	# Other elements...
	if ($Value != 0.0) {
	  return 0;
	}
      }
    }
  }
  return 1;
}

# Is it an upper bidiagonal matrix?
#
# A matrix is an upper bidiagonal matrix:
#   o It's a square matrix
#   o All its main diagonal and upper diagonal elements are non-zeros and all its
#     other elements are zeros
#
sub IsUpperBiDiagonal {
  my($This) = @_;

  # Is is a square matrix?
  if (!$This->IsSquare()) {
    return 0;
  }
  # Check matrix values...
  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex, $Value);
  ($NumOfRows, $NumOfCols) = $This->GetSize();

  for $RowIndex (0 .. ($NumOfRows - 1)) {
    for $ColIndex (0 .. ($NumOfCols - 1)) {
      $Value = $This->{Values}[$RowIndex][$ColIndex];
      if ($RowIndex == $ColIndex) {
	# Main diagonal...
	if ($Value == 0.0) {
	  return 0;
	}
      }
      elsif ($RowIndex == ($ColIndex - 1)) {
	# Upper diagonal...
	if ($Value == 0.0) {
	  return 0;
	}
      }
      else {
	# Other elements...
	if ($Value != 0.0) {
	  return 0;
	}
      }
    }
  }
  return 1;
}

# Is it a bidiagonal matrix?
#
# A matrix is a bidiagonal matrix:
#
sub IsBiDiagonal {
  my($This) = @_;

  return ($This->IsUpperBiDiagonal() || $This->IsLowerBiDiagonal()) ? 1 : 0;
}

# Is it a tridiagonal matrix?
#
# A matrix is a  tribidiagonal matrix:
#   o It's a square matrix
#   o All its main diagonal, upper diagonal, and lower diagonal elements are non-zeros and all its
#     other elements are zeros
#
#
sub IsTriDiagonal {
  my($This) = @_;

  # Is is a square matrix?
  if (!$This->IsSquare()) {
    return 0;
  }

  # Check matrix values...
  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex, $Value);
  ($NumOfRows, $NumOfCols) = $This->GetSize();

  for $RowIndex (0 .. ($NumOfRows - 1)) {
    for $ColIndex (0 .. ($NumOfCols - 1)) {
      $Value = $This->{Values}[$RowIndex][$ColIndex];
      if ($RowIndex == $ColIndex) {
	# Main diagonal...
	if ($Value == 0.0) {
	  return 0;
	}
      }
      elsif ($RowIndex == ($ColIndex - 1)) {
	# Upper diagonal...
	if ($Value == 0.0) {
	  return 0;
	}
      }
      elsif ($RowIndex == ($ColIndex + 1)) {
	# Lower diagonal...
	if ($Value == 0.0) {
	  return 0;
	}
      }
      else {
	# Other elements...
	if ($Value != 0.0) {
	  return 0;
	}
      }
    }
  }
  return 1;
}

# Is it a lower triangular matrix?
#
# A matrix is a lower triangular matrix:
#   o It's a square matrix
#   o All its entries above the main diagonal are zero
#
sub IsLowerTriangular {
  my($This) = @_;

  return $This->_IsLowerTriangularMatrix();
}

# Is it a left triangular matrix?
#
# A matrix is a left triangular matrix:
#   o It's a square matrix
#   o All its entries above the main diagonal are zero
#
sub IsLeftTriangular {
  my($This) = @_;

  return $This->IsLowerTriangular();
}

# Is it a strictly lower triangular matrix?
#
# A matrix is a strictly lower triangular matrix:
#   o It's a square matrix
#   o All its entries on and above the main diagonal are zero
#
sub IsStrictlyLowerTriangular {
  my($This) = @_;
  my($DiagonalValue);

  $DiagonalValue = 0;

  return $This->_IsLowerTriangularMatrix($DiagonalValue);
}

# Is it an unit lower triangular matrix?
#
# A matrix is an unit lower triangular matrix:
#   o It's a square matrix
#   o All its entries main diagonal are one
#   o All its entries above the main diagonal are zero
#
sub IsUnitLowerTriangular {
  my($This) = @_;
  my($DiagonalValue);

  $DiagonalValue = 1;

  return $This->_IsLowerTriangularMatrix($DiagonalValue);
}

# Is it a lower unitriangular matrix?
#
sub IsLowerUniTriangular {
  my($This) = @_;

  return $This->IsUnitLowerTriangular();
}

# Is it a lower triangular, strictly lower triangular, or unit lower triangular matrix?
#
sub _IsLowerTriangularMatrix {
  my($This, $DiagonalValue) = @_;

  # Is is a square matrix?
  if (!$This->IsSquare()) {
    return 0;
  }
  # Check matrix values...
  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex, $CheckDiagonalValues);

  $CheckDiagonalValues = defined($DiagonalValue) ? 1 : 0;
  ($NumOfRows, $NumOfCols) = $This->GetSize();

  for $RowIndex (0 .. ($NumOfRows - 1)) {
    for $ColIndex (0 .. ($NumOfCols - 1)) {
      if ($CheckDiagonalValues && $RowIndex == $ColIndex) {
	# Main diagonal...
	if ($This->{Values}[$RowIndex][$ColIndex] != $DiagonalValue) {
	  return 0;
	}
      }
      elsif ($RowIndex < $ColIndex) {
	# Elemens above the main diagonal...
	if ($This->{Values}[$RowIndex][$ColIndex] != 0.0) {
	  return 0;
	}
      }
    }
  }
  return 1;
}

# Is it an upper triangular matrix?
#
# A matrix is an upper triangular matrix:
#   o It's a square matrix
#   o All its entries below the main diagonal are zero
#
sub IsUpperTriangular {
  my($This) = @_;

  return $This->_IsUpperTriangularMatrix();
}

# Is it a right triangular matrix?
#
# A matrix is a right triangular matrix:
#   o It's a square matrix
#   o All its entries below the main diagonal are zero
#
sub IsRightTriangular {
  my($This) = @_;

  return $This->IsUpperTriangular();
}

# Is it a strictly upper triangular matrix?
#
# A matrix is a strictly upper triangular matrix:
#   o It's a square matrix
#   o All its entries on and below the main diagonal are zero
#
sub IsStrictlyUpperTriangular {
  my($This) = @_;
  my($DiagonalValue);

  $DiagonalValue = 0;

  return $This->_IsUpperTriangularMatrix($DiagonalValue);
}

# Is it a unit upper triangular matrix?
#
# A matrix is an unit upper triangular matrix:
#   o It's a square matrix
#   o All its entries main diagonal are one
#   o All its entries below the main diagonal are zero
#
sub IsUnitUpperTriangular {
  my($This) = @_;
  my($DiagonalValue);

  $DiagonalValue = 1;

  return $This->_IsUpperTriangularMatrix($DiagonalValue);
}

# Is it a upper unitriangular matrix?
#
sub IsUpperUniTriangular {
  my($This) = @_;

  return $This->IsUnitUpperTriangular();
}

# Is it an upper triangular, strictly upper triangular, or unit upper triangular matrix?
#
sub _IsUpperTriangularMatrix {
  my($This, $DiagonalValue) = @_;

  # Is is a square matrix?
  if (!$This->IsSquare()) {
    return 0;
  }
  # Check matrix values...
  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex, $CheckDiagonalValues);

  $CheckDiagonalValues = defined($DiagonalValue) ? 1 : 0;
  ($NumOfRows, $NumOfCols) = $This->GetSize();

  for $RowIndex (0 .. ($NumOfRows - 1)) {
    for $ColIndex (0 .. ($NumOfCols - 1)) {
      if ($CheckDiagonalValues && $RowIndex == $ColIndex) {
	# Main diagonal...
	if ($This->{Values}[$RowIndex][$ColIndex] != $DiagonalValue) {
	  return 0;
	}
      }
      elsif ($RowIndex > $ColIndex) {
	# Elemens below the main diagonal...
	if ($This->{Values}[$RowIndex][$ColIndex] != 0.0) {
	  return 0;
	}
      }
    }
  }
  return 1;
}

# Is it a symmetrix matrix?
#
# A matrix is a symmetric matrix:
#   o It's a square matrix
#   o Its elements are symmetric with respect to main diagonal. In other words,
#     elements below the main diagonal are equal to the elements above the main
#     diagonal.
#
# Transpose of a symmetric matrix equals the matrix itself.
#
sub IsSymmetric {
  my($This) = @_;

  # Is is a square matrix?
  if (!$This->IsSquare()) {
    return 0;
  }

  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
  ($NumOfRows, $NumOfCols) = $This->GetSize();

  for $RowIndex (0 .. ($NumOfRows - 1)) {
    for $ColIndex (0 .. ($RowIndex - 1)) {
      if ($This->{Values}[$RowIndex][$ColIndex] != $This->{Values}[$ColIndex][$RowIndex]) {
	return 0;
      }
    }
  }
  return 1;
}

# Is it a anti symmetrix matrix?
#
# A matrix is an anti symmetric matrix:
#   o It's a square matrix
#   o Its elements are asymmetric with respect to main diagonal. In other words,
#     elements below the main diagonal are equal to the negative of elements above
#     the main diagonal.
#
# Transpose of a anti symmetric matrix equals the negative of the matrix.
#
sub IsAntiSymmetric {
  my($This) = @_;

  # Is is a square matrix?
  if (!$This->IsSquare()) {
    return 0;
  }

  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
  ($NumOfRows, $NumOfCols) = $This->GetSize();

  for $RowIndex (0 .. ($NumOfRows - 1)) {
    for $ColIndex (0 .. ($RowIndex - 1)) {
      if ($This->{Values}[$RowIndex][$ColIndex] != -$This->{Values}[$ColIndex][$RowIndex]) {
	return 0;
      }
    }
  }
  return 1;
}

# Is it a skew symmetrix matrix?
#
# It's another name for AnitSymmetricMatrix.
#
sub IsSkewSymmetric {
  my($This) = @_;

  return $This->IsAntiSymmetric();
}

# Is it a positive matrix with all its values >= zero?
#
sub IsPositive {
  my($This) = @_;

  # Check matrix values...
  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
  ($NumOfRows, $NumOfCols) = $This->GetSize();

  for $RowIndex (0 .. ($NumOfRows - 1)) {
    for $ColIndex (0 .. ($NumOfCols - 1)) {
      if ($This->{Values}[$RowIndex][$ColIndex] < 0.0) {
	return 0;
      }
    }
  }
  return 1;
}

# Is it a positive matrix with all its values <= zero?
#
sub IsNegative {
  my($This) = @_;

  return $This->IsPositive() ? 0 : 1;
}

# Transpose the matrix by swaping rows with columns...
#
sub Transpose {
  my($This) = @_;
  my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  # Create the transpose matrix of size $NumOfCols x $NumOfRows
  #
  ($NumOfRows, $NumOfCols) = $This->GetSize();
  $Matrix = new Matrix($NumOfCols, $NumOfRows);

  # Swap rows and columns...
  for $RowIndex (0 .. ($NumOfCols - 1)) {
    for $ColIndex (0 .. ($NumOfRows - 1)) {
      $Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$ColIndex][$RowIndex];
    }
  }
  return $Matrix;
}

# Is it a matrix object?
sub IsMatrix ($) {
  my($Object) = @_;

  return _IsMatrix($Object);
}

# Set value print format for an individual object or the whole class during StringifyMatrix operation...
sub SetValuePrintFormat ($;$) {
  my($FirstParameter, $SecondParameter) = @_;

  if ((@_ == 2) && (_IsMatrix($FirstParameter))) {
    # Set value print format for the specific object...
    my($This, $ValuePrintFormat) = ($FirstParameter, $SecondParameter);

    $This->{ValueFormat} = $ValuePrintFormat;
  }
  else {
    # Set value print format for the class...
    my($ValuePrintFormat) = ($FirstParameter);

    $ValueFormat = $ValuePrintFormat;
  }
}

# Set print style for matrix rows for an individual object or the whole class during StringifyMatrix
# operation.
#
# Possible values: AllRowsInOneLine, OneRowPerLine. Default: AllRowsInOneLine
#
sub SetMatrixPrintStyle ($;$) {
  my($FirstParameter, $SecondParameter) = @_;

  if ((@_ == 2) && (_IsMatrix($FirstParameter))) {
    # Set value print format for the specific object...
    my($This, $MatrixPrintStyleValue) = ($FirstParameter, $SecondParameter);

    if ($MatrixPrintStyleValue !~ /^(AllRowsInOneLine|OneRowPerLine)$/i) {
      croak "Error: ${ClassName}->SetMatrixPrintStyle: Specified MatrixPrintStyle, $MatrixPrintStyleValue, is not valid. Supported values: AllRowsInOneLine, OneRowPerLine...";
    }

    $This->{MatrixPrintStyle} = $MatrixPrintStyleValue;
  }
  else {
    # Set value print format for the class...
    my($MatrixPrintStyleValue) = ($FirstParameter);

    if ($MatrixPrintStyleValue !~ /^(AllRowsInOneLine|OneRowPerLine)$/i) {
      croak "Error: ${ClassName}::SetMatrixPrintStyle: Specified MatrixPrintStyle, $MatrixPrintStyleValue, is not valid. Supported values: AllRowsInOneLine, OneRowPerLine...";
    }

    $MatrixPrintStyle = $MatrixPrintStyleValue;
  }
}

# Is it a matrix object?
#
sub _IsMatrix {
  my($Object) = @_;

  return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0;
}

# Make sure it's a matrix reference...
#
sub _ValidateMatrix {
  my($ErrorMsg, $Matrix) = @_;

  if (!_IsMatrix($Matrix)) {
    croak "Error: ${ClassName}->${ErrorMsg}: Object must be a matrix...";
  }
}

# Make sure both row and column indicies are valid...
#
sub _ValidateRowAndColumnIndicies {
  my($This, $ErrorMsgPrefix, $RowIndex, $ColumnIndex) = @_;

  $This->_ValidateRowIndex($ErrorMsgPrefix, $RowIndex);
  $This->_ValidateColumnIndex($ErrorMsgPrefix, $ColumnIndex);

  return $This;
}

# Make sure it's a valid row index...
#
sub _ValidateRowIndex {
  my($This, $ErrorMsgPrefix, $RowIndex) = @_;
  my($NumOfRows);

  if (!defined $RowIndex) {
    croak "$ErrorMsgPrefix: RowIndex must be defined...";
  }
  $NumOfRows = $This->GetNumOfRows();
  if ($RowIndex < 0 || $RowIndex >= $NumOfRows) {
    croak "$ErrorMsgPrefix: RowIndex value $RowIndex must be >= 0 and < $NumOfRows, NumOfRows, in matrix...";
  }
  return $This;
}

# Make sure it's a valid column index...
#
sub _ValidateColumnIndex {
  my($This, $ErrorMsgPrefix, $ColIndex) = @_;
  my($NumOfCols);

  if (!defined $ColIndex) {
    croak "$ErrorMsgPrefix: ColIndex must be defined...";
  }
  $NumOfCols = $This->GetNumOfColumns();
  if ($ColIndex < 0 || $ColIndex >= $NumOfCols) {
    croak "$ErrorMsgPrefix: ColIndex value $ColIndex must be >= 0 and < $NumOfCols, NumOfCols, in matrix...";
  }
  return $This;
}

#
# Matrix addition operator supports two addition modes:
#   . Addition of two matrices by adding corresponding matrix values
#   . Addition of a scalar value to matrix values ($Matrix + 1)
#
# Caveats:
#   . Addition of a matrix to scalar is not allowed (1 + $Matrix)
#
sub _MatrixAdditionOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);

  $ErrorMsg = "_MatrixAdditionOperator: Matrix addition failed";
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);

  my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();
  $Matrix = new Matrix($NumOfRows, $NumOfCols);

  if ($OtherIsMatrix) {
    # $OrderFlipped is set to false for two matrices...
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	$Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] + $Other->{Values}[$RowIndex][$ColIndex];
      }
    }
  }
  else {
    # Scalar addition...
    if ($OrderFlipped) {
      croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    }
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	$Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] + $Other;
      }
    }
  }
  return $Matrix;
}

#
# Matrix subtraction operator supports two subtraction modes:
#   . Subtraction of two matrices by subtracting corresponding matrix values
#   . Subtraction of a scalar value from matrix values ($Matrix - 1)
#
# Caveats:
#   . Subtraction of a matrix from scalar is not allowed (1 - $Matrix)
#
sub _MatrixSubtractionOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);

  $ErrorMsg = "_MatrixSubtractionOperator: Matrix subtraction failed";
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);

  my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();
  $Matrix = new Matrix($NumOfRows, $NumOfCols);

  if ($OtherIsMatrix) {
    # $OrderFlipped is set to false for two matrices...
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	$Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] - $Other->{Values}[$RowIndex][$ColIndex];
      }
    }
  }
  else {
    # Scalar subtraction...
    if ($OrderFlipped) {
      croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    }
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	$Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] - $Other;
      }
    }
  }
  return $Matrix;
}

#
# Matrix multiplication operator supports two multiplication modes:
#   . Multiplication of two matrices
#   . Multiplication of matrix values by a scalar ($Matrix * 1)
#
# Caveats:
#   . Multiplication of a scalar by a is not allowed (1 * $Matrix)
#
sub _MatrixMultiplicationOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg, $CheckSizes);

  $ErrorMsg = "_MatrixMultiplicationOperator: Matrix multiplication failed";
  $CheckSizes = 0;
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_, $CheckSizes);

  my($Matrix);

  if ($OtherIsMatrix) {
    # $OrderFlipped is set to false for two matrices...
    my($NumOfRows1, $NumOfCols1, $RowIndex1, $ColIndex1, $NumOfRows2, $NumOfCols2, $ColIndex2, $Value, $RowColIndex);

    ($NumOfRows1, $NumOfCols1) = $This->GetSize();
    ($NumOfRows2, $NumOfCols2) = $Other->GetSize();

    if ($NumOfCols1 != $NumOfRows2) {
      croak "Error: ${ClassName}->${ErrorMsg}: NumOfCols in first matrix of size $NumOfRows1 x $NumOfCols1 must be equal to NumOfRows in second matrix of size $NumOfRows2 x $NumOfCols2...";
    }

    $Matrix = new Matrix($NumOfRows1, $NumOfCols2);

    for $RowIndex1 (0 .. ($NumOfRows1 - 1)) {
      for $ColIndex2 (0 .. ($NumOfCols2 - 1)) {
	$Value = 0;
	for $RowColIndex (0 .. ($NumOfCols1 - 1)) {
	  $Value += $This->{Values}[$RowIndex1][$RowColIndex] * $Other->[$RowColIndex][$ColIndex2];
	}
	$Matrix->{Values}[$RowIndex1][$ColIndex2] = $Value;
      }
    }
  }
  else {
    my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

    ($NumOfRows, $NumOfCols) = $This->GetSize();
    $Matrix = new Matrix($NumOfRows, $NumOfCols);
    # Scalar subtraction...
    if ($OrderFlipped) {
      croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    }
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	$Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] * $Other;
      }
    }
  }
  return $Matrix;
}

#
# Matrix division operator supports two division modes:
#   . Division of two matrices by dividing corresponding matrix values
#   . Division matrix values  by a scalar($Matrix/2)
#
# Caveats:
#   . Division of scalar value by a matrix is not allowed (2/$Matrix)
#
sub _MatrixDivisionOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);

  $ErrorMsg = "_MatrixDivisionOperator: Matrix division failed";
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);

  my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();
  $Matrix = new Matrix($NumOfRows, $NumOfCols);

  if ($OtherIsMatrix) {
    # $OrderFlipped is set to false for two matrices...
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	$Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] / $Other->{Values}[$RowIndex][$ColIndex];
      }
    }
  }
  else {
    # Scalar subtraction...
    if ($OrderFlipped) {
      croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    }
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	$Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] / $Other;
      }
    }
  }
  return $Matrix;
}

#
# Matrix exponentiation operator supports two division modes:
#   . Exponent of two matrices by exponentiation of corresponding matrix values
#   . Exponentiation matrix values  by a scalar ($Matrix ** 2)
#
# Caveats:
#   . Exponentiation of scalar value by a matrix is not allowed (2 ** $Matrix)
#
sub _MatrixExponentiationOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);

  $ErrorMsg = "_MatrixExponentiationOperator: Matrix exponentiation failed";
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);

  my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();
  $Matrix = new Matrix($NumOfRows, $NumOfCols);

  if ($OtherIsMatrix) {
    # $OrderFlipped is set to false for two matrices...
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	$Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] ** $Other->{Values}[$RowIndex][$ColIndex];
      }
    }
  }
  else {
    # Scalar subtraction...
    if ($OrderFlipped) {
      croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    }
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	$Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] ** $Other;
      }
    }
  }
  return $Matrix;
}

#
# Matrix modulus operator supports two division modes:
#   . Modulus of two matrices by taking modulus between corresponding matrix values
#   . Modulus of matrix values  by a scalar ($Matrix % 2)
#
# Caveats:
#   . Modulus of scalar value by a matrix is not allowed (2 % $Matrix)
#
sub _MatrixModulusOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);

  $ErrorMsg = "_MatrixModulusOperator: Matrix modulus failed";
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);

  my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();
  $Matrix = new Matrix($NumOfRows, $NumOfCols);

  if ($OtherIsMatrix) {
    # $OrderFlipped is set to false for two matrices...
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	$Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] % $Other->{Values}[$RowIndex][$ColIndex];
      }
    }
  }
  else {
    # Scalar subtraction...
    if ($OrderFlipped) {
      croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    }
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	$Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] % $Other;
      }
    }
  }
  return $Matrix;
}

#
# Matrix booelan operator checks whether a matrix contains at least one non-zero
# value...
#
sub _MatrixBooleanOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);

  $ErrorMsg = "_MatrixBooleanOperator: Matrix boolean operation failed";
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);

  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();
  for $RowIndex (0 .. ($NumOfRows - 1)) {
    for $ColIndex (0 .. ($NumOfCols - 1)) {
      if ($This->{Values}[$RowIndex][$ColIndex] != 0.0) {
	return 1;
      }
    }
  }
  return 0;
}

#
# Matrix not booelan operator checks whether a matrix contains only zero values...
# value...
#
sub _MatrixNotBooleanOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);

  $ErrorMsg = "_MatrixNotBooleanOperator: Matrix not boolean operation failed";
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);

  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();
  for $RowIndex (0 .. ($NumOfRows - 1)) {
    for $ColIndex (0 .. ($NumOfCols - 1)) {
      if ($This->{Values}[$RowIndex][$ColIndex] != 0.0) {
	return 0;
      }
    }
  }
  return 1;
}

#
# Matrix equal operator supports two modes:
#   . Comparison of corresponding values in two matrices
#   . Comparing matrix values to a scalar ($Matrix == 2)
#
# Caveats:
#   . Comparison of a scalar to matrix values is not allowed (2 == $Matrix)
#
sub _MatrixEqualOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $CheckMatrixSizes, $ErrorMsg);

  $ErrorMsg = "_MatrixEqualOperator: Matrix equal failed";
  $CheckMatrixSizes = 0;
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_, $CheckMatrixSizes);

  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();

  if ($OtherIsMatrix) {
    # $OrderFlipped is set to false for two matrices...
    my($OtherNumOfRows, $OtherNumOfCols);

    # Check sizes...
    ($OtherNumOfRows, $OtherNumOfCols) = $Other->GetSize();
    if (!($NumOfRows == $OtherNumOfRows && $NumOfCols == $OtherNumOfCols)) {
      return 0;
    }

    # Check values...
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	if ($This->{Values}[$RowIndex][$ColIndex] != $Other->{Values}[$RowIndex][$ColIndex]) {
	  return 0;
	}
      }
    }
  }
  else {
    # Scalar comparison...
    if ($OrderFlipped) {
      croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    }
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	if ($This->{Values}[$RowIndex][$ColIndex] != $Other) {
	  return 0;
	}
      }
    }
  }
  return 1;
}

#
# Matrix not equal operator supports two modes:
#   . Comparison of corresponding values in two matrices
#   . Comparing matrix values to a scalar ($Matrix != 2)
#
# Caveats:
#   . Comparison of a scalar to matrix values is not allowed (2 != $Matrix)
#
sub _MatrixNotEqualOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $CheckMatrixSizes, $ErrorMsg);

  $ErrorMsg = "_MatrixNotEqualOperator: Matrix not equal failed";
  $CheckMatrixSizes = 0;
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_, $CheckMatrixSizes);

  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();

  if ($OtherIsMatrix) {
    # $OrderFlipped is set to false for two matrices...
    my($OtherNumOfRows, $OtherNumOfCols);

    # Check sizes...
    ($OtherNumOfRows, $OtherNumOfCols) = $Other->GetSize();
    if (!($NumOfRows == $OtherNumOfRows && $NumOfCols == $OtherNumOfCols)) {
      return 1;
    }

    # Check values...
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	if ($This->{Values}[$RowIndex][$ColIndex] == $Other->{Values}[$RowIndex][$ColIndex]) {
	  return 0;
	}
      }
    }
  }
  else {
    # Scalar comparison...
    if ($OrderFlipped) {
      croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    }
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	if ($This->{Values}[$RowIndex][$ColIndex] == $Other) {
	  return 0;
	}
      }
    }
  }
  return 1;
}

#
# Matrix less than operator supports two modes:
#   . Comparison of corresponding values in two matrices
#   . Comparing matrix values to a scalar ($Matrix < 2)
#
# Caveats:
#   . Comparison of a scalar to matrix values is not allowed (2 < $Matrix)
#
sub _MatrixLessThanOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $CheckMatrixSizes, $ErrorMsg);

  $ErrorMsg = "_MatrixLessThanOperator: Matrix less than failed";
  $CheckMatrixSizes = 0;
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_, $CheckMatrixSizes);

  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();

  if ($OtherIsMatrix) {
    # $OrderFlipped is set to false for two matrices...
    my($OtherNumOfRows, $OtherNumOfCols);

    # Check sizes...
    ($OtherNumOfRows, $OtherNumOfCols) = $Other->GetSize();
    if (!($NumOfRows == $OtherNumOfRows && $NumOfCols == $OtherNumOfCols)) {
      return 0;
    }

    # Check values...
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	if ($This->{Values}[$RowIndex][$ColIndex] >= $Other->{Values}[$RowIndex][$ColIndex]) {
	  return 0;
	}
      }
    }
  }
  else {
    # Scalar comparison...
    if ($OrderFlipped) {
      croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    }
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	if ($This->{Values}[$RowIndex][$ColIndex] >= $Other) {
	  return 0;
	}
      }
    }
  }
  return 1;
}

#
# Matrix less than equal operator supports two modes:
#   . Comparion of corresponding values in two matrices
#   . Comparing matrix values to a scalar ($Matrix <= 2)
#
# Caveats:
#   . Comparison of a scalar to matrix values is not allowed (2 <= $Matrix)
#
sub _MatrixLessThanEqualOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $CheckMatrixSizes, $ErrorMsg);

  $ErrorMsg = "_MatrixLessThanEqualOperator: Matrix less than equal failed";
  $CheckMatrixSizes = 0;
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_, $CheckMatrixSizes);

  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();

  if ($OtherIsMatrix) {
    # $OrderFlipped is set to false for two matrices...
    my($OtherNumOfRows, $OtherNumOfCols);

    # Check sizes...
    ($OtherNumOfRows, $OtherNumOfCols) = $Other->GetSize();
    if (!($NumOfRows == $OtherNumOfRows && $NumOfCols == $OtherNumOfCols)) {
      return 0;
    }

    # Check values...
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	if ($This->{Values}[$RowIndex][$ColIndex] > $Other->{Values}[$RowIndex][$ColIndex]) {
	  return 0;
	}
      }
    }
  }
  else {
    # Scalar comparison...
    if ($OrderFlipped) {
      croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    }
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	if ($This->{Values}[$RowIndex][$ColIndex] > $Other) {
	  return 0;
	}
      }
    }
  }
  return 1;
}

#
# Matrix greatar than operator supports two modes:
#   . Comparison of corresponding values in two matrices
#   . Comparing matrix values to a scalar ($Matrix > 2)
#
# Caveats:
#   . Comparison of a scalar to matrix values is not allowed (2 > $Matrix)
#
sub _MatrixGreatarThanOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $CheckMatrixSizes, $ErrorMsg);

  $ErrorMsg = "_MatrixGreatarThanOperator: Matrix greatar than failed";
  $CheckMatrixSizes = 0;
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_, $CheckMatrixSizes);

  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();

  if ($OtherIsMatrix) {
    # $OrderFlipped is set to false for two matrices...
    my($OtherNumOfRows, $OtherNumOfCols);

    # Check sizes...
    ($OtherNumOfRows, $OtherNumOfCols) = $Other->GetSize();
    if (!($NumOfRows == $OtherNumOfRows && $NumOfCols == $OtherNumOfCols)) {
      return 0;
    }

    # Check values...
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	if ($This->{Values}[$RowIndex][$ColIndex] <= $Other->{Values}[$RowIndex][$ColIndex]) {
	  return 0;
	}
      }
    }
  }
  else {
    # Scalar comparison...
    if ($OrderFlipped) {
      croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    }
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	if ($This->{Values}[$RowIndex][$ColIndex] <= $Other) {
	  return 0;
	}
      }
    }
  }
  return 1;
}

#
# Matrix greatar than equal operator supports two modes:
#   . Comparison of corresponding values in two matrices
#   . Comparing matrix values to a scalar ($Matrix >= 2)
#
# Caveats:
#   . Comparison of a scalar to matrix values is not allowed (2 >= $Matrix)
#
sub _MatrixGreatarThanEqualOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $CheckMatrixSizes, $ErrorMsg);

  $ErrorMsg = "_MatrixGreatarThanEqualOperator: Matrix greatar than equal failed";
  $CheckMatrixSizes = 0;
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_, $CheckMatrixSizes);

  my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();

  if ($OtherIsMatrix) {
    # $OrderFlipped is set to false for two matrices...
    my($OtherNumOfRows, $OtherNumOfCols);

    # Check sizes...
    ($OtherNumOfRows, $OtherNumOfCols) = $Other->GetSize();
    if (!($NumOfRows == $OtherNumOfRows && $NumOfCols == $OtherNumOfCols)) {
      return 0;
    }

    # Check values...
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	if ($This->{Values}[$RowIndex][$ColIndex] < $Other->{Values}[$RowIndex][$ColIndex]) {
	  return 0;
	}
      }
    }
  }
  else {
    # Scalar comparison...
    if ($OrderFlipped) {
      croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    }
    for $RowIndex (0 .. ($NumOfRows - 1)) {
      for $ColIndex (0 .. ($NumOfCols - 1)) {
	if ($This->{Values}[$RowIndex][$ColIndex] < $Other) {
	  return 0;
	}
      }
    }
  }
  return 1;
}

#
# Matrix negative value operator returns a matrix with values corresponding to
# negative values of a matrix
#
sub _MatrixNegativeValueOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);

  $ErrorMsg = "_MatrixNegativeValueOperator: Matrix negative value operation failed";
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);

  my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();
  $Matrix = new Matrix($NumOfRows, $NumOfCols);

  for $RowIndex (0 .. ($NumOfRows - 1)) {
    for $ColIndex (0 .. ($NumOfCols - 1)) {
      $Matrix->{Values}[$RowIndex][$ColIndex] = - $This->{Values}[$RowIndex][$ColIndex];
    }
  }
  return $Matrix;
}

#
# Matrix absolute value operator returns a matrix with values corresponding to
# absolute values of a matrix
#
sub _MatrixAbsoluteValueOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);

  $ErrorMsg = "_MatrixAbsoluteValueOperator: Matrix absolute value operation failed";
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);

  my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();
  $Matrix = new Matrix($NumOfRows, $NumOfCols);

  for $RowIndex (0 .. ($NumOfRows - 1)) {
    for $ColIndex (0 .. ($NumOfCols - 1)) {
      $Matrix->{Values}[$RowIndex][$ColIndex] = abs $This->{Values}[$RowIndex][$ColIndex];
    }
  }
  return $Matrix;
}

#
# Matrix exp natural base operator returns a matrix with values corresponding to
# e raised to the power of values in a matrix
#
sub _MatrixExpNaturalBaseOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);

  $ErrorMsg = "_MatrixExpNaturalBaseOperator: Matrix exp operation failed";
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);

  my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();
  $Matrix = new Matrix($NumOfRows, $NumOfCols);

  for $RowIndex (0 .. ($NumOfRows - 1)) {
    for $ColIndex (0 .. ($NumOfCols - 1)) {
      $Matrix->{Values}[$RowIndex][$ColIndex] = exp $This->{Values}[$RowIndex][$ColIndex];
    }
  }
  return $Matrix;
}

#
# Matrix log natural base operator returns a matrix with values corresponding to
# log of values in a matrix
#
sub _MatrixLogNaturalBaseOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);

  $ErrorMsg = "_MatrixLogNaturalBaseOperator: Matrix log operation failed";
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);

  my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();
  $Matrix = new Matrix($NumOfRows, $NumOfCols);

  for $RowIndex (0 .. ($NumOfRows - 1)) {
    for $ColIndex (0 .. ($NumOfCols - 1)) {
      $Matrix->{Values}[$RowIndex][$ColIndex] = log $This->{Values}[$RowIndex][$ColIndex];
    }
  }
  return $Matrix;
}

#
# Matrix square root operator returns a matrix with values corresponding to
# sqrt of values in a matrix
#
sub _MatrixSquareRootOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);

  $ErrorMsg = "_MatrixSquareRootOperator: Matrix sqrt operation failed";
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);

  my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();
  $Matrix = new Matrix($NumOfRows, $NumOfCols);

  for $RowIndex (0 .. ($NumOfRows - 1)) {
    for $ColIndex (0 .. ($NumOfCols - 1)) {
      $Matrix->{Values}[$RowIndex][$ColIndex] = sqrt $This->{Values}[$RowIndex][$ColIndex];
    }
  }
  return $Matrix;
}

#
# Matrix sine root operator returns a matrix with values corresponding to
# sin of values in a matrix
#
sub _MatrixSineOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);

  $ErrorMsg = "_MatrixSineOperator: Matrix sin operation failed";
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);

  my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();
  $Matrix = new Matrix($NumOfRows, $NumOfCols);

  for $RowIndex (0 .. ($NumOfRows - 1)) {
    for $ColIndex (0 .. ($NumOfCols - 1)) {
      $Matrix->{Values}[$RowIndex][$ColIndex] = sin $This->{Values}[$RowIndex][$ColIndex];
    }
  }
  return $Matrix;
}

#
# Matrix cosine root operator returns a matrix with values corresponding to
# cos of values in a matrix
#
sub _MatrixCosineOperator {
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);

  $ErrorMsg = "_MatrixCosineOperator: Matrix cos operation failed";
  ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);

  my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);

  ($NumOfRows, $NumOfCols) = $This->GetSize();
  $Matrix = new Matrix($NumOfRows, $NumOfCols);

  for $RowIndex (0 .. ($NumOfRows - 1)) {
    for $ColIndex (0 .. ($NumOfCols - 1)) {
      $Matrix->{Values}[$RowIndex][$ColIndex] = cos $This->{Values}[$RowIndex][$ColIndex];
    }
  }
  return $Matrix;
}

# Turn matrix into array for @{$Matrix} operation...
#
sub _MatrixToArrayOperator {
  my($This) = @_;

  return \@{$This->{Values}};
}

# Always return true in boolean context...
#
sub _BoolifyMatrix {
  my($This) = @_;

  return 1;
}

# Process parameters passed to overloaded operators...
#
# For uninary operators, $SecondParameter is not defined.
sub _ProcessOverloadedOperatorParameters {
  my($ErrorMsg, $FirstParameter, $SecondParameter, $ParametersOrderStatus, $CheckMatrixSizesStatus) = @_;
  my($This, $Other, $OrderFlipped, $OtherIsMatrix, $CheckMatrixSizes);

  ($This, $Other) =  ($FirstParameter, $SecondParameter);
  $OrderFlipped = (defined($ParametersOrderStatus) && $ParametersOrderStatus) ? 1 : 0;
  $CheckMatrixSizes = (defined $CheckMatrixSizesStatus) ? $CheckMatrixSizesStatus : 1;

  _ValidateMatrix($ErrorMsg, $This);

  $OtherIsMatrix = 0;
  if (defined($Other) && (ref $Other)) {
    # Make sure $Other is a matrix...
    _ValidateMatrix($ErrorMsg, $Other);
    if ($CheckMatrixSizes) {
      _ValidateMatrixSizesAreEqual($ErrorMsg, $This, $Other);
    }
    $OtherIsMatrix = 1;
  }
  return ($This, $Other, $OrderFlipped, $OtherIsMatrix);
}

# Make sure size of the two matrices contain the same number of values...
sub _ValidateMatrixSizesAreEqual {
  my($ErrorMsg, $Matrix1, $Matrix2) = @_;
  my($NumOfRows1, $NumOfCols1, $NumOfRows2, $NumOfCols2);

  ($NumOfRows1, $NumOfCols1) = $Matrix1->GetSize();
  ($NumOfRows2, $NumOfCols2) = $Matrix2->GetSize();

  if (!($NumOfRows1 == $NumOfRows2 && $NumOfCols1 == $NumOfCols2)) {
    croak "Error: ${ClassName}->${ErrorMsg}: Size of the matrices must be same...";
  }
}

# Return a string containing matrix values...
#
sub StringifyMatrix {
  my($This) = @_;
  my($MatrixString, $MatrixPrintStyleValue, $PrintFormat, $AllRowsInOneLine, $FormatString, $NumOfRows, $NumOfCols, $RowIndex, $RowNum, $RowString, @ValuesFormat);

  ($NumOfRows, $NumOfCols) = $This->GetSize();

  $MatrixPrintStyleValue = (exists $This->{MatrixPrintStyle}) ? $This->{MatrixPrintStyle} : $MatrixPrintStyle;
  $AllRowsInOneLine = ($MatrixPrintStyleValue =~ /^AllRowsInOneLine$/i) ? 1 : 0;

  $PrintFormat = (exists $This->{ValueFormat}) ? $This->{ValueFormat} : $ValueFormat;

  @ValuesFormat = ($PrintFormat) x $NumOfCols;
  $FormatString = join ' ', @ValuesFormat;

  $MatrixString = sprintf "<Size: $NumOfRows x $NumOfCols;";
  if ($AllRowsInOneLine) {
    $MatrixString .= sprintf " Values:";
  }
  else {
    $MatrixString .= sprintf " Values:\n";
  }

  $RowNum = 0;
  for $RowIndex (0 .. ($NumOfRows -1)) {
    $RowNum++;
    $RowString = sprintf "Row${RowNum}:[$FormatString]", @{$This->{Values}[$RowIndex]};
    if ($AllRowsInOneLine) {
      $MatrixString .= " $RowString";
    }
    else {
      $MatrixString .= "$RowString\n";
    }
  }
  $MatrixString .= ">";
  return $MatrixString;
}

1;

__END__

=head1 NAME

Matrix

=head1 SYNOPSIS

use Matrix;

use Matrix qw(:all);

=head1 DESCRIPTION

B<Matrix> class provides the following methods:

new, AddColumnValues, AddRowValues, Copy, GetColumnValues,
GetColumnValuesAsColumnMatrix, GetColumnValuesAsRowMatrix,
GetColumnValuesAsString, GetColumnValuesAsVector, GetDiagonalValues,
GetDiagonalValuesAsColumnMatrix, GetDiagonalValuesAsRowMatrix,
GetDiagonalValuesAsString, GetDiagonalValuesAsVector, GetDimension,
GetMatrixValuesReference, GetNumOfColumns, GetNumOfRows, GetRowValues,
GetRowValuesAsColumnMatrix, GetRowValuesAsRowMatrix, GetRowValuesAsString,
GetRowValuesAsVector, GetSize, GetValue, IdentityMatrix, IsAntiSymmetric,
IsBiDiagonal, IsDiagonal, IsIdentity, IsLeftTriangular, IsLowerBiDiagonal,
IsLowerTriangular, IsLowerUniTriangular, IsMatrix, IsNegative, IsPositive,
IsRightTriangular, IsSkewSymmetric, IsSquare, IsStrictlyLowerTriangular,
IsStrictlyUpperTriangular, IsSymmetric, IsTriDiagonal, IsUnit,
IsUnitLowerTriangular, IsUnitUpperTriangular, IsUpperBiDiagonal,
IsUpperTriangular, IsUpperUniTriangular, NewFromColumns, NewFromDiagonal,
NewFromRows, One, SetAllValues, SetColumnValues, SetDiagonalValues,
SetMatrixPrintStyle, SetRowValues, SetValue, SetValuePrintFormat, StringifyMatrix,
Transpose, UnitMatrix, Zero, ZeroMatrix

The following functions are available:

IsMatrix, IdentityMatrix, NewFromRows, NewFromColumns, NewFromDiagonal,
UnitMatrix, ZeroMatrix

The following operators are overloaded:

    "" bool !
    @{}
    + - * / ** %
    == != < <= > >=
    neg
    abs exp log sqrt cos sin

The matrix row and column indicies start from zero.

=head2 FUNCTIONS

=over 4

=item B<new>

    $NewMatrix = $Matrix->new($NumOfRows, $NumOfCols);

Creates a new B<Matrix> of size I<NumOfRows x NumOfCol>  and returns B<NewMatrix>
object.

=item B<AddColumnValues>

    $Matrix->AddColumnValues(@Values);
    $Matrix->AddColumnValues(\@Values);
    $Matrix->AddColumnValues($VectorObject);
    $Matrix->AddColumnValues("Value1 Value2 Value3 ...");

Adds column values to I<Matrix> using an array, reference to an array, another vector, or space
delimited value string and returns I<Matrix>.

=item B<AddRowValues>

    $Matrix->AddRowValues(@Values);
    $Matrix->AddRowValues(\@Values);
    $Matrix->AddRowValues($VectorObject);
    $Matrix->AddRowValues("Value1 Value2 Value3 ...");

Adds row values to I<Matrix> using an array, reference to an array, another vector, or space
delimited value string and returns B<Matrix>.

=item B<Copy>

    $NewMatrix = $Matrix->Copy();

Creates a copy of I<Matrix> and returns B<NewMatrix>.

=item B<GetColumnValues>

    @Values = $Matrix->GetColumnValues($ColIndex);
    $ValueCount = $Matrix->GetColumnValues($ColIndex);

Returns an array containing column value specified using I<ColIndex> with column index
starting at 0. In scalar context, number of column values is returned.

=item B<GetColumnValuesAsColumnMatrix>

    $ColumnMatrix = $Matrix->GetColumnValuesAsColumnMatrix($ColIndex);

Returns a new B<ColumnMatrix> containing column values specified using I<ColIndex> with
column index starting at 0.

=item B<GetColumnValuesAsRowMatrix>

    $RowMatrix = $Matrix->GetColumnValuesAsRowMatrix($ColIndex);

Returns a new B<RowMatrix> containing column values specified using I<ColIndex> with
column index starting at 0.

=item B<GetColumnValuesAsString>

    $ColumnValuesString = $Matrix->GetColumnValuesAsString($ColIndex);

Returns a space delimited B<ColumnValuesString> column values specified using I<ColIndex> with
column index starting at 0.

=item B<GetColumnValuesAsVector>

    $ColumnVector = $Matrix->GetColumnValuesAsVector($ColIndex);

Returns a new B<ColumnVector> column values specified using I<RowIndex> with
column index starting at 0.

=item B<GetDiagonalValues>

    @Values = $Matrix->GetDiagonalValues();
    $ValueCount = $Matrix->GetDiagonalValues();

Returns an array containing diagonal values. In scalar context, number of diagonal
values is returned.

=item B<GetDiagonalValuesAsColumnMatrix>

    $ColumnMatrix = $Matrix->GetDiagonalValuesAsColumnMatrix();

Returns a new B<ColumnMatrix> containing diagonal values corresponding to I<Matrix>.

=item B<GetDiagonalValuesAsRowMatrix>

    $RowMatrix = $Matrix->GetDiagonalValuesAsRowMatrix();

Returns a new B<RowMatrix> containing diagonal values corresponding to I<Matrix>.

=item B<GetDiagonalValuesAsString>

    $DiagonalValuesString = $Matrix->GetDiagonalValuesAsString();

Returns a space delimited B<DiagonalValuesString> containing diagonal values corresponding to
I<Matrix>.

=item B<GetDiagonalValuesAsVector>

    $DiagonalVector = $Matrix->GetDiagonalValuesAsVector();

Returns a new B<DiagonalVector> containing diagonal values corresponding to I<Matrix>.

=item B<GetDimension>

    ($NumOfRows, $NumOfCols) = $Matrix->GetDimension();

Returns size of I<Matrix>.

=item B<GetMatrixValuesReference>

    $ValuesRef = $Matrix->GetMatrixValuesReference();

Returns a reference to array containing rows and column values corresponding to I<Matrix>.

=item B<GetNumOfColumns>

    $NumOfCols = $Matrix->GetNumOfColumns();

Returns B<NumOfCols> in I<Matrix>.

=item B<GetNumOfRows>

    $NumOfRows = $Matrix->GetNumOfRows();

Returns B<NumOfRows> in I<Matrix>.

=item B<GetRowValues>

    @Values = $Matrix->GetRowValues($RowIndex);
    $ValueCount = $Matrix->GetRowValues($RowIndex);

Returns an array containing row value specified using I<RowIndex> with row index
starting at 0. In scalar context, number of row values is returned.

=item B<GetRowValuesAsColumnMatrix>

    $ColumnMatrix = $Matrix->GetRowValuesAsColumnMatrix($RowIndex);

Returns a new B<ColumnMatrix> containing row values specified using I<RowIndex> with
column index starting at 0.

=item B<GetRowValuesAsRowMatrix>

    $RowMatrix = $Matrix->GetRowValuesAsRowMatrix($RowIndex);

Returns a new B<RowMatrix> containing row values specified using I<RowIndex> with
row index starting at 0.

=item B<GetRowValuesAsString>

    $RowValuesString = $Matrix->GetRowValuesAsString($RowIndex);

Returns a space delimited B<RowValuesString> row values specified using I<RowIndex> with
row index starting at 0.

=item B<GetRowValuesAsVector>

    $RowVector = $Matrix->GetColumnValuesAsVector($RowIndex);

Returns a new B<RowVector> row values specified using I<RowIndex> with
row index starting at 0.

=item B<GetSize>

    ($NumOfRows, $NumOfCols) = $Matrix->GetSize();

Returns size of I<Matrix>.

=item B<GetValue>

    $Value = $Matrix->GetValue($RowIndex, $ColIndex, [$SkipIndexCheck]);

Returns B<Value> of I<Matrix> element specified using I<RowIndex> and I<ColIndex> with indicies
starting at 0 with optional validation of specified index values.

=item B<IdentityMatrix>

    $NewIdentityMatrix = $Matrix->IdentityMatrix($NumOfRows, $NumOfCols);
    $NewIdentityMatrix = Matrix::IdentityMatrix($NumOfRows, $NumOfCols);
    $NewIdentityMatrix = Matrix::IdentityMatrix();

Creates a new B<IdentityMatrix> of specified size I<NumOfRows x NumOfCol> or of size 3 x 3 and
returns B<NewIdentityMatrix> object.

=item B<IsAntiSymmetric>

    $Status = $Matrix->IsAntiSymmetric();

Returns 1 or 0 based on whether I<Matrix> is an anti symmetric matrix.

A matrix is an anti symmetric matrix:

    . It's a square matrix
    . Its elements are asymmetric with respect to main diagonal. In other words,
      elements below the main diagonal are equal to the negative of elements above
      the main diagonal.

Transpose of an anti symmetric matrix equals the negative of the matrix.

=item B<IsBiDiagonal>

    $Status = $Matrix->IsBiDiagonal();

Returns 1 or 0 based on whether I<Matrix> is upper or lower bidiagonal matrix.

=item B<IsDiagonal>

    $Status = $Matrix->IsDiagonal();

Returns 1 or 0 based on whether I<Matrix> is a diagonal matrix.

A matrix is a diagonal matrix:

    . It's a square matrix
    . All its off-diagonal elements are zeros and its diagonal elements may or may not
      be zeros

=item B<IsIdentity>

    $Status = $Matrix->IsIdentity();

Returns 1 or 0 based on whether I<Matrix> is an identity matrix.

=item B<IsLeftTriangular>

    $Status = $Matrix->IsLeftTriangular();

Returns 1 or 0 based on whether I<Matrix> is a left or lower matrix.

A matrix is a left triangular matrix:

    . It's a square matrix
    . All its entries above the main diagonal are zero

=item B<IsLowerBiDiagonal>

    $Status = $Matrix->IsLowerBiDiagonal();

Returns 1 or 0 based on whether I<Matrix> is a lower bidiagonal matrix.

A matrix is a lower bidiagonal matrix:

    . It's a square matrix
    . All its main diagonal and lower diagonal elements are non-zeros and all its
      other elements are zeros

=item B<IsLowerTriangular>

    $Status = $Matrix->IsLowerTriangular();

Returns 1 or 0 based on whether I<Matrix> is a left or lower triangular matrix.

A matrix is a lower triangular matrix:

    . It's a square matrix
    . All its entries above the main diagonal are zero

=item B<IsLowerUniTriangular>

    $Status = $Matrix->IsLowerUniTriangular();

Returns 1 or 0 based on whether I<Matrix> is a lower triangular matrix.

=item B<IsMatrix>

    $Status = Matrix::IsMatrix($Object);

Returns 1 or 0 based on whether I<Object> is a B<Matrix> object.

=item B<IsNegative>

    $Status = $Matrix->IsNegative();

Returns 1 or 0 based on whether I<Matrix> is a negative matrix containing only values
less than or equal to zero.

=item B<IsPositive>

    $Status = $Matrix->IsPositive();

Returns 1 or 0 based on whether I<Matrix> is a negative matrix containing only values
greater than or equal to zero.

=item B<IsRightTriangular>

    $Status = $Matrix->IsRightTriangular();

Returns 1 or 0 based on whether I<Matrix> is a right or upper triangular matrix.

=item B<IsSkewSymmetric>

    $Status = $Matrix->IsSkewSymmetric();

Returns 1 or 0 based on whether I<Matrix> is a skew or anti symmetric matrix.

=item B<IsSquare>

    $Status = $Matrix->IsSquare();

Returns 1 or 0 based on whether I<Matrix> is a square matrix containing equal
number of rows and columns.

=item B<IsStrictlyLowerTriangular>

    $Status = $Matrix->IsStrictlyLowerTriangular();

Returns 1 or 0 based on whether I<Matrix> is a strictly lower triangular matrix.

A matrix is a strictly lower triangular matrix:

    . It's a square matrix
    . All its entries on and above the main diagonal are zero

=item B<IsStrictlyUpperTriangular>

    $Status = $Matrix->IsStrictlyUpperTriangular();

Returns 1 or 0 based on whether I<Matrix> is a strictly upper triangular matrix.

A matrix is a strictly upper triangular matrix:

    . It's a square matrix
    . All its entries on and below the main diagonal are zero

=item B<IsSymmetric>

    $Status = $Matrix->IsSymmetric();

Returns 1 or 0 based on whether I<Matrix> is a symmetric matrix.

A matrix is a symmetric matrix:

    . It's a square matrix
    . Its elements are symmetric with respect to main diagonal. In other words,
      elements below the main diagonal are equal to the elements above the main
      diagonal.

Transpose of a symmetric matrix equals the matrix itself.

=item B<IsTriDiagonal>

    $Status = $Matrix->IsTriDiagonal();

Returns 1 or 0 based on whether I<Matrix> is a tridiagonal matrix.

A matrix is a  tribidiagonal matrix:

    . It's a square matrix
    . All its main diagonal, upper diagonal, and lower diagonal elements are non-zeros and all its
      other elements are zeros

=item B<IsUnit>

    $Status = $Matrix->IsUnit();

Returns 1 or 0 based on whether I<Matrix> is a unit matrix.

A matrix is a unit matrix:

    . It's a square matrix
    . All its diagonal elements are ones and its off-diagonal elements are zeros

=item B<IsUnitLowerTriangular>

    $Status = $Matrix->IsUnitLowerTriangular();

Returns 1 or 0 based on whether I<Matrix> is an unit lower triangular matrix.

A matrix is an unit lower triangular matrix:

    . It's a square matrix
    . All its entries main diagonal are one
    . All its entries above the main diagonal are zero

=item B<IsUnitUpperTriangular>

    $Status = $Matrix->IsUnitUpperTriangular();

Returns 1 or 0 based on whether I<Matrix> is an unit upper triangular matrix.

A matrix is an unit upper triangular matrix:

    . It's a square matrix
    . All its entries main diagonal are one
    . All its entries below the main diagonal are zero

=item B<IsUpperBiDiagonal>

    $Status = $Matrix->IsUpperBiDiagonal();

Returns 1 or 0 based on whether I<Matrix> is an upper bidiagonal matrix.

A matrix is an upper bidiagonal matrix:

    . It's a square matrix
    . All its main diagonal and upper diagonal elements are non-zeros and all its
      other elements are zeros

=item B<IsUpperTriangular>

    $Status = $Matrix->IsUpperTriangular();

Returns 1 or 0 based on whether I<Matrix> is a right or upper triangular matrix.

A matrix is an upper triangular matrix:

    . It's a square matrix
    . All its entries below the main diagonal are zero

=item B<IsUpperUniTriangular>

    $Status = $Matrix->IsUpperUniTriangular();

Returns 1 or 0 based on whether I<Matrix> is a right or upper triangular matrix.

=item B<NewFromColumns>

    $NewMatrix = Matrix::NewFromColumns($Col1Vector, $Col2Vector, ...);
    $NewMatrix = Matrix::NewFromColumns($Col1ValuesRef, $Col2ValuesRef, ...);
    $NewMatrix = Matrix::NewFromColumns("Val1 Val2 ...", "Val1 Val2", ...);

    $NewMatrix = $Matrix->NewFromColumns($Col1Vector, $Col2Vector, ...);
    $NewMatrix = $Matrix->NewFromColumns($Col1ValuesRef, $Col2ValuesRef, ...);
    $NewMatrix = $Matrix->NewFromColumns("Val1 Val2 ...", "Val1 Val2", ...);

Creates a new B<Matrix> using specified column values and returns B<NewMatrix> object.

The column values can be specified in one of the following formats:

    . List of vector objects
    . References to list of values
    . List of strings containing columns values delimited by space

Each column must contain the same number of values.

=item B<NewFromDiagonal>

    $NewMatrix = Matrix::NewFromDiagonal($DiagonalVector);
    $NewMatrix = Matrix::NewFromDiagonal($DiagonalValuesRef);
    $NewMatrix = Matrix::NewFromDiagonal("Val1 Val2 ...");

    $NewMatrix = Matrix->NewFromDiagonal($DiagonalVector);
    $NewMatrix = Matrix->NewFromDiagonal($DiagonalValuesRef);
    $NewMatrix = Matrix->NewFromDiagonal("Val1 Val2 ...");

Creates a new B<Matrix> using specified diagonal values and returns B<NewMatrix> object.

The column values can be specified in one of the following formats:

    . A vector object
    . Reference to list of values
    . Strings containing diagonal values delimited by space

=item B<NewFromRows>

    $NewMatrix = Matrix::NewFromRows($Row1Vector, $RowVector, ...);
    $NewMatrix = Matrix::NewFromRows($Row1ValuesRef, $Row2ValuesRef, ...);
    $NewMatrix = Matrix::NewFromRows("Val1 Val2 ...", "Val1 Val2", ...);

    $NewMatrix = $Matrix->NewFromRows($Row1Vector, $Row2Vector, ...);
    $NewMatrix = $Matrix->NewFromRows($Row1ValuesRef, $Row2ValuesRef, ...);
    $NewMatrix = $Matrix->NewFromRows("Val1 Val2 ...", "Val1 Val2", ...);

Creates a new B<Matrix> using specified row values and returns B<NewMatrix> object.

The row values can be specified in one of the following formats:

    . List of vector objects
    . References to list of values
    . List of strings containing columns values delimited by space

Each row must contain the same number of values.

=item B<One>

    $Matrix->One();

Sets values of all I<Matrix> elements to 1 and returns I<Matrix>.

=item B<SetAllValues>

    $Matrix->SetAllValues($Value);

Sets values of all I<Matrix> elements to specified I<Value> and returns I<Matrix>.

=item B<SetColumnValues>

    $Matrix->SetColumnValues($ColIndex, @Values);
    $Matrix->SetColumnValues($ColIndex, \@Values);
    $Matrix->SetColumnValues($ColIndex, $VectorObject);
    $Matrix->SetColumnValues($ColIndex, "Value1 Value2 Value3 ...");

Sets column values of a specified I<ColIndex> of I<Matrix> using an array, reference to an array,
another vector, or space delimited value string and returns I<Matrix>.

=item B<SetDiagonalValues>

    $Matrix->SetDiagonalValues(@Values);
    $Matrix->SetDiagonalValues(\@Values);
    $Matrix->SetDiagonalValues($VectorObject);
    $Matrix->SetDiagonalValues("Value1 Value2 Value3 ...");

Sets values of the diagonal in square I<Matrix> and returns I<Matrix>.

=item B<SetMatrixPrintStyle>

    $Matrix->SetMatrixPrintStyle($MatrixStyle);
    $Matrix::SetMatrixPrintStyle($MatrixStyle);

Sets print style for matrix rows for an individual object or the whole class during StringifyMatrix
operation. Possible I<MatrixStyle> values: I<AllRowsInOneLine, OneRowPerLine>. Default:
I<AllRowsInOneLine>.

=item B<SetRowValues>

    $Matrix->SetRowValues($ColIndex, @Values);
    $Matrix->SetRowValues($ColIndex, \@Values);
    $Matrix->SetRowValues($ColIndex, $VectorObjext);
    $Matrix->SetRowValues($ColIndex, "Value1 Value2 Value3 ...");

Sets row values of a specified I<RowIndex> of I<Matrix> using an array, reference to an array,
another vector, or space delimited value string and returns I<Matrix>.

=item B<SetValue>

    $Matrix->SetValue($RowIndex, $ColIndex, $Value, [$SkipIndexCheck]);

Sets B<Value> of I<Matrix> element specified using I<RowIndex> and I<ColIndex> with indicies
starting at 0 with optional validation of specified index values and return I<Matrix>.

=item B<SetValuePrintFormat>

    $Matrix->SetValuePrintFormat($ValueFormat);
    $Matrix::SetValuePrintFormat($ValueFormat);

Sets value print format for an individual object or the whole class during StringifyMatrix operation
and returns I<Matrix>.

=item B<StringifyMatrix>

    $String = $Matrix->StringifyMatrix();

Returns a string containing information about I<Matrix> object.

=item B<Transpose>

    $Matrix->Transpose();

Transposes I<Matrix> by swaping rows with columns and returns I<Matrix>.

=item B<UnitMatrix>

    $NewUnitMatrix = $Matrix::UnitMatrix($NumOfRows, $NumOfCols);
    $NewUnitMatrix = $Matrix::UnitMatrix();
    $NewUnitMatrix = $Matrix->UnitMatrix($NumOfRows, $NumOfCols);

Creates a new B<UnitMatrix> of specified size I<NumOfRows x NumOfCol> or of size 3 x 3 and
returns B<NewUnitMatrix> object.

=item B<Zero>

    $Matrix->Zero();

Sets values of all I<Matrix> elements to 0 and returns I<Matrix>.

=item B<ZeroMatrix>

    $NewZeroMatrix = $Matrix::ZeroMatrix($NumOfRows, $NumOfCols);
    $NewZeroMatrix = $Matrix::ZeroMatrix();
    $NewZeroMatrix = $Matrix->ZeroMatrix($NumOfRows, $NumOfCols);

Creates a new B<ZeroMatrix> of specified size I<NumOfRows x NumOfCol> or of size 3 x 3 and
returns B<NewZeroMatrix> object.

=back

=head1 AUTHOR

Manish Sud <msud@san.rr.com>

=head1 SEE ALSO

Vector.pm

=head1 COPYRIGHT

Copyright (C) 2015 Manish Sud. All rights reserved.

This file is part of MayaChemTools.

MayaChemTools is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option)
any later version.

=cut
