Expected error or incompleteness with quantified permissions and wildcards? - assertion

Consider the following Viper program:
field f: Int
method g(r: Ref, N: Int)
requires forall tid: Int :: 0 <= tid && tid < N ==> acc(r.f, wildcard)
{
// Got error: insufficient permission for r.f
assert 0 < N ==> acc(r.f, wildcard)
}
method h(r: Ref, N: Int)
requires 0 < N ==> acc(r.f, wildcard);
{
assert 0 < N ==> acc(r.f, wildcard)
assert 0 < N ==> acc(r.f, wildcard) && acc(r.f, wildcard)
// Got error: receiver of r.f might not be injective
assert forall tid: Int :: 0 <= tid && tid < N ==> acc(r.f, wildcard)
}
Intuitively, the failing asserts should hold. Is there a subtle aspect of wildcard permissions that causes the asserts to fail, or is this a completeness issue?
(Originally filed as an issue for one of Viper's backends, but copied here since the explanation might help others to avoid similar pitfalls)

The failures have different root causes, but both are expected.
Error "receiver might not be injective"
For technical reasons (see this paper for details), a quantified permission assertion of the shape
forall x :: c(x) ==> acc(e(x).f)
must be injective in e(x). I.e. for two different instantiations of x, the expression e(x) evaluates to two different receivers. That is not the case for your assertion
forall tid: Int :: 0 <= tid && tid < N ==> acc(r.f, wildcard)
and hence the error message.
If you're wondering why you don't get the same error for method g's precondition: since the precondition is inhaled, Viper assumes (this might change in the future) that it is well-defined, i.e. that the receiver is injective. If you call g, then you'll get the same error:
method caller(r: Ref, N: Int) {
g(r, N) // receiver r.f might not be injective
}
So g's current precondition is basically false (since r is not injective in x). If you're wondering why Viper cannot (yet) prove false right after the precondition, please continue reading.
Error "insufficient permission"
Ignoring the unsound assumption about receiver injectivity, then this error is due to triggering: the quantifier
forall tid: Int :: 0 <= tid && tid < N ==> acc(r.f, wildcard)
admits no triggers that Viper could pick for you, since valid triggers (i) must not include interpreted functions (from the SMT solvers point of view), but (ii) they must include all quantified variables. Here, that rules out subexpressions such as 0 <= tid (interpreted function), but also r.f (doesn't mention quantified variable tid). It is basically undefined what SMT solvers do with quantifiers that don't have triggers; here, it isn't instantiated at all.
The only real option you have in this situation (other than faith) is to add an artificial trigger. Here are two examples:
/* ------------- Example 1 ------------- */
domain dummy {
function T(n: Int): Bool
axiom T_always_true { forall n: Int :: T(n) }
}
method g1(r: Ref, N: Int)
requires (forall tid: Int :: {T(tid)} 0 <= tid && tid < N ==> acc(r.f, wildcard))
{
assert T(0) // Must mention T(_) somewhere to trigger quantifier
assert 0 < N /* && T(0) */ ==> acc(r.f, wildcard)
}
/* ------------- Example 2 ------------- */
function id(r: Ref, n: Int): Ref { r }
method g2(r: Ref, N: Int)
requires (forall tid: Int :: {id(r, tid)} 0 <= tid && tid < N ==> acc(id(r, tid).f, wildcard))
{
assert 0 < N ==> acc(id(r, 0).f, wildcard)
}
By the way, it is strongly advised to always add triggers explicitly. For one, because it forces one to think about triggers and triggering strategies (how do different axioms interact and when should which be instantiated). The other reason is that Viper does a best-effort approach to choose quantifiers, but for complex, interacting quantifiers, you should not rely on Vipers heuristics.
Let me know if you have further questions regarding triggers. The Viper tutorial also has a section on quantifiers, and references a great paper on triggers.
Error "receiver might not be injective" (continued)
With the triggering issue resolved, we can now get Viper to prove false in the body of g:
method g(r: Ref, N: Int)
requires (forall tid: Int :: 0 <= tid && tid < N ==> acc(id(r, tid).f, wildcard))
{
assume 2 < N
assert id(r, 0) == id(r, 1)
assert false // holds, because the malformed precondition is inhaled
}

Related

Why this dafny post-condition is not inferred?

I have proved some purely existential lemmas (with no out-results) in a similar constructive way to this one:
https://rise4fun.com/Dafny/Wvly
lemma DivModExistsUnique_Lemma (x:nat, y:nat)
requires y != 0
ensures exists q:nat, r:nat :: x == y*q + r && r < y
{
var q:nat, r:nat := 0, x;
while r >= y
invariant x == y*q + r
{
q := q + 1;
r := r - y;
}
assert x == y*q + r && r < y;
}
I can’t think why this post-condition is not inferred from the last assertion in the proof.
There is some additional hint that can be given to Dafny?
Nice proof! The problem is that the quantifier in the postcondition has no "matching pattern" (also known as "trigger") that involves r. Dafny gives a warning about this. So what does that mean?
To prove an existential quantifier, the verifier attempts to find a witness. In searching for a witness, the verifier is not going to try every possible term in mathematics, and it's not even going to try every term that appears elsewhere in your program. Instead, the verifier limits its attention to terms that both appear elsewhere in the proof context and have the shape of a "matching pattern" for the quantifier. In the Dafny IDE (VS Code or Emacs), you can place the cursor over the quantifier and the IDE will show you the triggers that Dafny selected. (Again, in your case, it will instead say "No triggers".)
There are certain rules about what triggers can and cannot be (see the Dafny FAQ or many answered questions on StackOverflow). For q, the verifier will select the term y*q as the trigger. It is allowed, because Dafny allows * to appear in triggers. However, the only possibly useful triggers for r would be ... + r and r < ..., neither of which is allowed because triggers are not allowed to mention + or <. Therefore, Dafny finds no trigger for the quantifier, which essentially means that it will never find witnesses that prove the existential quantifier.
To work around this problem, you can introduce a function that involves the quantified variables. For example,
function MulAdd(a: nat, b: nat, c: nat): nat {
a*b + c
}
lemma DivModExistsUnique_Lemma(x:nat, y:nat)
requires y != 0
ensures exists q:nat, r:nat :: x == MulAdd(y, q, r) && r < y
{
var q:nat, r:nat := 0, x;
while r >= y
invariant x == MulAdd(y, q, r)
{
q := q + 1;
r := r - y;
}
}
will prove your program. The IDE will then also show you that Mul(y, q, r) is selected as a trigger.
Quantifiers without triggers tend to be ones that only use "built-in symbols" like + and < and &&. When your problems have other functions, the verifier can usually find triggers.
For your proof, the most elegant solution to my taste is to use lemma out-parameters. That means the lemma can just "return" the q and r it computes. It also makes the lemma easier to use, because the caller of the lemma would otherwise typically need to Skolemize the quantifier (which you do in Dafny with the assign-such-that operator :| -- which also involves triggers, by the way). But you said you're (for some reason) trying to avoid out-parameters. If so, then the MulAdd function will do the trick.

Unknown assertion failed in FStar

I'd like to understand what's wrong with this simple exercise.
let even n = (n % 2) = 0
let rec even_sqr (n:nat {even n}) : Lemma (even (n * n)) =
match n with
| 0 -> ()
| _ -> even_sqr (n - 2)
FStar returns '(Error) Unknown assertion failed'.
An "Unknown assertion failed" error means that Z3 did not give F* any reason for the proof failing. Usually, this is either evidence for the need for a more detailed proof, or that the statement is false. In this particular case, the statement is true, it is just that Z3 is not great at non-linear arithmetic (and this example combines both multiplication and modulo operators).
In such a case, some small amount of hand-holding helps. You can do this by adding some assertions that might help point it in the right direction.
In this particular case, I added a new assertion that expands n*n in terms of n-2, and the proof then goes through:
let rec even_sqr (n:nat {even n}) : Lemma (even (n * n)) =
match n with
| 0 -> ()
| _ ->
assert (n * n == (n - 2) * (n - 2) + 4 * n - 4); (* OBSERVE *)
even_sqr (n - 2)
Notice that I am not adding any complex proofs, but merely surfacing some properties that might be helpful for the solver to go along. Sometimes with non-linear proofs, however, this may not be enough, and you may require writing a few lemmas, at which point, a good resource is FStar.Math.Lemmas in the standard library.

correct use of ``progress`` label

According to the man pages,
Progress labels are used to define correctness claims. A progress label states the requirement that the labeled global state must be visited infinitely often in any infinite system execution. Any violation of this requirement can be reported by the verifier as a non-progress cycle.
and
Spin has a special mode to prove absence of non-progress cycles. It does so with the predefined LTL formula:
(<>[] np_)
which formalizes non-progress as a standard Buchi acceptance property.
But let's take a look at the very primitive promela specification
bool initialised = 0;
init{
progress:
initialised++;
assert(initialised == 1);
}
In my understanding, the assert should hold but verification fail because initialised++ is executed exactly once whereas the progress label claims it should be possible to execute it arbitrarily often.
However, even with the above LTL formula, this verifies just fine in ispin (see below).
How do I correctly test whether a statement can be executed arbitrarily often (e.g. for a locking scheme)?
(Spin Version 6.4.7 -- 19 August 2017)
+ Partial Order Reduction
Full statespace search for:
never claim + (:np_:)
assertion violations + (if within scope of claim)
non-progress cycles + (fairness disabled)
invalid end states - (disabled by never claim)
State-vector 28 byte, depth reached 7, errors: 0
6 states, stored (8 visited)
3 states, matched
11 transitions (= visited+matched)
0 atomic steps
hash conflicts: 0 (resolved)
Stats on memory usage (in Megabytes):
0.000 equivalent memory usage for states (stored*(State-vector + overhead))
0.293 actual memory usage for states
64.000 memory used for hash table (-w24)
0.343 memory used for DFS stack (-m10000)
64.539 total actual memory usage
unreached in init
(0 of 3 states)
pan: elapsed time 0.001 seconds
No errors found -- did you verify all claims?
UPDATE
Still not sure how to use this ...
bool initialised = 0;
init{
initialised = 1;
}
active [2] proctype reader()
{
assert(_pid >= 1);
(initialised == 1)
do
:: else ->
progress_reader:
assert(true);
od
}
active [2] proctype writer()
{
assert(_pid >= 1);
(initialised == 1)
do
:: else ->
(initialised == 0)
progress_writer:
assert(true);
od
}
And let's select testing for non-progress cycles. Then ispin runs this as
spin -a test.pml
gcc -DMEMLIM=1024 -O2 -DXUSAFE -DNP -DNOCLAIM -w -o pan pan.c
./pan -m10000 -l
Which verifies without error.
So let's instead try this with ltl properties ...
/*pid: 0 = init, 1-2 = reader, 3-4 = writer*/
ltl progress_reader1{ []<> reader[1]#progress_reader }
ltl progress_reader2{ []<> reader[2]#progress_reader }
ltl progress_writer1{ []<> writer[3]#progress_writer }
ltl progress_writer2{ []<> writer[4]#progress_writer }
bool initialised = 0;
init{
initialised = 1;
}
active [2] proctype reader()
{
assert(_pid >= 1);
(initialised == 1)
do
:: else ->
progress_reader:
assert(true);
od
}
active [2] proctype writer()
{
assert(_pid >= 1);
(initialised == 1)
do
:: else ->
(initialised == 0)
progress_writer:
assert(true);
od
}
Now, first of all,
the model contains 4 never claims: progress_writer2, progress_writer1, progress_reader2, progress_reader1
only one claim is used in a verification run
choose which one with ./pan -a -N name (defaults to -N progress_reader1)
or use e.g.: spin -search -ltl progress_reader1 test.pml
Fine, I don't care, I just want this to finally run, so let's just keep progress_writer1 and worry about how to stitch it all together later:
/*pid: 0 = init, 1-2 = reader, 3-4 = writer*/
/*ltl progress_reader1{ []<> reader[1]#progress_reader }*/
/*ltl progress_reader2{ []<> reader[2]#progress_reader }*/
ltl progress_writer1{ []<> writer[3]#progress_writer }
/*ltl progress_writer2{ []<> writer[4]#progress_writer }*/
bool initialised = 0;
init{
initialised = 1;
}
active [2] proctype reader()
{
assert(_pid >= 1);
(initialised == 1)
do
:: else ->
progress_reader:
assert(true);
od
}
active [2] proctype writer()
{
assert(_pid >= 1);
(initialised == 1)
do
:: else ->
(initialised == 0)
progress_writer:
assert(true);
od
}
ispin runs this with
spin -a test.pml
ltl progress_writer1: [] (<> ((writer[3]#progress_writer)))
gcc -DMEMLIM=1024 -O2 -DXUSAFE -DSAFETY -DNOCLAIM -w -o pan pan.c
./pan -m10000
Which does not yield an error, but instead reports
unreached in claim progress_writer1
_spin_nvr.tmp:3, state 5, "(!((writer[3]._p==progress_writer)))"
_spin_nvr.tmp:3, state 5, "(1)"
_spin_nvr.tmp:8, state 10, "(!((writer[3]._p==progress_writer)))"
_spin_nvr.tmp:10, state 13, "-end-"
(3 of 13 states)
Yeah? Splendid! I have absolutely no idea what to do about this.
How do I get this to run?
The problem with your code example is that it does not have any infinite system execution.
Progress labels are used to define correctness claims. A progress
label states the requirement that the labeled global state must be
visited infinitely often in any infinite system execution. Any
violation of this requirement can be reported by the verifier as a
non-progress cycle.
Try this example instead:
short val = 0;
init {
do
:: val == 0 ->
val = 1;
// ...
val = 0;
:: else ->
progress:
// super-important progress state
printf("progress-state\n");
assert(val != 0);
od;
};
A normal check does not find any error:
~$ spin -search test.pml
(Spin Version 6.4.3 -- 16 December 2014)
+ Partial Order Reduction
Full statespace search for:
never claim - (none specified)
assertion violations +
cycle checks - (disabled by -DSAFETY)
invalid end states +
State-vector 12 byte, depth reached 2, errors: 0
3 states, stored
1 states, matched
4 transitions (= stored+matched)
0 atomic steps
hash conflicts: 0 (resolved)
Stats on memory usage (in Megabytes):
0.000 equivalent memory usage for states (stored*(State-vector + overhead))
0.292 actual memory usage for states
128.000 memory used for hash table (-w24)
0.534 memory used for DFS stack (-m10000)
128.730 total actual memory usage
unreached in init
test.pml:12, state 5, "printf('progress-state\n')"
test.pml:13, state 6, "assert((val!=0))"
test.pml:15, state 10, "-end-"
(3 of 10 states)
pan: elapsed time 0 seconds
whereas, checking for progress yields the error:
~$ spin -search -l test.pml
pan:1: non-progress cycle (at depth 2)
pan: wrote test.pml.trail
(Spin Version 6.4.3 -- 16 December 2014)
Warning: Search not completed
+ Partial Order Reduction
Full statespace search for:
never claim + (:np_:)
assertion violations + (if within scope of claim)
non-progress cycles + (fairness disabled)
invalid end states - (disabled by never claim)
State-vector 20 byte, depth reached 7, errors: 1
4 states, stored
0 states, matched
4 transitions (= stored+matched)
0 atomic steps
hash conflicts: 0 (resolved)
Stats on memory usage (in Megabytes):
0.000 equivalent memory usage for states (stored*(State-vector + overhead))
0.292 actual memory usage for states
128.000 memory used for hash table (-w24)
0.534 memory used for DFS stack (-m10000)
128.730 total actual memory usage
pan: elapsed time 0 seconds
WARNING: ensure to write -l after option -search, otherwise it is not handed over to the verifier.
You ask:
How do I correctly test whether a statement can be executed arbitrarily often (e.g. for a locking scheme)?
Simply write a liveness property:
ltl prop { [] <> proc[0]#label };
This checks that process with name proc and pid 0 executes infinitely often the statement corresponding to label.
Since your edit substantially changes the question, I write a new answer to avoid confusion. This answer addresses only the new content. Next time, consider creating a new, separate, question instead.
This is one of those cases in which paying attention to the unreached in ... warning message is really important, because it affects the outcome of the verification process.
The warning message:
unreached in claim progress_writer1
_spin_nvr.tmp:3, state 5, "(!((writer[3]._p==progress_writer)))"
_spin_nvr.tmp:3, state 5, "(1)"
_spin_nvr.tmp:8, state 10, "(!((writer[3]._p==progress_writer)))"
_spin_nvr.tmp:10, state 13, "-end-"
(3 of 13 states)
relates to the content of file _spin_nvr.tmp that is created during the compilation process:
...
never progress_writer1 { /* !([] (<> ((writer[3]#progress_writer)))) */
T0_init:
do
:: (! (((writer[3]#progress_writer)))) -> goto accept_S4 // state 5
:: (1) -> goto T0_init
od;
accept_S4:
do
:: (! (((writer[3]#progress_writer)))) -> goto accept_S4 // state 10
od;
} // state 13 '-end-'
...
Roughly speaking, you can view this as the specification of a Buchi Automaton which accepts executions of your writer process with _pid equal to 3 in which it does not reach the statement with label progress_writer infinitely often, i.e. it does so only a finite number of times.
To understand this you should know that, to verify an ltl property φ, spin builds an automaton containing all paths in the original Promela model that do not satisfy φ. This is done by computing the synchronous product of the automaton modeling the original system with the automaton representing the negation of the property φ you want to verify. In your example, the negation of φ is given by the excerpt of code above taken from _spin_nvr.tmp and labeled with never progress_writer1. Then, Spin checks if there is any possible execution of this automaton:
if there is, then property φ is violated and such execution trace is a witness (aka counter-example) of your property
otherwise, property φ is verified.
The warning tells you that in the resulting synchronous product none of those states is ever reachable. Why is this the case?
Consider this:
active [2] proctype writer()
{
1: assert(_pid >= 1);
2: (initialised == 1)
3: do
4: :: else ->
5: (initialised == 0);
6: progress_writer:
7: assert(true);
8: od
}
At line 2:, you check that initialised == 1. This statement forces writer to block at line 2: until when initialised is set to 1. Luckily, this is done by the init process.
At line 5:, you check that initialised == 0. This statement forces writer to block at line 5: until when initialised is set to 0. However, no process ever sets initialised to 0 anywhere in the code. Therefore, the line of code labeled with progress_writer: is effectively unreachable.
See the documentation:
(1) /* always executable */
(0) /* never executable */
skip /* always executable, same as (1) */
true /* always executable, same as skip */
false /* always blocks, same as (0) */
a == b /* executable only when a equals b */
A condition statement can only be executed (passed) if it holds. [...]

Trouble in Making an isPrime Function

This is a homework. OCaml seems to be made by a psychopath.
let prime : int -> bool
= fun n ->
if n > 2 then
let a = n - 1 in
let rec divisor n a =
if a > 1 && n mod a = 0 then false
else if a = 2 && n mod a <> 0 then true
else divisor n a-1 ;;
else if n = 2 then true
else if n = 1 then false
I am not good at coding and I know that my isPrime algorithm is wrong.
But I wonder where in my code is the mistake that produces the syntax error.
Also is there any way to define the isPrime function in a recursive form?
Example:
let rec prime n = ~
You'll get better responses from experts if you don't gratuitously insult their language :-) But I'm an easygoing guy, so I'll take a stab at your syntax error.
There are quite a few problems in this code. Here are 3 that I see right off:
The symbol ;; is used to tell the interpreter that you've entered a full expression that you want it to evaluate. It's definitely out of place in the middle of a function declaration.
Your second let doesn't have an associated in. Every let must have an in after it. The only exception is for defining values at the top level of a module (like your prime function).
The expression divisor n a-1 is parsed as (divisor n a) - 1. You want parentheses like this: divisor a (n - 1).

What is the "-->" operator in C++?

After reading Hidden Features and Dark Corners of C++/STL on comp.lang.c++.moderated, I was completely surprised that the following snippet compiled and worked in both Visual Studio 2008 and G++ 4.4.
Here's the code:
#include <stdio.h>
int main()
{
int x = 10;
while (x --> 0) // x goes to 0
{
printf("%d ", x);
}
}
Output:
9 8 7 6 5 4 3 2 1 0
I'd assume this is C, since it works in GCC as well. Where is this defined in the standard, and where has it come from?
--> is not an operator. It is in fact two separate operators, -- and >.
The conditional's code decrements x, while returning x's original (not decremented) value, and then compares the original value with 0 using the > operator.
To better understand, the statement could be written as follows:
while( (x--) > 0 )
Or for something completely different... x slides to 0.
while (x --\
\
\
\
> 0)
printf("%d ", x);
Not so mathematical, but... every picture paints a thousand words...
That's a very complicated operator, so even ISO/IEC JTC1 (Joint Technical Committee 1) placed its description in two different parts of the C++ Standard.
Joking aside, they are two different operators: -- and > described respectively in §5.2.6/2 and §5.9 of the C++03 Standard.
x can go to zero even faster in the opposite direction in C++:
int x = 10;
while( 0 <---- x )
{
printf("%d ", x);
}
8 6 4 2
You can control speed with an arrow!
int x = 100;
while( 0 <-------------------- x )
{
printf("%d ", x);
}
90 80 70 60 50 40 30 20 10
;)
It's equivalent to
while (x-- > 0)
x-- (post decrement) is equivalent to x = x-1 (but returning the original value of x), so the code transforms to:
while(x > 0) {
x = x-1;
// logic
}
x--; // The post decrement done when x <= 0
It's
#include <stdio.h>
int main(void) {
int x = 10;
while (x-- > 0) { // x goes to 0
printf("%d ", x);
}
return 0;
}
Just the space makes the things look funny, -- decrements and > compares.
The usage of --> has historical relevance. Decrementing was (and still is in some cases), faster than incrementing on the x86 architecture. Using --> suggests that x is going to 0, and appeals to those with mathematical backgrounds.
Utterly geek, but I will be using this:
#define as ;while
int main(int argc, char* argv[])
{
int n = atoi(argv[1]);
do printf("n is %d\n", n) as ( n --> 0);
return 0;
}
while( x-- > 0 )
is how that's parsed.
One book I read (I don't remember correctly which book) stated: Compilers try to parse expressions to the biggest token by using the left right rule.
In this case, the expression:
x-->0
Parses to biggest tokens:
token 1: x
token 2: --
token 3: >
token 4: 0
conclude: x-- > 0
The same rule applies to this expression:
a-----b
After parse:
token 1: a
token 2: --
token 3: --
token 4: -
token 5: b
conclude: (a--)-- - b
This is exactly the same as
while (x--)
Anyway, we have a "goes to" operator now. "-->" is easy to be remembered as a direction, and "while x goes to zero" is meaning-straight.
Furthermore, it is a little more efficient than "for (x = 10; x > 0; x --)" on some platforms.
This code first compares x and 0 and then decrements x. (Also said in the first answer: You're post-decrementing x and then comparing x and 0 with the > operator.) See the output of this code:
9 8 7 6 5 4 3 2 1 0
We now first compare and then decrement by seeing 0 in the output.
If we want to first decrement and then compare, use this code:
#include <stdio.h>
int main(void)
{
int x = 10;
while( --x> 0 ) // x goes to 0
{
printf("%d ", x);
}
return 0;
}
That output is:
9 8 7 6 5 4 3 2 1
My compiler will print out 9876543210 when I run this code.
#include <iostream>
int main()
{
int x = 10;
while( x --> 0 ) // x goes to 0
{
std::cout << x;
}
}
As expected. The while( x-- > 0 ) actually means while( x > 0). The x-- post decrements x.
while( x > 0 )
{
x--;
std::cout << x;
}
is a different way of writing the same thing.
It is nice that the original looks like "while x goes to 0" though.
There is a space missing between -- and >. x is post decremented, that is, decremented after checking the condition x>0 ?.
-- is the decrement operator and > is the greater-than operator.
The two operators are applied as a single one like -->.
It's a combination of two operators. First -- is for decrementing the value, and > is for checking whether the value is greater than the right-hand operand.
#include<stdio.h>
int main()
{
int x = 10;
while (x-- > 0)
printf("%d ",x);
return 0;
}
The output will be:
9 8 7 6 5 4 3 2 1 0
C and C++ obey the "maximal munch" rule. The same way a---b is translated to (a--) - b, in your case x-->0 translates to (x--)>0.
What the rule says essentially is that going left to right, expressions are formed by taking the maximum of characters which will form a valid token.
Actually, x is post-decrementing and with that condition is being checked. It's not -->, it's (x--) > 0
Note: value of x is changed after the condition is checked, because it post-decrementing. Some similar cases can also occur, for example:
--> x-->0
++> x++>0
-->= x-->=0
++>= x++>=0
char sep = '\n' /1\
; int i = 68 /1 \
; while (i --- 1\
\
/1/1/1 /1\
/1\
/1\
/1\
/1\
/ 1\
/ 1 \
/ 1 \
/ 1 \
/1 /1 \
/1 /1 \
/1 /1 /1/1> 0) std::cout \
<<i<< sep;
For larger numbers, C++20 introduces some more advanced looping features.
First to catch i we can build an inverse loop-de-loop and deflect it onto the std::ostream. However, the speed of i is implementation-defined, so we can use the new C++20 speed operator <<i<< to speed it up. We must also catch it by building wall, if we don't, i leaves the scope and de referencing it causes undefined behavior. To specify the separator, we can use:
std::cout \
sep
and there we have a for loop from 67 to 1.
Instead of regular arrow operator (-->) you can use armor-piercing arrow operator: --x> (note those sharp barbs on the arrow tip). It adds +1 to armor piercing, so it finishes the loop 1 iteration faster than regular arrow operator. Try it yourself:
int x = 10;
while( --x> 0 )
printf("%d ", x);
Why all the complication?
The simple answer to the original question is just:
#include <stdio.h>
int main()
{
int x = 10;
while (x > 0)
{
printf("%d ", x);
x = x-1;
}
}
It does the same thing. I am not saying you should do it like this, but it does the same thing and would have answered the question in one post.
The x-- is just shorthand for the above, and > is just a normal greater-than operator. No big mystery!
There are too many people making simple things complicated nowadays ;)
Conventional way we define condition in while loop parenthesis"()" and terminating condition inside the braces"{}", but this -- & > is a way one defines all at once.
For example:
int abc(){
int a = 5
while((a--) > 0){ // Decrement and comparison both at once
// Code
}
}
It says, decrement a and run the loop till the time a is greater than 0
Other way it should have been like:
int abc() {
int a = 5;
while(a > 0) {
a = a -1 // Decrement inside loop
// Code
}
}
Both ways, we do the same thing and achieve the same goals.
(x --> 0) means (x-- > 0).
You can use (x -->)
Output: 9 8 7 6 5 4 3 2 1 0
You can use (-- x > 0) It's mean (--x > 0)
Output: 9 8 7 6 5 4 3 2 1
You can use
(--\
\
x > 0)
Output: 9 8 7 6 5 4 3 2 1
You can use
(\
\
x --> 0)
Output: 9 8 7 6 5 4 3 2 1 0
You can use
(\
\
x --> 0
\
\
)
Output: 9 8 7 6 5 4 3 2 1 0
You can use also
(
x
-->
0
)
Output: 9 8 7 6 5 4 3 2 1 0
Likewise, you can try lot of methods to execute this command successfully.
This --> is not an operator at all. We have an operator like ->, but not like -->. It is just a wrong interpretation of while(x-- >0) which simply means x has the post decrement operator and this loop will run till it is greater than zero.
Another simple way of writing this code would be while(x--). The while loop will stop whenever it gets a false condition and here there is only one case, i.e., 0. So it will stop when the x value is decremented to zero.
Here -- is the unary post decrement operator.
while (x-- > 0) // x goes to 0
{
printf("%d ", x);
}
In the beginning, the condition will evaluate as
(x > 0) // 10 > 0
Now because the condition is true, it will go into the loop with a decremented value
x-- // x = 9
That's why the first printed value is 9
And so on. In the last loop x=1, so the condition is true. As per the unary operator, the value changed to x = 0 at the time of print.
Now, x = 0, which evaluates the condition (x > 0 ) as false and the while loop exits.
--> is not an operator, it is the juxtaposition of -- (post-decrement) and > (greater than comparison).
The loop will look more familiar as:
#include <stdio.h>
int main() {
int x = 10;
while (x-- > 0) { // x goes to 0
printf("%d ", x);
}
}
This loop is a classic idiom to enumerate values between 10 (the excluded upper bound) and 0 the included lower bound, useful to iterate over the elements of an array from the last to the first.
The initial value 10 is the total number of iterations (for example the length of the array), and one plus the first value used inside the loop. The 0 is the last value of x inside the loop, hence the comment x goes to 0.
Note that the value of x after the loop completes is -1.
Note also that this loop will operate the same way if x has an unsigned type such as size_t, which is a strong advantage over the naive alternative for (i = length-1; i >= 0; i--).
For this reason, I am actually a fan of this surprising syntax: while (x --> 0). I find this idiom eye-catching and elegant, just like for (;;) vs: while (1) (which looks confusingly similar to while (l)). It also works in other languages whose syntax is inspired by C: C++, Objective-C, java, javascript, C# to name a few.
That's what you mean.
while((x--) > 0)
We heard in childhood,
Stop don't, Let Go (روکو مت، جانے دو)
Where a Comma makes confusion
Stop, don't let go. (روکو، مت جانے دو)
Same Happens in Programming now, a SPACE makes confusion. :D
The operator you use is called "decrement-and-then-test". It is defined in the C99 standard, which is the latest version of the C programming language standard. The C99 standard added a number of new operators, including the "decrement-and-then-test" operator, to the C language. Many C++ compilers have adopted these new operators as extensions to the C++ language.
Here is how the code without using the "decrement-and-then-test" operator:
#include <stdio.h>
int main()
{
int x = 10;
while (x > 0)
{
printf("%d ", x);
x--;
}
}
In this version of the code, the while loop uses the > operator to test whether x is greater than 0. The x-- statement is used to decrement x by 1 at the end of each iteration of the loop.