diff --git a/lib/Value/Matrix.pm b/lib/Value/Matrix.pm index 5b1892cdd..3cb15be14 100644 --- a/lib/Value/Matrix.pm +++ b/lib/Value/Matrix.pm @@ -113,10 +113,14 @@ Examples: element : Real/Complex/Fraction value when passed the same number of arguments as the degree of the Matrix. If passed more than n arguments, null. If the degree of the Matrix is n and C is passed - k arguments with k < n, then this produces the corresponding degree (n-k) tensor. + k arguments with k < n, then this produces the corresponding degree (n-k) tensor. =head3 Update values (these need to be added) + replace(value, [ i, j, ... ]) + For a degree n matrix, the array reference for the second entry should have n elements. + The entry at that location will be replaced with value. + see C in MatrixReduce and L =head3 Advanced @@ -1342,7 +1346,50 @@ sub element { return $M->extract(@_); } -# @@@ assign @@@ +=head3 C + +Replace an entry with a new element and return the replaced element. + +Usage: + + $A = Matrix([ [ 1, 2, 3 ], [ 4, 5, 6 ] ]); + $A->replace(7, [ 2, 1 ]); + # Now $A is the matrix [ [ 1, 2, 3 ], [ 7, 5, 6 ] ] + + # Also, the method returns the value that is replaced. + $x = $A->replace(8, [ 1, 3 ]); + # Now $A is [ [ 1, 2, 8 ], [ 7, 5, 6 ] ] and $x is 3. + + # It is also OK to specify the indices as an array instead of an array reference + $A->replace(7, 2, 1); + +=cut + +sub replace { + my ($self, $value, @indices) = @_; + $value = Value::makeValue($value) unless Value::isValue($value); + $self->Error('Cannot replace a matrix entry with a Formula') if Value::isFormula($value); + my $dim = $self->degree; + @indices = @{ $indices[0] } if (ref $indices[0] eq 'ARRAY'); + $self->Error("There should be $dim indices") unless $dim == @indices; + my $data = $self->{data}; + my $i = pop(@indices) - 1; + + for (my $n = 0; @indices; $n++) { + my $j = shift(@indices) - 1; + $self->Error('The ' . $self->NameForNumber($n + 1) . ' index is outside of the array bounds') + if $j < 0 || $j >= scalar(@$data); + $data = $data->[$j]->{data}; + } + $self->Error('The last index is outside of the array bounds') + if $i < 0 || $i >= scalar(@$data); + $self->Error('The new entry value should be ' . $data->[$i]->showType . ' not ' . $value->showType) + unless $value->type eq $data->[$i]->type; + my $old = $data->[$i]; + $data->[$i] = $value; + return $old; +} + # @@@ removeRow, removeColumn @@@ # @@@ Minor @@@ diff --git a/t/math_objects/matrix.t b/t/math_objects/matrix.t index 32dfe3d45..6e6e2390b 100644 --- a/t/math_objects/matrix.t +++ b/t/math_objects/matrix.t @@ -215,6 +215,37 @@ subtest 'Extract a column' => sub { }, qr/Column must be a positive integer/, 'Test that an error is thrown for passing a non-positive integer'; }; +subtest 'Replace a value' => sub { + my $A = Matrix([ 1, 2, 3 ]); + my $B = Matrix([ [ 1, 2, 3 ], [ 4, 5, 6 ] ]); + my $C = Matrix([ [ 1, 2, 3 ], [ 4, 5, 6 ] ]); + my $D = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ]); + + is $A->replace(9, [2]), 2, 'Replace an element from a degree 1 matrix, returning replaced element'; + is $A->TeX, Matrix([ 1, 9, 3 ])->TeX, 'Replace an element from a degree 1 matrix'; + is $B->replace(9, [ 2, 1 ]), 4, 'Replace an element from a degree 2 matrix, returning replaced element'; + is $B->TeX, Matrix([ [ 1, 2, 3 ], [ 9, 5, 6 ] ])->TeX, 'Replace an element from a degree 2 matrix'; + is $C->replace(9, 2, 1), 4, 'Replace an element from a degree 2 matrix, returning replaced element'; + is $C->TeX, Matrix([ [ 1, 2, 3 ], [ 9, 5, 6 ] ])->TeX, 'Replace an element from a degree 2 matrix'; + is $D->replace(9, [ 2, 1, 2 ]), 6, 'Replace an element from a degree 3 matrix, returning replaced element'; + is $D->TeX, Matrix([ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 9 ], [ 7, 8 ] ] ])->TeX, + 'Replace an element from a degree 3 matrix'; + + like dies { + $B->replace(10, [ 1, 2, 3 ]); + }, qr/There should be 2 indices/, 'Check correct number of indices passed'; + like dies { + $B->replace(10, [ 3, 1 ]); + }, qr/The first index is outside of the array bounds/, 'Check index is within bounds'; + like dies { + $B->replace(Point(1, 2), [ 2, 1 ]); + }, qr/The new entry value should be a Number not a Point/, 'Check replaced value has the same type'; + like dies { + $B->replace(Formula('x'), [ 2, 1 ]); + }, qr/Cannot replace a matrix entry with a Formula/, 'Check replaced value is not a Formula'; + +}; + subtest 'Construct an identity matrix' => sub { my $I = Value::Matrix->I(3); my $B = Matrix([ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ]);