Section Powers_of_2.
(** * Powers of 2 *)
(** For the powers of two, that will be widely used, a more direct calculus is possible. [shift n m] computes [2^n * m], i.e. [m] shifted by [n] positions *)
Definition shift_nat (n:nat) (z:positive) := nat_rect _ z (fun _ => xO) n.
Definition shift_pos (n z:positive) := Pos.iter xO z n.
Definition shift (n:Z) (z:positive) :=
match n with
| Z0 => z
| Zpos p => Pos.iter xO z p
| Zneg p => z
end.
Definition two_power_nat (n:nat) := Zpos (shift_nat n 1).
Definition two_power_pos (x:positive) := Zpos (shift_pos x 1).
Definition two_p (x:Z) :=
match x with
| Z0 => 1
| Zpos y => two_power_pos y
| Zneg y => 0
end.
(** Equivalence with notions defined in BinInt *)

(* Specifications: *)
Lemma shift_nat_equiv n p : shift_nat n p = Pos.shiftl_nat p n.
Lemma two_power_nat_equiv n : two_power_nat n = 2 ^ (Z.of_nat n).
Lemma two_power_pos_equiv p : two_power_pos p = 2 ^ Zpos p.
Lemma two_p_equiv x : two_p x = 2 ^ x.
(** Properties of these old versions of powers of two *)
Lemma two_power_nat_S n : two_power_nat (S n) = 2 * two_power_nat n.
Theorem shift_nat_correct n x :
Zpos (shift_nat n x) = Zpower_nat 2 n * Zpos x.
Theorem two_power_nat_correct n : two_power_nat n = Zpower_nat 2 n.
Lemma shift_pos_nat p x : shift_pos p x = shift_nat (Pos.to_nat p) x.
Lemma two_power_pos_nat p : two_power_pos p = two_power_nat (Pos.to_nat p).
Theorem shift_pos_correct p x :
Zpos (shift_pos p x) = Z.pow_pos 2 p * Zpos x.
Theorem two_power_pos_correct x : two_power_pos x = Z.pow_pos 2 x.
Theorem two_power_pos_is_exp x y :
two_power_pos (x + y) = two_power_pos x * two_power_pos y.
Lemma two_p_correct x : two_p x = 2^x.
Proof (two_p_equiv x).
Theorem two_p_is_exp x y :
0 <= x -> 0 <= y -> two_p (x + y) = two_p x * two_p y.
Lemma two_p_gt_ZERO x : 0 <= x -> two_p x > 0.
Lemma two_p_S x : 0 <= x -> two_p (Z.succ x) = 2 * two_p x.
Lemma two_p_pred x : 0 <= x -> two_p (Z.pred x) < two_p x.
End Powers_of_2.