//
// 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 System.Runtime.Serialization;
using IStation.Numerics.Threading;
namespace IStation.Numerics.LinearAlgebra.Storage
{
[Serializable]
[DataContract(Namespace = "urn:IStation/Numerics/LinearAlgebra")]
public class SparseVectorStorage : VectorStorage
where T : struct, IEquatable, IFormattable
{
// [ruegg] public fields are OK here
///
/// Array that contains the indices of the non-zero values.
///
[DataMember(Order = 1)]
public int[] Indices;
///
/// Array that contains the non-zero elements of the vector.
///
[DataMember(Order = 2)]
public T[] Values;
///
/// Gets the number of non-zero elements in the vector.
///
[DataMember(Order = 3)]
public int ValueCount;
internal SparseVectorStorage(int length)
: base(length)
{
Indices = new int[0];
Values = new T[0];
ValueCount = 0;
}
///
/// True if the vector storage format is dense.
///
public override bool IsDense => false;
///
/// Retrieves the requested element without range checking.
///
public override T At(int index)
{
// Search if item index exists in NonZeroIndices array in range "0 - nonzero values count"
var itemIndex = Array.BinarySearch(Indices, 0, ValueCount, index);
return itemIndex >= 0 ? Values[itemIndex] : Zero;
}
///
/// Sets the element without range checking.
///
public override void At(int index, T value)
{
// Search if "index" already exists in range "0 - nonzero values count"
var itemIndex = Array.BinarySearch(Indices, 0, ValueCount, index);
if (itemIndex >= 0)
{
// Non-zero item found in matrix
if (Zero.Equals(value))
{
// Delete existing item
RemoveAtIndexUnchecked(itemIndex);
}
else
{
// Update item
Values[itemIndex] = value;
}
}
else
{
// Item not found. Add new value
if (!Zero.Equals(value))
{
InsertAtIndexUnchecked(~itemIndex, index, value);
}
}
}
internal void InsertAtIndexUnchecked(int itemIndex, int index, T value)
{
// Check if the storage needs to be increased
if ((ValueCount == Values.Length) && (ValueCount < Length))
{
// Value and Indices arrays are completely full so we increase the size
var size = Math.Min(Values.Length + GrowthSize(), Length);
Array.Resize(ref Values, size);
Array.Resize(ref Indices, size);
}
// Move all values (with a position larger than index) in the value array to the next position
// Move all values (with a position larger than index) in the columIndices array to the next position
Array.Copy(Values, itemIndex, Values, itemIndex + 1, ValueCount - itemIndex);
Array.Copy(Indices, itemIndex, Indices, itemIndex + 1, ValueCount - itemIndex);
// Add the value and the column index
Values[itemIndex] = value;
Indices[itemIndex] = index;
// increase the number of non-zero numbers by one
ValueCount += 1;
}
internal void RemoveAtIndexUnchecked(int itemIndex)
{
// Value is zero. Let's delete it from Values and Indices array
Array.Copy(Values, itemIndex + 1, Values, itemIndex, ValueCount - itemIndex - 1);
Array.Copy(Indices, itemIndex + 1, Indices, itemIndex, ValueCount - itemIndex - 1);
ValueCount -= 1;
// Check whether we need to shrink the arrays. This is reasonable to do if
// there are a lot of non-zero elements and storage is two times bigger
if ((ValueCount > 1024) && (ValueCount < Indices.Length / 2))
{
Array.Resize(ref Values, ValueCount);
Array.Resize(ref Indices, ValueCount);
}
}
///
/// Calculates the amount with which to grow the storage array's if they need to be
/// increased in size.
///
/// The amount grown.
int GrowthSize()
{
int delta;
if (Values.Length > 1024)
{
delta = Values.Length / 4;
}
else
{
if (Values.Length > 256)
{
delta = 512;
}
else
{
delta = Values.Length > 64 ? 128 : 32;
}
}
return delta;
}
public override bool Equals(VectorStorage other)
{
// Reject equality when the argument is null or has a different shape.
if (other == null || Length != other.Length)
{
return false;
}
// Accept if the argument is the same object as this.
if (ReferenceEquals(this, other))
{
return true;
}
if (other is SparseVectorStorage otherSparse)
{
int i = 0, j = 0;
while (i < ValueCount || j < otherSparse.ValueCount)
{
if (j >= otherSparse.ValueCount || i < ValueCount && Indices[i] < otherSparse.Indices[j])
{
if (!Zero.Equals(Values[i++]))
{
return false;
}
continue;
}
if (i >= ValueCount || j < otherSparse.ValueCount && otherSparse.Indices[j] < Indices[i])
{
if (!Zero.Equals(otherSparse.Values[j++]))
{
return false;
}
continue;
}
if (!Values[i].Equals(otherSparse.Values[j]))
{
return false;
}
i++;
j++;
}
return true;
}
return base.Equals(other);
}
///
/// 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 override int GetHashCode()
{
var values = Values;
var hashNum = Math.Min(ValueCount, 25);
int hash = 17;
unchecked
{
for (var i = 0; i < hashNum; i++)
{
hash = hash * 31 + values[i].GetHashCode();
}
}
return hash;
}
// CLEARING
public override void Clear()
{
ValueCount = 0;
}
public override void Clear(int index, int count)
{
if (index == 0 && count == Length)
{
Clear();
return;
}
var first = Array.BinarySearch(Indices, 0, ValueCount, index);
var last = Array.BinarySearch(Indices, 0, ValueCount, index + count - 1);
if (first < 0) first = ~first;
if (last < 0) last = ~last - 1;
int itemCount = last - first + 1;
if (itemCount > 0)
{
Array.Copy(Values, first + itemCount, Values, first, ValueCount - first - itemCount);
Array.Copy(Indices, first + itemCount, Indices, first, ValueCount - first - itemCount);
ValueCount -= itemCount;
}
// Check whether we need to shrink the arrays. This is reasonable to do if
// there are a lot of non-zero elements and storage is two times bigger
if ((ValueCount > 1024) && (ValueCount < Indices.Length / 2))
{
Array.Resize(ref Values, ValueCount);
Array.Resize(ref Indices, ValueCount);
}
}
// INITIALIZATION
public static SparseVectorStorage OfVector(VectorStorage vector)
{
var storage = new SparseVectorStorage(vector.Length);
vector.CopyToUnchecked(storage, ExistingData.AssumeZeros);
return storage;
}
public static SparseVectorStorage OfValue(int length, T value)
{
if (Zero.Equals(value))
{
return new SparseVectorStorage(length);
}
if (length < 0)
{
throw new ArgumentOutOfRangeException(nameof(length), "Value must not be negative (zero is ok).");
}
var indices = new int[length];
var values = new T[length];
for (int i = 0; i < indices.Length; i++)
{
indices[i] = i;
values[i] = value;
}
return new SparseVectorStorage(length)
{
Indices = indices,
Values = values,
ValueCount = length
};
}
public static SparseVectorStorage OfInit(int length, Func init)
{
if (length < 0)
{
throw new ArgumentOutOfRangeException(nameof(length), "Value must not be negative (zero is ok).");
}
var indices = new List();
var values = new List();
for (int i = 0; i < length; i++)
{
var item = init(i);
if (!Zero.Equals(item))
{
values.Add(item);
indices.Add(i);
}
}
return new SparseVectorStorage(length)
{
Indices = indices.ToArray(),
Values = values.ToArray(),
ValueCount = values.Count
};
}
public static SparseVectorStorage OfEnumerable(IEnumerable data)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
var indices = new List();
var values = new List();
int index = 0;
foreach (T item in data)
{
if (!Zero.Equals(item))
{
values.Add(item);
indices.Add(index);
}
index++;
}
return new SparseVectorStorage(index)
{
Indices = indices.ToArray(),
Values = values.ToArray(),
ValueCount = values.Count
};
}
public static SparseVectorStorage OfIndexedEnumerable(int length, IEnumerable> data)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
var indices = new List();
var values = new List();
foreach (var item in data)
{
if (!Zero.Equals(item.Item2))
{
values.Add(item.Item2);
indices.Add(item.Item1);
}
}
var indicesArray = indices.ToArray();
var valuesArray = values.ToArray();
Sorting.Sort(indicesArray, valuesArray);
return new SparseVectorStorage(length)
{
Indices = indicesArray,
Values = valuesArray,
ValueCount = values.Count
};
}
// VECTOR COPY
internal override void CopyToUnchecked(VectorStorage target, ExistingData existingData)
{
if (target is SparseVectorStorage sparseTarget)
{
CopyToUnchecked(sparseTarget);
return;
}
// FALL BACK
if (existingData == ExistingData.Clear)
{
target.Clear();
}
if (ValueCount != 0)
{
for (int i = 0; i < ValueCount; i++)
{
target.At(Indices[i], Values[i]);
}
}
}
void CopyToUnchecked(SparseVectorStorage target)
{
if (ReferenceEquals(this, target))
{
return;
}
if (Length != target.Length)
{
var message = $"Matrix dimensions must agree: op1 is {Length}, op2 is {target.Length}.";
throw new ArgumentException(message, nameof(target));
}
target.ValueCount = ValueCount;
target.Values = new T[ValueCount];
target.Indices = new int[ValueCount];
if (ValueCount != 0)
{
Array.Copy(Values, 0, target.Values, 0, ValueCount);
Buffer.BlockCopy(Indices, 0, target.Indices, 0, ValueCount * Constants.SizeOfInt);
}
}
// Row COPY
internal override void CopyToRowUnchecked(MatrixStorage target, int rowIndex, ExistingData existingData)
{
if (existingData == ExistingData.Clear)
{
target.ClearUnchecked(rowIndex, 1, 0, Length);
}
if (ValueCount == 0)
{
return;
}
for (int i = 0; i < ValueCount; i++)
{
target.At(rowIndex, Indices[i], Values[i]);
}
}
// COLUMN COPY
internal override void CopyToColumnUnchecked(MatrixStorage target, int columnIndex, ExistingData existingData)
{
if (existingData == ExistingData.Clear)
{
target.ClearUnchecked(0, Length, columnIndex, 1);
}
if (ValueCount == 0)
{
return;
}
for (int i = 0; i < ValueCount; i++)
{
target.At(Indices[i], columnIndex, Values[i]);
}
}
// SUB-VECTOR COPY
internal override void CopySubVectorToUnchecked(VectorStorage target,
int sourceIndex, int targetIndex, int count, ExistingData existingData)
{
if (target is SparseVectorStorage sparseTarget)
{
CopySubVectorToUnchecked(sparseTarget, sourceIndex, targetIndex, count, existingData);
return;
}
// FALL BACK
var offset = targetIndex - sourceIndex;
var sourceFirst = Array.BinarySearch(Indices, 0, ValueCount, sourceIndex);
var sourceLast = Array.BinarySearch(Indices, 0, ValueCount, sourceIndex + count - 1);
if (sourceFirst < 0) sourceFirst = ~sourceFirst;
if (sourceLast < 0) sourceLast = ~sourceLast - 1;
if (existingData == ExistingData.Clear)
{
target.Clear(targetIndex, count);
}
for (int i = sourceFirst; i <= sourceLast; i++)
{
target.At(Indices[i] + offset, Values[i]);
}
}
void CopySubVectorToUnchecked(SparseVectorStorage target,
int sourceIndex, int targetIndex, int count,
ExistingData existingData)
{
var offset = targetIndex - sourceIndex;
var sourceFirst = Array.BinarySearch(Indices, 0, ValueCount, sourceIndex);
var sourceLast = Array.BinarySearch(Indices, 0, ValueCount, sourceIndex + count - 1);
if (sourceFirst < 0) sourceFirst = ~sourceFirst;
if (sourceLast < 0) sourceLast = ~sourceLast - 1;
int sourceCount = sourceLast - sourceFirst + 1;
// special case when copying to itself
if (ReferenceEquals(this, target))
{
var values = new T[sourceCount];
var indices = new int[sourceCount];
Array.Copy(Values, sourceFirst, values, 0, sourceCount);
for (int i = 0; i < indices.Length; i++)
{
indices[i] = Indices[i + sourceFirst];
}
if (existingData == ExistingData.Clear)
{
Clear(targetIndex, count);
}
for (int i = sourceFirst; i <= sourceLast; i++)
{
At(indices[i] + offset, values[i]);
}
return;
}
// special case for empty target - much faster
if (target.ValueCount == 0)
{
var values = new T[sourceCount];
var indices = new int[sourceCount];
Array.Copy(Values, sourceFirst, values, 0, sourceCount);
for (int i = 0; i < indices.Length; i++)
{
indices[i] = Indices[i + sourceFirst] + offset;
}
target.ValueCount = sourceCount;
target.Values = values;
target.Indices = indices;
return;
}
if (existingData == ExistingData.Clear)
{
target.Clear(targetIndex, count);
}
for (int i = sourceFirst; i <= sourceLast; i++)
{
target.At(Indices[i] + offset, Values[i]);
}
}
// EXTRACT
public override T[] ToArray()
{
var ret = new T[Length];
for (int i = 0; i < ValueCount; i++)
{
ret[Indices[i]] = Values[i];
}
return ret;
}
// ENUMERATION
public override IEnumerable Enumerate()
{
int k = 0;
for (int i = 0; i < Length; i++)
{
yield return k < ValueCount && Indices[k] == i
? Values[k++]
: Zero;
}
}
public override IEnumerable> EnumerateIndexed()
{
int k = 0;
for (int i = 0; i < Length; i++)
{
yield return k < ValueCount && Indices[k] == i
? new Tuple(i, Values[k++])
: new Tuple(i, Zero);
}
}
public override IEnumerable EnumerateNonZero()
{
return Values.Take(ValueCount).Where(x => !Zero.Equals(x));
}
public override IEnumerable> EnumerateNonZeroIndexed()
{
for (var i = 0; i < ValueCount; i++)
{
if (!Zero.Equals(Values[i]))
{
yield return new Tuple(Indices[i], Values[i]);
}
}
}
// FIND
public override Tuple Find(Func predicate, Zeros zeros)
{
for (int i = 0; i < ValueCount; i++)
{
if (predicate(Values[i]))
{
return new Tuple(Indices[i], Values[i]);
}
}
if (zeros == Zeros.Include && ValueCount < Length && predicate(Zero))
{
for (int i = 0; i < Length; i++)
{
if (i >= ValueCount || Indices[i] != i)
{
return new Tuple(i, Zero);
}
}
}
return null;
}
internal override Tuple Find2Unchecked(VectorStorage other, Func predicate, Zeros zeros)
{
if (other is DenseVectorStorage denseOther)
{
TOther[] otherData = denseOther.Data;
int k = 0;
for (int i = 0; i < otherData.Length; i++)
{
if (k < ValueCount && Indices[k] == i)
{
if (predicate(Values[k], otherData[i]))
{
return new Tuple(i, Values[k], otherData[i]);
}
k++;
}
else
{
if (predicate(Zero, otherData[i]))
{
return new Tuple(i, Zero, otherData[i]);
}
}
}
return null;
}
if (other is SparseVectorStorage sparseOther)
{
int[] otherIndices = sparseOther.Indices;
TOther[] otherValues = sparseOther.Values;
int otherValueCount = sparseOther.ValueCount;
TOther otherZero = BuilderInstance.Matrix.Zero;
// Full Scan
int k = 0, otherk = 0;
if (zeros == Zeros.Include && ValueCount < Length && sparseOther.ValueCount < Length && predicate(Zero, otherZero))
{
for (int i = 0; i < Length; i++)
{
var left = k < ValueCount && Indices[k] == i ? Values[k++] : Zero;
var right = otherk < otherValueCount && otherIndices[otherk] == i ? otherValues[otherk++] : otherZero;
if (predicate(left, right))
{
return new Tuple(i, left, right);
}
}
return null;
}
// Sparse Scan
k = 0;
otherk = 0;
while (k < ValueCount || otherk < otherValueCount)
{
if (k == ValueCount || otherk < otherValueCount && Indices[k] > otherIndices[otherk])
{
if (predicate(Zero, otherValues[otherk++]))
{
return new Tuple(otherIndices[otherk - 1], Zero, otherValues[otherk - 1]);
}
}
else if (otherk == otherValueCount || Indices[k] < otherIndices[otherk])
{
if (predicate(Values[k++], otherZero))
{
return new Tuple(Indices[k - 1], Values[k - 1], otherZero);
}
}
else
{
if (predicate(Values[k++], otherValues[otherk++]))
{
return new Tuple(Indices[k - 1], Values[k - 1], otherValues[otherk - 1]);
}
}
}
return null;
}
// FALL BACK
return base.Find2Unchecked(other, predicate, zeros);
}
// FUNCTIONAL COMBINATORS: MAP
public override void MapInplace(Func f, Zeros zeros)
{
var indices = new List();
var values = new List(ValueCount);
if (zeros == Zeros.Include || !Zero.Equals(f(Zero)))
{
int k = 0;
for (int i = 0; i < Length; i++)
{
var item = k < ValueCount && (Indices[k]) == i ? f(Values[k++]) : f(Zero);
if (!Zero.Equals(item))
{
values.Add(item);
indices.Add(i);
}
}
}
else
{
for (int i = 0; i < ValueCount; i++)
{
var item = f(Values[i]);
if (!Zero.Equals(item))
{
values.Add(item);
indices.Add(Indices[i]);
}
}
}
Indices = indices.ToArray();
Values = values.ToArray();
ValueCount = values.Count;
}
public override void MapIndexedInplace(Func f, Zeros zeros)
{
var indices = new List();
var values = new List(ValueCount);
if (zeros == Zeros.Include)
{
int k = 0;
for (int i = 0; i < Length; i++)
{
var item = k < ValueCount && (Indices[k]) == i ? f(i, Values[k++]) : f(i, Zero);
if (!Zero.Equals(item))
{
values.Add(item);
indices.Add(i);
}
}
}
else
{
for (int i = 0; i < ValueCount; i++)
{
var item = f(Indices[i], Values[i]);
if (!Zero.Equals(item))
{
values.Add(item);
indices.Add(Indices[i]);
}
}
}
Indices = indices.ToArray();
Values = values.ToArray();
ValueCount = values.Count;
}
internal override void MapToUnchecked(VectorStorage target, Func f, Zeros zeros, ExistingData existingData)
{
if (target is SparseVectorStorage sparseTarget)
{
var indices = new List();
var values = new List();
if (zeros == Zeros.Include || !Zero.Equals(f(Zero)))
{
int k = 0;
for (int i = 0; i < Length; i++)
{
var item = k < ValueCount && (Indices[k]) == i ? f(Values[k++]) : f(Zero);
if (!Zero.Equals(item))
{
values.Add(item);
indices.Add(i);
}
}
}
else
{
for (int i = 0; i < ValueCount; i++)
{
var item = f(Values[i]);
if (!Zero.Equals(item))
{
values.Add(item);
indices.Add(Indices[i]);
}
}
}
sparseTarget.Indices = indices.ToArray();
sparseTarget.Values = values.ToArray();
sparseTarget.ValueCount = values.Count;
return;
}
if (target is DenseVectorStorage denseTarget)
{
if (existingData == ExistingData.Clear)
{
denseTarget.Clear();
}
if (zeros == Zeros.Include || !Zero.Equals(f(Zero)))
{
int k = 0;
for (int i = 0; i < Length; i++)
{
denseTarget.Data[i] = k < ValueCount && (Indices[k]) == i
? f(Values[k++])
: f(Zero);
}
}
else
{
CommonParallel.For(0, ValueCount, 4096, (a, b) =>
{
for (int i = a; i < b; i++)
{
denseTarget.Data[Indices[i]] = f(Values[i]);
}
});
}
return;
}
// FALL BACK
base.MapToUnchecked(target, f, zeros, existingData);
}
internal override void MapIndexedToUnchecked(VectorStorage target, Func f, Zeros zeros, ExistingData existingData)
{
if (target is SparseVectorStorage sparseTarget)
{
var indices = new List();
var values = new List();
if (zeros == Zeros.Include || !Zero.Equals(f(0, Zero)))
{
int k = 0;
for (int i = 0; i < Length; i++)
{
var item = k < ValueCount && (Indices[k]) == i ? f(i, Values[k++]) : f(i, Zero);
if (!Zero.Equals(item))
{
values.Add(item);
indices.Add(i);
}
}
}
else
{
for (int i = 0; i < ValueCount; i++)
{
var item = f(Indices[i], Values[i]);
if (!Zero.Equals(item))
{
values.Add(item);
indices.Add(Indices[i]);
}
}
}
sparseTarget.Indices = indices.ToArray();
sparseTarget.Values = values.ToArray();
sparseTarget.ValueCount = values.Count;
return;
}
if (target is DenseVectorStorage denseTarget)
{
if (existingData == ExistingData.Clear)
{
denseTarget.Clear();
}
if (zeros == Zeros.Include || !Zero.Equals(f(0, Zero)))
{
int k = 0;
for (int i = 0; i < Length; i++)
{
denseTarget.Data[i] = k < ValueCount && (Indices[k]) == i
? f(i, Values[k++])
: f(i, Zero);
}
}
else
{
CommonParallel.For(0, ValueCount, 4096, (a, b) =>
{
for (int i = a; i < b; i++)
{
denseTarget.Data[Indices[i]] = f(Indices[i], Values[i]);
}
});
}
return;
}
// FALL BACK
base.MapIndexedToUnchecked(target, f, zeros, existingData);
}
internal override void Map2ToUnchecked(VectorStorage target, VectorStorage other, Func f, Zeros zeros, ExistingData existingData)
{
var processZeros = zeros == Zeros.Include || !Zero.Equals(f(Zero, Zero));
var denseTarget = target as DenseVectorStorage;
var denseOther = other as DenseVectorStorage;
if (denseTarget == null && (denseOther != null || processZeros))
{
// The handling is effectively dense but we're supposed to push
// to a sparse target. Let's use a dense target instead,
// then copy it normalized back to the sparse target.
var intermediate = new DenseVectorStorage(target.Length);
Map2ToUnchecked(intermediate, other, f, zeros, ExistingData.AssumeZeros);
intermediate.CopyTo(target, existingData);
return;
}
if (denseOther != null)
{
T[] targetData = denseTarget.Data;
T[] otherData = denseOther.Data;
int k = 0;
for (int i = 0; i < otherData.Length; i++)
{
if (k < ValueCount && Indices[k] == i)
{
targetData[i] = f(Values[k], otherData[i]);
k++;
}
else
{
targetData[i] = f(Zero, otherData[i]);
}
}
return;
}
var sparseOther = other as SparseVectorStorage;
if (sparseOther != null && denseTarget != null)
{
T[] targetData = denseTarget.Data;
int[] otherIndices = sparseOther.Indices;
T[] otherValues = sparseOther.Values;
int otherValueCount = sparseOther.ValueCount;
if (processZeros)
{
int p = 0, q = 0;
for (int i = 0; i < targetData.Length; i++)
{
var left = p < ValueCount && Indices[p] == i ? Values[p++] : Zero;
var right = q < otherValueCount && otherIndices[q] == i ? otherValues[q++] : Zero;
targetData[i] = f(left, right);
}
}
else
{
if (existingData == ExistingData.Clear)
{
denseTarget.Clear();
}
int p = 0, q = 0;
while (p < ValueCount || q < otherValueCount)
{
if (q >= otherValueCount || p < ValueCount && Indices[p] < otherIndices[q])
{
targetData[Indices[p]] = f(Values[p], Zero);
p++;
}
else if (p >= ValueCount || q < otherValueCount && Indices[p] > otherIndices[q])
{
targetData[otherIndices[q]] = f(Zero, otherValues[q]);
q++;
}
else
{
Debug.Assert(Indices[p] == otherIndices[q]);
targetData[Indices[p]] = f(Values[p], otherValues[q]);
p++;
q++;
}
}
}
return;
}
if (sparseOther != null && target is SparseVectorStorage sparseTarget)
{
var indices = new List();
var values = new List();
int[] otherIndices = sparseOther.Indices;
T[] otherValues = sparseOther.Values;
int otherValueCount = sparseOther.ValueCount;
int p = 0, q = 0;
while (p < ValueCount || q < otherValueCount)
{
if (q >= otherValueCount || p < ValueCount && Indices[p] < otherIndices[q])
{
var value = f(Values[p], Zero);
if (!Zero.Equals(value))
{
indices.Add(Indices[p]);
values.Add(value);
}
p++;
}
else if (p >= ValueCount || q < otherValueCount && Indices[p] > otherIndices[q])
{
var value = f(Zero, otherValues[q]);
if (!Zero.Equals(value))
{
indices.Add(otherIndices[q]);
values.Add(value);
}
q++;
}
else
{
var value = f(Values[p], otherValues[q]);
if (!Zero.Equals(value))
{
indices.Add(Indices[p]);
values.Add(value);
}
p++;
q++;
}
}
sparseTarget.Indices = indices.ToArray();
sparseTarget.Values = values.ToArray();
sparseTarget.ValueCount = values.Count;
return;
}
// FALL BACK
base.Map2ToUnchecked(target, other, f, zeros, existingData);
}
// FUNCTIONAL COMBINATORS: MAP
internal override TState Fold2Unchecked(VectorStorage other, Func f, TState state, Zeros zeros)
{
if (other is SparseVectorStorage sparseOther)
{
int[] otherIndices = sparseOther.Indices;
TOther[] otherValues = sparseOther.Values;
int otherValueCount = sparseOther.ValueCount;
TOther otherZero = BuilderInstance.Vector.Zero;
if (zeros == Zeros.Include)
{
int p = 0, q = 0;
for (int i = 0; i < Length; i++)
{
var left = p < ValueCount && Indices[p] == i ? Values[p++] : Zero;
var right = q < otherValueCount && otherIndices[q] == i ? otherValues[q++] : otherZero;
state = f(state, left, right);
}
}
else
{
int p = 0, q = 0;
while (p < ValueCount || q < otherValueCount)
{
if (q >= otherValueCount || p < ValueCount && Indices[p] < otherIndices[q])
{
state = f(state, Values[p], otherZero);
p++;
}
else if (p >= ValueCount || q < otherValueCount && Indices[p] > otherIndices[q])
{
state = f(state, Zero, otherValues[q]);
q++;
}
else
{
Debug.Assert(Indices[p] == otherIndices[q]);
state = f(state, Values[p], otherValues[q]);
p++;
q++;
}
}
}
return state;
}
if (other is DenseVectorStorage denseOther)
{
TOther[] otherData = denseOther.Data;
int k = 0;
for (int i = 0; i < otherData.Length; i++)
{
if (k < ValueCount && Indices[k] == i)
{
state = f(state, Values[k], otherData[i]);
k++;
}
else
{
state = f(state, Zero, otherData[i]);
}
}
return state;
}
return base.Fold2Unchecked(other, f, state, zeros);
}
}
}