// // Math.NET Numerics, part of the Math.NET Project // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // // Copyright (c) 2009-2015 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using IStation.Numerics.Distributions; using IStation.Numerics.LinearAlgebra.Storage; using IStation.Numerics.Providers.LinearAlgebra; using IStation.Numerics.Threading; namespace IStation.Numerics.LinearAlgebra.Single { /// /// A matrix type for diagonal matrices. /// /// /// Diagonal matrices can be non-square matrices but the diagonal always starts /// at element 0,0. A diagonal matrix will throw an exception if non diagonal /// entries are set. The exception to this is when the off diagonal elements are /// 0.0 or NaN; these settings will cause no change to the diagonal matrix. /// [Serializable] [DebuggerDisplay("DiagonalMatrix {RowCount}x{ColumnCount}-Single")] public class DiagonalMatrix : Matrix { /// /// Gets the matrix's data. /// /// The matrix's data. readonly float[] _data; /// /// Create a new diagonal matrix straight from an initialized matrix storage instance. /// The storage is used directly without copying. /// Intended for advanced scenarios where you're working directly with /// storage for performance or interop reasons. /// public DiagonalMatrix(DiagonalMatrixStorage storage) : base(storage) { _data = storage.Data; } /// /// Create a new square diagonal matrix with the given number of rows and columns. /// All cells of the matrix will be initialized to zero. /// /// If the order is less than one. public DiagonalMatrix(int order) : this(new DiagonalMatrixStorage(order, order)) { } /// /// Create a new diagonal matrix with the given number of rows and columns. /// All cells of the matrix will be initialized to zero. /// /// If the row or column count is less than one. public DiagonalMatrix(int rows, int columns) : this(new DiagonalMatrixStorage(rows, columns)) { } /// /// Create a new diagonal matrix with the given number of rows and columns. /// All diagonal cells of the matrix will be initialized to the provided value, all non-diagonal ones to zero. /// /// If the row or column count is less than one. public DiagonalMatrix(int rows, int columns, float diagonalValue) : this(rows, columns) { for (var i = 0; i < _data.Length; i++) { _data[i] = diagonalValue; } } /// /// Create a new diagonal matrix with the given number of rows and columns directly binding to a raw array. /// The array is assumed to contain the diagonal elements only and is used directly without copying. /// Very efficient, but changes to the array and the matrix will affect each other. /// public DiagonalMatrix(int rows, int columns, float[] diagonalStorage) : this(new DiagonalMatrixStorage(rows, columns, diagonalStorage)) { } /// /// Create a new diagonal matrix as a copy of the given other matrix. /// This new matrix will be independent from the other matrix. /// The matrix to copy from must be diagonal as well. /// A new memory block will be allocated for storing the matrix. /// public static DiagonalMatrix OfMatrix(Matrix matrix) { return new DiagonalMatrix(DiagonalMatrixStorage.OfMatrix(matrix.Storage)); } /// /// Create a new diagonal matrix as a copy of the given two-dimensional array. /// This new matrix will be independent from the provided array. /// The array to copy from must be diagonal as well. /// A new memory block will be allocated for storing the matrix. /// public static DiagonalMatrix OfArray(float[,] array) { return new DiagonalMatrix(DiagonalMatrixStorage.OfArray(array)); } /// /// Create a new diagonal matrix and initialize each diagonal value from the provided indexed enumerable. /// Keys must be provided at most once, zero is assumed if a key is omitted. /// This new matrix will be independent from the enumerable. /// A new memory block will be allocated for storing the matrix. /// public static DiagonalMatrix OfIndexedDiagonal(int rows, int columns, IEnumerable> diagonal) { return new DiagonalMatrix(DiagonalMatrixStorage.OfIndexedEnumerable(rows, columns, diagonal)); } /// /// Create a new diagonal matrix and initialize each diagonal value from the provided enumerable. /// This new matrix will be independent from the enumerable. /// A new memory block will be allocated for storing the matrix. /// public static DiagonalMatrix OfDiagonal(int rows, int columns, IEnumerable diagonal) { return new DiagonalMatrix(DiagonalMatrixStorage.OfEnumerable(rows, columns, diagonal)); } /// /// Create a new diagonal matrix and initialize each diagonal value using the provided init function. /// public static DiagonalMatrix Create(int rows, int columns, Func init) { return new DiagonalMatrix(DiagonalMatrixStorage.OfInit(rows, columns, init)); } /// /// Create a new square sparse identity matrix where each diagonal value is set to One. /// public static DiagonalMatrix CreateIdentity(int order) { return new DiagonalMatrix(DiagonalMatrixStorage.OfValue(order, order, One)); } /// /// Create a new diagonal matrix with diagonal values sampled from the provided random distribution. /// public static DiagonalMatrix CreateRandom(int rows, int columns, IContinuousDistribution distribution) { return new DiagonalMatrix(new DiagonalMatrixStorage(rows, columns, Generate.RandomSingle(Math.Min(rows, columns), distribution))); } /// /// Negate each element of this matrix and place the results into the result matrix. /// /// The result of the negation. protected override void DoNegate(Matrix result) { if (result is DiagonalMatrix diagResult) { LinearAlgebraControl.Provider.ScaleArray(-1, _data, diagResult._data); return; } result.Clear(); for (var i = 0; i < _data.Length; i++) { result.At(i, i, -_data[i]); } } /// /// Adds another matrix to this matrix. /// /// The matrix to add to this matrix. /// The matrix to store the result of the addition. /// If the two matrices don't have the same dimensions. protected override void DoAdd(Matrix other, Matrix result) { // diagonal + diagonal = diagonal if (other is DiagonalMatrix diagOther && result is DiagonalMatrix diagResult) { LinearAlgebraControl.Provider.AddArrays(_data, diagOther._data, diagResult._data); return; } other.CopyTo(result); for (int i = 0; i < _data.Length; i++) { result.At(i, i, result.At(i, i) + _data[i]); } } /// /// Subtracts another matrix from this matrix. /// /// The matrix to subtract. /// The matrix to store the result of the subtraction. /// If the two matrices don't have the same dimensions. protected override void DoSubtract(Matrix other, Matrix result) { // diagonal - diagonal = diagonal if (other is DiagonalMatrix diagOther && result is DiagonalMatrix diagResult) { LinearAlgebraControl.Provider.SubtractArrays(_data, diagOther._data, diagResult._data); return; } other.Negate(result); for (int i = 0; i < _data.Length; i++) { result.At(i, i, result.At(i, i) + _data[i]); } } /// /// Multiplies each element of the matrix by a scalar and places results into the result matrix. /// /// The scalar to multiply the matrix with. /// The matrix to store the result of the multiplication. /// If the result matrix's dimensions are not the same as this matrix. protected override void DoMultiply(float scalar, Matrix result) { if (scalar == 0.0) { result.Clear(); return; } if (scalar == 1.0) { CopyTo(result); return; } if (result is DiagonalMatrix diagResult) { LinearAlgebraControl.Provider.ScaleArray(scalar, _data, diagResult._data); } else { base.DoMultiply(scalar, result); } } /// /// Multiplies this matrix with a vector and places the results into the result vector. /// /// The vector to multiply with. /// The result of the multiplication. protected override void DoMultiply(Vector rightSide, Vector result) { var d = Math.Min(ColumnCount, RowCount); if (d < RowCount) { result.ClearSubVector(ColumnCount, RowCount - ColumnCount); } if (d == ColumnCount) { if (rightSide.Storage is DenseVectorStorage denseOther && result.Storage is DenseVectorStorage denseResult) { LinearAlgebraControl.Provider.PointWiseMultiplyArrays(_data, denseOther.Data, denseResult.Data); return; } } for (var i = 0; i < d; i++) { result.At(i, _data[i]*rightSide.At(i)); } } /// /// Multiplies this matrix with another matrix and places the results into the result matrix. /// /// The matrix to multiply with. /// The result of the multiplication. protected override void DoMultiply(Matrix other, Matrix result) { if (other is DiagonalMatrix diagonalOther && result is DiagonalMatrix diagonalResult) { var thisDataCopy = new float[diagonalResult._data.Length]; var otherDataCopy = new float[diagonalResult._data.Length]; Array.Copy(_data, 0, thisDataCopy, 0, (diagonalResult._data.Length > _data.Length) ? _data.Length : diagonalResult._data.Length); Array.Copy(diagonalOther._data, 0, otherDataCopy, 0, (diagonalResult._data.Length > diagonalOther._data.Length) ? diagonalOther._data.Length : diagonalResult._data.Length); LinearAlgebraControl.Provider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, diagonalResult._data); return; } if (other.Storage is DenseColumnMajorMatrixStorage denseOther) { var dense = denseOther.Data; var diagonal = _data; var d = Math.Min(denseOther.RowCount, RowCount); if (d < RowCount) { result.ClearSubMatrix(denseOther.RowCount, RowCount - denseOther.RowCount, 0, denseOther.ColumnCount); } int index = 0; for (int i = 0; i < denseOther.ColumnCount; i++) { for (int j = 0; j < d; j++) { result.At(j, i, dense[index]*diagonal[j]); index++; } index += (denseOther.RowCount - d); } return; } if (ColumnCount == RowCount) { other.Storage.MapIndexedTo(result.Storage, (i, j, x) => x*_data[i], Zeros.AllowSkip, ExistingData.Clear); } else { result.Clear(); other.Storage.MapSubMatrixIndexedTo(result.Storage, (i, j, x) => x*_data[i], 0, 0, Math.Min(RowCount, other.RowCount), 0, 0, other.ColumnCount, Zeros.AllowSkip, ExistingData.AssumeZeros); } } /// /// Multiplies this matrix with transpose of another matrix and places the results into the result matrix. /// /// The matrix to multiply with. /// The result of the multiplication. protected override void DoTransposeAndMultiply(Matrix other, Matrix result) { if (other is DiagonalMatrix diagonalOther && result is DiagonalMatrix diagonalResult) { var thisDataCopy = new float[diagonalResult._data.Length]; var otherDataCopy = new float[diagonalResult._data.Length]; Array.Copy(_data, 0, thisDataCopy, 0, (diagonalResult._data.Length > _data.Length) ? _data.Length : diagonalResult._data.Length); Array.Copy(diagonalOther._data, 0, otherDataCopy, 0, (diagonalResult._data.Length > diagonalOther._data.Length) ? diagonalOther._data.Length : diagonalResult._data.Length); LinearAlgebraControl.Provider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, diagonalResult._data); return; } if (other.Storage is DenseColumnMajorMatrixStorage denseOther) { var dense = denseOther.Data; var diagonal = _data; var d = Math.Min(denseOther.ColumnCount, RowCount); if (d < RowCount) { result.ClearSubMatrix(denseOther.ColumnCount, RowCount - denseOther.ColumnCount, 0, denseOther.RowCount); } int index = 0; for (int j = 0; j < d; j++) { for (int i = 0; i < denseOther.RowCount; i++) { result.At(j, i, dense[index]*diagonal[j]); index++; } } return; } base.DoTransposeAndMultiply(other, result); } /// /// Multiplies the transpose of this matrix with another matrix and places the results into the result matrix. /// /// The matrix to multiply with. /// The result of the multiplication. protected override void DoTransposeThisAndMultiply(Matrix other, Matrix result) { if (other is DiagonalMatrix diagonalOther && result is DiagonalMatrix diagonalResult) { var thisDataCopy = new float[diagonalResult._data.Length]; var otherDataCopy = new float[diagonalResult._data.Length]; Array.Copy(_data, 0, thisDataCopy, 0, (diagonalResult._data.Length > _data.Length) ? _data.Length : diagonalResult._data.Length); Array.Copy(diagonalOther._data, 0, otherDataCopy, 0, (diagonalResult._data.Length > diagonalOther._data.Length) ? diagonalOther._data.Length : diagonalResult._data.Length); LinearAlgebraControl.Provider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, diagonalResult._data); return; } if (other.Storage is DenseColumnMajorMatrixStorage denseOther) { var dense = denseOther.Data; var diagonal = _data; var d = Math.Min(denseOther.RowCount, ColumnCount); if (d < ColumnCount) { result.ClearSubMatrix(denseOther.RowCount, ColumnCount - denseOther.RowCount, 0, denseOther.ColumnCount); } int index = 0; for (int i = 0; i < denseOther.ColumnCount; i++) { for (int j = 0; j < d; j++) { result.At(j, i, dense[index]*diagonal[j]); index++; } index += (denseOther.RowCount - d); } return; } if (ColumnCount == RowCount) { other.Storage.MapIndexedTo(result.Storage, (i, j, x) => x*_data[i], Zeros.AllowSkip, ExistingData.Clear); } else { result.Clear(); other.Storage.MapSubMatrixIndexedTo(result.Storage, (i, j, x) => x*_data[i], 0, 0, other.RowCount, 0, 0, other.ColumnCount, Zeros.AllowSkip, ExistingData.AssumeZeros); } } /// /// Multiplies the transpose of this matrix with a vector and places the results into the result vector. /// /// The vector to multiply with. /// The result of the multiplication. protected override void DoTransposeThisAndMultiply(Vector rightSide, Vector result) { var d = Math.Min(ColumnCount, RowCount); if (d < ColumnCount) { result.ClearSubVector(RowCount, ColumnCount - RowCount); } if (d == RowCount) { if (rightSide.Storage is DenseVectorStorage denseOther && result.Storage is DenseVectorStorage denseResult) { LinearAlgebraControl.Provider.PointWiseMultiplyArrays(_data, denseOther.Data, denseResult.Data); return; } } for (var i = 0; i < d; i++) { result.At(i, _data[i]*rightSide.At(i)); } } /// /// Divides each element of the matrix by a scalar and places results into the result matrix. /// /// The scalar to divide the matrix with. /// The matrix to store the result of the division. protected override void DoDivide(float divisor, Matrix result) { if (divisor == 1.0f) { CopyTo(result); return; } if (result is DiagonalMatrix diagResult) { LinearAlgebraControl.Provider.ScaleArray(1.0f/divisor, _data, diagResult._data); return; } result.Clear(); for (int i = 0; i < _data.Length; i++) { result.At(i, i, _data[i]/divisor); } } /// /// Divides a scalar by each element of the matrix and stores the result in the result matrix. /// /// The scalar to add. /// The matrix to store the result of the division. protected override void DoDivideByThis(float dividend, Matrix result) { if (result is DiagonalMatrix diagResult) { var resultData = diagResult._data; CommonParallel.For(0, _data.Length, 4096, (a, b) => { for (int i = a; i < b; i++) { resultData[i] = dividend/_data[i]; } }); return; } result.Clear(); for (int i = 0; i < _data.Length; i++) { result.At(i, i, dividend/_data[i]); } } /// /// Computes the determinant of this matrix. /// /// The determinant of this matrix. public override float Determinant() { if (RowCount != ColumnCount) { throw new ArgumentException("Matrix must be square."); } return _data.Aggregate(1.0f, (current, t) => current * t); } /// /// Returns the elements of the diagonal in a . /// /// The elements of the diagonal. /// For non-square matrices, the method returns Min(Rows, Columns) elements where /// i == j (i is the row index, and j is the column index). public override Vector Diagonal() { return new DenseVector(_data).Clone(); } /// /// Copies the values of the given array to the diagonal. /// /// The array to copy the values from. The length of the vector should be /// Min(Rows, Columns). /// If the length of does not /// equal Min(Rows, Columns). /// For non-square matrices, the elements of are copied to /// this[i,i]. public override void SetDiagonal(float[] source) { if (source.Length != _data.Length) { throw new ArgumentException("The array arguments must have the same length.", nameof(source)); } Buffer.BlockCopy(source, 0, _data, 0, source.Length * Constants.SizeOfFloat); } /// /// Copies the values of the given to the diagonal. /// /// The vector to copy the values from. The length of the vector should be /// Min(Rows, Columns). /// If the length of does not /// equal Min(Rows, Columns). /// For non-square matrices, the elements of are copied to /// this[i,i]. public override void SetDiagonal(Vector source) { if (source is DenseVector denseSource) { if (_data.Length != denseSource.Values.Length) { throw new ArgumentException("All vectors must have the same dimensionality.", nameof(source)); } Buffer.BlockCopy(denseSource.Values, 0, _data, 0, denseSource.Values.Length * Constants.SizeOfFloat); } else { base.SetDiagonal(source); } } /// Calculates the induced L1 norm of this matrix. /// The maximum absolute column sum of the matrix. public override double L1Norm() { return _data.Aggregate(0f, (current, t) => Math.Max(current, Math.Abs(t))); } /// Calculates the induced L2 norm of the matrix. /// The largest singular value of the matrix. public override double L2Norm() { return _data.Aggregate(0f, (current, t) => Math.Max(current, Math.Abs(t))); } /// Calculates the induced infinity norm of this matrix. /// The maximum absolute row sum of the matrix. public override double InfinityNorm() { return L1Norm(); } /// Calculates the entry-wise Frobenius norm of this matrix. /// The square root of the sum of the squared values. public override double FrobeniusNorm() { return Math.Sqrt(_data.Sum(t => t * t)); } /// Calculates the condition number of this matrix. /// The condition number of the matrix. public override float ConditionNumber() { var maxSv = float.NegativeInfinity; var minSv = float.PositiveInfinity; foreach (var t in _data) { maxSv = Math.Max(maxSv, Math.Abs(t)); minSv = Math.Min(minSv, Math.Abs(t)); } return maxSv / minSv; } /// Computes the inverse of this matrix. /// If is not a square matrix. /// If is singular. /// The inverse of this matrix. public override Matrix Inverse() { if (RowCount != ColumnCount) { throw new ArgumentException("Matrix must be square."); } var inverse = (DiagonalMatrix)Clone(); for (var i = 0; i < _data.Length; i++) { if (_data[i] != 0.0) { inverse._data[i] = 1.0f / _data[i]; } else { throw new ArgumentException("Matrix must not be singular."); } } return inverse; } /// /// Returns a new matrix containing the lower triangle of this matrix. /// /// The lower triangle of this matrix. public override Matrix LowerTriangle() { return Clone(); } /// /// Puts the lower triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. /// If the result matrix's dimensions are not the same as this matrix. public override void LowerTriangle(Matrix result) { if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } if (ReferenceEquals(this, result)) { return; } result.Clear(); for (var i = 0; i < _data.Length; i++) { result.At(i, i, _data[i]); } } /// /// Returns a new matrix containing the lower triangle of this matrix. The new matrix /// does not contain the diagonal elements of this matrix. /// /// The lower triangle of this matrix. public override Matrix StrictlyLowerTriangle() { return new DiagonalMatrix(RowCount, ColumnCount); } /// /// Puts the strictly lower triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. /// If the result matrix's dimensions are not the same as this matrix. public override void StrictlyLowerTriangle(Matrix result) { if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } result.Clear(); } /// /// Returns a new matrix containing the upper triangle of this matrix. /// /// The upper triangle of this matrix. public override Matrix UpperTriangle() { return Clone(); } /// /// Puts the upper triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. /// If the result matrix's dimensions are not the same as this matrix. public override void UpperTriangle(Matrix result) { if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } result.Clear(); for (var i = 0; i < _data.Length; i++) { result.At(i, i, _data[i]); } } /// /// Returns a new matrix containing the upper triangle of this matrix. The new matrix /// does not contain the diagonal elements of this matrix. /// /// The upper triangle of this matrix. public override Matrix StrictlyUpperTriangle() { return new DiagonalMatrix(RowCount, ColumnCount); } /// /// Puts the strictly upper triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. /// If the result matrix's dimensions are not the same as this matrix. public override void StrictlyUpperTriangle(Matrix result) { if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } result.Clear(); } /// /// Creates a matrix that contains the values from the requested sub-matrix. /// /// The row to start copying from. /// The number of rows to copy. Must be positive. /// The column to start copying from. /// The number of columns to copy. Must be positive. /// The requested sub-matrix. /// If: is /// negative, or greater than or equal to the number of rows. /// is negative, or greater than or equal to the number /// of columns. /// (columnIndex + columnLength) >= Columns /// (rowIndex + rowLength) >= Rows /// If or /// is not positive. public override Matrix SubMatrix(int rowIndex, int rowCount, int columnIndex, int columnCount) { var target = rowIndex == columnIndex ? (Matrix)new DiagonalMatrix(rowCount, columnCount) : new SparseMatrix(rowCount, columnCount); Storage.CopySubMatrixTo(target.Storage, rowIndex, 0, rowCount, columnIndex, 0, columnCount, ExistingData.AssumeZeros); return target; } /// /// Permute the columns of a matrix according to a permutation. /// /// The column permutation to apply to this matrix. /// Always thrown /// Permutation in diagonal matrix are senseless, because of matrix nature public override void PermuteColumns(Permutation p) { throw new InvalidOperationException("Permutations in diagonal matrix are not allowed"); } /// /// Permute the rows of a matrix according to a permutation. /// /// The row permutation to apply to this matrix. /// Always thrown /// Permutation in diagonal matrix are senseless, because of matrix nature public override void PermuteRows(Permutation p) { throw new InvalidOperationException("Permutations in diagonal matrix are not allowed"); } /// /// Evaluates whether this matrix is symmetric. /// public sealed override bool IsSymmetric() { return true; } /// /// Computes the canonical modulus, where the result has the sign of the divisor, /// for the given divisor each element of the matrix. /// /// The scalar denominator to use. /// Matrix to store the results in. protected override void DoModulus(float divisor, Matrix result) { if (result is DiagonalMatrix diagonalResult) { CommonParallel.For(0, _data.Length, 4096, (a, b) => { var r = diagonalResult._data; for (var i = a; i < b; i++) { r[i] = Euclid.Modulus(_data[i], divisor); } }); } else { base.DoModulus(divisor, result); } } /// /// Computes the canonical modulus, where the result has the sign of the divisor, /// for the given dividend for each element of the matrix. /// /// The scalar numerator to use. /// A vector to store the results in. protected override void DoModulusByThis(float dividend, Matrix result) { if (result is DiagonalMatrix diagonalResult) { CommonParallel.For(0, _data.Length, 4096, (a, b) => { var r = diagonalResult._data; for (var i = a; i < b; i++) { r[i] = Euclid.Modulus(dividend, _data[i]); } }); } else { base.DoModulusByThis(dividend, result); } } /// /// Computes the remainder (% operator), where the result has the sign of the dividend, /// for the given divisor each element of the matrix. /// /// The scalar denominator to use. /// Matrix to store the results in. protected override void DoRemainder(float divisor, Matrix result) { if (result is DiagonalMatrix diagonalResult) { CommonParallel.For(0, _data.Length, 4096, (a, b) => { var r = diagonalResult._data; for (var i = a; i < b; i++) { r[i] = _data[i] % divisor; } }); } else { base.DoRemainder(divisor, result); } } /// /// Computes the remainder (% operator), where the result has the sign of the dividend, /// for the given dividend for each element of the matrix. /// /// The scalar numerator to use. /// A vector to store the results in. protected override void DoRemainderByThis(float dividend, Matrix result) { if (result is DiagonalMatrix diagonalResult) { CommonParallel.For(0, _data.Length, 4096, (a, b) => { var r = diagonalResult._data; for (var i = a; i < b; i++) { r[i] = dividend % _data[i]; } }); } else { base.DoRemainderByThis(dividend, result); } } } }