/// <summary> /// Compare two unsigned little endian <see cref="byte" /> arrays /// </summary> /// <param name="left">the left side operand</param> /// <param name="right">the right side operand</param> public static int CompareUnsignedLittleEndian([NotNull] byte[] left, [NotNull] byte[] right) { if (left == null) { throw new ArgumentNullException(nameof(left)); } if (right == null) { throw new ArgumentNullException(nameof(right)); } var enumerable = new ConcurrentLittleEndianByteEnumerable(left, right).GetMsbToLsbEnumerable(); foreach (var(leftByte, rightByte) in enumerable) { var comparison = leftByte.CompareTo(rightByte); if (comparison != 0) { return(comparison > 0 ? 1 : -1); } } return(0); // bytes are equal }
public static byte[] AddUnsignedLittleEndian([NotNull] byte[] left, [NotNull] byte[] right) { if (left == null) { throw new ArgumentNullException(nameof(left)); } if (right == null) { throw new ArgumentNullException(nameof(right)); } var enumerable = new ConcurrentLittleEndianByteEnumerable(left, right).GetLsbToMsbEnumerable(); var resultQueue = new Queue <byte>(); // use stack for fifo var carry = 0; foreach (var(leftByte, rightByte) in enumerable) { var sum = carry + leftByte + rightByte; resultQueue.Enqueue((byte)(sum & 0xFF)); // push the byte value of sum carry = sum >> 8; // new carry value is sum shifted by 8 bits (a byte) } if (carry != 0) // if a carry value exists it should be pushed as it is the new MSB { resultQueue.Enqueue((byte)carry); // enqueue carry } return(resultQueue.ToArray()); }
public void ReverseIterate_FromByteEnumerable_Trimmed_Test(byte[] leftBytes, byte[] rightBytes) { // Arrange this._testOutputHelper.WriteLine($"left: [{leftBytes.ToString("h")}]"); this._testOutputHelper.WriteLine($"right: [{rightBytes.ToString("h")}]"); var expectedTrimmedLeftLength = leftBytes.Reverse() .SkipWhile(b => b == 0x00) .Count(); var expectedTrimmedRightLength = rightBytes.Reverse() .SkipWhile(b => b == 0x00) .Count(); var length = Math.Max(expectedTrimmedLeftLength, expectedTrimmedRightLength); this._testOutputHelper.WriteLine($"expectedLength: {length}"); var expectedLeft = leftBytes.Take(expectedTrimmedLeftLength) .Concat(Enumerable.Repeat((byte)0x00, length - expectedTrimmedLeftLength)) .Reverse() .ToArray(); var expectedRight = rightBytes.Take(expectedTrimmedRightLength) .Concat(Enumerable.Repeat((byte)0x00, length - expectedTrimmedRightLength)) .Reverse() .ToArray(); this._testOutputHelper.WriteLine($"expected left: [{expectedLeft.ToString("h")}]"); this._testOutputHelper.WriteLine($"expected right: [{expectedRight.ToString("h")}]"); var enumerable = new ConcurrentLittleEndianByteEnumerable(leftBytes, rightBytes); // Act var result = enumerable.ReverseEnumerable() .ToList(); // Assert Assert.NotNull(result); Assert.IsAssignableFrom <IReadOnlyCollection <(byte leftByte, byte rightByte)> >(result); Assert.Equal(length, result.Count); var leftResult = result.Select(tuple => tuple.leftByte) .ToArray(); var rightResult = result.Select(tuple => tuple.rightByte) .ToArray(); this._testOutputHelper.WriteLine($"resulting left: [{leftResult.ToString("h")}]"); this._testOutputHelper.WriteLine($"resulting right: [{rightResult.ToString("h")}]"); Assert.Equal(expectedLeft, leftResult); Assert.Equal(expectedRight, rightResult); Assert.Equal(length, enumerable.Count); }
public static byte[] SubtractUnsignedLittleEndian([NotNull] byte[] left, [NotNull] byte[] right) { if (left == null) { throw new ArgumentNullException(nameof(left)); } if (right == null) { throw new ArgumentNullException(nameof(right)); } var comparison = CompareUnsignedLittleEndian(left, right); if (comparison == 0) { return(Array.Empty <byte>()); } if (comparison == -1) { throw new InvalidOperationException("The operation would result in a non-unsigned value"); } var enumerable = new ConcurrentLittleEndianByteEnumerable(left, right).GetLsbToMsbEnumerable(); var resultQueue = new Queue <byte>(); var borrowed = false; foreach (var(leftByte, rightByte) in enumerable) { byte minuend; // min·u·end /ˈminyəˌwend/ (noun) a quantity or number from which another is to be subtracted. Not to be confused with the subtrahend. Duh. var canBorrow = leftByte > 0; if (borrowed && canBorrow) // previous operation needed to borrow, and a borrow is possible { minuend = (byte)(leftByte - 1); // decrement as this value has been borrowed from in the previous iteration } else if (borrowed) // && !canBorrow (implied) { minuend = 0xff; // 0 byte becomes 255 } else { minuend = leftByte; } if (minuend >= rightByte) // left is big enough to subtract right { var difference = (byte)(minuend - rightByte); resultQueue.Enqueue(difference); borrowed = borrowed && !canBorrow; // set borrow if a borrow happened some time previously but it could not be accommodated in this iteration; borrow from next } else // left is less than right, automatically borrow from next iteration { var difference = (byte)((minuend + 0x0100) - rightByte); // handle borrowed resultQueue.Enqueue(difference); borrowed = true; } } return(resultQueue.ToArray() .TrimLittleEndianLeadingZeroBytes()); // TODO, do without explicit trimming? }