using System.Buffers.Binary;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;

namespace Vanara.PInvoke;

/// <summary>Managed instance of the SIZE_T type.</summary>
[StructLayout(LayoutKind.Sequential), Serializable]
[TypeConverter(typeof(SizeTTypeConverter))]
[DebuggerDisplay("{Value}")]
public struct SizeT : IEquatable<SizeT>, IComparable<SizeT>, IConvertible, IComparable, ISerializable
#if NET7_0_OR_GREATER
	, IParsable<SizeT>, ISpanParsable<SizeT>, IBinaryInteger<SizeT>, IUnsignedNumber<SizeT>
#endif
{
	/// <summary>
	/// Represents the largest possible value of <see cref="SizeT"/>. This property is determined by the maximum bit-size of a pointer.
	/// </summary>
	public static readonly SizeT MaxValue =
#if NET6_0_OR_GREATER
		nuint.MaxValue;
#else
		UIntPtr.Size == 8 ? new(ulong.MaxValue) : new(uint.MaxValue);
#endif

	/// <summary>Represents the smallest possible value of <see cref="SizeT"/>. This field is constant.</summary>
	public static readonly SizeT MinValue = 0;

	/// <summary>Represents the zero value of <see cref="SizeT"/>. This field is constant.</summary>
	public static readonly SizeT Zero = default;

	private nuint val;

	/// <summary>Initializes a new instance of the <see cref="SizeT"/> struct.</summary>
	/// <param name="value">The value.</param>
	public SizeT(uint value) => val = value;

	/// <summary>Initializes a new instance of the <see cref="SizeT"/> struct.</summary>
	/// <param name="value">The value.</param>
	public SizeT(ulong value) => val = new UIntPtr(value);

	/// <summary>Initializes a new instance of the <see cref="SizeT"/> struct.</summary>
	/// <param name="value">The value.</param>
	public unsafe SizeT(void* value) => val = (nuint)value;

	private SizeT(SerializationInfo info, StreamingContext context) => val = (nuint)info.GetUInt64("value");

	/// <inheritdoc/>
	void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
	{
		if (info is null) throw new ArgumentNullException(nameof(info));
		info.AddValue("value", (ulong)val);
	}

	/// <summary>Gets the value.</summary>
	/// <value>The value.</value>
	public ulong Value { get => val; private set => val = new UIntPtr(value); }

#if NET7_0_OR_GREATER
	/// <inheritdoc/>
	static SizeT IAdditiveIdentity<SizeT, SizeT>.AdditiveIdentity => Zero;

	/// <inheritdoc/>
	static SizeT IMultiplicativeIdentity<SizeT, SizeT>.MultiplicativeIdentity => new(1UL);

	/// <inheritdoc/>
	static SizeT INumberBase<SizeT>.One => new(1UL);

	/// <inheritdoc/>
	static int INumberBase<SizeT>.Radix => 2;

	/// <inheritdoc/>
	static SizeT INumberBase<SizeT>.Zero => Zero;
#endif

	/// <summary>Performs an implicit conversion from <see cref="SizeT"/> to <see cref="int"/>.</summary>
	/// <param name="value">The value.</param>
	/// <returns>The result of the conversion.</returns>
	public static implicit operator int(SizeT value) => ((IConvertible)value).ToInt32(null);

	/// <summary>Performs an implicit conversion from <see cref="SizeT"/> to <see cref="long"/>.</summary>
	/// <param name="value">The value.</param>
	/// <returns>The result of the conversion.</returns>
	public static implicit operator long(SizeT value) => ((IConvertible)value).ToInt64(null);

	/// <summary>Performs an implicit conversion from <see cref="int"/> to <see cref="SizeT"/>.</summary>
	/// <param name="value">The value.</param>
	/// <returns>The result of the conversion.</returns>
	public static implicit operator SizeT(int value) => value >= 0 ? new SizeT((uint)value) : throw new ArgumentOutOfRangeException(nameof(value));

	/// <summary>Performs an implicit conversion from <see cref="uint"/> to <see cref="SizeT"/>.</summary>
	/// <param name="value">The value.</param>
	/// <returns>The result of the conversion.</returns>
	public static implicit operator SizeT(uint value) => new(value);

	/// <summary>Performs an implicit conversion from <see cref="long"/> to <see cref="SizeT"/>.</summary>
	/// <param name="value">The value.</param>
	/// <returns>The result of the conversion.</returns>
	public static implicit operator SizeT(long value) => value >= 0 ? new SizeT((ulong)value) : throw new ArgumentOutOfRangeException(nameof(value));

	/// <summary>Performs an implicit conversion from <see cref="ulong"/> to <see cref="SizeT"/>.</summary>
	/// <param name="value">The value.</param>
	/// <returns>The result of the conversion.</returns>
	public static implicit operator SizeT(ulong value) => new(value);

	/// <summary>Performs an implicit conversion from <see cref="SizeT"/> to <see cref="uint"/>.</summary>
	/// <param name="value">The value.</param>
	/// <returns>The result of the conversion.</returns>
	public static implicit operator uint(SizeT value) => (uint)value.val;

	/// <summary>Performs an implicit conversion from <see cref="SizeT"/> to <see cref="ulong"/>.</summary>
	/// <param name="value">The value.</param>
	/// <returns>The result of the conversion.</returns>
	public static implicit operator ulong(SizeT value) => value.val;

	/// <summary>Subtracts two specified <see cref="SizeT"/> values.</summary>
	/// <param name="s1">The minuend.</param>
	/// <param name="s2">The subtrahend.</param>
	/// <returns>The result of subtracting <paramref name="s2"/> from <paramref name="s1"/>.</returns>
	public static SizeT operator -(SizeT s1, SizeT s2) => s1.Value - s2.Value;

	/// <inheritdoc/>
	public static SizeT operator -(SizeT value) => UIntPtr.Zero - value.val;

	/// <summary>Decrements the <see cref="SizeT"/> by 1.</summary>
	/// <param name="s1">The value to decrement.</param>
	/// <returns>The value of <paramref name="s1"/> decremented by 1.</returns>
	public static SizeT operator --(SizeT s1) => s1.Value += 1;

	/// <summary>Indicates whether two <see cref="SizeT"/> instances are not equal.</summary>
	/// <param name="s1">The first integral size to compare.</param>
	/// <param name="s2">The second integral size to compare.</param>
	/// <returns>
	/// <see langword="true"/> if the value of <paramref name="s1"/> is not equal to the value of <paramref name="s2"/>; otherwise, <see langword="false"/>.
	/// </returns>
	public static bool operator !=(SizeT s1, SizeT s2) => !s1.Equals(s2);

	/// <summary>Returns the remainder resulting from dividing two specified <see cref="SizeT"/> values.</summary>
	/// <param name="s1">The divident.</param>
	/// <param name="s2">The divisor.</param>
	/// <returns>The remainder resulting from dividing <paramref name="s1"/> by <paramref name="s2"/>.</returns>
	public static SizeT operator %(SizeT s1, SizeT s2) => s1.Value % s2.Value;

	/// <inheritdoc/>
	public static SizeT operator &(SizeT left, SizeT right) => left.Value & right.Value;

	/// <summary>Multiplies two specified <see cref="SizeT"/> values.</summary>
	/// <param name="s1">The first value to multiply.</param>
	/// <param name="s2">The second value to multiply.</param>
	/// <returns>The result of multiplying <paramref name="s1"/> by <paramref name="s2"/>.</returns>
	public static SizeT operator *(SizeT s1, SizeT s2) => s1.Value * s2.Value;

	/// <summary>Divides two specified <see cref="SizeT"/> values.</summary>
	/// <param name="s1">The divident.</param>
	/// <param name="s2">The divisor.</param>
	/// <returns>The result of dividing <paramref name="s1"/> by <paramref name="s2"/>.</returns>
	public static SizeT operator /(SizeT s1, SizeT s2) => s1.Value / s2.Value;

	/// <inheritdoc/>
	public static SizeT operator ^(SizeT left, SizeT right) => left.Value ^ right.Value;

	/// <inheritdoc/>
	public static SizeT operator ~(SizeT value) => ~value.Value;

	/// <summary>Adds two specified <see cref="SizeT"/> values.</summary>
	/// <param name="s1">The first value to add.</param>
	/// <param name="s2">The second value to add.</param>
	/// <returns>The result of adding <paramref name="s1"/> and <paramref name="s2"/>.</returns>
	public static SizeT operator +(SizeT s1, SizeT s2) => s1.Value + s2.Value;

	/// <inheritdoc/>
	public static SizeT operator +(SizeT value) => +value.Value;

	/// <summary>Increments the <see cref="SizeT"/> by 1.</summary>
	/// <param name="s1">The value to increment.</param>
	/// <returns>The value of <paramref name="s1"/> incremented by 1.</returns>
	public static SizeT operator ++(SizeT s1) => s1.Value += 1;

	/// <summary>Indicates whether a specified <see cref="SizeT"/> is less than another specified <see cref="SizeT"/>.</summary>
	/// <param name="s1">The first integral size to compare.</param>
	/// <param name="s2">The second integral size to compare.</param>
	/// <returns>
	/// <see langword="true"/> if the value of <paramref name="s1"/> is less than the value of <paramref name="s2"/>; otherwise, <see langword="false"/>.
	/// </returns>
	public static bool operator <(SizeT s1, SizeT s2) => s1.CompareTo(s2) < 0;

	/// <inheritdoc/>
	public static SizeT operator <<(SizeT value, int shiftAmount) => value.Value << shiftAmount;

	/// <summary>Indicates whether a specified <see cref="SizeT"/> is less than or equal to another specified <see cref="SizeT"/>.</summary>
	/// <param name="s1">The first integral size to compare.</param>
	/// <param name="s2">The second integral size to compare.</param>
	/// <returns>
	/// <see langword="true"/> if the value of <paramref name="s1"/> is less than or equal to the value of <paramref name="s2"/>;
	/// otherwise, <see langword="false"/>.
	/// </returns>
	public static bool operator <=(SizeT s1, SizeT s2) => s1.CompareTo(s2) <= 0;

	/// <summary>Indicates whether two <see cref="SizeT"/> instances are equal.</summary>
	/// <param name="s1">The first integral size to compare.</param>
	/// <param name="s2">The second integral size to compare.</param>
	/// <returns>
	/// <see langword="true"/> if the value of <paramref name="s1"/> is equal to the value of <paramref name="s2"/>; otherwise, <see langword="false"/>.
	/// </returns>
	public static bool operator ==(SizeT s1, SizeT s2) => s1.Equals(s2);

	/// <summary>Indicates whether a specified <see cref="SizeT"/> is greater than another specified <see cref="SizeT"/>.</summary>
	/// <param name="s1">The first integral size to compare.</param>
	/// <param name="s2">The second integral size to compare.</param>
	/// <returns>
	/// <see langword="true"/> if the value of <paramref name="s1"/> is greater than the value of <paramref name="s2"/>; otherwise, <see langword="false"/>.
	/// </returns>
	public static bool operator >(SizeT s1, SizeT s2) => s1.CompareTo(s2) > 0;

	/// <summary>Indicates whether a specified <see cref="SizeT"/> is greater than or equal to another specified <see cref="SizeT"/>.</summary>
	/// <param name="s1">The first integral size to compare.</param>
	/// <param name="s2">The second integral size to compare.</param>
	/// <returns>
	/// <see langword="true"/> if the value of <paramref name="s1"/> is greater than or equal to the value of <paramref name="s2"/>;
	/// otherwise, <see langword="false"/>.
	/// </returns>
	public static bool operator >=(SizeT s1, SizeT s2) => s1.CompareTo(s2) >= 0;

	/// <inheritdoc/>
	public static SizeT operator >>(SizeT value, int shiftAmount) => value.Value >> shiftAmount;

	/// <inheritdoc/>
	public static SizeT operator >>>(SizeT value, int shiftAmount) => value.Value >>> shiftAmount;

	/// <inheritdoc/>
	public static SizeT Parse(string s, IFormatProvider? provider = null) => Parse(s, NumberStyles.Any, provider);

	/// <inheritdoc/>
	public static SizeT Parse(string s, NumberStyles style, IFormatProvider? provider) => ulong.Parse(s, style, provider);

	/// <inheritdoc/>
	public static SizeT Parse(ReadOnlySpan<char> s, IFormatProvider? provider) => Parse(s.ToString(), NumberStyles.Any, provider);

	/// <inheritdoc/>
	public static SizeT Parse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider) => Parse(s.ToString(), style, provider);

	/// <inheritdoc/>
	public static bool TryParse(string? s, IFormatProvider? provider, out SizeT result) => TryParse(s, NumberStyles.Any, provider, out result);

	/// <inheritdoc/>
	public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, out SizeT result) => TryParse(s.ToString(), NumberStyles.Any, provider, out result);

	/// <inheritdoc/>
	public static bool TryParse(string? s, NumberStyles style, IFormatProvider? provider, out SizeT result) { var b = ulong.TryParse(s, style, provider, out var r); result = b ? r : default; return b; }

	/// <inheritdoc/>
	public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out SizeT result) => TryParse(s.ToString(), style, provider, out result);

	/// <inheritdoc/>
	public int CompareTo(SizeT other) => Value.CompareTo(other.Value);

	/// <inheritdoc/>
	public override bool Equals(object? obj) => obj is SizeT s ? Equals(s) : Value.Equals(obj);

	/// <inheritdoc/>
	public bool Equals(SizeT other) => Value.Equals(other.Value);

	/// <inheritdoc/>
	public override int GetHashCode() => Value.GetHashCode();

	/// <inheritdoc/>
	public TypeCode GetTypeCode() => Value.GetTypeCode();

	/// <inheritdoc/>
	public override string ToString() => Value.ToString();

	/// <inheritdoc/>
	public string ToString(IFormatProvider? provider) => Value.ToString(provider);

	/// <inheritdoc/>
	public string ToString(string? format, IFormatProvider? formatProvider) => Value.ToString(format, formatProvider);

#if NET6_0_OR_GREATER || NETCOREAPP3_1_OR_GREATER
	/// <inheritdoc/>
	public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider? provider) =>
		Value.TryFormat(destination, out charsWritten, format, provider);
#endif

#if NET7_0_OR_GREATER
	/// <inheritdoc/>
	static SizeT INumberBase<SizeT>.Abs(SizeT value) => value;

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.IsCanonical(SizeT value) => true;

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.IsComplexNumber(SizeT value) => false;

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.IsEvenInteger(SizeT value) => (value.Value & 1) == 0;

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.IsFinite(SizeT value) => true;

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.IsImaginaryNumber(SizeT value) => false;

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.IsInfinity(SizeT value) => false;

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.IsInteger(SizeT value) => true;

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.IsNaN(SizeT value) => false;

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.IsNegative(SizeT value) => false;

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.IsNegativeInfinity(SizeT value) => false;

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.IsNormal(SizeT value) => value.Value != 0;

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.IsOddInteger(SizeT value) => (value.Value & 1) != 0;

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.IsPositive(SizeT value) => true;

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.IsPositiveInfinity(SizeT value) => false;

	/// <inheritdoc/>
	static bool IBinaryNumber<SizeT>.IsPow2(SizeT value) => nuint.IsPow2(value.val);

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.IsRealNumber(SizeT value) => true;

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.IsSubnormal(SizeT value) => false;

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.IsZero(SizeT value) => value.val == nuint.Zero;

	/// <inheritdoc/>
	static SizeT IBinaryNumber<SizeT>.Log2(SizeT value) => nuint.Log2(value.val);

	/// <inheritdoc/>
	static SizeT INumberBase<SizeT>.MaxMagnitude(SizeT x, SizeT y) => Math.Max(x.Value, y.Value);

	/// <inheritdoc/>
	static SizeT INumberBase<SizeT>.MaxMagnitudeNumber(SizeT x, SizeT y) => Math.Max(x.Value, y.Value);

	/// <inheritdoc/>
	static SizeT INumberBase<SizeT>.MinMagnitude(SizeT x, SizeT y) => Math.Min(x.Value, y.Value);

	/// <inheritdoc/>
	static SizeT INumberBase<SizeT>.MinMagnitudeNumber(SizeT x, SizeT y) => Math.Min(x.Value, y.Value);

	/// <inheritdoc/>
	public static SizeT operator |(SizeT left, SizeT right) => left.Value | right.Value;

	/// <inheritdoc/>
	static SizeT IBinaryInteger<SizeT>.PopCount(SizeT value) => nuint.PopCount(value.val);

	/// <inheritdoc/>
	static SizeT IBinaryInteger<SizeT>.TrailingZeroCount(SizeT value) => nuint.TrailingZeroCount(value.val);

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.TryConvertFromChecked<TOther>(TOther value, out SizeT result)
	{
		// In order to reduce overall code duplication and improve the inlinabilty of these
		// methods for the corelib types we have `ConvertFrom` handle the same sign and
		// `ConvertTo` handle the opposite sign. However, since there is an uneven split
		// between signed and unsigned types, the one that handles unsigned will also
		// handle `Decimal`.
		//
		// That is, `ConvertFrom` for `ulong` will handle the other unsigned types and
		// `ConvertTo` will handle the signed types

		if (typeof(TOther) == typeof(byte))
		{
			byte actualValue = (byte)(object)value;
			result = (ulong)actualValue;
			return true;
		}
		else if (typeof(TOther) == typeof(char))
		{
			char actualValue = (char)(object)value;
			result = (ulong)actualValue;
			return true;
		}
		else if (typeof(TOther) == typeof(decimal))
		{
			decimal actualValue = (decimal)(object)value;
			result = checked((ulong)actualValue);
			return true;
		}
		else if (typeof(TOther) == typeof(ushort))
		{
			ushort actualValue = (ushort)(object)value;
			result = (ulong)actualValue;
			return true;
		}
		else if (typeof(TOther) == typeof(uint))
		{
			uint actualValue = (uint)(object)value;
			result = actualValue;
			return true;
		}
		else if (typeof(TOther) == typeof(ulong))
		{
			ulong actualValue = (ulong)(object)value;
			result = actualValue;
			return true;
		}
		else if (typeof(TOther) == typeof(UInt128))
		{
			UInt128 actualValue = (UInt128)(object)value;
			result = checked((ulong)actualValue);
			return true;
		}
		else if (typeof(TOther) == typeof(nuint))
		{
			nuint actualValue = (nuint)(object)value;
			result = actualValue;
			return true;
		}
		else
		{
			result = default;
			return false;
		}
	}

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.TryConvertFromSaturating<TOther>(TOther value, out SizeT result)
	{
		// In order to reduce overall code duplication and improve the inlinabilty of these
		// methods for the corelib types we have `ConvertFrom` handle the same sign and
		// `ConvertTo` handle the opposite sign. However, since there is an uneven split
		// between signed and unsigned types, the one that handles unsigned will also
		// handle `Decimal`.
		//
		// That is, `ConvertFrom` for `ulong` will handle the other unsigned types and
		// `ConvertTo` will handle the signed types

		if (typeof(TOther) == typeof(byte))
		{
			byte actualValue = (byte)(object)value;
			result = (uint)actualValue;
			return true;
		}
		else if (typeof(TOther) == typeof(char))
		{
			char actualValue = (char)(object)value;
			result = (uint)actualValue;
			return true;
		}
		else if (typeof(TOther) == typeof(decimal))
		{
			decimal actualValue = (decimal)(object)value;
			result = (actualValue >= (ulong)MaxValue) ? MaxValue :
					 (actualValue <= (ulong)MinValue) ? MinValue : (ulong)actualValue;
			return true;
		}
		else if (typeof(TOther) == typeof(ushort))
		{
			ushort actualValue = (ushort)(object)value;
			result = (uint)actualValue;
			return true;
		}
		else if (typeof(TOther) == typeof(uint))
		{
			uint actualValue = (uint)(object)value;
			result = actualValue;
			return true;
		}
		else if (typeof(TOther) == typeof(ulong))
		{
			ulong actualValue = (ulong)(object)value;
			result = actualValue;
			return true;
		}
		else if (typeof(TOther) == typeof(UInt128))
		{
			UInt128 actualValue = (UInt128)(object)value;
			result = (actualValue >= (ulong)MaxValue) ? MaxValue : (ulong)actualValue;
			return true;
		}
		else if (typeof(TOther) == typeof(nuint))
		{
			nuint actualValue = (nuint)(object)value;
			result = actualValue;
			return true;
		}
		else
		{
			result = default;
			return false;
		}
	}

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.TryConvertFromTruncating<TOther>(TOther value, out SizeT result)
	{
		// In order to reduce overall code duplication and improve the inlinabilty of these
		// methods for the corelib types we have `ConvertFrom` handle the same sign and
		// `ConvertTo` handle the opposite sign. However, since there is an uneven split
		// between signed and unsigned types, the one that handles unsigned will also
		// handle `Decimal`.
		//
		// That is, `ConvertFrom` for `ulong` will handle the other unsigned types and
		// `ConvertTo` will handle the signed types

		if (typeof(TOther) == typeof(byte))
		{
			byte actualValue = (byte)(object)value;
			result = (uint)actualValue;
			return true;
		}
		else if (typeof(TOther) == typeof(char))
		{
			char actualValue = (char)(object)value;
			result = (uint)actualValue;
			return true;
		}
		else if (typeof(TOther) == typeof(decimal))
		{
			decimal actualValue = (decimal)(object)value;
			result = (actualValue >= (ulong)MaxValue) ? MaxValue :
					 (actualValue <= (ulong)MinValue) ? MinValue : (ulong)actualValue;
			return true;
		}
		else if (typeof(TOther) == typeof(ushort))
		{
			ushort actualValue = (ushort)(object)value;
			result = (uint)actualValue;
			return true;
		}
		else if (typeof(TOther) == typeof(uint))
		{
			uint actualValue = (uint)(object)value;
			result = actualValue;
			return true;
		}
		else if (typeof(TOther) == typeof(ulong))
		{
			ulong actualValue = (ulong)(object)value;
			result = actualValue;
			return true;
		}
		else if (typeof(TOther) == typeof(UInt128))
		{
			UInt128 actualValue = (UInt128)(object)value;
			result = (ulong)actualValue;
			return true;
		}
		else if (typeof(TOther) == typeof(nuint))
		{
			nuint actualValue = (nuint)(object)value;
			result = actualValue;
			return true;
		}
		else
		{
			result = default;
			return false;
		}
	}

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.TryConvertToChecked<TOther>(SizeT value, [MaybeNullWhen(false)] out TOther result)
	{
		// In order to reduce overall code duplication and improve the inlinabilty of these
		// methods for the corelib types we have `ConvertFrom` handle the same sign and
		// `ConvertTo` handle the opposite sign. However, since there is an uneven split
		// between signed and unsigned types, the one that handles unsigned will also
		// handle `Decimal`.
		//
		// That is, `ConvertFrom` for `nuint` will handle the other unsigned types and
		// `ConvertTo` will handle the signed types

		if (typeof(TOther) == typeof(double))
		{
			double actualResult = value.val;
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(Half))
		{
			Half actualResult = (Half)value.val;
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(short))
		{
			short actualResult = checked((short)value);
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(int))
		{
			int actualResult = checked((int)value);
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(long))
		{
			long actualResult = checked((long)value);
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(Int128))
		{
			Int128 actualResult = value.val;
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(nint))
		{
			nint actualResult = checked((nint)value);
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(sbyte))
		{
			sbyte actualResult = checked((sbyte)value);
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(float))
		{
			float actualResult = value.val;
			result = (TOther)(object)actualResult;
			return true;
		}
		else
		{
			result = default;
			return false;
		}
	}

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.TryConvertToSaturating<TOther>(SizeT value, [MaybeNullWhen(false)] out TOther result)
	{
		if (typeof(TOther) == typeof(double))
		{
			double actualResult = value.val;
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(Half))
		{
			Half actualResult = (Half)value.val;
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(short))
		{
			short actualResult = (value >= (nuint)short.MaxValue) ? short.MaxValue : (short)value;
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(int))
		{
			int actualResult = (value >= int.MaxValue) ? int.MaxValue : (int)value;
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(long))
		{
			long actualResult = (value >= long.MaxValue) ? long.MaxValue : (long)value;
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(Int128))
		{
			Int128 actualResult = value.val;
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(nint))
		{
			nint actualResult = (value >= (nuint)nint.MaxValue) ? nint.MaxValue : (nint)value;
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(sbyte))
		{
			sbyte actualResult = (value >= (nuint)sbyte.MaxValue) ? sbyte.MaxValue : (sbyte)value;
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(float))
		{
			float actualResult = value.val;
			result = (TOther)(object)actualResult;
			return true;
		}
		else
		{
			result = default;
			return false;
		}
	}

	/// <inheritdoc/>
	static bool INumberBase<SizeT>.TryConvertToTruncating<TOther>(SizeT value, [MaybeNullWhen(false)] out TOther result)
	{
		// In order to reduce overall code duplication and improve the inlinabilty of these
		// methods for the corelib types we have `ConvertFrom` handle the same sign and
		// `ConvertTo` handle the opposite sign. However, since there is an uneven split
		// between signed and unsigned types, the one that handles unsigned will also
		// handle `Decimal`.
		//
		// That is, `ConvertFrom` for `nuint` will handle the other unsigned types and
		// `ConvertTo` will handle the signed types

		if (typeof(TOther) == typeof(double))
		{
			double actualResult = value.val;
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(Half))
		{
			Half actualResult = (Half)value.val;
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(short))
		{
			short actualResult = (short)value;
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(int))
		{
			int actualResult = (int)value;
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(long))
		{
			long actualResult = (long)value;
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(Int128))
		{
			Int128 actualResult = value.val;
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(nint))
		{
			nint actualResult = (nint)value;
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(sbyte))
		{
			sbyte actualResult = (sbyte)value;
			result = (TOther)(object)actualResult;
			return true;
		}
		else if (typeof(TOther) == typeof(float))
		{
			float actualResult = value.val;
			result = (TOther)(object)actualResult;
			return true;
		}
		else
		{
			result = default;
			return false;
		}
	}

	/// <inheritdoc/>
	static bool IBinaryInteger<SizeT>.TryReadBigEndian(ReadOnlySpan<byte> source, bool isUnsigned, out SizeT value)
	{
		ulong result = default;

		if (source.Length != 0)
		{
			if (!isUnsigned && sbyte.IsNegative((sbyte)source[0]))
			{
				// When we are signed and the sign bit is set, we are negative and therefore
				// definitely out of range

				value = result;
				return false;
			}

			if ((source.Length > sizeof(ulong)) && (source[..^sizeof(ulong)].IndexOfAnyExcept((byte)0x00) >= 0))
			{
				// When we have any non-zero leading data, we are a large positive and therefore
				// definitely out of range

				value = result;
				return false;
			}

			ref byte sourceRef = ref MemoryMarshal.GetReference(source);

			if (source.Length >= sizeof(ulong))
			{
				sourceRef = ref Unsafe.Add(ref sourceRef, source.Length - sizeof(ulong));

				// We have at least 8 bytes, so just read the ones we need directly
				result = Unsafe.ReadUnaligned<ulong>(ref sourceRef);

				if (BitConverter.IsLittleEndian)
				{
					result = BinaryPrimitives.ReverseEndianness(result);
				}
			}
			else
			{
				// We have between 1 and 7 bytes, so construct the relevant value directly
				// since the data is in Big Endian format, we can just read the bytes and
				// shift left by 8-bits for each subsequent part

				for (int i = 0; i < source.Length; i++)
				{
					result <<= 8;
					result |= Unsafe.Add(ref sourceRef, i);
				}
			}
		}

		value = result;
		return true;
	}

	/// <inheritdoc/>
	static bool IBinaryInteger<SizeT>.TryReadLittleEndian(ReadOnlySpan<byte> source, bool isUnsigned, out SizeT value)
	{
		ulong result = default;

		if (source.Length != 0)
		{
			if (!isUnsigned && sbyte.IsNegative((sbyte)source[^1]))
			{
				// When we are signed and the sign bit is set, we are negative and therefore
				// definitely out of range

				value = result;
				return false;
			}

			if ((source.Length > sizeof(ulong)) && (source[sizeof(ulong)..].IndexOfAnyExcept((byte)0x00) >= 0))
			{
				// When we have any non-zero leading data, we are a large positive and therefore
				// definitely out of range

				value = result;
				return false;
			}

			ref byte sourceRef = ref MemoryMarshal.GetReference(source);

			if (source.Length >= sizeof(ulong))
			{
				// We have at least 8 bytes, so just read the ones we need directly
				result = Unsafe.ReadUnaligned<ulong>(ref sourceRef);

				if (!BitConverter.IsLittleEndian)
				{
					result = BinaryPrimitives.ReverseEndianness(result);
				}
			}
			else
			{
				// We have between 1 and 7 bytes, so construct the relevant value directly
				// since the data is in Little Endian format, we can just read the bytes and
				// shift left by 8-bits for each subsequent part, then reverse endianness to
				// ensure the order is correct. This is more efficient than iterating in reverse
				// due to current JIT limitations

				for (int i = 0; i < source.Length; i++)
				{
					ulong part = Unsafe.Add(ref sourceRef, i);
					part <<= (i * 8);
					result |= part;
				}
			}
		}

		value = result;
		return true;
	}

	/// <inheritdoc/>
	int IBinaryInteger<SizeT>.GetByteCount() => ((IBinaryInteger<nuint>)val).GetByteCount();

	/// <inheritdoc/>
	int IBinaryInteger<SizeT>.GetShortestBitLength() => ((IBinaryInteger<nuint>)val).GetShortestBitLength();

	/// <inheritdoc/>
	bool IBinaryInteger<SizeT>.TryWriteBigEndian(Span<byte> destination, out int bytesWritten) => ((IBinaryInteger<nuint>)val).TryWriteBigEndian(destination, out bytesWritten);

	/// <inheritdoc/>
	bool IBinaryInteger<SizeT>.TryWriteLittleEndian(Span<byte> destination, out int bytesWritten) => ((IBinaryInteger<nuint>)val).TryWriteLittleEndian(destination, out bytesWritten);
#endif

	/// <inheritdoc/>
	int IComparable.CompareTo(object? obj) => Value.CompareTo(Convert.ChangeType(obj, typeof(ulong)));

	/// <inheritdoc/>
	bool IConvertible.ToBoolean(IFormatProvider? provider) => ((IConvertible)Value).ToBoolean(provider);

	/// <inheritdoc/>
	byte IConvertible.ToByte(IFormatProvider? provider)
	{
		var ul = Value;
		if (ul < byte.MaxValue)
			return (byte)ul;
		if (ul is uint.MaxValue or ulong.MaxValue)
			return byte.MaxValue;
		throw new OverflowException();
	}

	/// <inheritdoc/>
	char IConvertible.ToChar(IFormatProvider? provider) => ((IConvertible)Value).ToChar(provider);

	/// <inheritdoc/>
	DateTime IConvertible.ToDateTime(IFormatProvider? provider) => ((IConvertible)Value).ToDateTime(provider);

	/// <inheritdoc/>
	decimal IConvertible.ToDecimal(IFormatProvider? provider)
	{
		var ul = Value;
		if (ul < decimal.MaxValue)
			return ul;
		if (ul is uint.MaxValue or ulong.MaxValue)
			return decimal.MaxValue;
		throw new OverflowException();
	}

	/// <inheritdoc/>
	double IConvertible.ToDouble(IFormatProvider? provider)
	{
		var ul = Value;
		if (ul < double.MaxValue)
			return ul;
		if (ul is uint.MaxValue or ulong.MaxValue)
			return double.MaxValue;
		throw new OverflowException();
	}

	/// <inheritdoc/>
	short IConvertible.ToInt16(IFormatProvider? provider)
	{
		var ul = Value;
		if (ul < (ulong)short.MaxValue)
			return (short)ul;
		if (ul is uint.MaxValue or ulong.MaxValue)
			return short.MaxValue;
		throw new OverflowException();
	}

	/// <inheritdoc/>
	int IConvertible.ToInt32(IFormatProvider? provider)
	{
		var ul = Value;
		if (ul < int.MaxValue)
			return (int)ul;
		if (ul is uint.MaxValue or ulong.MaxValue)
			return int.MaxValue;
		throw new OverflowException();
	}

	/// <inheritdoc/>
	long IConvertible.ToInt64(IFormatProvider? provider)
	{
		var ul = Value;
		if (ul < long.MaxValue)
			return (long)ul;
		if (ul is uint.MaxValue or ulong.MaxValue)
			return long.MaxValue;
		throw new OverflowException();
	}

	/// <inheritdoc/>
	sbyte IConvertible.ToSByte(IFormatProvider? provider)
	{
		var ul = Value;
		if (ul < (ulong)sbyte.MaxValue)
			return (sbyte)ul;
		if (ul is uint.MaxValue or ulong.MaxValue)
			return sbyte.MaxValue;
		throw new OverflowException();
	}

	/// <inheritdoc/>
	float IConvertible.ToSingle(IFormatProvider? provider)
	{
		var ul = Value;
		if (ul < float.MaxValue)
			return ul;
		if (ul is uint.MaxValue or ulong.MaxValue)
			return float.MaxValue;
		throw new OverflowException();
	}

	/// <inheritdoc/>
	object IConvertible.ToType(Type conversionType, IFormatProvider? provider) => ((IConvertible)Value).ToType(conversionType, provider);

	/// <inheritdoc/>
	ushort IConvertible.ToUInt16(IFormatProvider? provider)
	{
		var ul = Value;
		if (ul < ushort.MaxValue)
			return (ushort)ul;
		if (ul is uint.MaxValue or ulong.MaxValue)
			return ushort.MaxValue;
		throw new OverflowException();
	}

	/// <inheritdoc/>
	uint IConvertible.ToUInt32(IFormatProvider? provider)
	{
		var ul = Value;
		if (ul < uint.MaxValue)
			return (uint)ul;
		if (ul is uint.MaxValue or ulong.MaxValue)
			return uint.MaxValue;
		throw new OverflowException();
	}

	/// <inheritdoc/>
	ulong IConvertible.ToUInt64(IFormatProvider? provider) => Value;

	internal class SizeTTypeConverter : UInt64Converter
	{
		public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) =>
			new SizeT((ulong)base.ConvertFrom(context, culture, value)!);

		public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) =>
					value is SizeT sz ? base.ConvertTo(context, culture, sz.Value, destinationType) : throw new NotSupportedException();
	}
}