Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SE: Add RemainderOperation #7263

Merged
merged 7 commits into from
May 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ internal sealed partial class Binary
BinaryOperatorKind.Subtract => NumberConstraint.From(left.Min - right.Max, left.Max - right.Min),
BinaryOperatorKind.Multiply => CalculateMultiply(left, right),
BinaryOperatorKind.Divide => CalculateDivide(left, right),
antonioaversa marked this conversation as resolved.
Show resolved Hide resolved
BinaryOperatorKind.Remainder when left.IsSingleValue && right.IsSingleValue && right.Min != 0 => NumberConstraint.From(left.Min.Value % right.Min.Value),
BinaryOperatorKind.Remainder => CalculateRemainder(left, right),
BinaryOperatorKind.And when left.IsSingleValue && right.IsSingleValue => NumberConstraint.From(left.Min.Value & right.Min.Value),
BinaryOperatorKind.And => NumberConstraint.From(CalculateAndMin(left, right), CalculateAndMax(left, right)),
BinaryOperatorKind.Or when left.IsSingleValue && right.IsSingleValue => NumberConstraint.From(left.Min.Value | right.Min.Value),
Expand Down Expand Up @@ -113,6 +115,69 @@ private static NumberConstraint CalculateDivide(NumberConstraint left, NumberCon
return NumberConstraint.From(min, max);
}

private static NumberConstraint CalculateRemainder(NumberConstraint left, NumberConstraint right)
{
if (right.Min == 0 && right.Max == 0)
{
return null;
}
else
{
right = AccountForZero(right);
return NumberConstraint.From(CalculateRemainderMin(left, right), CalculateRemainderMax(left, right));
}
}

private static BigInteger? CalculateRemainderMin(NumberConstraint left, NumberConstraint right)
{
// If the absolute value of the divisor is bigger than the absolute value of the dividend, the result is equal to the dividend =>
// If the absolute value of every divisor is bigger than the absolute value of any dividend => resulting range == dividend range.
// Otherwise, the result is between 0 and the absolute value of the divisor - 1 for positive dividends or in the same range multiplied by -1 for negative dividends.
if (left.CanBeNegative || left.Max < MinOfAbsoluteValues(right))
{
if (right.Min is null || right.Max is null)
{
// If right is not finite, there will always be a divisor for which its absolute value is bigger than the absolute value of any value in left => resulting range == dividend range.
return left.Min;
}
else
{
// Otherwise, the result value is limited by the dividend and the divisor.
var minDerivedFromRight = -MaxOfAbsoluteValues(right) + 1;
return left.Min is null ? minDerivedFromRight : BigInteger.Max(left.Min.Value, minDerivedFromRight);
}
}
else
{
return 0;
}
}

private static BigInteger? CalculateRemainderMax(NumberConstraint left, NumberConstraint right)
{
// If the absolute value of the divisor is bigger than the absolute value of the dividend, the result is equal to the dividend =>
// If the absolute value of every divisor is bigger than the absolute value of any dividend => resulting range == dividend range.
// Otherwise, the result is between 0 and the absolute value of the divisor - 1 for positive dividends or in the same range multiplied by -1 for negative dividends.
if (left.CanBePositive || -left.Max < MinOfAbsoluteValues(right))
{
if (right.Min is null || right.Max is null)
{
// If right is not finite, there will always be a divisor for which its absolute value is bigger than the absolute value of any value in left => resulting range == dividend range.
return left.Max;
antonioaversa marked this conversation as resolved.
Show resolved Hide resolved
}
else
{
// Otherwise, the result value is limited by the dividend and the divisor.
var maxDerivedFromRight = MaxOfAbsoluteValues(right) - 1;
return left.Max is null ? maxDerivedFromRight : BigInteger.Min(left.Max.Value, maxDerivedFromRight);
}
}
else
{
return 0;
}
}

private static NumberConstraint AccountForZero(NumberConstraint constraint)
{
if (constraint.Min == 0)
Expand All @@ -129,6 +194,25 @@ private static NumberConstraint AccountForZero(NumberConstraint constraint)
}
}

private static BigInteger MaxOfAbsoluteValues(NumberConstraint constraint) =>
BigInteger.Max(BigInteger.Abs(constraint.Min.Value), BigInteger.Abs(constraint.Max.Value));

private static BigInteger MinOfAbsoluteValues(NumberConstraint constraint)
{
if (constraint.IsPositive)
{
return constraint.Min.Value;
}
else if (constraint.IsNegative)
{
return -constraint.Max.Value;
}
else
{
return 0;
}
}

private static BigInteger? CalculateAndMin(NumberConstraint left, NumberConstraint right)
{
// The result can only be negative if both operands are negative => The result must be >= 0 unless both ranges include negative numbers.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,121 @@ public void Binary_Division_Range(string expression, int? expectedMin, int? expe
SETestContext.CreateCS(code, "int i, int j").Validator.TagValue("Value").Should().HaveOnlyConstraints(ObjectConstraint.NotNull, NumberConstraint.From(expectedMin, expectedMax));
}

[DataTestMethod]
[DataRow(21, 4, 1)]
[DataRow(21, -4, 1)]
[DataRow(-21, 4, -1)]
[DataRow(-21, -4, -1)]
[DataRow(4, 5, 4)]
[DataRow(-4, 5, -4)]
[DataRow(4, -5, 4)]
[DataRow(-4, -5, -4)]
[DataRow(5, 5, 0)]
[DataRow(5, -5, 0)]
[DataRow(-5, 5, 0)]
[DataRow(-5, -5, 0)]
[DataRow(5, 1, 0)]
[DataRow(5, -1, 0)]
[DataRow(-5, 1, 0)]
[DataRow(-5, -1, 0)]
public void Binary_Remainder_SingleValue(int left, int right, int expected)
antonioaversa marked this conversation as resolved.
Show resolved Hide resolved
{
var code = $"""
var left = {left};
var right = {right};
var value = left % right;
Tag("Value", value);
""";
SETestContext.CreateCS(code).Validator.TagValue("Value").Should().HaveOnlyConstraints(ObjectConstraint.NotNull, NumberConstraint.From(expected));
}

[DataTestMethod]
[DataRow("i >= 21 && j >= 5", 0, null)]
[DataRow("i >= 21 && j >= -5", 0, null)]
[DataRow("i >= 21 && j == 5", 0, 4)]
[DataRow("i >= 21 && j == -5", 0, 4)]
[DataRow("i >= 21 && j <= 5", 0, null)]
[DataRow("i >= 21 && j <= -5", 0, null)]
[DataRow("i >= -21 && j >= 5", -21, null)]
[DataRow("i >= -21 && j >= -5", -21, null)]
[DataRow("i >= -21 && j == 5", -4, 4)]
[DataRow("i >= -21 && j == -5", -4, 4)]
[DataRow("i >= -21 && j <= 5", -21, null)]
[DataRow("i >= -21 && j <= -5", -21, null)]
[DataRow("i == 21 && j >= 5", 0, 21)]
[DataRow("i == 21 && j >= -5", 0, 21)]
[DataRow("i == 21 && j <= 5", 0, 21)]
[DataRow("i == 21 && j <= -5", 0, 21)]
[DataRow("i == -21 && j >= 5", -21, 0)]
[DataRow("i == -21 && j >= -5", -21, 0)]
[DataRow("i == -21 && j <= 5", -21, 0)]
[DataRow("i == -21 && j <= -5", -21, 0)]
[DataRow("i <= 21 && j >= 5", null, 21)]
[DataRow("i <= 21 && j >= -5", null, 21)]
[DataRow("i <= 21 && j == 5", -4, 4)]
[DataRow("i <= 21 && j == -5", -4, 4)]
[DataRow("i <= 21 && j <= 5", null, 21)]
[DataRow("i <= 21 && j <= -5", null, 21)]
[DataRow("i <= -21 && j >= 5", null, 0)]
[DataRow("i <= -21 && j >= -5", null, 0)]
[DataRow("i <= -21 && j == 5", -4, 0)]
[DataRow("i <= -21 && j == -5", -4, 0)]
[DataRow("i <= -21 && j <= 5", null, 0)]
[DataRow("i <= -21 && j <= -5", null, 0)]
[DataRow("i >= 21 && j >= 0", 0, null)]
[DataRow("i >= 21 && j == 0", null, null)]
[DataRow("i >= 21 && j <= 0", 0, null)]
[DataRow("i >= -21 && j >= 0", -21, null)]
[DataRow("i >= -21 && j == 0", null, null)]
[DataRow("i >= -21 && j <= 0", -21, null)]
[DataRow("i <= 21 && j >= 0", null, 21)]
[DataRow("i <= 21 && j == 0", null, null)]
[DataRow("i <= 21 && j <= 0", null, 21)]
[DataRow("i <= -21 && j >= 0", null, 0)]
[DataRow("i <= -21 && j == 0", null, null)]
[DataRow("i <= -21 && j <= 0", null, 0)]
[DataRow("i >= 21 && j == 1", 0, 0)]
[DataRow("i >= -21 && j == 1", 0, 0)]
[DataRow("i <= 21 && j == 1", 0, 0)]
[DataRow("i <= -21 && j == 1", 0, 0)]
[DataRow("i >= 21 && j >= 5 && j <= 10", 0, 9)]
[DataRow("i >= 21 && j >= -5 && j <= 10", 0, 9)]
[DataRow("i >= 21 && j >= -10 && j <= 5", 0, 9)]
[DataRow("i >= 21 && j >= -10 && j <= -5", 0, 9)]
[DataRow("i >= -21 && j >= 5 && j <= 10", -9, 9)]
[DataRow("i >= -21 && j >= -5 && j <= 10", -9, 9)]
[DataRow("i >= -21 && j >= -10 && j <= 5", -9, 9)]
[DataRow("i >= -21 && j >= -10 && j <= -5", -9, 9)]
[DataRow("i <= 21 && j >= 5 && j <= 10", -9, 9)]
[DataRow("i <= 21 && j >= -5 && j <= 10", -9, 9)]
[DataRow("i <= 21 && j >= -10 && j <= 5", -9, 9)]
[DataRow("i <= 21 && j >= -10 && j <= -5", -9, 9)]
[DataRow("i <= -21 && j >= 5 && j <= 10", -9, 0)]
[DataRow("i <= -21 && j >= -5 && j <= 10", -9, 0)]
[DataRow("i <= -21 && j >= -10 && j <= 5", -9, 0)]
[DataRow("i <= -21 && j >= -10 && j <= -5", -9, 0)]
[DataRow("i >= 5 && i <= 10 && j >= 21", 5, 10)]
[DataRow("i >= 5 && i <= 10 && j <= -21", 5, 10)]
[DataRow("i >= -5 && i <= 10 && j >= 21", -5, 10)]
[DataRow("i >= -5 && i <= 10 && j <= -21", -5, 10)]
[DataRow("i >= -10 && i <= 5 && j >= 21", -10, 5)]
[DataRow("i >= -10 && i <= 5 && j <= -21", -10, 5)]
[DataRow("i >= -10 && i <= -5 && j >= 21", -10, -5)]
[DataRow("i >= -10 && i <= -5 && j <= -21", -10, -5)]
[DataRow("i >= 4 && i <= 7 && j >= 5 && j <= 10", 0, 7)]
[DataRow("i == 5 && j == 0", null, null)]
public void Binary_Remainder_Range(string expression, int? expectedMin, int? expectedMax)
{
var code = $$"""
if ({{expression}})
{
var value = i % j;
Tag("Value", value);
}
""";
SETestContext.CreateCS(code, "int i, int j").Validator.TagValue("Value").Should().HaveOnlyConstraints(ObjectConstraint.NotNull, NumberConstraint.From(expectedMin, expectedMax));
}

[DataTestMethod]
[DataRow(0b0000, 0b0000, 0b0000)]
[DataRow(0b0101, 0b0101, 0b0101)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public void BasicOperators()
_ = i * 2147483600; // FN

i = 2 % j;
_ = i * 2147483600; // FN
_ = i * 2147483600; // Noncompliant
antonioaversa marked this conversation as resolved.
Show resolved Hide resolved
}

public void AssignmentOperators()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ Public Class Sample
__ = i * 2147483600 ' FN

i = 2 Mod j
__ = i * 2147483600 ' FN
__ = i * 2147483600 ' Noncompliant

Dim Result = Integer.MaxValue ^ 2 ' Compliant, result is Double
End Sub
Expand Down