///
/// This file is part of ILNumerics Community Edition.
///
/// ILNumerics Community Edition - high performance computing for applications.
/// Copyright (C) 2006 - 2012 Haymo Kutschbach, http://ilnumerics.net
///
/// ILNumerics Community Edition is free software: you can redistribute it and/or modify
/// it under the terms of the GNU General Public License version 3 as published by
/// the Free Software Foundation.
///
/// ILNumerics Community Edition 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 ILNumerics Community Edition. See the file License.txt in the root
/// of your distribution package. If not, see .
///
/// In addition this software uses the following components and/or licenses:
///
/// =================================================================================
/// The Open Toolkit Library License
///
/// Copyright (c) 2006 - 2009 the Open Toolkit library.
///
/// 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.
///
/// =================================================================================
///
using System;
using ILNumerics.Storage;
using ILNumerics.Exceptions;
using System.Collections.Generic;
namespace ILNumerics
{
///
/// ILSize - dimensions for array objects (immutable)
///
/// The class internally manages the dimensions of ILNumerics arrays.
/// The class is immutable. Therefore, once created, it informs the user
/// about all dimension related properties, but cannot get altered.
[Serializable]
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
public class ILSize {
#region attributes
internal int [] m_dims;
int m_nrDims = 0;
int m_numberOfElements = 0;
int m_length = 0;
int [] m_seqDistancesCache;
static ILSize s_emptyDimension = new ILSize(0,0);
static ILSize s_scalarDimension = new ILSize(1,1);
static ILSize s_twoElementDim = new ILSize(2,1);
static ILSize s_threeElementDim = new ILSize(3,1);
[ThreadStatic]
static List s_listCache;
static List ListCache {
get {
List ret = s_listCache;
if (ret == null) {
s_listCache = new List();
ret = s_listCache;
}
return ret;
}
}
#endregion
#region constructors
///
/// Create new ILSize
///
/// variable length dimensions specifier
/// Trailing singleton dimensions of dims will be kept.
public ILSize(params int[] dims) {
if (dims == null)
throw new ILArgumentException("invalid dimension specification. the number of dimensions must not be 0.");
int outLen = Math.Max(2, dims.Length);
m_dims = new int[outLen];
for (int i = 0; i < dims.Length; i++) {
m_dims[i] = dims[i];
}
for (int i = dims.Length; i < 2; i++) {
m_dims[i] = 1;
}
m_numberOfElements = 1;
m_nrDims = m_dims.Length;
int t = 0,
d;
for (; t < m_dims.Length; t++) {
d = m_dims[t];
if (d > m_length) m_length = d;
m_numberOfElements *= d;
}
}
///
/// Create new size descriptor from given data
///
/// Size description
public ILSize(params ILBaseArray[] size)
: this (convert2int(size)) {
}
///
/// Create new size descriptor
///
/// true: trailing singleton
/// dimensions will be trimmed, false: keep trailing singleton dimensions
/// Size description
public ILSize (bool trimSingletons, params int[] size) {
if (!trimSingletons) {
if (size == null)
throw new ILArgumentException("invalid size specification. the number of dimensions must not be 0.");
int outLen = Math.Max(2, size.Length);
m_dims = new int[outLen];
for (int i = 0; i < size.Length; i++) {
m_dims[i] = size[i];
}
for (int i = size.Length; i < 2; i++) {
m_dims[i] = 1;
}
m_numberOfElements = 1;
m_nrDims = m_dims.Length;
int t = 0,
d;
for (; t < m_dims.Length; t++) {
d = m_dims[t];
if (d > m_length) m_length = d;
m_numberOfElements *= d;
}
} else {
ListCache.Clear();
s_listCache.AddRange(size);
while (s_listCache.Count < 2)
s_listCache.Add(1);
if (trimSingletons)
for (int i = s_listCache.Count; i-- > 2; ) {
if (s_listCache[i] == 1) s_listCache.RemoveAt(i);
else break;
}
m_dims = s_listCache.ToArray();
m_numberOfElements = 1;
m_nrDims = m_dims.Length;
int t = 0,
d;
for (; t < m_dims.Length; t++) {
d = m_dims[t];
if (d > m_length) m_length = d;
m_numberOfElements *= d;
}
}
}
///
/// An size descriptor of size 0x0
///
public static ILSize Empty00 {
get {
return s_emptyDimension; }
}
///
/// An size descriptor of size 1x1
///
public static ILSize Scalar1_1
{
get {
return s_scalarDimension; }
}
///
/// An size descriptor of size 2x1
///
public static ILSize Column2_1 {
get {
return s_twoElementDim; }
}
///
/// An size descriptor of size 3x1
///
public static ILSize Column3_1 {
get {
return s_threeElementDim; }
}
#endregion
#region properties
/// Get number of dimensions.
public int NumberOfDimensions {
get {
return m_nrDims;
}
}
///
/// Number of non singleton dimensions of the array
///
/// Non singleton dimensions are dimensions which length is larger than 1.
/// Empty dimensions (length = 0) will not be taken into account.
public int NonSingletonDimensions {
// ToDo: Definition of non-singelton (= not 1) should be reconsidered. Here 0 is not counted as non-singelton
get {
int ret = 0;
for (int i = 0; i < m_nrDims; i++) {
if (m_dims[i] > 1) ret++;
}
return ret;
}
}
///
/// Number of elements in the array
///
public int NumberOfElements {
get {
return m_numberOfElements;
}
}
///
/// Length of the longest dimension
///
public int Longest {
get {
return m_length;
}
}
///
/// Find dimension to work on, if non was specified by user
///
/// Index of first non singleton dimension (i.e. dimension that is not 1) or 0, if this array is a scalar.
internal int WorkingDimension() {
// if (m_numberOfElements <= 1) return -1;
for (int i = 0; i < m_nrDims; i++) {
if (m_dims[i] != 1) return i;
}
// return -1; // this should not happen! Test on scalar above
return 0; // All dimensions are 1, return first one
}
#endregion
#region public member
///
/// Storage distance of elements in dimension dim
///
/// 0-based index of dimension to query the element distance for
/// Storage distance of elements between adjacent elements of dimension dim
///
/// If dimension index dim is larger than the number of
/// dimensions of this array, the number of elements will
/// be returned (trailing singleton dimensions).
public int SequentialIndexDistance(int dim) {
if (dim >= m_nrDims)
return m_numberOfElements;
dim %= m_nrDims;
int ret = 1;
for (int i = 0; i < dim && i < m_nrDims; i++) {
ret *= m_dims[i];
}
return ret;
}
///
/// Distances between adjacent elements for all dimensions
///
/// minimum length of array to be
/// returned. If this is larger than the number of dimensions
/// in this size descriptor, the array will have minLength elements,
/// with elements outside this dimensions repeating the value
/// of the last dimension. The length of the array returned will
/// be equal or greater than max(minLength,NumberOfDimensions).
/// This is provided for performance reasons and should be
/// used internally only. It enables developer of index access routines
/// to cache the elements distances directly inside their functions
/// without having to query the info on every index access.
/// Keep in mind, only the distances for the number of my
/// dimensions are returned. Higher dimensions must be set to
/// NumberOfElements if needed. This is different than querying
/// the distances by SequentialIndexDistance(int), which will assume
/// and return trailing dimensions to be 1.
/// IMPORTANT: ALTERING THE ARRAY RETURNED IS NOT ALLOWED AND
/// MAY LEAD TO SERIOUS INSTABILITY AND UNWANTED SIDE EFFECTS!
internal int[] GetSequentialIndexDistances(int minLength) {
minLength = Math.Max(m_nrDims,minLength);
if (m_seqDistancesCache == null || m_seqDistancesCache.Length < minLength){
m_seqDistancesCache = new int[minLength];
int tmp = 1, i = 0;
for (; i < m_nrDims; i++) {
m_seqDistancesCache[i] = tmp;
tmp *= m_dims[i];
}
for (; i < m_seqDistancesCache.Length; i++) {
m_seqDistancesCache[i] = tmp;
}
}
return m_seqDistancesCache;
}
///
/// Transfer my dimensions to integer array
///
/// Integer array containing a copy of all dimensions length
public int[] ToIntArray(){
return (int[])m_dims.Clone();
}
///
/// Transfer my dimensions to integer array
///
/// Minimum length of output array. If length
/// is larger than my dimensions, trailing ones will be added.
/// Integer array containing a copy of dimensions length.
/// Trailing elements outside my dims will be one.
internal int[] ToIntArray (int length) {
int[] ret;
ret = new int[length > m_nrDims ? length : m_nrDims];
Array.Copy(m_dims,0,ret,0,m_nrDims);
for (int i = m_nrDims; i < length; i++) {
ret[i] = 1;
}
return ret;
}
///
/// return dimension vector, fixed length, for subarray operations
///
///
/// dimension vector, corresponds to reshaped or unlimited dimensions
internal int[] ToIntArrayEx (int length) {
int[] ret;
if (length == m_nrDims) {
ret = new int[length];
Array.Copy(m_dims,0,ret,0,m_nrDims);
} else if (length > m_nrDims) {
ret = new int[length];
int i = 0;
for (; i < m_dims.Length; i++) {
ret[i] = m_dims[i];
}
for (; i < length; i++) {
ret[i] = 1;
}
} else if (length > 0) {
ret = new int[length];
int i = 0;
for (; i < length; i++) {
ret[i] = m_dims[i];
}
for (int a = i--; a < m_dims.Length; a++) {
ret[i] *= m_dims[a];
}
} else
throw new ILArgumentException("the length parameter must be positive");
return ret;
}
///
/// Transform indices from int[] System.Array into sequential index of underlying 1dim array
///
/// int array of nrDims length, min length: 1, all indices must fit into my dimensions
/// Index pointing to element defined by 'idx'
public int IndexFromArray(int[] idx) {
int ret = idx[0];
if (ret < 0 || ret >= m_dims[0])
throw new ILArgumentException("index out of bounds, dimension # 0");
int i = 1, tmp, len = (idx.Length < m_nrDims) ? idx.Length : m_nrDims;
for (; i < len-1; i++) {
tmp = idx[i];
if (tmp >= m_dims[i] || tmp < 0)
throw new ILArgumentException("index out of bounds, dimension # " + i);
ret += tmp * SequentialIndexDistance(i);
}
if (i < len) {
tmp = idx[i];
if (tmp < 0 || tmp >= m_dims[i]) throw new ILArgumentException("index out of bounds, dimension # " + i);
ret += tmp * SequentialIndexDistance(i);
}
return ret;
}
///
/// Transform dimension position into sequential index, gather expand
/// information
///
/// int array of arbitrary length
/// [output] true, if the indices
/// given address an element outside of
/// this dimensions size. In this case, the output parameter
/// 'Dimensions' carry the sizes
/// of new dimensions needed. False otherwise
/// sizes of dimension if expansion is needed.
/// Must be predefined to length of max(idx.Length,m_nrDims) at least
/// Index number pointing to the value's position in
/// sequential storage.
/// no checks are made for idx to fit inside dimensions!
/// This functions is used for left side assignments. Therefore it
/// computes the destination index also if it lays outside
/// the array bounds.
internal int IndexFromArray(ref bool MustExpand, ref int[] dimensions, int[] idx) {
int tmp;
if (idx.Length < m_nrDims) {
#region idx < nrDims
// expanding is allowed for all but the last specified dimension
// reason: if less than m_nrDims idx entries exist, the array is
// reshaped to that number of dimensions and the index access
// computed according to the reshaped version. Attempts to expand
// that last (virtual) dimension is not allowed than since that
// dimension would be ambigous.
#region special case: sequential addressing
if (idx.Length < 2) {
if (idx.Length == 1) {
tmp = idx[0];
if (tmp >= m_numberOfElements) {
// allowed only for scalars and vectors
if (m_dims[0] == m_numberOfElements) {
dimensions[0] = tmp+1;
MustExpand = true;
return tmp;
} else if (m_dims[1] == m_numberOfElements) {
dimensions[1] = tmp+1;
MustExpand = true;
return tmp;
} else
throw new ILArgumentException("invalid attempt to expand non vector sized array");
} else {
return tmp;
}
}
throw new ILArgumentException("invalid index specification: must not be empty");
}
#endregion
int d = 0, faktor = 1;
int ret = 0;
tmp = idx[0];
while (d < idx.Length-1) {
if (tmp < 0)
throw new ILArgumentException("check index at dimension #" + d.ToString() + "!");
if (tmp >= m_dims[d]) {
dimensions[d] = tmp+1;
MustExpand = true;
ret += (faktor * tmp);
faktor *= tmp+1;
} else {
ret += (faktor * tmp);
faktor *= m_dims[d];
}
tmp = idx[++d];
}
while (d < m_nrDims) {
ret += faktor * ((tmp % m_dims[d]));
tmp /= m_dims[d];
faktor *= m_dims[d++];
}
if (tmp > 0)
throw new ILArgumentException("expanding is allowed for explicitly bounded dimensions only! You must specify indices into all existing dimensions.");
return ret;
#endregion
} else if (idx.Length == m_nrDims) {
// expanding is allowed for all dimensions
int d = 0,faktor = 1;
int ret = 0;
while (d < idx.Length) {
tmp = idx[d];
if (tmp < 0)
throw new ILArgumentException("check index at dimension #" + d.ToString() + " !");
if (tmp >= m_dims[d]) {
dimensions[d] = tmp+1;
MustExpand = true;
ret += (faktor * tmp);
faktor *= tmp+1;
} else {
ret += (faktor * tmp);
faktor *= m_dims[d];
}
d++;
}
return ret;
} else {
// idx dimensions are larger than my dimensions
int d = 0, faktor = 1;
int ret = 0;
tmp = idx[0];
while (d < m_nrDims) {
tmp = idx[d];
if (tmp < 0)
throw new ILArgumentException("check index at dimension " + d.ToString() + "!");
if (tmp >= m_dims[d]) {
dimensions[d] = tmp+1;
MustExpand = true;
ret += (faktor * tmp);
faktor *= tmp+1;
} else {
ret += (faktor * tmp);
faktor *= m_dims[d];
}
d++;
}
while (d < idx.Length) {
tmp = idx[d];
if (tmp > 0) {
dimensions[d] = tmp +1;
MustExpand = true;
}
d++;
faktor *= tmp;
ret += faktor;
}
return ret;
}
}
///
/// Unshift dimensions of indices from int[] Array
/// and translate to index for sequential storage access
/// in my dimensions
/// int array of the same length as
/// the number of dimensions of this storage. Indices must
/// lay within my dimensions.
/// Number of dimensions to unshift
/// idx before computing index
/// Index number pointing to the value's position
/// in sequential storage.
/// If idx contains elements (indices) larger than
/// my dimension bounds, an exception will be thrown. If unshift
/// is 0, the length of idx may be smaller than the length of
/// my dimensions. However, with unshift > 0 the result
/// is undefined.
public int IndexFromArray(int[] idx, int unshift) {
unshift %= m_nrDims;
int faktor = m_dims[0];
int ret = idx[(-unshift) % m_nrDims];
int d = 1;
for (; d
/// Return shifted version
///
/// Number of dimensions to shift. The value
/// will be considered modules the number of dimensions of
/// this size descriptor.
/// Shifted version of this size descriptor object.
public ILSize GetShifted(int shift){
shift %= m_nrDims;
if (shift < 0) shift += m_nrDims;
int [] tmp = new int [m_nrDims];
int id;
for (int d = 0; d < m_nrDims; d++) {
id = (d + shift) % m_nrDims;
tmp[d] = m_dims[id];
}
return new ILSize(tmp);
}
///
/// Create dimension sizes for reshaping index adressing
///
/// Needed number of destination array dimensions
/// Dimension sizes, cutted trailing dimensions are multiplied to last dimension returned.
public int[] GetReshapedSize(int dimCount) {
dimCount = Math.Max(1,dimCount);
if (dimCount >= m_nrDims) {
return ToIntArray(dimCount);
}
int[] ret = new int[Math.Max(dimCount,2)];
for (int i = 0; i < dimCount; i++) {
ret[i] = m_dims[i];
}
for (int i = dimCount; i < m_nrDims; i++) {
ret[dimCount - 1] *= m_dims[i];
}
if (dimCount < 2)
ret[1] = 1;
return ret;
}
///
/// Get length for dimension specified (Readonly)
///
/// Index of dimension
/// Length of dimension specified by idx
/// If idx is negative
/// For idx corresponds to an existing dimension,
/// the length of that dimension is returned. If idx is larger than
/// the number of dimensions 1 is returned.
///
public int this [int idx] {
get {
if (idx < 0)
throw new ArgumentOutOfRangeException("index","Index out of Range!");
else if (idx >= m_nrDims) {
return 1;
}
return m_dims[idx];
}
}
///
/// Compares the size of this dimension to another dimension object.
///
/// size descriptor to compare this to.
/// Returns true if the sizes are the same, else returns false.
/// The comparison is made by recognizing singleton dimensions. Therefore
/// only non singleton dimensions are compared in the order of their
/// appearance.
/// The function returns true, if the squeezed dimensions of
/// both size descriptors match.
public bool IsSameSize(ILSize dim2) {
if (dim2.NumberOfElements != m_numberOfElements) return false;
for (int d2 = dim2.NumberOfDimensions,d1 = m_nrDims;d1 >= 0;) {
d1--; d2--;
while (d1 >= 0 && m_dims[d1]== 1) d1--;
while (d2 >= 0 && dim2[d2] == 1) d2--;
if (d1 >= 0 && d2 >= 0) {
if (m_dims[d1] != dim2[d2])
return false;
}
}
return true;
}
///
/// Compares the shape of this dimension to another dimension object
///
/// size descriptor to compare this to.
/// Returns true if the shapes are the same, else returns false.
/// This function is more strict than IsSameSize. In order
/// for two dimensions to have the same shape, ALL dimensions must match -
/// even singleton dimensions.
public bool IsSameShape(ILSize dim2) {
if (dim2.NumberOfElements != m_numberOfElements) return false;
if (dim2.NumberOfDimensions != m_nrDims) return false;
for (int d1 = m_nrDims;d1-->0;) {
if (m_dims[d1] != dim2.m_dims[d1])
return false;
}
return true;
}
///
/// [deprecated] Create copy of this size descriptor having all singleton
/// dimensions removed.
///
/// a squeezed copy
/// This function is deprecated. Use the ILSize.Squeeze()
/// memeber instead.
[Obsolete("Use the ILSize.Squeeze() instead")]
public ILSize GetSqueezed() {
return Squeeze();
}
///
/// Create and return copy without singleton dimensions
///
/// Copy of this size descriptor having all singleton dimensions removed.
/// This function does not alter this object (since ILSize is
/// immutable).
/// All arrays in ILNumerics have at least 2 dimensions.
/// Therefore all but the first two singleton dimensions can be removed.
///
public ILSize Squeeze() {
List tmp = new List();
foreach (int d in m_dims) {
if (d != 1) tmp.Add(d);
}
while (tmp.Count < 2) {
tmp.Add(1);
}
return new ILSize(tmp.ToArray());
}
///
/// Return size descriptor, having trailing singleton dimensions removed
///
/// Copy without trailing singleton dimensions
/// This object will NOT be altered. As usual for all ILArrays,
/// the result wil have at least 2 dimensions.
public ILSize Trim() {
if (m_nrDims == 2 || m_dims[m_nrDims-1] != 1) {
return this;
}
int i = m_nrDims;
for (; i-->2; ) if (m_dims[i] != 1) break;
int[] newdims = new int[++i];
System.Array.Copy(m_dims,0,newdims,0,i);
return new ILSize(newdims);
}
///
/// Pretty print dimensions in the format "[a,b,c]"
///
/// Dimensions as String
public override String ToString (){
String s = "[";
for (int t = 0; t < m_nrDims; t++) {
s = s + m_dims[t];
if (t < m_nrDims -1 )
s = s + ",";
}
s = s + "]";
return s;
}
///
/// Generate hash code based on the dimension information
///
/// Hash code
public override int GetHashCode() {
int ret = m_dims.Length;
foreach (int i in m_dims) {
ret = ret * 17 + i;
}
return ret;
}
///
/// Checks for equaltiy of this dimensions to another dimensions object
///
/// Dimensions object to compare this instance with
/// true, if both dimensions have the same shape
/// This is equivalent to IsSameShape((ILSize)obj).
public override bool Equals(object obj) {
if (object.ReferenceEquals(obj, null)) return false;
ILSize objDim = obj as ILSize;
if (objDim != null) {
return IsSameShape(objDim);
} else {
return false;
}
}
#endregion
#region private helpers
private static int[] convert2int(ILBaseArray[] dimensions) {
using (ILScope.Enter(dimensions)) {
if (dimensions == null || dimensions.Length == 0) {
return new int[2];
}
int[] ret = new int[dimensions.Length];
if (dimensions.Length > 1) {
for (int i = 0; i < ret.Length; i++) {
ILBaseArray A = dimensions[i];
if (object.Equals(A, null) || !A.IsScalar || !A.IsNumeric) {
throw new ILArgumentException("dimension specifier must be numeric scalars");
}
ret[i] = (int)ILMath.todouble(A).GetValue(0);
}
} else {
// if exactly one array was given, it must be a numeric int vector with the dim specification
ILBaseArray A = dimensions[0];
if (object.Equals(A, null) || !A.IsVector || !A.IsNumeric) {
throw new ILArgumentException("invalid dimension specification");
}
ILArray Aint = ILMath.toint32(A);
Aint.ExportValues(ref ret);
}
return ret;
}
}
#endregion
}
}