\myheading{Proving several floor()/ceiling() properties using Z3}

I've found couple problems in [James L. Hein -- Discrete Structures, Logic, and Computability].
First is:

\begin{lstlisting}
Find numbers x and y such that floor(x + y) != floor(x) + floor(y) and ceiling(x + y) != ceiling(x) + ceiling(y).
\end{lstlisting}

I can emulate real numbers using fixed point arithmetic over bitvectors.
In a 16-bit variable, high 8-bit part will be integer part and low 8-bit part is fractional part.
This is also called Q8.8\footnote{\url{https://en.wikipedia.org/wiki/Fixed-point_arithmetic}}.

floor() function is simple --- just zero fractional part (low 8 bits).
ceiling() function --- if \textit{something} is present in fractional part (low 8 bits), increment high 8 bits.

\begin{lstlisting}[style=custompy]
from z3 import *

# Find numbers x and y such that floor(x + y) != floor(x) + floor(y) and 
# ceiling(x + y) != ceiling(x) + ceiling(y).

def floor(x):
    return x&0xff00

def ceiling(x):
    return If((x&0xff)!=0, (x&0xff00)+0x100, x)

s=Solver()

x,y = BitVecs('x y', 16)

s.add(floor(x+y) != floor(x) + floor(y))
s.add(ceiling(x+y) != ceiling(x) + ceiling(y))

print s.check()
m=s.model()

print "x=0x%04x or %f" % (m[x].as_long(), float(m[x].as_long())/0x100)
print "y=0x%04x or %f" % (m[y].as_long(), float(m[y].as_long())/0x100)
\end{lstlisting}

\begin{lstlisting}[caption=Output]
sat
x=0x03e0 or 3.875000
y=0x3f20 or 63.125000
\end{lstlisting}

Let's check this in Wolfram Mathematica:

\begin{lstlisting}[caption=Output of Wolfram Mathematica]
In[]:= x = 3.875;

In[]:= y = 63.125;

In[]:= {Floor[x + y], Floor[x] + Floor[y]}
Out[]= {67, 66}

In[]:= {Ceiling[x + y], Ceiling[x] + Ceiling[y]}
Out[]= {67, 68}
\end{lstlisting}

The second problem:

\begin{lstlisting}[style=custompy]
from z3 import *

# Find the values of x that satisfy each of the following equations.
# a. ceiling((x - 1)/2) = floor(x/2)
# b. ceiling((x - 1)/3) = floor(x/3)

def floor(x):
    return x&0xff00

def ceiling(x):
    return If((x&0xff)!=0, (x&0xff00)+0x100, x)

s=Solver()

x = BitVec('x', 16)

# 1 in Q8.8 is just 0x0100 or 1<<8
s.add(ceiling((x-0x100)/2) == floor(x/2))
#s.add(ceiling((x-0x100)/3) == floor(x/3))

print s.check()
m=s.model()

print "x=0x%04x or %f" % (m[x].as_long(), float(m[x].as_long())/0x100)
\end{lstlisting}

\begin{lstlisting}[caption=Output for (a)]
sat
x=0x4000 or 64.000000
\end{lstlisting}

\begin{lstlisting}[caption=Output for (b)]
sat
x=0x0000 or 0.000000
\end{lstlisting}

The next problem (unsat for all).

\ac{IFF} is just "==" in Z3, so to find counterexample (if any), we just use "!=".

\begin{lstlisting}[style=custompy]
from z3 import *

"""
Prove each of the following statements about inequalities with the floor and ceiling,
where x is a real number and n is an integer.
a. floor(x) < n iff x < n.
b. n < ceiling(x) iff n < x.
c. n <= floor(x) iff n <= x.
d. floor(x) <= n iff x <= n.
"""

def floor(x):
    return x&0xff00

def ceiling(x):
    return If((x&0xff)!=0, (x&0xff00)+0x100, x)

s=Solver()

x = BitVec('x', 16)
n = BitVec('n', 16)

s.add((n&0xff)==0) # n is always integer, it has no fraction

# prevent integer overflow, x and n must be positive
s.add(x<0x8000)
s.add(n<0x8000)

s.add((floor(x) < n) != (x < n)) # a
#s.add((n < ceiling(x)) != (n < x)) # b
#s.add((n <= floor(x)) != (n <= x)) # c
#s.add((floor(x) <= n) != (x <= n)) # d

# must be unsat for a/b/c/d
print s.check()
\end{lstlisting}

Problem 4, also unsat for all:

\begin{lstlisting}[style=custompy]
from z3 import *

"""
Prove each statement, where n is an integer.
a. ceiling(n/2) = floor((n + 1)/2)
b. floor(n/2) = ceiling((n - 1)/2)
"""

def floor(x):
    return x&0xff00

def ceiling(x):
    return If((x&0xff)!=0, (x&0xff00)+0x100, x)

s=Solver()

n = BitVec('n', 16)

s.add((n&0xff)==0) # n is always integer, it has no fraction

# prevent integer overflow, n is always positive
s.add(n<0x8000)

s.add(ceiling(n/2) != (floor((n+0x100)/2))) # a
#s.add(floor(n/2) != (ceiling((n-0x100)/2))) # b

# must be unsat for a/b
print s.check()
\end{lstlisting}
