// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. namespace System.Numerics { /// <summary>Defines an integer type that is represented in a base-2 format.</summary> /// <typeparam name="TSelf">The type that implements the interface.</typeparam> public interface IBinaryInteger<TSelf> : IBinaryNumber<TSelf>, IShiftOperators<TSelf, int, TSelf> where TSelf : IBinaryInteger<TSelf>? { /// <summary>Computes the quotient and remainder of two values.</summary> /// <param name="left">The value which <paramref name="right" /> divides.</param> /// <param name="right">The value which divides <paramref name="left" />.</param> /// <returns>The quotient and remainder of <paramref name="left" /> divided-by <paramref name="right" />.</returns> static virtual (TSelf Quotient, TSelf Remainder) DivRem(TSelf left, TSelf right) { TSelf quotient = left / right; return (quotient, (left - (quotient * right))); } /// <summary>Computes the quotient and remainder of two values using the specified division rounding mode.</summary> /// <param name="left">The value which <paramref name="right" /> divides.</param> /// <param name="right">The value which divides <paramref name="left" />.</param> /// <param name="mode">The <see cref="DivisionRounding"/> mode.</param> /// <returns>The quotient and remainder of <paramref name="left" /> divided-by <paramref name="right" /> with the specified division rounding mode.</returns> static virtual (TSelf Quotient, TSelf Remainder) DivRem(TSelf left, TSelf right, DivisionRounding mode) { (TSelf quotient, TSelf remainder) = TSelf.DivRem(left, right); if (TSelf.IsZero(remainder)) { return (quotient, remainder); } switch (mode) { case DivisionRounding.Truncate: { break; } case DivisionRounding.Floor: { if (TSelf.IsPositive(left) != TSelf.IsPositive(right)) { quotient--; remainder += right; } break; } case DivisionRounding.Ceiling: { if (TSelf.IsPositive(left) == TSelf.IsPositive(right)) { quotient++; remainder -= right; } break; } case DivisionRounding.AwayFromZero: { if (TSelf.IsPositive(left) != TSelf.IsPositive(right)) { quotient--; remainder += right; } else { quotient++; remainder -= right; } break; } case DivisionRounding.Euclidean: { if (TSelf.IsNegative(left)) { if (TSelf.IsPositive(right)) { quotient--; remainder += right; } else { quotient++; remainder -= right; } } break; } default: { ThrowHelper.ThrowArgumentException_InvalidEnumValue(mode); break; } } return (quotient, remainder); } /// <summary>Computes the quotient of two values using the specified division rounding mode.</summary> /// <param name="left">The value which <paramref name="right" /> divides.</param> /// <param name="right">The value which divides <paramref name="left" />.</param> /// <param name="mode">The <see cref="DivisionRounding"/> mode.</param> /// <returns>The quotient of <paramref name="left" /> divided-by <paramref name="right" /> with the specified division rounding mode.</returns> static virtual TSelf Divide(TSelf left, TSelf right, DivisionRounding mode) { (TSelf quotient, TSelf remainder) = TSelf.DivRem(left, right); if (TSelf.IsZero(remainder)) { return quotient; } switch (mode) { case DivisionRounding.Truncate: { break; } case DivisionRounding.Floor: { if (TSelf.IsPositive(left) != TSelf.IsPositive(right)) { quotient--; } break; } case DivisionRounding.Ceiling: { if (TSelf.IsPositive(left) == TSelf.IsPositive(right)) { quotient++; } break; } case DivisionRounding.AwayFromZero: { if (TSelf.IsPositive(left) != TSelf.IsPositive(right)) { quotient--; } else { quotient++; } break; } case DivisionRounding.Euclidean: { if (TSelf.IsNegative(left)) { if (TSelf.IsPositive(right)) { quotient--; } else { quotient++; } } break; } default: { ThrowHelper.ThrowArgumentException_InvalidEnumValue(mode); break; } } return quotient; } /// <summary>Computes the remainder of two values using the specified division rounding mode.</summary> /// <param name="left">The value which <paramref name="right" /> divides.</param> /// <param name="right">The value which divides <paramref name="left" />.</param> /// <param name="mode">The <see cref="DivisionRounding"/> mode.</param> /// <returns>The remainder of <paramref name="left" /> divided-by <paramref name="right" /> with the specified division rounding mode.</returns> static virtual TSelf Remainder(TSelf left, TSelf right, DivisionRounding mode) { TSelf remainder = left % right; if (TSelf.IsZero(remainder)) { return remainder; } switch (mode) { case DivisionRounding.Truncate: { break; } case DivisionRounding.Floor: { if (TSelf.IsPositive(left) != TSelf.IsPositive(right)) { remainder += right; } break; } case DivisionRounding.Ceiling: { if (TSelf.IsPositive(left) == TSelf.IsPositive(right)) { remainder -= right; } break; } case DivisionRounding.AwayFromZero: { if (TSelf.IsPositive(left) != TSelf.IsPositive(right)) { remainder += right; } else { remainder -= right; } break; } case DivisionRounding.Euclidean: { if (TSelf.IsNegative(left)) { if (TSelf.IsPositive(right)) { remainder += right; } else { remainder -= right; } } break; } default: { ThrowHelper.ThrowArgumentException_InvalidEnumValue(mode); break; } } return remainder; } /// <summary>Computes the integer logarithm base 10 of a value.</summary> /// <param name="value">The value whose integer logarithm base 10 is to be computed.</param> /// <returns>The integer logarithm base 10 of <paramref name="value" />.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="value" /> is negative.</exception> /// <remarks>The result of computing the integer logarithm base 10 of zero is zero.</remarks> static virtual TSelf Log10(TSelf value) { if (!typeof(TSelf).IsValueType) { ArgumentNullException.ThrowIfNull(value); } if (TSelf.IsNegative(value)) { ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); } TSelf ten = TSelf.CreateChecked(10); TSelf result = TSelf.Zero; while (value >= ten) { value /= ten; result++; } return result; } /// <summary>Computes the number of leading zero bits in a value.</summary> /// <param name="value">The value whose leading zero bits are to be counted.</param> /// <returns>The number of leading zero bits in <paramref name="value" />.</returns> static virtual TSelf LeadingZeroCount(TSelf value) { if (!typeof(TSelf).IsValueType) { ArgumentNullException.ThrowIfNull(value); } TSelf bitCount = TSelf.CreateChecked(value!.GetByteCount() * 8L); if (value == TSelf.Zero) { return TSelf.CreateChecked(bitCount); } return TSelf.IsNegative(value) ? TSelf.Zero : ((bitCount - TSelf.One) ^ TSelf.Log2(value)); } /// <summary>Computes the number of bits that are set in a value.</summary> /// <param name="value">The value whose set bits are to be counted.</param> /// <returns>The number of set bits in <paramref name="value" />.</returns> static abstract TSelf PopCount(TSelf value); /// <summary>Reads a two's complement number from a given array, in big-endian format, and converts it to an instance of the current type.</summary> /// <param name="source">The array from which the two's complement number should be read.</param> /// <param name="isUnsigned"><c>true</c> if <paramref name="source" /> represents an unsigned two's complement number; otherwise, <c>false</c> to indicate it represents a signed two's complement number.</param> /// <returns>The value read from <paramref name="source" />.</returns> /// <exception cref="OverflowException"><paramref name="source" /> is not representable by <typeparamref name="TSelf" /></exception> static virtual TSelf ReadBigEndian(byte[] source, bool isUnsigned) { if (!TSelf.TryReadBigEndian(source, isUnsigned, out TSelf value)) { ThrowHelper.ThrowOverflowException(); } return value; } /// <summary>Reads a two's complement number from a given array, in big-endian format, and converts it to an instance of the current type.</summary> /// <param name="source">The array from which the two's complement number should be read.</param> /// <param name="startIndex">The starting index from which the value should be read.</param> /// <param name="isUnsigned"><c>true</c> if <paramref name="source" /> represents an unsigned two's complement number; otherwise, <c>false</c> to indicate it represents a signed two's complement number.</param> /// <returns>The value read from <paramref name="source" /> starting at <paramref name="startIndex" />.</returns> /// <exception cref="OverflowException"><paramref name="source" /> is not representable by <typeparamref name="TSelf" /></exception> static virtual TSelf ReadBigEndian(byte[] source, int startIndex, bool isUnsigned) { if (!TSelf.TryReadBigEndian(source.AsSpan(startIndex), isUnsigned, out TSelf value)) { ThrowHelper.ThrowOverflowException(); } return value; } /// <summary>Reads a two's complement number from a given span, in big-endian format, and converts it to an instance of the current type.</summary> /// <param name="source">The array from which the two's complement number should be read.</param> /// <param name="isUnsigned"><c>true</c> if <paramref name="source" /> represents an unsigned two's complement number; otherwise, <c>false</c> to indicate it represents a signed two's complement number.</param> /// <returns>The value read from <paramref name="source" />.</returns> /// <exception cref="OverflowException"><paramref name="source" /> is not representable by <typeparamref name="TSelf" /></exception> static virtual TSelf ReadBigEndian(ReadOnlySpan<byte> source, bool isUnsigned) { if (!TSelf.TryReadBigEndian(source, isUnsigned, out TSelf value)) { ThrowHelper.ThrowOverflowException(); } return value; } /// <summary>Reads a two's complement number from a given array, in little-endian format, and converts it to an instance of the current type.</summary> /// <param name="source">The array from which the two's complement number should be read.</param> /// <param name="isUnsigned"><c>true</c> if <paramref name="source" /> represents an unsigned two's complement number; otherwise, <c>false</c> to indicate it represents a signed two's complement number.</param> /// <returns>The value read from <paramref name="source" />.</returns> /// <exception cref="OverflowException"><paramref name="source" /> is not representable by <typeparamref name="TSelf" /></exception> static virtual TSelf ReadLittleEndian(byte[] source, bool isUnsigned) { if (!TSelf.TryReadLittleEndian(source, isUnsigned, out TSelf value)) { ThrowHelper.ThrowOverflowException(); } return value; } /// <summary>Reads a two's complement number from a given array, in little-endian format, and converts it to an instance of the current type.</summary> /// <param name="source">The array from which the two's complement number should be read.</param> /// <param name="startIndex">The starting index from which the value should be read.</param> /// <param name="isUnsigned"><c>true</c> if <paramref name="source" /> represents an unsigned two's complement number; otherwise, <c>false</c> to indicate it represents a signed two's complement number.</param> /// <returns>The value read from <paramref name="source" /> starting at <paramref name="startIndex" />.</returns> /// <exception cref="OverflowException"><paramref name="source" /> is not representable by <typeparamref name="TSelf" /></exception> static virtual TSelf ReadLittleEndian(byte[] source, int startIndex, bool isUnsigned) { if (!TSelf.TryReadLittleEndian(source.AsSpan(startIndex), isUnsigned, out TSelf value)) { ThrowHelper.ThrowOverflowException(); } return value; } /// <summary>Reads a two's complement number from a given span, in little-endian format, and converts it to an instance of the current type.</summary> /// <param name="source">The array from which the two's complement number should be read.</param> /// <param name="isUnsigned"><c>true</c> if <paramref name="source" /> represents an unsigned two's complement number; otherwise, <c>false</c> to indicate it represents a signed two's complement number.</param> /// <returns>The value read from <paramref name="source" />.</returns> /// <exception cref="OverflowException"><paramref name="source" /> is not representable by <typeparamref name="TSelf" /></exception> static virtual TSelf ReadLittleEndian(ReadOnlySpan<byte> source, bool isUnsigned) { if (!TSelf.TryReadLittleEndian(source, isUnsigned, out TSelf value)) { ThrowHelper.ThrowOverflowException(); } return value; } /// <summary>Rotates a value left by a given amount.</summary> /// <param name="value">The value which is rotated left by <paramref name="rotateAmount" />.</param> /// <param name="rotateAmount">The amount by which <paramref name="value" /> is rotated left.</param> /// <returns>The result of rotating <paramref name="value" /> left by <paramref name="rotateAmount" />.</returns> static virtual TSelf RotateLeft(TSelf value, int rotateAmount) { if (!typeof(TSelf).IsValueType) { ArgumentNullException.ThrowIfNull(value); } int bitCount = checked(value!.GetByteCount() * 8); return (value << rotateAmount) | (value >>> (bitCount - rotateAmount)); } /// <summary>Rotates a value right by a given amount.</summary> /// <param name="value">The value which is rotated right by <paramref name="rotateAmount" />.</param> /// <param name="rotateAmount">The amount by which <paramref name="value" /> is rotated right.</param> /// <returns>The result of rotating <paramref name="value" /> right by <paramref name="rotateAmount" />.</returns> static virtual TSelf RotateRight(TSelf value, int rotateAmount) { if (!typeof(TSelf).IsValueType) { ArgumentNullException.ThrowIfNull(value); } int bitCount = checked(value!.GetByteCount() * 8); return (value >>> rotateAmount) | (value << (bitCount - rotateAmount)); } /// <summary>Computes the number of trailing zero bits in a value.</summary> /// <param name="value">The value whose trailing zero bits are to be counted.</param> /// <returns>The number of trailing zero bits in <paramref name="value" />.</returns> static abstract TSelf TrailingZeroCount(TSelf value); /// <summary>Tries to read a two's complement number from a span, in big-endian format, and convert it to an instance of the current type.</summary> /// <param name="source">The span from which the two's complement number should be read.</param> /// <param name="isUnsigned"><c>true</c> if <paramref name="source" /> represents an unsigned two's complement number; otherwise, <c>false</c> to indicate it represents a signed two's complement number.</param> /// <param name="value">On return, contains the value read from <paramref name="source" /> or <c>default</c> if a value could not be read.</param> /// <returns><c>true</c> if the value was succesfully read from <paramref name="source" />; otherwise, <c>false</c>.</returns> static abstract bool TryReadBigEndian(ReadOnlySpan<byte> source, bool isUnsigned, out TSelf value); /// <summary>Tries to read a two's complement number from a span, in little-endian format, and convert it to an instance of the current type.</summary> /// <param name="source">The span from which the two's complement number should be read.</param> /// <param name="isUnsigned"><c>true</c> if <paramref name="source" /> represents an unsigned two's complement number; otherwise, <c>false</c> to indicate it represents a signed two's complement number.</param> /// <param name="value">On return, contains the value read from <paramref name="source" /> or <c>default</c> if a value could not be read.</param> /// <returns><c>true</c> if the value was succesfully read from <paramref name="source" />; otherwise, <c>false</c>.</returns> static abstract bool TryReadLittleEndian(ReadOnlySpan<byte> source, bool isUnsigned, out TSelf value); /// <summary>Gets the number of bytes that will be written as part of <see cref="TryWriteLittleEndian(Span{byte}, out int)" />.</summary> /// <returns>The number of bytes that will be written as part of <see cref="TryWriteLittleEndian(Span{byte}, out int)" />.</returns> int GetByteCount(); /// <summary>Gets the length, in bits, of the shortest two's complement representation of the current value.</summary> /// <returns>The length, in bits, of the shortest two's complement representation of the current value.</returns> int GetShortestBitLength(); /// <summary>Tries to write the current value, in big-endian format, to a given span.</summary> /// <param name="destination">The span to which the current value should be written.</param> /// <param name="bytesWritten">The number of bytes written to <paramref name="destination" />.</param> /// <returns><c>true</c> if the value was successfully written to <paramref name="destination" />; otherwise, <c>false</c>.</returns> bool TryWriteBigEndian(Span<byte> destination, out int bytesWritten); /// <summary>Tries to write the current value, in little-endian format, to a given span.</summary> /// <param name="destination">The span to which the current value should be written.</param> /// <param name="bytesWritten">The number of bytes written to <paramref name="destination" />.</param> /// <returns><c>true</c> if the value was successfully written to <paramref name="destination" />; otherwise, <c>false</c>.</returns> bool TryWriteLittleEndian(Span<byte> destination, out int bytesWritten); /// <summary>Writes the current value, in big-endian format, to a given array.</summary> /// <param name="destination">The array to which the current value should be written.</param> /// <returns>The number of bytes written to <paramref name="destination" />.</returns> int WriteBigEndian(byte[] destination) { if (!TryWriteBigEndian(destination, out int bytesWritten)) { ThrowHelper.ThrowArgumentException_DestinationTooShort(); } return bytesWritten; } /// <summary>Writes the current value, in big-endian format, to a given array.</summary> /// <param name="destination">The array to which the current value should be written.</param> /// <param name="startIndex">The starting index at which the value should be written.</param> /// <returns>The number of bytes written to <paramref name="destination" /> starting at <paramref name="startIndex" />.</returns> int WriteBigEndian(byte[] destination, int startIndex) { if (!TryWriteBigEndian(destination.AsSpan(startIndex), out int bytesWritten)) { ThrowHelper.ThrowArgumentException_DestinationTooShort(); } return bytesWritten; } /// <summary>Writes the current value, in big-endian format, to a given span.</summary> /// <param name="destination">The span to which the current value should be written.</param> /// <returns>The number of bytes written to <paramref name="destination" />.</returns> int WriteBigEndian(Span<byte> destination) { if (!TryWriteBigEndian(destination, out int bytesWritten)) { ThrowHelper.ThrowArgumentException_DestinationTooShort(); } return bytesWritten; } /// <summary>Writes the current value, in little-endian format, to a given array.</summary> /// <param name="destination">The array to which the current value should be written.</param> /// <returns>The number of bytes written to <paramref name="destination" />.</returns> int WriteLittleEndian(byte[] destination) { if (!TryWriteLittleEndian(destination, out int bytesWritten)) { ThrowHelper.ThrowArgumentException_DestinationTooShort(); } return bytesWritten; } /// <summary>Writes the current value, in little-endian format, to a given array.</summary> /// <param name="destination">The array to which the current value should be written.</param> /// <param name="startIndex">The starting index at which the value should be written.</param> /// <returns>The number of bytes written to <paramref name="destination" /> starting at <paramref name="startIndex" />.</returns> int WriteLittleEndian(byte[] destination, int startIndex) { if (!TryWriteLittleEndian(destination.AsSpan(startIndex), out int bytesWritten)) { ThrowHelper.ThrowArgumentException_DestinationTooShort(); } return bytesWritten; } /// <summary>Writes the current value, in little-endian format, to a given span.</summary> /// <param name="destination">The span to which the current value should be written.</param> /// <returns>The number of bytes written to <paramref name="destination" />.</returns> int WriteLittleEndian(Span<byte> destination) { if (!TryWriteLittleEndian(destination, out int bytesWritten)) { ThrowHelper.ThrowArgumentException_DestinationTooShort(); } return bytesWritten; } } }