Given an integer x, pow(x, n), the nth power of x, can be defined
inductively as follows:
A direct implementation of this definition is given as follows:
which is of time-complexity O(n) (assuming multiplication is O(1)). A
more efficient implmentation can be given as follows:
which makes use of the property that
pow(x, n) equals
pow(x*x, n/2) if n is even or x *
pow(x*x, n/2) if n is
odd. This is referred to as fast exponentiation. Note that
ifastpow is of time-complexity O(log(n)).
Clearly, what is done above is not restricted to exponentiation on
integers. As long as the underlying multiplication is associative, fast
exponentiation can be employed to compute powers of any given element. In
particular, powers of square matrices can be computed in this way. I now
present as follows a verified generic implementation of fast exponentiation.
Handling generic data properly in a verified implementation often requires some
finesse with the type system of ATS. Let us first introduce an abstract type
constructor E as follows:
This is often referred to as
stamping. For each type T and stamp
x,
E(T, x) is just T as far as data representation is concerned.
The stamps are imaginary and they are solely used for the purpose of
specification. We next introduce an abstract prop-type
MUL and
a function template
mul_elt_elt:
Please do not confuse
MUL with the one of the same name
that is declared in
prelude/SATS/arith.sats. To state that the encoded multiplication
is associative, we can introduce the following proof function:
The keyword
praxi indicates that
mul_assoc is treated as
a form of axiom, which is not expected to be implemented.
The abstract power function can be readily specified in terms of the
abstract prop-type MUL:
As can be expected, generic fast exponentiation is given the following
interface:
With the preparation done above, a straightforward implementation of
fastpow_elt_int can now be presented as follows:
Note that this implementation of
fastpow_elt_int is not
tail-recursive. The function template
mulunit, which is called to
produce a unit for the underlying multiplication, is assigned the following
interface:
The proof function
lemma simply establishes that
pow(x,
2*n)=
pow(x*x, n) for each natural number n. I have made an
implementation of
lemma available on-line but I suggest that the
interested reader give it a try first before taking a look. Note that the
following axioms are needed to implement
lemma:
Another interesting (and possibly a bit challenging) exercise is to
implement
fastpow_elt_int in a tail-recursive fashion.
Please find on-line the two files
fastexp.sats and
fastexp.dats that contain the
entirety of the above presented code.
Now we have implemented fastpow_elt_int. How can we use it? Please
find on-line an example in
which fastpow_elt_int is called to implement fast exponentiation
on a 2-by-2 matrix so that Fibonacci numbers can be computed in a highly
efficient manner.