Local Open Scope Z_scope.
Fixpoint repeat_op_nat{T: Type}(n: nat)(start: T)(op: T -> T): T := match n with
| O => start
| S m => op (repeat_op_nat m start op)
end.
Definition repeat_op{T: Type}(n: Z)(start: T)(op: T -> T): T := repeat_op_nat (Z.to_nat n) start op.
Definition times2(x: int): int :=
Int.and
(Int.xor (Int.shl x (Int.repr 1))
(if Int.eq (Int.and x (Int.repr 128)) Int.zero then Int.zero else Int.repr 27))
(Int.repr 255).
Definition pow2(e: Z): int := repeat_op e (Int.repr 1) times2.
Definition times3(x: int): int :=
Int.and
(Int.xor x (Int.xor (Int.shl x (Int.repr 1))
(if Int.eq (Int.and x (Int.repr 128)) Int.zero then Int.zero else Int.repr 27)))
(Int.repr 255).
Definition pow3(e: Z): int := repeat_op e (Int.repr 1) times3.
Fixpoint log3_nat(p: int)(n: nat): Z :=
if Int.eq p (pow3 (Z.of_nat n)) then Z.of_nat n
else match n with
| O => -1 (* illegal argument *)
| S m => log3_nat p m
end.
(* Note: For (log3 1), we have two possible return values: 0 and 255. We choose 255, because then both the domain and the codomain of log3 are 1..255. *)
Definition log3(p: int): Z := log3_nat p 255.
Definition mul(x y: int): int :=
if Int.eq x Int.zero then Int.zero else
if Int.eq y Int.zero then Int.zero else
pow3 (Int.unsigned (Int.mods (Int.repr (log3 x + log3 y)) (Int.repr 255))).

Lemma pow3_not0: forall i, pow3 i <> Int.zero.