code: plan9front

ref: 5622b0bbd878dbc34045cc6fd37cffa64461eabe
dir: /sys/man/1/forp/

View raw version
.TH FORP 1
.SH NAME
forp \- formula prover
.SH SYNOPSIS
.B forp
[
.B -m
]
[
.I file
]
.SH DESCRIPTION
.I Forp
is a tool for proving formulae involving finite-precision arithmetic.
Given a formula it will attempt to find a counterexample; if it can't find one the formula has been proven correct.
.PP
.I Forp
is invoked on an input file with the syntax as defined below.
If no input file is provided, standard input is used instead.
The
.B -m
flag instructs
.I forp
to produce a table of all counterexamples rather than report just one.
Note that counterexamples may report bits as
.BR ? ,
meaning that either value will lead to a counterexample.
.PP
The input file consists of statements terminated by semicolons and comments using C syntax (using
.B //
or
.B "/* */"
syntax).
Valid statements are
.IP
Variable definitions, roughly:
.I type
.I var
.B ;
.br
Expressions (including assignments):
.I expr
.B ;
.br
Assertions:
.B obviously
.I expr
.B ;
.br
Assumptions:
.B assume
.I expr
.B ;
.PP
Assertions are formulae to be proved.
If multiple assertions are given, they are effectively "and"-ed together.
Each input file must have at least one assertion to be valid.
Assumptions are formulae that are assumed, i.e. counterexamples that would violate assumptions are never considered.
Exercise care with them, as contradictory assumptions will lead to any formula being true (the logician's principle of explosion).
.PP
Variables can be defined with C notation, but the only types supported are
.B bit
and 1D arrays of
.B bit
(corresponding to machine integers of the specified size).
Signed integers are indicated with the keyword
.BR signed .
Like
.B int
in C, the
.B bit
keyword can be omitted in the presence of
.BR signed .
For example,
.PP
.EX
	bit a, b[4], c[8];
	signed bit d[3];
	signed e[16];
.EE
.PP
is a set of valid declarations.
.PP
Unlike a programming language, it is perfectly legitimate to use a variable before it is assigned value; this means the variable is an "input" variable.
.I Forp
tries to find assignments for all input variables that render the assertions invalid.
.PP
Expressions can be formed just as in C, however when used in an expression, all variables are automatically promoted to an infinite size signed type.
The valid operators are listed below, in decreasing precedence. Note that logical operations treat all non-zero values as 1, whereas bitwise operators operate on all bits independently.
.TP "\w'\fL<\fR, \fL<=\fR, \fL>\fR, \fL>=\fR  'u"
\fL[]\fR
Array indexing. The syntax is \fIvar\fL[\fIa\fL:\fIb\fR], with \fIa\fR denoting the MSB and \fIb\fR denoting the LSB.
Omitting \fL:\fIb\fR addresses a single bit.
The result is always treated as unsigned.
.TP
\fL!\fR, \fL~\fR, \fL+\fR, \fL-\fR
(Unary operators) Logical and bitwise "not", unary plus (no-op), arithmetic negation. Because of promotion, \fL~\fR and \fL-\fR operate beyond the width of variables.
.TP
\fL*\fR, \fL/\fR, \fL%\fR
Multiplication, division, modulo.
Division and modulo add an assumption that the divisor is non-zero.
.TP
\fL+\fR, \fL-\fR
Addition, subtraction.
.TP
\fL<<\fR, \fL>>\fR
Left shift, arithmetic right shift. Because of promotion, this is effectively a logical right shift on unsigned variables.
.TP
\fL<\fR, \fL<=\fR, \fL>\fR, \fL>=\fR
Less than, less than or equal to, greater than, greater than or equal to.
.TP
\fL==\fR, \fL!=\fR
Equal to, not equal to.
.TP
\fL&\fR
Bitwise "and".
.TP
\fL^\fR
Bitwise "xor".
.TP
\fL|\fR
Bitwise "or".
.TP
\fL&&\fR
Logical "and"
.TP
\fL||\fR
Logical "or".
.TP
\fL<=>\fR, \fL=>\fR
Logical equivalence and logical implication (equivalent to
.B "(a != 0) == (b != 0)"
and
.BR "!a || b" ,
respectively).
.TP
\fL?:\fR
Ternary operator (\fLa?b:c\fR equals \fLb\fR if \fLa\fR is true and \fLc\fR otherwise).
.TP
\fL=\fR
Assignment.
.PP
One subtle point concerning assignments is that they forcibly override any previous values, i.e. expressions use the value of the latest assignments preceding them.
Note that the values reported as the counterexample are always the values given by the last assignment.
.SH EXAMPLES
We know that, mathematically, \fIa\fR + \fIb\fR ≥ \fIa\fR if \fIb\fR ≥ 0 (which is always true for an unsigned number).
We can ask
.I forp
to prove this using
.PP
.EX
	bit a[32], b[32];
	obviously a + b >= a;
.EE
.PP
.I Forp
will report "Proved", since it cannot find a counterexample for which this is not true.
In C, on the other hand, we know that this is not necessarily true.
The reason is that, depending on the types involved, results are truncated.
We can emulate this by writing
.PP
.EX
	bit a[32], b[32], c[32];
	c = a + b;
	obviously c >= a;
.EE
.PP
Given this,
.I forp
will now report it as incorrect by providing a counterexample, for example
.PP
.EX
	a = 10000000000000000000000000000000
	b = 10000000000000000000000000000000
	c = 00000000000000000000000000000000
.EE
.PP
Can we use \fIc\fR < \fIa\fR to check for overflow?
We can ask
.I forp
to confirm this using
.PP
.EX
	bit a[32], b[32], c[32];
	c = a + b;
	obviously c < a <=> c != a+b;
.EE
.PP
Here the statement to be proved is "\fIc\fR is less than \fIa\fR if and only if \fIc\fR does not equal the mathematical sum \fIa\fR + \fIb\fR (i.e. overflow has occurred)".
.SH SOURCE
.B /sys/src/cmd/forp
.SH "SEE ALSO"
.IR spin (1)
.SH BUGS
Any proof is only as good as the assumptions made, in particular care has to be taken with respect to truncation of intermediate results.
.PP
Array indices must be constants.
.PP
Left shifting can produce a huge number of intermediate bits.
.I Forp
will try to identify the minimum needed number but it may be a good idea to help it by assigning the result of a left shift to a variable.
.SH HISTORY
.I Forp
first appeared in 9front in March, 2018.