/// <summary> /// Determines whether the integer is greater than the provided integer. /// </summary> /// <param name="other">The other integer.</param> /// <param name="signed">Indicates the integers should be interpreted as signed or unsigned integers.</param> /// <returns><c>true</c> if the current integer is greater than the provided integer, <c>false</c> if not, and /// <c>null</c> if the conclusion of the comparison is not certain.</returns> public virtual Trilean IsGreaterThan(IntegerValue other, bool signed) { if (signed) { var thisSigned = GetLastBit(); var otherSigned = other.GetLastBit(); if (thisSigned.IsUnknown || otherSigned.IsUnknown) { return(Trilean.Unknown); } // If the signs do not match, we know the result if (thisSigned ^ otherSigned) { return(otherSigned); } if (thisSigned) { return(IsLessThan(other, false)); } } // The following implements the "truth" table: // "-" indicates we do not know the answer yet. // // | 0 | 1 | ? // ---+---+---+--- // 0 | - | 0 | 0 // --+---+---+--- // 1 | 1 | - | ? // --+---+---+--- // ? | ? | 0 | ? AssertSameBitSize(other); for (int i = Size * 8 - 1; i >= 0; i--) { Trilean a = GetBit(i); Trilean b = other.GetBit(i); switch (a.Value, b.Value) {
/// <summary> /// Multiplies the current integer with a second (partially) known integer. /// </summary> /// <param name="other">The integer to multiply with.</param> /// <exception cref="ArgumentException">Occurs when the sizes of the integers do not match.</exception> public virtual void Multiply(IntegerValue other) { AssertSameBitSize(other); // We implement the standard multiplication by adding and shifting, but instead of storing all intermediate // results, we can precompute the two possible intermediate results and shift those and add them to an end // result to preserve time and memory. // First possible intermediate result is the current value multiplied by 0. It is redundant to compute this. // Second possibility is the current value multiplied by 1. var multipliedByOne = (IntegerValue)Copy(); // Third possibility is thee current value multiplied by ?. This is effectively marking all set bits unknown. // TODO: We could probably optimise the following operations for the native integer types. var multipliedByUnknown = (IntegerValue)Copy(); Span <byte> maskBuffer = stackalloc byte[Size]; multipliedByOne.GetMask(maskBuffer); Span <byte> bitsBuffer = stackalloc byte[Size]; multipliedByOne.GetBits(bitsBuffer); var mask = new BitField(maskBuffer); var bits = new BitField(bitsBuffer); mask.Not(); mask.Or(bits); mask.Not(); Span <byte> unknownBits = stackalloc byte[Size]; multipliedByUnknown.GetBits(unknownBits); multipliedByUnknown.SetBits(unknownBits, maskBuffer); // Final result. var result = new IntegerNValue(Size); int lastShiftByOne = 0; int lastShiftByUnknown = 0; for (int i = 0; i < Size * 8; i++) { Trilean bit = other.GetBit(i); if (!bit.IsKnown) { multipliedByUnknown.LeftShift(i - lastShiftByUnknown); result.Add(multipliedByUnknown); lastShiftByUnknown = i; } else if (bit.ToBoolean()) { multipliedByOne.LeftShift(i - lastShiftByOne); result.Add(multipliedByOne); lastShiftByOne = i; } } Span <byte> resultBits = stackalloc byte[Size]; Span <byte> resultMask = stackalloc byte[Size]; result.GetBits(resultBits); result.GetMask(resultMask); SetBits(resultBits, resultMask); }
/// <summary> /// Divides the current integer with a second (partially) known integer and returns remainder of division. /// </summary> /// <param name="other">The integer to divide with</param> /// <exception cref="ArgumentException">Occurs when the sizes of the integers do not match or there is dividing by zero.</exception> public virtual void Remainder(IntegerValue other) { AssertSameBitSize(other); // Throw exception because of dividing by zero if (other.IsZero == Trilean.True) { throw new ArgumentException("Divisor is zero and dividing by zero is not allowed."); } // There are 2 possibilities // First is that both numbers are known. In this possibility remainder is counted as usual. // Second possibility is that some of numbers are unknown. // Remainder in second possibility is count as shorter number of this two numbers with all bits set to unknown. var firstNum = (IntegerValue)Copy(); var secondNum = (IntegerValue)other.Copy(); var result = (IntegerValue)Copy(); // Possibilities of count if (firstNum.IsKnown && secondNum.IsKnown) { // Remainder is count by classic way while (firstNum.IsGreaterThan(secondNum, false) || firstNum.IsEqualTo(secondNum)) { firstNum.Subtract(secondNum); } result = firstNum; } else { // Setting all unknown bits to True because of finding out smaller number for (int i = 0; i < Size * 8; i++) { if (firstNum.GetBit(i) == Trilean.Unknown) { firstNum.SetBit(i, Trilean.True); } if (secondNum.GetBit(i) == Trilean.Unknown) { secondNum.SetBit(i, Trilean.True); } } // Result is shorter number result = (firstNum.IsGreaterThan(secondNum, false)) ? (IntegerValue)secondNum.Copy() : (IntegerValue)firstNum.Copy(); // Setting all bits to unknown in result bool isZeroBit = false; for (int i = Size * 8 - 1; i >= 0; i--) { // Jumping through zeros if (result.GetBit(i) != Trilean.False || isZeroBit) { isZeroBit = true; result.SetBit(i, Trilean.Unknown); } } } Span <byte> resultBits = stackalloc byte[Size]; Span <byte> resultMask = stackalloc byte[Size]; result.GetBits(resultBits); result.GetMask(resultMask); SetBits(resultBits, resultMask); }
/// <summary> /// Divides the current integer with a second (partially) known integer. /// </summary> /// <param name="other">The integer to divide with</param> /// <exception cref="ArgumentException">Occurs when the sizes of the integers do not match or there is dividing by zero.</exception> public virtual void Divide(IntegerValue other) { AssertSameBitSize(other); // Throw exception because of dividing by zero if (other.IsZero == Trilean.True) { throw new ArgumentException("Divisor is zero and dividing by zero is not allowed."); } // There is only one possibility to cover all possible result // The solution is to get first number as big as possible by changing all Uknown bits to True // And second number needs to be as small as possible by changing all Unknown bits to false // First possibility var firstNum = (IntegerValue)Copy(); var secondNum = (IntegerValue)other.Copy(); var oneNum = (IntegerValue)Copy(); var result = (IntegerValue)Copy(); for (int i = 0; i < Size * 8; i++) { if (firstNum.GetBit(i) == Trilean.Unknown) { firstNum.SetBit(i, Trilean.True); } if (secondNum.GetBit(i) == Trilean.Unknown) { secondNum.SetBit(i, Trilean.False); } oneNum.SetBit(i, Trilean.False); result.SetBit(i, Trilean.False); } oneNum.SetBit(0, Trilean.True); // Adding 1 to second number if it is zero if (secondNum.IsZero) { secondNum.Add(oneNum); } while ((firstNum.IsGreaterThan(secondNum, false) == Trilean.True || firstNum.IsEqualTo(secondNum))) { result.Add(oneNum); firstNum.Subtract(secondNum); } // Changing all known bits to unknown in greater result if (!IsKnown || !other.IsKnown) { bool isZeroBit = false; for (int i = Size * 8 - 1; i >= 0; i--) { // Jumping through zeros if (result.GetBit(i) != Trilean.False || isZeroBit) { isZeroBit = true; result.SetBit(i, Trilean.Unknown); } } } Span <byte> resultBits = stackalloc byte[Size]; Span <byte> resultMask = stackalloc byte[Size]; result.GetBits(resultBits); result.GetMask(resultMask); SetBits(resultBits, resultMask); }