// // Math.NET Numerics, part of the Math.NET Project // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // // Copyright (c) 2009-2013 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; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using IStation.Numerics.LinearAlgebra.Storage; namespace IStation.Numerics.LinearAlgebra { [DebuggerDisplay("Vector {" + nameof(Count) + "}")] public abstract partial class Vector { /// /// Indicates whether the current object is equal to another object of the same type. /// /// An object to compare with this object. /// /// true if the current object is equal to the parameter; otherwise, false. /// public bool Equals(Vector other) { return other != null && Storage.Equals(other.Storage); } /// /// Determines whether the specified is equal to this instance. /// /// The to compare with this instance. /// /// true if the specified is equal to this instance; otherwise, false. /// public sealed override bool Equals(object obj) { return obj is Vector other && Storage.Equals(other.Storage); } /// /// Returns a hash code for this instance. /// /// /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. /// public sealed override int GetHashCode() { return Storage.GetHashCode(); } #if !NETSTANDARD1_3 /// /// Creates a new object that is a copy of the current instance. /// /// /// A new object that is a copy of this instance. /// object ICloneable.Clone() { return Clone(); } #endif int IList.IndexOf(T item) { for (int i = 0; i < Count; ++i) { if (At(i).Equals(item)) return i; } return -1; } void IList.Insert(int index, T item) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } bool ICollection.IsReadOnly => false; void ICollection.Add(T item) { throw new NotSupportedException(); } bool ICollection.Remove(T item) { throw new NotSupportedException(); } bool ICollection.Contains(T item) { // ReSharper disable once LoopCanBeConvertedToQuery foreach (var x in this) { if (x.Equals(item)) return true; } return false; } void ICollection.CopyTo(T[] array, int arrayIndex) { if (array == null) { throw new ArgumentNullException(nameof(array)); } Storage.CopySubVectorTo(new DenseVectorStorage(array.Length, array), 0, arrayIndex, Count); } bool IList.IsReadOnly => false; bool IList.IsFixedSize => true; object IList.this[int index] { get => Storage[index]; set => Storage[index] = (T) value; } int IList.IndexOf(object value) { if (!(value is T)) { return -1; } return ((IList) this).IndexOf((T) value); } bool IList.Contains(object value) { if (!(value is T)) { return false; } return ((ICollection) this).Contains((T) value); } void IList.Insert(int index, object value) { throw new NotSupportedException(); } int IList.Add(object value) { throw new NotSupportedException(); } void IList.Remove(object value) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } bool ICollection.IsSynchronized => false; object ICollection.SyncRoot => Storage; void ICollection.CopyTo(Array array, int index) { if (array == null) { throw new ArgumentNullException(nameof(array)); } if (array.Rank != 1) { throw new ArgumentException("Array must have exactly one dimension (and not be null).", nameof(array)); } Storage.CopySubVectorTo(new DenseVectorStorage(array.Length, (T[]) array), 0, index, Count); } /// /// Returns an enumerator that iterates through the collection. /// /// /// A that can be used to iterate through the collection. /// IEnumerator IEnumerable.GetEnumerator() { return Enumerate().GetEnumerator(); } /// /// Returns an enumerator that iterates through a collection. /// /// /// An object that can be used to iterate through the collection. /// IEnumerator IEnumerable.GetEnumerator() { return Enumerate().GetEnumerator(); } /// /// Returns a string that describes the type, dimensions and shape of this vector. /// public virtual string ToTypeString() { return FormattableString.Invariant($"{GetType().Name} {Count}-{typeof(T).Name}"); } public string[,] ToVectorStringArray(int maxPerColumn, int maxCharactersWidth, int padding, string ellipsis, Func formatValue) { // enforce minima to avoid pathetic cases maxPerColumn = Math.Max(maxPerColumn, 3); maxCharactersWidth = Math.Max(maxCharactersWidth, 16); var columns = new List>(); int chars = 0; int offset = 0; while (offset < Count) { // full column int height = Math.Min(maxPerColumn, Count - offset); var candidate = FormatCompleteColumn(offset, height, formatValue); chars += candidate.Item1 + padding; if (chars > maxCharactersWidth && offset > 0) { break; } columns.Add(candidate); offset += height; } if (offset < Count) { // we're not done yet, but adding the last column has failed // --> make the last column partial var last = columns[columns.Count - 1]; var c = last.Item2; c[c.Length - 2] = ellipsis; c[c.Length - 1] = formatValue(At(Count - 1)); } int rows = columns[0].Item2.Length; int cols = columns.Count; var array = new string[rows, cols]; int colIndex = 0; foreach (var column in columns) { for (int k = 0; k < column.Item2.Length; k++) { array[k, colIndex] = column.Item2[k]; } for (int k = column.Item2.Length; k < rows; k++) { array[k, colIndex] = ""; } colIndex++; } return array; } static string FormatStringArrayToString(string[,] array, string columnSeparator, string rowSeparator) { var rows = array.GetLength(0); var cols = array.GetLength(1); var widths = new int[cols]; for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { widths[j] = Math.Max(widths[j], array[i, j].Length); } } var sb = new StringBuilder(); for (int i = 0; i < rows; i++) { sb.Append(array[i, 0].PadLeft(widths[0])); for (int j = 1; j < cols; j++) { sb.Append(columnSeparator); sb.Append(array[i, j].PadLeft(widths[j])); } sb.Append(rowSeparator); } return sb.ToString(); } Tuple FormatCompleteColumn(int offset, int height, Func formatValue) { var c = new string[height]; int index = 0; for (var k = 0; k < height; k++) { c[index++] = formatValue(At(offset + k)); } int w = c.Max(x => x.Length); return new Tuple(w, c); } /// /// Returns a string that represents the content of this vector, column by column. /// /// Maximum number of entries and thus lines per column. Typical value: 12; Minimum: 3. /// Maximum number of characters per line over all columns. Typical value: 80; Minimum: 16. /// Character to use to print if there is not enough space to print all entries. Typical value: "..". /// Character to use to separate two columns on a line. Typical value: " " (2 spaces). /// Character to use to separate two rows/lines. Typical value: Environment.NewLine. /// Function to provide a string for any given entry value. public string ToVectorString(int maxPerColumn, int maxCharactersWidth, string ellipsis, string columnSeparator, string rowSeparator, Func formatValue) { return FormatStringArrayToString( ToVectorStringArray(maxPerColumn, maxCharactersWidth, columnSeparator.Length, ellipsis, formatValue), columnSeparator, rowSeparator); } /// /// Returns a string that represents the content of this vector, column by column. /// /// Maximum number of entries and thus lines per column. Typical value: 12; Minimum: 3. /// Maximum number of characters per line over all columns. Typical value: 80; Minimum: 16. /// Floating point format string. Can be null. Default value: G6. /// Format provider or culture. Can be null. public string ToVectorString(int maxPerColumn, int maxCharactersWidth, string format = null, IFormatProvider provider = null) { if (format == null) { format = "G6"; } return ToVectorString(maxPerColumn, maxCharactersWidth, "..", " ", Environment.NewLine, x => x.ToString(format, provider)); } /// /// Returns a string that represents the content of this vector, column by column. /// /// Floating point format string. Can be null. Default value: G6. /// Format provider or culture. Can be null. public string ToVectorString(string format = null, IFormatProvider provider = null) { if (format == null) { format = "G6"; } return ToVectorString(12, 80, "..", " ", Environment.NewLine, x => x.ToString(format, provider)); } /// /// Returns a string that summarizes this vector, column by column and with a type header. /// /// Maximum number of entries and thus lines per column. Typical value: 12; Minimum: 3. /// Maximum number of characters per line over all columns. Typical value: 80; Minimum: 16. /// Floating point format string. Can be null. Default value: G6. /// Format provider or culture. Can be null. public string ToString(int maxPerColumn, int maxCharactersWidth, string format = null, IFormatProvider provider = null) { return string.Concat(ToTypeString(), Environment.NewLine, ToVectorString(maxPerColumn, maxCharactersWidth, format, provider)); } /// /// Returns a string that summarizes this vector. /// The maximum number of cells can be configured in the class. /// public sealed override string ToString() { return string.Concat(ToTypeString(), Environment.NewLine, ToVectorString()); } /// /// Returns a string that summarizes this vector. /// The maximum number of cells can be configured in the class. /// The format string is ignored. /// public string ToString(string format = null, IFormatProvider formatProvider = null) { return string.Concat(ToTypeString(), Environment.NewLine, ToVectorString(format, formatProvider)); } } }