C++ What are the conditions of short circuit evaluation? [duplicate] - c++

This question already has answers here:
A warning - comparison between signed and unsigned integer expressions
(6 answers)
Is short-circuiting logical operators mandated? And evaluation order?
(7 answers)
Closed 6 months ago.
if (i - word.size() >= 0 && dp[i - word.size()] && s.substr(i - word.size(), word.size()) == word)
dp[i] = true;
i - word.size() >= 0 is the condition that I was using to prevent an out-of-bound indexing. The expectation was that if the condition was not satisfied, the equation would short circuit and the reference dp[i - word.size()] would not be happen.
But the reference happened regardless of the first condition. Only when I changed the code to the following, it started to short circuit.
int lookback = i - word.size();
if (lookback >= 0 && dp[lookback] && s.substr(i - word.size(), word.size()) == word)
dp[i] = true;
Why does the second code short circuit while the first one doesn't? I've read that if the logical operators && and || are overloaded, short circuiting doesn't happen, but that doesn't seem to be the case here and I can't figure out why it's not happening.
Here's the full code for context
bool wordBreakFaulty(string s, vector<string>& wordDict) {
int n = s.size() + 1;
vector<bool> dp(n, false);
dp[0] = true;
for (int i = 1; i < dp.size(); ++i) {
for (auto& word : wordDict) {
if (i - word.size() >= 0 && dp[i - word.size()] && s.substr(i - word.size(), word.size()) == word)
dp[i] = true;
}
}
return dp.back();
}

Related

how to fix hamming weight invariants

I am learning Dafny, attempting to write a specification for the hamming weight problem, aka the number of 1 bits in a number. I believe I have gotten the specification correct, but it still doesn't verify. For speed of verification I limited it to 8 bit numbers;
problem definition: https://leetcode.com/problems/number-of-1-bits/
function method twoPow(x: bv16): bv16
requires 0 <= x <= 16
{
1 << x
}
function method oneMask(n: bv16): bv16
requires 0 <= n <= 16
ensures oneMask(n) == twoPow(n)-1
{
twoPow(n)-1
}
function countOneBits(n:bv8): bv8 {
if n == 0 then 0 else (n & 1) + countOneBits(n >> 1)
}
method hammingWeight(n: bv8) returns (count: bv8 )
ensures count == countOneBits(n)
{
count := 0;
var i := 0;
var n' := n;
assert oneMask(8) as bv8 == 255; //passes
while i < 8
invariant 0 <= i <= 8
invariant n' == n >> i
invariant count == countOneBits(n & oneMask(i) as bv8);
{
count := count + n' & 1;
n' := n' >> 1;
i := i + 1;
}
}
I have written the same code in javascript to test the behavior and example the invariant values before and after the loop. I don't seen any problems.
function twoPow(x) {
return 1 << x;
}
function oneMask(n) {
return twoPow(n)-1;
}
function countOneBits(n) {
return n === 0 ? 0 : (n & 1) + countOneBits(n >> 1)
}
function hammingWeight(n) {
if(n < 0 || n > 256) throw new Error("out of range")
console.log(`n: ${n} also ${n.toString(2)}`)
let count = 0;
let i = 0;
let nprime = n;
console.log("beforeloop",`i: ${i}`, `n' = ${nprime}`, `count: ${count}`, `oneMask: ${oneMask(i)}`, `cb: ${countOneBits(n & oneMask(i))}`)
console.log("invariants", i >= 0 && i <= 8, nprime == n >> i, count == countOneBits(n & oneMask(i)));
while (i < 8) {
console.log("");
console.log('before',`i: ${i}`, `n' = ${nprime}`, `count: ${count}`, `oneMask: ${oneMask(i)}`, `cb: ${countOneBits(n & oneMask(i))}`)
console.log("invariants", i >= 0 && i <= 8, nprime == n >> i, count == countOneBits(n & oneMask(i)));
count += nprime & 1;
nprime = nprime >> 1;
i++;
console.log('Afterloop',`i: ${i}`, `n' = ${nprime}`, `count: ${count}`, `oneMask: ${oneMask(i)}`, `cb: ${countOneBits(n & oneMask(i))}`)
console.log("invariants", i >= 0 && i <= 8, nprime == n >> i, count == countOneBits(n & oneMask(i)));
}
return count;
};
hammingWeight(128);
All invariants evaluate as true. I must be missing something. it says invariant count == countOneBits(n & oneMask(i) as bv8); might not be maintained by the loop. Running the javascript shows that they are all true. Is it due to the cast of oneMask to bv8?
edit:
I replaced the mask function with one that didn't require casting and that still not resolve the problem.
function method oneMaskOr(n: bv8): bv8
requires 0 <= n <= 8
ensures oneMaskOr(n) as bv16 == oneMask(n as bv16)
{
if n == 0 then 0 else (1 << (n-1)) | oneMaskOr(n-1)
}
One interesting thing I found is that it shows me a counter example where it has reached the end of the loop and the final bit of the input variable n is set, so values 128 or greater. But when I add an assertion above the loop that value equals the count at the end of the loop it then shows me the another value of n.
assert 1 == countOneBits(128 & OneMaskOr(8)); //counterexample -> 192
assert 2 == countOneBits(192 & OneMaskOr(8)); //counterexample -> 160
So it seems like it isn't evaluating the loop invariant after the end of loop? I thought the whole point of the invariants was to evaluate after the end of loop.
Edit 2:
I figured it out, apparently adding the explicit decreases clause to the while loop fixed it. I don't get it though. I thought Dafny could figure this out.
while i < 8
invariant 0 <= i <= 8
invariant n' == n >> i
invariant count == countOneBits(n & oneMask(i) as bv8);
decreases 8 - i
{
I see one line in the docs for loop termination saying
If the decreases clause of a loop specifies *, then no termination check will be performed. Use of this feature is sound only with respect to partial correctness.
So is if the decreases clause is missing does it default to *?
After playing around, I did find a version which passes though it required reworking countOneBits() so that its recursion followed the order of iteration:
function countOneBits(n:bv8, i: int, j:int): bv8
requires i ≥ 0 ∧ i ≤ j ∧ j ≤ 8
decreases 8-i {
if i == j then 0
else (n&1) + countOneBits(n >> 1, i+1, j)
}
method hammingWeight(n: bv8) returns (count: bv8 )
ensures count == countOneBits(n,0,8)
{
count ≔ 0;
var i ≔ 0;
var n' ≔ n;
//
assert count == countOneBits(n,0,i);
//
while i < 8
invariant 0 ≤ i ≤ 8;
invariant n' == n >> i;
invariant count == countOneBits(n,0,i);
{
count ≔ (n' & 1) + count;
n' ≔ n' >> 1;
i ≔ i + 1;
}
}
The intuition here is that countOneBits(n,i,j) returns the number of 1 bits between i (inclusive) and j (exclusive). This then reflects what the loop is doing as we increase i.

Index error while moving on a matrix object C++

I am trying to fit a word to a matrix by iteration but when I enter a row = 0, the function tries to check arbit_mat[r][c - 1] == '-' in the while loop and gives error. How can I fix this issue?
if (rotation == "CW")
{
switch (direction[0]) {
case 'l':
// left situation
while ((c - 1 >= 0) & (arbit_mat[r][c - 1] == '-')) {
arbit_mat[r][c - 1] = word[idx];
idx++;
c--;
printMatrix(arbit_mat);
if (idx == word.length())
{
return arbit_mat;
}
if (c == 0)
{
break;
}
}
I have written an if statement inside the loop to solve the problem but when the row starts with 0, it gives error of:
Vector subscript out of range!
Thank you for contributing.
You want
while ((c - 1 >= 0) && (arbit_mat[r][c - 1] == '-')) {
& is the bitwise and operator
&& is the logical and operator

How can I create an "isalnum" equivalent?

I was wondering if I am writing correct "isalnum" logic. This program is checking if the string is a palindrome or not and when I input the string "race a car", it keeps saying it is true, i.e. it's a palindrome.
bool isPalindrome(string s) {
for (int i = 0, j = s.size() - 1; i < j; i++, j--) {
while ((s[i] < 'a' || s[i] > 'z') && (i < j) ||
(s[i] < 'A' || s[i] > 'Z') && (i < j) ||
(s[i] < '0' || s[i] > '9') && (i < j))
i++;
while ((s[j] < 'a' || s[j] > 'z') && (i < j) ||
(s[j] < 'A' || s[j] > 'Z') && (i < j) ||
(s[j] < '0' || s[j] > '9') && (i < j))
j--;
if (toupper(s[i]) != toupper(s[j])) return false;
}
return true;
}
No, your logic is not correct, well, at least not for all the character sets that C++ can use. In ASCII, the letters of the alphabet are in contiguous blocks, so something like
(s[i]<'A'||s[i]>'Z')
works just fine. The issue with that is that ASCII isn't the only character set C++ supports. The most common example to counterpoint ASCII is EBCDIC which has the characters {, }, and \ in between A and Z.
One thing that is guaranteed though is that 0 through 9 are contiguous in all character sets that C++ supports so it's always legal to text if a character is a number using
if (char_var >= '0' && char_var <= '9')
Assuming contiguous alphabets (which isn't going to hold in reality), your logic is broken by the fact that you're doing multi-range checking, where disqualification in one range is still qualified in another.
Specifically, this:
while((s[i]<'a'||s[i]>'z')&&(i<j)||(s[i]<'A'||s[i]>'Z')&&(i<j)||(s[i]<'0'||s[i]>'9')&&(i<j))i++;
Now, consider this: suppose a[i] is in 'a'..'z', so the first range check will be false. But, if that's the case then it is NOT in 'A'..'Z' and is certainly not in '0'..'9'. Since both of those tests result in true, the loop advances i and continues on. As your loop is written, so long as the character is not in at least one of those ranges the loop continues. Since the ranges are mutually exclusive, there will ALWAYS be at least one the current character is not within. That OR separating condition is wrong. It shouldn't be not-in one of those ranges; it should be not-in ALL of those ranges. Thus.. AND is appropriate.
Short work with a debugger will tell you the very first pass of your outer-for-loop is advancing i all the way to j. The second loop is skipped because j and i are already equal, and since s[i] == s[j] is definitely true when i == j, the result is true.
Short version: your loop conditions are broken, even on contiguous character sequence platforms.
The loop you're more inclined to succeed with would be something like:
while( (i < j) && (s[i]<'a'|| s[i]>'z') && (s[i]<'A'||s[i]>'Z') && (s[i]<'0'||s[i]>'9') )
i++;
I leave the other loop and consideration for not doing any of this because of encodings where it will not work as an exercise for you.

Issues with commutative property of && operator

The code below that I have been having strange issues with is meant to trim off the unused portion of an integer array, and then convert it into a string.
Ex:
_ABC__DE______ would become _ABC__DE.
The problems show up when the input is filled with the default character. ("_" in the example).
sLength is the length of the integer array chars
The problematic code:
int inputLength = sLength - 1;
while (chars[inputLength] == defaultChar && inputLength >= 0) {
inputLength--;
}
inputLength++;
Serial.println("input length: " + String(inputLength));
// (in)sanity check
Serial.println(inputLength);
Serial.println(String(inputLength));
Serial.println(inputLength <= 0);
Serial.println(0 <= 0);
Serial.println(inputLength == 0);
Serial.println(0 == 0);
if (inputLength <= 0) {
//reset cursor position
Serial.println("index set to 0");
index = 0;
} else {
output = "";
for (int i = 0; i < inputLength; i++) {
char c = charSet[chars[i]];
if (c == '_') {
c = ' ';
}
output += c;
}
done = true;
}
The output when given an array filled with defaultChar:
input length: 0
0
0
0
1
0
1
If I'm interpreting correctly, the output means that 0 > 0 and 0 =/= 0 on even lines, but 0 <= 0 and 0 = 0 on odd lines.
The workaround I've come up with is replacing
while (chars[inputLength] == defaultChar && inputLength >= 0) {
inputLength--;
}
with one of the following
while (inputLength >= 0 && chars[inputLength] == defaultChar) {
inputLength--;
}
.
while (chars[inputLength] == defaultChar) {
inputLength--;
if (inputLength < 0) {
break;
}
}
which both result in an output of:
input length: 0
0
0
1
1
1
1
index set to 0
Why does this change the result?
As far as I knew until now, the && operator was commutative.
Is there something that I am missing that makes
chars[inputLength] == defaultChar && inputLength >= 0
not equal to
inputLength >= 0 && chars[inputLength] == defaultChar?
If It's relevant, this is being run on an 328P Arduino Nano with the old bootloader using IDE 1.8.8
&& is not commutative. It evaluates the left operand first and then stops if the left operand evaluated to 0.
Your original code fails because at some point it evaluates chars[-1] (which causes undefined behaviour if chars is an array). The alternative version does not have that problem because it performs the >= 0 test before using inputLength as an array index.
&& is commutative in the sense that the result of a && b is same as the result of b && a. But the built-in operator && has a short-circuiting behavior. This means that if the result of a && b can be decided by evaluating the first operand alone, the second one is not evaluated.
So when the first operand is chars[inputLength] == defaultChar and inputLength is -1, you enter the territory of undefined behavior which means the behavior of the program is unpredictable. But with the workarounds, you avoid undefined behavior because of the inputLength >= 0 and inputLength < 0 checks and therefore the code works as intended.
As #PeteBecker notes: a() && b() is not commutative if either a() or b() has side effects.

Else if statement executing unexpectedly, behaving as else statement [duplicate]

This question already has answers here:
Is (4 > y > 1) a valid statement in C++? How do you evaluate it if so?
(5 answers)
Closed 5 years ago.
I'm working on a simple function to convert ascii codes to array indexes, and I'm have a bit of trouble with the else-if statement.
int getIndex(char character) {
int code = int(character);
int index = -1;
if (code == int(' ')){
index = 26;
} else if (int('a') <= code <= int('z')) {
index = code - int('a');
}
return index;
}
The if condition works fine and fires on spaces, but the else if statement fires in every other case, regardless of whether the character is in the range a to z or not. Is there a problem I can't see with the condition?
c++ doesn't support expressions with multiple comparisons (like, e.g., python does). You need to break the expression in the else if branch into two expression with the logical and (&&) operator between them:
if (code == int(' ')){
index = 26;
} else if (int('a') <= code && code <= int('z')) {
// Here -----------------^
index = code - int('a');
}
return index;