#region License Information
/* HeuristicLab
* Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
namespace HeuristicLab.Data {
[Item("TriangularMatrix", "Represents a lower triangular matrix.")]
[StorableClass]
public class TriangularMatrix : ValueTypeMatrix, IStringConvertibleMatrix where T : struct {
[Storable]
private readonly T[] storage;
private readonly int dimension;
private readonly long capacity;
private TriangularMatrix() { }
public TriangularMatrix(int dimension) {
this.dimension = dimension;
capacity = (long)dimension * (dimension + 1) / 2;
storage = new T[capacity];
readOnly = true;
}
[StorableConstructor]
protected TriangularMatrix(bool deserializing) : base(deserializing) { }
protected TriangularMatrix(TriangularMatrix original, Cloner cloner) : base(original, cloner) {
capacity = original.capacity;
dimension = original.dimension;
storage = (T[])original.storage.Clone();
}
public override IDeepCloneable Clone(Cloner cloner) {
return new TriangularMatrix(this, cloner);
}
public override int Rows {
get { return dimension; }
protected set { throw new NotSupportedException(); }
}
public override int Columns {
get { return dimension; }
protected set { throw new NotSupportedException(); }
}
public int Length { get { return storage.Length; } }
// the indexing rule for the (lower-)triangular matrix is that always i <= j, otherwise an IndexOutOfBounds exception will occur
public override T this[int rowIndex, int columnIndex] {
get {
// provide symmetry of returned values
if (columnIndex > rowIndex) return this[columnIndex, rowIndex];
return storage[rowIndex * (rowIndex + 1) / 2 + columnIndex];
}
set {
if (columnIndex > rowIndex) this[columnIndex, rowIndex] = value;
else storage[rowIndex * (rowIndex + 1) / 2 + columnIndex] = value;
}
}
public T this[int index] {
get { return storage[index]; }
set { storage[index] = value; }
}
protected virtual string GetValue(int rowIndex, int columnIndex) {
return this[rowIndex, columnIndex].ToString(); // see above indexing rule
}
protected virtual bool SetValue(string value, int rowIndex, int columnIndex) {
T val;
if (!TryParse(value, out val))
return false;
this[rowIndex, columnIndex] = val;
return true;
}
protected virtual bool Validate(string value, out string errorMessage) {
T val;
errorMessage = "";
if (!TryParse(value, out val)) {
errorMessage = string.Format("Could not parse string \"{0}\" as {1}.", value, typeof(T));
return false;
}
return true;
}
public override IEnumerator GetEnumerator() {
return storage.Cast().GetEnumerator();
}
private static bool TryParse(string value, out T val) {
if (typeof(T) == typeof(sbyte)) {
sbyte v;
if (sbyte.TryParse(value, out v)) {
val = (T)(object)v;
return true;
}
} else if (typeof(T) == typeof(byte)) {
byte v;
if (byte.TryParse(value, out v)) {
val = (T)(object)v;
return true;
}
} else if (typeof(T) == typeof(char)) {
char v;
if (char.TryParse(value, out v)) {
val = (T)(object)v;
return true;
}
} else if (typeof(T) == typeof(short)) {
short v;
if (short.TryParse(value, out v)) {
val = (T)(object)v;
return true;
}
} else if (typeof(T) == typeof(ushort)) {
ushort v;
if (ushort.TryParse(value, out v)) {
val = (T)(object)v;
return true;
}
} else if (typeof(T) == typeof(int)) {
int v;
if (int.TryParse(value, out v)) {
val = (T)(object)v;
return true;
}
} else if (typeof(T) == typeof(uint)) {
uint v;
if (uint.TryParse(value, out v)) {
val = (T)(object)v;
return true;
}
} else if (typeof(T) == typeof(long)) {
long v;
if (long.TryParse(value, out v)) {
val = (T)(object)v;
return true;
}
} else if (typeof(T) == typeof(ulong)) {
ulong v;
if (ulong.TryParse(value, out v)) {
val = (T)(object)v;
return true;
}
}
val = default(T);
return false;
}
public void GetMatrixCoordinates(int index, out int row, out int col) {
var root = TriangularRoot(index);
row = (int)Math.Floor(root);
col = index - row * (row + 1) / 2;
}
private static double TriangularRoot(double x) {
return (Math.Sqrt(8 * x + 1) - 1) / 2;
}
#region IStringConvertibleMatrix Members
int IStringConvertibleMatrix.Rows {
get { return dimension; }
set { throw new NotSupportedException("The triangular matrix does not support changing the number of rows."); }
}
int IStringConvertibleMatrix.Columns {
get { return dimension; }
set { throw new NotSupportedException("The triangular matrix does not support changing the number of columns."); }
}
string IStringConvertibleMatrix.GetValue(int rowIndex, int columnIndex) {
return GetValue(rowIndex, columnIndex);
}
bool IStringConvertibleMatrix.SetValue(string value, int rowIndex, int columnIndex) {
return SetValue(value, rowIndex, columnIndex);
}
bool IStringConvertibleMatrix.Validate(string value, out string errorMessage) {
return Validate(value, out errorMessage);
}
#endregion
}
}