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 6 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 => 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,64 @@ 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;
}
right = AccountForZero(right);
return NumberConstraint.From(CalculateRemainderMin(left, right), CalculateRemainderMax(left, right));
antonioaversa marked this conversation as resolved.
Show resolved Hide resolved
}

private static BigInteger? CalculateRemainderMin(NumberConstraint left, NumberConstraint right)
{
// If every divisor is absolutely bigger than any dividend => resulting range == dividend range.
antonioaversa marked this conversation as resolved.
Show resolved Hide resolved
// Otherwise, the result is bigger or equals 0 for positive dividend values and absolutely bigger than the divisor for negative dividend values.
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 which is absolutely bigger than any value in left => resulting range == dividend range.
return left.Min;
}
else
{
// The result cannot be absolutely bigger than the absolute divisor - 1. For negative dividends the inverse is true.
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 every divisor is absolutely bigger than any dividend => resulting range == dividend range.
// Otherwise, the result is smaller or equals 0 for negative dividend values and absolutely smaller than the divisor for positive dividend values.
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 which is absolutely bigger than any value in left => resulting range == dividend range.
return left.Max;
antonioaversa marked this conversation as resolved.
Show resolved Hide resolved
}
else
{
// The result cannot be absolutely bigger than the absolute divisor - 1.
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 +189,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,120 @@ 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)]
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