Translated using translate google. The original language is Russian. Original page https://hi-aga.ru/index.php/homepage/boolean-1-20

Here we will number all examples with the prefix “BOOL”, since “B” was used in the first group of tasks. We will also often need a word that outputs “True” or “False” depending on the results of calculations. Let's call it "B."

: B. ( Bool -> ) \ Bool – Boolean variable, 0-False, otherwise True
    IF." True" ELSE." False" THEN;

We should introduce you to a variable of type Bool, as well as a new construction of the form:

IF ( B -> ) <Code if B = True> ELSE <Code if B = False> THEN

where a variable of this type is used. Bool in SP-Forth is no different from previously used variables of the integer type, the only difference is its interpretation, when it is equal to zero, it means “False”, in all other cases “True” is encoded. The built-in constant in SP-Forth "FALSE" leaves the number "0" on the stack, and "TRUE" - "4294967295(-1)". You can also see that SP-Forth interprets integers differently: either unsigned (4294967295) or signed (-1). The “IF ... ELSE ... THEN” construction is code branching operators.
The word "IF" takes a number from the stack and interprets it as a Boolean variable, if true, executes the code between "IF ... ELSE", otherwise the code between "ELSE ... THEN", which is optional, then nothing is executed and continues interpreting the code after " THEN." A simplified version of the code would be: “IF ... THEN”. Now the work of the word “B.” should be clear; if there is a “Bool” on the stack that is not equal to zero, then the string “True” is printed on the screen, otherwise “False”.

Example 1. Checking a number for positivity.
: BOOL1 ( A -> )
    0 > B.
;
5 BOOL1
True Ok
0 BOOL1
False Ok
-1 BOOL1
False Ok

Let us further explain the comparison with zero. In SP-Forth, we first write zero (“0”), which will go onto the stack, then the word greater than (“>”), which takes the top 2 elements on the stack and compares them, and depending on the result, leaves a boolean value or “0”, or "-1", which already parses the word "B.". The stack notation for the word ">" is (NUMBER1 NUMBER2 -> BOOL).

Example 2. Checking whether a number is odd.

: BOOL2 ( A -> )
    2 MOD \ take the remainder of division by 2
    B. \ remainder 1 is true and 0 is false
;
3BOOL2
True Ok
8BOOL2
False Ok

The "2 MOD" code leaves either 0 or 1, which can be interpreted as a boolean value, so we don't need to use the comparison operator this time.

Example 3. Checking a number for evenness. It differs slightly from the previous one. Now the remainder 0 is true and 1 is false. Only the interpretation of the division result changes.

: BOOL3 ( A -> )
    2 MOD \ take the remainder of division by 2
    0= B. \ remainder 1 is false and 0 is true
;
3 BOOL3
False Ok
8BOOL3
True Ok

The line “2 MOD” is the same as in the previous case, “0=” is a check for parity, because an even number leaves a remainder of zero when divided by two. And the standard end "B." printout of the result.

Example 4. Checking inequalities A>2 and B<=3.

: BOOL4 ( A B -> )
    1- 3 <\A B-1<3
    SWAP 2 > \ B-1<3 A>2
    AND \ simultaneously B-1<3 and A>2
    B.
;

SP-Forth does not have a built-in comparison operator “<=” (Less than or equal, it’s too lazy to add another library, but laziness is the engine of progress). To do this, we will use an artificial technique. If “B<=3”, then “B-1<3”, since we are working with discrete integers, then this technique is acceptable. The AND operator tests the simultaneous fulfillment of two conditions; it is a logical multiplication of two Boolean variables, left as the results of two comparisons.

3 3 BOOL4
True Ok
2 3 BOOL4
False Ok
3 4 BOOL4
False Ok

If at least one of the conditions is not met, the result is “False”.

Example 5. Checking inequalities A>=0 and B<-2. Identical to the previous one.

: BOOL5 ( A B -> )
    -2 <\A B<-2
    SWAP 1+ 0 > \ B<-2 A+1>0
    AND \ simultaneously B<-2 and A+1>0
    B.
;
0 -3 BOOL5
True Ok
0 -2 BOOL5
False Ok
-1 -3 BOOL5
False Ok
1 -3 BOOL5
True Ok

Example 6. Checking the inequality A<B<C.

: BOOL6 ( A B C -> )
    OVER > \ A B C -> A B C>B
    ROT ROT < \ A B C>B -> C>B A<B
    AND \ C>B A<B -> [C>B]AND[A<B]
    B.
;
1 2 3 BOOL6
True Ok
1 1 2 BOOL6
False Ok
1 2 2 BOOL6
False Ok

Example 7. Reduces to checking the inequality A<=B<=C.

: BOOL7 ( A B C -> )
    1+ OVER > \ A B C -> A B C+1>B
    ROT 1- ROT <\ A B C+1>B -> C+1>B A-1<B
    AND \ C>=B A<=B -> [C>=B]AND[A<=B]
    B.
;
1 2 3 BOOL7
True Ok
1 1 3 BOOL7
True Ok
1 2 2 BOOL7
True Ok
3 2 2 BOOL7
False Ok
1 2 1 BOOL7
False Ok

Example 8. Numbers A and B are both odd.

: BOOL8 ( A B -> )
    2 MOD SWAP 2 MOD AND \ remainder A/2 and remainder B/2
    B.
;
3 5 BOOL8
True Ok
2 5 BOOL8
False Ok
3 8 BOOL8
False Ok

Example 9. At least one of the two numbers is odd. It differs from the previous one by replacing the AND operator with OR.

: BOOL9 ( A B -> )
    2 MOD SWAP 2 MOD OR \ remainder A/2 or remainder B/2
    B.
;
3 5 BOOL9
True Ok
2 5 BOOL9
True Ok
3 8 BOOL9
True Ok
2 8 BOOL9
False Ok

The OR operator is false only when both operands are false, otherwise true, and the AND operator is true when both operands are true, otherwise false.

Example 10. Only one of two numbers is odd. The difference is replacing the OR operator with XOR. All these Boolean operators have their own truth tables and their own operating logic. It is assumed that you know all this or study it yourself.

: BOOL10 ( A B -> )
    2 MOD SWAP 2 MOD XOR \ remainder A/2 XOR remainder B/2
    B.
;
3 5 BOOL10
False Ok
2 5 BOOL10
True Ok
3 8 BOOL10
True Ok
2 8 BOOL10
False Ok

Example 11. Two numbers have the same parity. Now we replace the XOR operator with the equal sign “=”, that is, the remainders from division by two for both numbers must be the same - the same remainders - the same parity.

: BOOL11 ( A B -> )
    2 MOD SWAP 2 MOD = \ balance A/2 = balance B/2
    B.
;
3 5 BOOL11
True Ok
2 5 BOOL11
False Ok
3 8 BOOL11
False Ok
2 8 BOOL11
True Ok

Example 12. Check that each of the numbers A, B, C is positive.

: BOOL12 ( A B C -> )
    0 >\ A B C -> A B C>0
    ROT 0 > AND \ A B C>0 -> B [C>0]AND[A>0]
    SWAP 0 > AND \ B [C>0]AND[A>0] -> [C>0]AND[A>0]AND[B>0]
    B.
;
1 2 3 BOOL12
True Ok
-1 2 3 BOOL12
False Ok
1 -2 3 BOOL12
False Ok
1 2 -3 BOOL12
False Ok
0 2 3 BOOL12
False Ok
1 0 3 BOOL12
False Ok
1 2 0 BOOL12
False Ok

Example 13. Check that at least one of the numbers A, B, C is positive. We obtain from the previous one by replacing AND with OR.

: BOOL13 ( A B C -> )
    0 >\ A B C -> A B C>0
    ROT 0 > OR \ A B C>0 -> B [C>0]OR[A>0]
    SWAP 0 > OR \ B [C>0]OR[A>0] -> [C>0]OR[A>0]OR[B>0]
    B.
;
0 0 -1 BOOL13
False Ok
1 0 -3 BOOL13
True Ok

Example 14. Check that exactly one of the numbers A, B, C is positive. Replacing OR in the previous example with XOR does not solve the problem, since in the case of three ones “1 XOR 1 = 0”, and “0 XOR 1 =1”. Without further ado, you can foolishly introduce a counter at the top of the stack.

: BOOL14 ( A B C -> )
    0 > IF 1 ELSE 0 THEN \ A B Count
    ROT 0 > IF 1+ THEN \ B Count
    SWAP 0 > IF 1+ THEN \ Count
    1 = B. \ if counter = 1, then true, otherwise false
;

Code description:

The first line is the standard description of the Forth word
If C>0, then we leave 1 on the stack, otherwise 0. This will be our counter
If A>0, then we increase the counter by one, otherwise we do nothing
Similarly to the previous line we process B
In the fifth line we compare the counter with one, if equal then true, otherwise false
1 2 3 BOOL14
False Ok
1 0 0 BOOL14
True Ok
-1 0 10 BOOL14
True Ok

Example 15. Check that exactly two of the numbers A, B, C are positive. We get it by modifying the code of the previous example. Let's replace the check that the counter is equal to one by two.

: BOOL15 ( A B C -> )
    0 > IF 1 ELSE 0 THEN \ A B Count
    ROT 0 > IF 1+ THEN \ B Count
    SWAP 0 > IF 1+ THEN \ Count
    2 = B. \ if counter = 2, then true, otherwise false
;
1 2 0 BOOL15
True Ok
0 2 3 BOOL15
True Ok
1 0 2 BOOL15
True Ok
-1 -2 0 BOOL15
False Ok

Example 16. Check that a number is both even and two-digit. The parity check is trivial and already familiar, and double digit means that the number is greater than 9 and less than 100.

: BOOL16 ( A -> )
    DUP 9 >\ A -> A A>9
    OVER 100 < AND \ A A>9 -> A [A>9]AND[A<100]
    SWAP 2 MOD 0 = AND \ A [A>9]AND[A<100] -> [A>9]AND[A<100]AND{Remainder[A/2]=0}
    B.
;
50 BOOL16
True Ok
8BOOL16
False Ok
102 BOOL16
False Ok
51 BOOL16
False Ok

You can make the calculations lazy, when if one of the conditions is not met, the others are not checked. This is possible for the AND operation (logical multiplication), since “0 AND X = 0”, for any X, for any number of operands “0 AND X1 AND X2 ... AND XN = 0”.

: BOOL16 ( A -> )
    DUP 9 > IF
        DUP 100 <IF
            2 MOD 0 =
            B. EXIT
        THEN
    THEN
    DROP 0 B.
;

You can test it yourself using the same examples of the first version of the code, the results are identical. Due to lazy evaluation, the second code should run faster on average. Decide for yourself which option is preferable.

Example 17. Check that a number is both odd and three-digit. We get it by minor modifications to the code of the previous example. We change the check for “greater than 9” to 99 and “less than 100” to 1000. And replace the check for even parity with odd parity.

: BOOL17 ( A -> )
    DUP 99 >\ A -> A A>99
    OVER 1000 < AND \ A A>99 -> A [A>99]AND[A<1000]
    SWAP 2 MOD AND \ A [A>99]AND[A<1000] -> [A>99]AND[A<1000]AND{Remainder[A/2]=1}
    B.
;
999 BOOL17
True Ok
1000 BOOL17
False Ok
1001 BOOL17
False Ok
101 BOOL17
True Ok

Lazy option with conditional operator.

: BOOL17 ( A -> )
        DUP 99 > IF
            DUP 1000 <IF
                2 MOD
                B. EXIT
            THEN
        THEN
        DROP 0 B.
;

Example 18. Check that among three numbers there is at least one pair of equals. That is, you need to program the check A=B or B=C or A=C.

: BOOL18 ( A B C -> )
    2DUP = \ A B C -> A B C B=C
    2SWAP OVER = \ A B C B=C -> C B=C A B=A
    ROT OR \ C B=C A B=A -> C A [B=A]OR[B=C]
    ROT ROT = OR \ C A [B=A]OR[B=C] -> [B=A]OR[B=C]OR[C=A]
    B.
;
1 1 2 BOOL18
True Ok
1 2 2 BOOL18
True Ok
1 2 1 BOOL18
True Ok
1 2 3 BOOL18
False Ok
0 0 0 BOOL18
True Ok

If necessary, you can rewrite it yourself with a conditional operator.

Example 19. Differs from the previous one by replacing the equality check with equality with the opposite sign, that is, A=-B or B=-C or A=-C.

: BOOL19 ( A B C -> )
    2DUP NEGATE = \ A B C -> A B C B=-C
    2SWAP OVER NEGATE = \ A B C B=-C -> C B=-C A B=-A
    ROT OR \ C B=-C A B=-A -> C A [B=-A]OR[B=-C]
    ROT ROT NEGATE = OR \ C A [B=-A]OR[B=-C] -> [B=-A]OR[B=-C]OR[C=-A]
    B.
;

The new word NEGATE simply reverses the sign of the number.

1 2 3 BOOL19
False Ok
1 -1 0 BOOL19
True Ok
0 0 1 BOOL19
True Ok
0 1 -1 BOOL19
True Ok
2 2 -2 BOOL19
True Ok

Example 20. Check that for a given three-digit number all digits are different. We can reduce it to the previous examples. First, let's break the number into digits, then check the conditions A<>B and B<>C and A<>C.

: BOOL20 ( ABC -> )
    10 /MOD 10 /MOD \ ABC -> C B A
    2DUP = \ C B A -> C B A B=A
    2SWAP OVER = \ C B A B=A -> A B=A C B=C
    ROT OR \ A B=A C B=C -> A C [B=C]OR[B=A]
    ROT ROT = OR \ A C [B=C]OR[B=A] -> [B=C]OR[B=A]OR[A=C]
    0 = B.
;
123 BOOL20
True Ok
110 BOOL20
False Ok
131 BOOL20
False Ok
199 BOOL20
False Ok
100 BOOL20
False Ok

The first line is a standard Forth word declaration.
Secondly, we break the number into digits in the second line. The order was reversed, but in our case this does not matter.
Lines three through six are identical to the code in example 18. If at least one of the equalities is true (A=B or B=C or A=C), then the result will be true.
Then finally in line seven we check if the result is equal to zero, because only in this case will all the numbers be different, that is, A<>B and B<>C and A<>C.