using System;
using System.Collections.Generic;
namespace Unity.MLAgents
{
///
/// An array-like object that stores up to four elements.
/// This is a value type that does not allocate any additional memory.
///
///
/// This does not implement any interfaces such as IList, in order to avoid any accidental boxing allocations.
///
///
public struct InplaceArray : IEquatable> where T : struct
{
private const int k_MaxLength = 4;
private readonly int m_Length;
private T m_Elem0;
private T m_Elem1;
private T m_Elem2;
private T m_Elem3;
///
/// Create a length-1 array.
///
///
public InplaceArray(T elem0)
{
m_Length = 1;
m_Elem0 = elem0;
m_Elem1 = new T();
m_Elem2 = new T();
m_Elem3 = new T();
}
///
/// Create a length-2 array.
///
///
///
public InplaceArray(T elem0, T elem1)
{
m_Length = 2;
m_Elem0 = elem0;
m_Elem1 = elem1;
m_Elem2 = new T();
m_Elem3 = new T();
}
///
/// Create a length-3 array.
///
///
///
///
public InplaceArray(T elem0, T elem1, T elem2)
{
m_Length = 3;
m_Elem0 = elem0;
m_Elem1 = elem1;
m_Elem2 = elem2;
m_Elem3 = new T();
}
///
/// Create a length-3 array.
///
///
///
///
///
public InplaceArray(T elem0, T elem1, T elem2, T elem3)
{
m_Length = 4;
m_Elem0 = elem0;
m_Elem1 = elem1;
m_Elem2 = elem2;
m_Elem3 = elem3;
}
///
/// Construct an InplaceArray from an IList (e.g. Array or List).
/// The source must be non-empty and have at most 4 elements.
///
///
///
///
public static InplaceArray FromList(IList elems)
{
switch (elems.Count)
{
case 1:
return new InplaceArray(elems[0]);
case 2:
return new InplaceArray(elems[0], elems[1]);
case 3:
return new InplaceArray(elems[0], elems[1], elems[2]);
case 4:
return new InplaceArray(elems[0], elems[1], elems[2], elems[3]);
default:
throw new ArgumentOutOfRangeException();
}
}
///
/// Per-element access.
///
///
///
public T this[int index]
{
get
{
if (index >= Length)
{
throw new IndexOutOfRangeException();
}
switch (index)
{
case 0:
return m_Elem0;
case 1:
return m_Elem1;
case 2:
return m_Elem2;
case 3:
return m_Elem3;
default:
throw new IndexOutOfRangeException();
}
}
set
{
if (index >= Length)
{
throw new IndexOutOfRangeException();
}
switch (index)
{
case 0:
m_Elem0 = value;
break;
case 1:
m_Elem1 = value;
break;
case 2:
m_Elem2 = value;
break;
case 3:
m_Elem3 = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
}
///
/// The length of the array.
///
public int Length
{
get => m_Length;
}
///
/// Returns a string representation of the array's elements.
///
///
///
public override string ToString()
{
switch (m_Length)
{
case 1:
return $"[{m_Elem0}]";
case 2:
return $"[{m_Elem0}, {m_Elem1}]";
case 3:
return $"[{m_Elem0}, {m_Elem1}, {m_Elem2}]";
case 4:
return $"[{m_Elem0}, {m_Elem1}, {m_Elem2}, {m_Elem3}]";
default:
throw new IndexOutOfRangeException();
}
}
///
/// Check that the arrays have the same length and have all equal values.
///
///
///
/// Whether the arrays are equivalent.
public static bool operator ==(InplaceArray lhs, InplaceArray rhs)
{
return lhs.Equals(rhs);
}
///
/// Check that the arrays are not equivalent.
///
///
///
/// Whether the arrays are not equivalent
public static bool operator !=(InplaceArray lhs, InplaceArray rhs) => !lhs.Equals(rhs);
///
/// Check that the arrays are equivalent.
///
///
/// Whether the arrays are not equivalent
public override bool Equals(object other) => other is InplaceArray other1 && this.Equals(other1);
///
/// Check that the arrays are equivalent.
///
///
/// Whether the arrays are not equivalent
public bool Equals(InplaceArray other)
{
// See https://montemagno.com/optimizing-c-struct-equality-with-iequatable/
var thisTuple = (m_Elem0, m_Elem1, m_Elem2, m_Elem3, Length);
var otherTuple = (other.m_Elem0, other.m_Elem1, other.m_Elem2, other.m_Elem3, other.Length);
return thisTuple.Equals(otherTuple);
}
///
/// Get a hashcode for the array.
///
///
public override int GetHashCode()
{
return (m_Elem0, m_Elem1, m_Elem2, m_Elem3, Length).GetHashCode();
}
}
}