Implementation of the logarithm of base 2

This file contains an algorithm to compute the integer logarithm (base 2) of a given positive integer. The algorithm we give first not only computes this logarithm, but returns also an information on its exactness.

Since we are interested in using Coq for low-levels programs, we choosed to realize the specification of log2 with a while loop using shift (multiplication by 2) and unshift (quotient by 2, and parity check). See the module shift.

About the realization ...

Remember the specification of the logarithm, given in log2_spec:
Definition log2_spec:=[n0:nat]
                      {l:nat & {n0=(two_power l)}+
                                 {(lt (two_power l) n0) /\
                                  (lt n0 (two_power (S l)))}}.
We want to prove the lemma:
log2_impl: (n0:nat)(lt O n0)->(log2_spec n0).

In order to do that, we used the imperative and while modules.

Our idea

we had in mind the following program, written in a free C-like syntax:
{ /* computation of the integer logarithm of n0 (base 2) */
  n := n0;
  p:= O;
  b := true; 
  while (n>O)  
  {  
    if (b)  
      { /* n0=2^p * n  */
         if(n is even) 
            { p:=p+1;
              n:=unshift(n);
              b:=true}
         else
             {p:=p+1;
              n:=unshift(n);
              b:=false}
      }
     else   /* 2^p * < n0 < 2^p(n+1) /\ n>0 */
      {
       p:=p+1;
       n:=unshift(n);
       b:=false}
    }
    return (p,b);
} 

The states

The preceding program uses 3 local variables: n,p:nat, and b:bool. It is straightforward to define a Set:
 Record state:Set :=
     mkstate {
        state_n:nat;
        state_p:nat;
        state_b:bool}.

The postcondition

A state (n,p,b) satisfies the postcondition ok if So, it is easy to define:
  Inductive ok:state->Prop:=
      oktrue: (p:nat)n0=(two_power p)->
                      (ok (mkstate one p true))
    | okfalse: (p:nat)(lt (two_power p) n0)->
                      (lt n0 (two_power (S p))) ->
                      (ok (mkstate one p false)).

The precondition

We express by a proposition the initial values of the local variables (n,p,b):
Local init:=[s:state](state_n s)=n0 /\ 
                     (state_p s)=O /\
                     (state_b s)=true.

loop parameters

When to stop looping ?

The while loop above stops when the value of the local variable n reach the value 1:
 Inductive final:state->Prop :=
    mkfinal:(p:nat)(b:bool)(final (mkstate one p b)).
 

loop invariant

In a state (n,p,b), the boolean b expresses the exactness of the logarithm to compute:
 Inductive invar:state->Prop :=
     exact:(n,p:nat)
             n0=(mult (two_power p) n)->
             (lt O n)->
             (invar (mkstate n p true))
   |inexact:(n,p:nat)
             (lt (mult (two_power p)n) n0)->
             (lt n0 (mult (two_power p) (S n)))->
             (lt O n)->
             (invar (mkstate n p false)).

The termination order

We use the term
ltof=[A:Set][f:A->nat][a,b:A](lt (f a) (f b))
defined in the module ARITH/Wf_nat of the distribution. The theorem well_founded_ltof ensures us that the order
 stat_order:=(ltof state state_n)
is well founded.

The test of the loop

The test part of the while loop is realized by:
 Realizer [s:state]
               <bool>if (le_lt_eq_dec (S O) (state_n s))
                     then false
                     else true

The loop body

The body of the while loop is realized by:
 Realizer [s:state
    <state>if (state_b s) 
    then  
     <state>let (m:nat;b':bool)=(Unshift (state_n s))
               in
               <state> if b' 
               then (mkstate m (S (state_p s)) true)
               else (mkstate m (S (state_p s)) false)
     else
      <state>let (m:nat;b:bool)=(Unshift (state_n s))
         in   (mkstate m (S (state_p s)) false)

The end of the block

We have to prove (see imperative.v) that the postcondition on states realizes the specification log2_spec. This is done with a
Realizer [a:state]
          <(prod nat bool)>Case (state_b a) of
             ((state_p a),true)
             ((state_p a),false)
             end

The rest of the proof

The rest of the proof solves the informative goals with Program, and a lot of logical goals as usual.

Remarks

1

The piece of code return (p,b); of the C-like program is tranlated into:
      <(prod nat bool)>Case (state_b a) of
             ((state_p a),true)
             ((state_p a),false)
             end
and not into:
((state_p a),(state_b a))
We intented to do this way, but we failed.

2

Please notice the various inversion lemmas, obtained with the Inversion_clear command:
invar_inv1:(n,p:nat)(invar (mkstate n p true))->
                    n0=(mult (two_power p) n).

invar_inv2: (n,p:nat)(invar (mkstate n p false))->
                     (lt (mult (two_power p)n) n0)  /\
                     (lt n0 (mult (two_power p) (S n))).



etc ...
Click here to look at the source.

Apologies

We had no time to build a user-friendly "imperative" package, dealing with all sorts of loops, and constrol structures, breaks, and so on, with macros allowing to write directly commands of the form
Realizer {nat x y; while (zerop x) { ... }; return y;} 
We hope this will be done in few weeks.

A demo in Caml Light

Load the file Demo.ml, and open it.

You will notice that the extraction algorithm generated a function of type
int -> (int, sumbool) sigS and not of int -> int*bool. In consequence, the result has the form existS(p,left) for an exact logarithm, and existS(p,right) for an inexact (default) logarithm.

 
#load "Demo.ml";;
#open "Demo";;

#log2_impl;;
- : int -> (int, sumbool) sigS = 

#log2_impl 1023;;
- : (int, sumbool) sigS = existS (9, right)

#log2_impl 1024;;
- : (int, sumbool) sigS = existS (10, left)

#log2_impl 1025;;
- : (int, sumbool) sigS = existS (10, right)