The class called bool3 implements a three-valued boolean: false,
uncertain and true. A variable of type bool3 has a default initial
value of uncertain. To avoid problems with reserved words the three
truth values are actually called:
bool3::DefinitelyFalse
bool3::uncertain
bool3::DefinitelyTrue
There are three functions for testing the value of a bool3 expression:
(note that these functions return a C++ bool value)
IsDefinitelyFalse(expr) true iff expr is bool3::DefinitelyFalse
IsUncertain(expr) true iff expr is bool3::uncertain
IsDefinitelyTrue(expr) true iff expr is bool3::DefinitelyTrue
These functions are the only way of converting a bool3 to a standard
C++ bool value -- there is no automatic conversion from a bool3 value to
a standard C++ bool.
To convert a normal bool to a bool3 value, you must call the ctor
explicitly:
bool3(true) is the same as bool3::DefinitelyTrue
bool3(false) is the same as bool3::DefinitelyFalse
An ideal has a mutable bool3 field called IhaveMonomialGensValue;
the function IsMonomial(vector<RingElem>) returns a bool.
This is how IhaveMonomialGensValue is set and returned as a bool:
bool SparsePolyRingBase::IdealImpl::IhaveMonomialGens() const
{
if (IsUncertain(IhaveMonomialGensValue))
IhaveMonomialGensValue = IsMonomial(myGensValue);
return IsDefinitelyTrue(IhaveMonomialGensValue);
}
Conversely, a variable of type bool3 may be assigned a C++ bool value
in which case true maps to bool3::DefinitelyTrue and false to
bool3::DefinitelyFalse. It is also possible to assign one of the three
bool3 truth values to a variable of type bool3.
bool3 values may be printed in the usual way. The printed forms are:
bool3::DefinitelyFalse
bool3::uncertain
bool3::DefinitelyTrue
There are no arithmetic operations on bool3 values.
Note that bool3 is not the same as BOOST's tribool, though the two are
fairly similar. The principal difference is that bool3 does not have
automatic conversion to bool because we feel that explicit use of the
three testing functions leads to more comprehensible code -- just have a
look at the examples in BOOST's documentation!
The implementation is very simple. The only point to watch is that the
order of the constants in the enum TruthValues was chosen to allow a
simple implementation of the function cmp (which is currently removed
from bool3.H, see Bugs and Shortcomings below). If you change the
order, you will have to change the definition of cmp.
All functions/operations are implemented inline except for I/O. I have avoided const-ref arguments since it is surely cheaper simply to copy the enum value.
I made the bool3 ctor from ``bool explicit because otherwise it
makes calls like OMOut << n ambiguous where n is of integral
type. The problem stems from the definition in OpenMath of operator<<
which expects an arg of type MachineInt, and the compiler will
convert as readily from an integral type to MachineInt or bool3
(I think this is a C++ unfeature, but I have to live with it).
I do feel quite uneasy about disagreeing with BOOST's tribool design, but
their example of a three-way if statement looks to me to be a recipe for
programmer grief -- one has to suppress the law of the excluded middle to
read their code without finding it odd and surprising. As you can see
below, I used to have a conversion from bool3 to bool which threw an
error if the value to be converted was bool3::uncertain. I do feel that
this would be a better choice than what BOOST does because it would force
the programmer to deal first with the bool3::uncertain case, and the
remaining cases would then have normal boolean behaviour.
Boolean arithmetic operations are not defined since we have not needed them so far. It would be a simple matter, but I prefer to wait until there is a real need for such operations.
The printed forms of bool3 values are rather ugly. Perhaps remove
the bool3:: prefix?
Is the cmp function ever going to be useful???
There was also a function cmp for comparing two bool3 values:
cmp(b1, b2) returns an int <0, =0 or >0 according as b1 <,=,> b2
(assuming this ordering: DefinitelyFalse < uncertain < DefinitelyTrue)
> friend int cmp(bool3 lhs, bool3 rhs); // must be friend function
> inline int cmp(bool3 lhs, bool3 rhs)
> {
> return lhs.myTruthValue - rhs.myTruthValue;
> }
This function used to exist, but now I think it is clearer to use
IsDefinitelyTrue with a comment explaining why its argument cannot have
the value bool3::uncertain.
> inline bool AsBool(bool3 flag)
> {
> if (IsUncertain(flag)) error(CoCoAError(ERR::BadBool3, "AsBool(bool3)"));
> return IsDefinitelyTrue(flag);
> }
The conversion could be made automatically just by adding operator bool to the class.