theory Uint64 imports
Uint_Common
Code_Target_Word begin
text‹
PolyML (in version 5.7) provides a Word64 structure only when run in 64-bit mode.
Therefore, we by default provide an implementation of 64-bit words using \verb$IntInf.int$ and
masking. The code target \texttt{SML\_word} replaces this implementation and maps the operations
directly to the \verb$Word64$ structure provided by the Standard ML implementations.
The \verb$Eval$ target used by @{command value} and @{method eval} dynamically tests at
runtime for the version of PolyML and uses PolyML's Word64 structure if it detects a 64-bit
version which does not suffer from a division bug found in PolyML 5.6. ›
section‹Type definition and primitive operations›
typedef uint64 = ‹UNIV :: 64 word set› ..
global_interpretation uint64: word_type_copy Abs_uint64 Rep_uint64 using type_definition_uint64 by (rule word_type_copy.intro)
setup_lifting type_definition_uint64
declare uint64.of_word_of [code abstype]
declare Quotient_uint64 [transfer_rule]
instantiation uint64 :: ‹{comm_ring_1, semiring_modulo, equal, linorder, order_bot, order_top}› begin
uint64: word_type_copy_ring Abs_uint64 Rep_uint64
by standard (fact zero_uint64.rep_eq one_uint64.rep_eq
plus_uint64.rep_eq uminus_uint64.rep_eq minus_uint64.rep_eq
times_uint64.rep_eq divide_uint64.rep_eq modulo_uint64.rep_eq
equal_uint64.rep_eq less_eq_uint64.rep_eq less_uint64.rep_eq
bot_uint64.rep_eq top_uint64.rep_eq)+
proof -
show ‹OFCLASS(uint64, comm_ring_1_class)›
by (rule uint64.of_class_comm_ring_1)
show ‹OFCLASS(uint64, semiring_modulo_class)›
by (fact uint64.of_class_semiring_modulo)
show ‹OFCLASS(uint64, equal_class)›
by (fact uint64.of_class_equal)
show ‹OFCLASS(uint64, linorder_class)›
by (fact uint64.of_class_linorder)
show ‹OFCLASS(uint64, order_bot_class)›
by (fact uint64.of_class_order_bot)
show ‹OFCLASS(uint64, order_top_class)›
by (fact uint64.of_class_order_top)
uint64 :: ‹{interval_bot, interval_top}›
by (fact uint64.of_class_interval_bot uint64.of_class_interval_top)+
uint64 :: ring_bit_operations
bit_uint64 :: ‹uint64 ==> nat ==> bool› is bit .
not_uint64 :: ‹uint64 ==> uint64› is ‹Bit_Operations.not› .
and_uint64 :: ‹uint64 ==> uint64 ==> uint64› is ‹Bit_Operations.and› .
or_uint64 :: ‹uint64 ==> uint64 ==> uint64› is ‹Bit_Operations.or› .
xor_uint64 :: ‹uint64 ==> uint64 ==> uint64› is ‹Bit_Operations.xor› .
mask_uint64 :: ‹nat ==> uint64› is mask .
push_bit_uint64 :: ‹nat ==> uint64 ==> uint64› is push_bit .
drop_bit_uint64 :: ‹nat ==> uint64 ==> uint64› is drop_bit .
signed_drop_bit_uint64 :: ‹nat ==> uint64 ==> uint64› is signed_drop_bit .
take_bit_uint64 :: ‹nat ==> uint64 ==> uint64› is take_bit .
set_bit_uint64 :: ‹nat ==> uint64 ==> uint64› is Bit_Operations.set_bit .
unset_bit_uint64 :: ‹nat ==> uint64 ==> uint64› is unset_bit .
flip_bit_uint64 :: ‹nat ==> uint64 ==> uint64› is flip_bit .
using uint64.of_class_bit_comprehension
by simp_all standard
‹Code setup›
‹ For SML, we generate an implementation of unsigned 64-bit words using \verb$IntInf.int$.
If @{ML "LargeWord.wordSize > 63"} of the Isabelle/ML runtime environment holds, then we assume
that there is also a ‹Word64› structure available and accordingly replace the implementation
for the target \verb$Eval$. ›
code_printing code_module "Uint64" \<rightharpoonup> (SML) \<open>(* Test that words can handle numbers between 0 and 63 *)
val _ = if6 <= Word.wordSize then () else raise (Fail ("wordSize less than 6"));
structure Uint64 : sig
eqtype uint64;
val zero : uint64;
val one : uint64;
val fromInt : IntInf.int -> uint64;
val toInt : uint64 -> IntInf.int;
val toLarge : uint64 -> LargeWord.word;
val fromLarge : LargeWord.word -> uint64
val plus : uint64 -> uint64 -> uint64;
val minus : uint64 -> uint64 -> uint64;
val times : uint64 -> uint64 -> uint64;
val divide : uint64 -> uint64 -> uint64;
val modulus : uint64 -> uint64 -> uint64;
val negate : uint64 -> uint64;
val less_eq : uint64 -> uint64 -> bool;
val less : uint64 -> uint64 -> bool;
val notb : uint64 -> uint64;
val andb : uint64 -> uint64 -> uint64;
val orb : uint64 -> uint64 -> uint64;
val xorb : uint64 -> uint64 -> uint64;
val shiftl : uint64 -> IntInf.int -> uint64;
val shiftr : uint64 -> IntInf.int -> uint64;
val shiftr_signed : uint64 -> IntInf.int -> uint64;
val test_bit : uint64 -> IntInf.int -> bool; end = struct
type uint64 = IntInf.int;
val mask = 0xFFFFFFFFFFFFFFFF : IntInf.int;
val zero = 0 : IntInf.int;
val one = 1 : IntInf.int;
fun fromInt x = IntInf.andb(x, mask);
fun toInt x = x
fun toLarge x = LargeWord.fromLargeInt (IntInf.toLarge x);
fun fromLarge x = IntInf.fromLarge (LargeWord.toLargeInt x);
fun plus x y = IntInf.andb(IntInf.+(x, y), mask);
fun minus x y = IntInf.andb(IntInf.-(x, y), mask);
fun negate x = IntInf.andb(IntInf.~(x), mask);
fun times x y = IntInf.andb(IntInf.*(x, y), mask);
fun divide x y = IntInf.div(x, y);
fun modulus x y = IntInf.mod(x, y);
fun less_eq x y = IntInf.<=(x, y);
fun less x y = IntInf.<(x, y);
fun notb x = IntInf.andb(IntInf.notb(x), mask);
fun orb x y = IntInf.orb(x, y);
fun andb x y = IntInf.andb(x, y);
fun xorb x y = IntInf.xorb(x, y);
val maxWord = IntInf.pow (2, Word.wordSize);
fun shiftl x n = if n < maxWord then IntInf.andb(IntInf.<< (x, Word.fromLargeInt (IntInf.toLarge n)), mask)
else 0;
fun shiftr x n = if n < maxWord then IntInf.~>> (x, Word.fromLargeInt (IntInf.toLarge n))
else 0;
val msb_mask = 0x8000000000000000 : IntInf.int;
fun shiftr_signed x i = if IntInf.andb(x, msb_mask) = 0then shiftr x i
else if i >= 64then0xFFFFFFFFFFFFFFFF
else let
val x' = shiftr x i
val m' = IntInf.andb(IntInf.<<(mask, Word.max(0w64 - Word.fromLargeInt (IntInf.toLarge i), 0w0)), mask) in IntInf.orb(x', m') end;
fun test_bit x n = if n < maxWord then IntInf.andb (x, IntInf.<< (1, Word.fromLargeInt (IntInf.toLarge n))) <> 0
else false;
end › code_reserved (SML) Uint64
setup‹ let valpolyml64=LargeWord.wordSize>63; (* PolyML 5.6 has bugs in its Word64 implementation. We test for one such bug and refrain fromusingWord64inthatcase.Testingisdonewithdynamiccodeevaluationsuchthat thecompilerdoesnotchokeontheWord64structure,whichneednotbepresentina32bit
environment. *)
val error_msg = "Buggy Word64 structure";
val test_code = "val _ = if Word64.div (0w18446744073709551611 : Word64.word, 0w3) = 0w6148914691236517203 then ()\n" ^ "else raise (Fail \"" ^ error_msg ^ "\");";
val f = Exn.result (fn () => ML_Compiler.eval ML_Compiler.flags Position.none (ML_Lex.tokenize test_code))
val use_Word64 = polyml64 andalso
(case f () of
Exn.Res _ => true
| Exn.Exn (e as ERROR m) => if String.isSuffix error_msg m then false else Exn.reraise e
| Exn.Exn e => Exn.reraise e)
;
val newline = "\n";
val content = "structure Uint64 : sig" ^ newline ^ " eqtype uint64;" ^ newline ^ " val zero : uint64;" ^ newline ^ " val one : uint64;" ^ newline ^ " val fromInt : IntInf.int -> uint64;" ^ newline ^ " val toInt : uint64 -> IntInf.int;" ^ newline ^ " val toLarge : uint64 -> LargeWord.word;" ^ newline ^ " val fromLarge : LargeWord.word -> uint64" ^ newline ^ " val plus : uint64 -> uint64 -> uint64;" ^ newline ^ " val minus : uint64 -> uint64 -> uint64;" ^ newline ^ " val times : uint64 -> uint64 -> uint64;" ^ newline ^ " val divide : uint64 -> uint64 -> uint64;" ^ newline ^ " val modulus : uint64 -> uint64 -> uint64;" ^ newline ^ " val negate : uint64 -> uint64;" ^ newline ^ " val less_eq : uint64 -> uint64 -> bool;" ^ newline ^ " val less : uint64 -> uint64 -> bool;" ^ newline ^ " val notb : uint64 -> uint64;" ^ newline ^ " val andb : uint64 -> uint64 -> uint64;" ^ newline ^ " val orb : uint64 -> uint64 -> uint64;" ^ newline ^ " val xorb : uint64 -> uint64 -> uint64;" ^ newline ^ " val shiftl : uint64 -> IntInf.int -> uint64;" ^ newline ^ " val shiftr : uint64 -> IntInf.int -> uint64;" ^ newline ^ " val shiftr_signed : uint64 -> IntInf.int -> uint64;" ^ newline ^ " val test_bit : uint64 -> IntInf.int -> bool;" ^ newline ^ "end = struct" ^ newline ^ "" ^ newline ^ "type uint64 = Word64.word;" ^ newline ^ "" ^ newline ^ "val zero = (0wx0 : uint64);" ^ newline ^ "" ^ newline ^ "val one = (0wx1 : uint64);" ^ newline ^ "" ^ newline ^ "fun fromInt x = Word64.fromLargeInt (IntInf.toLarge x);" ^ newline ^ "" ^ newline ^ "fun toInt x = IntInf.fromLarge (Word64.toLargeInt x);" ^ newline ^ "" ^ newline ^ "fun fromLarge x = Word64.fromLarge x;" ^ newline ^ "" ^ newline ^ "fun toLarge x = Word64.toLarge x;" ^ newline ^ "" ^ newline ^ "fun plus x y = Word64.+(x, y);" ^ newline ^ "" ^ newline ^ "fun minus x y = Word64.-(x, y);" ^ newline ^ "" ^ newline ^ "fun negate x = Word64.~(x);" ^ newline ^ "" ^ newline ^ "fun times x y = Word64.*(x, y);" ^ newline ^ "" ^ newline ^ "fun divide x y = Word64.div(x, y);" ^ newline ^ "" ^ newline ^ "fun modulus x y = Word64.mod(x, y);" ^ newline ^ "" ^ newline ^ "fun less_eq x y = Word64.<=(x, y);" ^ newline ^ "" ^ newline ^ "fun less x y = Word64.<(x, y);" ^ newline ^ "" ^ newline ^ "fun shiftl x n =" ^ newline ^ " Word64.<< (x, Word.fromLargeInt (IntInf.toLarge n))" ^ newline ^ "" ^ newline ^ "fun shiftr x n =" ^ newline ^ " Word64.>> (x, Word.fromLargeInt (IntInf.toLarge n))" ^ newline ^ "" ^ newline ^ "fun shiftr_signed x n =" ^ newline ^ " Word64.~>> (x, Word.fromLargeInt (IntInf.toLarge n))" ^ newline ^ "" ^ newline ^ "fun test_bit x n =" ^ newline ^ " Word64.andb (x, Word64.<< (0wx1, Word.fromLargeInt (IntInf.toLarge n))) <> Word64.fromInt 0" ^ newline ^ "" ^ newline ^ "val notb = Word64.notb" ^ newline ^ "" ^ newline ^ "fun andb x y = Word64.andb(x, y);" ^ newline ^ "" ^ newline ^ "fun orb x y = Word64.orb(x, y);" ^ newline ^ "" ^ newline ^ "fun xorb x y = Word64.xorb(x, y);" ^ newline ^ "" ^ newline ^ "end (*struct Uint64*)"
val target_SML64 = "SML_word"; in
(if use_Word64 then Code_Target.set_printings (Code_Symbol.Module ("Uint64", [(Code_Runtime.target, SOME (content, []))])) else I)
#> Code_Target.set_printings (Code_Symbol.Module ("Uint64", [(target_SML64, SOME (content, []))])) end ›
code_printing code_module Uint64 ⇀ (Haskell) ‹module Uint64(Int64, Word64) where
(* negative numbers have their highest bit set,
so they are greater than positive ones *) let less x y = if Int64.compare x Int64.zero < 0then
Int64.compare y Int64.zero < 0 && Int64.compare x y < 0
else Int64.compare y Int64.zero < 0 || Int64.compare x y < 0;;
let less_eq x y = if Int64.compare x Int64.zero < 0then
Int64.compare y Int64.zero < 0 && Int64.compare x y <= 0
else Int64.compare y Int64.zero < 0 || Int64.compare x y <= 0;;
let shiftl x n = Int64.shift_left x (Z.to_int n);;
let shiftr x n = Int64.shift_right_logical x (Z.to_int n);;
let shiftr_signed x n = Int64.shift_right x (Z.to_int n);;
let test_bit x n =
Int64.compare
(Int64.logand x (Int64.shift_left Int64.one (Z.to_int n)))
Int64.zero
<> 0;;
text‹
OCaml's conversion from Big\_int to int64 demands that the value fits int a signed 64-bit integer.
The following justifies the implementation. ›
context includes bit_operations_syntax begin
definition Uint64_signed :: "integer ==> uint64" where"Uint64_signed i = (if i < -(0x8000000000000000) ∨ i ≥ 0x8000000000000000 then undefined Uint64 i else Uint64 i)"
lemma Uint64_code [code]: "Uint64 i = (let i' = i AND 0xFFFFFFFFFFFFFFFF in if bit i' 63 then Uint64_signed (i' - 0x10000000000000000) else Uint64_signed i')"
including undefined_transfer and integer.lifting unfolding Uint64_signed_def apply transfer apply (subst word_of_int_via_signed) apply (auto simp add: mask_eq_exp_minus_1 word_of_int_via_signed cong del: if_cong) done
lemma Uint64_signed_code [code]: "Rep_uint64 (Uint64_signed i) = (if i < -(0x8000000000000000) ∨ i ≥ 0x8000000000000000 then Rep_uint64 (undefined Uint64 i) else word_of_int (int_of_integer i))" unfolding Uint64_signed_def Uint64_def by (simp add: Abs_uint64_inverse)
end
text‹
Avoid @{term Abs_uint64} in generated code, use @{term Rep_uint64'} instead.
The symbolic implementations for code\_simp use @{term Rep_uint64}.
The new destructor @{term Rep_uint64'} is executable.
As the simplifier is given the [code abstract] equations literally,
we cannot implement @{term Rep_uint64} directly, because that makes code\_simp loop.
If code generation raises Match, some equation probably contains @{term Rep_uint64}
([code abstract] equations for @{typ uint64} may use @{term Rep_uint64} because
these instances will be folded away.)
To convert @{typ "64 word"} values into @{typ uint64}, use @{term "Abs_uint64'"}. ›
definition Rep_uint64' where [simp]: "Rep_uint64' = Rep_uint64"
definition uint64_divmod :: "uint64 ==> uint64 ==> uint64 × uint64"where "uint64_divmod x y = (if y = 0 then (undefined ((div) :: uint64 ==> _) x (0 :: uint64), undefined ((mod) :: uint64 ==> _) x (0 :: uint64)) else (x div y, x mod y))"
definition uint64_div :: "uint64 ==> uint64 ==> uint64" where"uint64_div x y = fst (uint64_divmod x y)"
definition uint64_mod :: "uint64 ==> uint64 ==> uint64" where"uint64_mod x y = snd (uint64_divmod x y)"
lemma div_uint64_code [code]: "x div y = (if y = 0 then 0 else uint64_div x y)"
including undefined_transfer unfolding uint64_divmod_def uint64_div_def by transfer (simp add: word_div_def)
lemma mod_uint64_code [code]: "x mod y = (if y = 0 then x else uint64_mod x y)"
including undefined_transfer unfolding uint64_mod_def uint64_divmod_def by transfer (simp add: word_mod_def)
definition uint64_sdiv :: "uint64 ==> uint64 ==> uint64" where [code del]: "uint64_sdiv x y = (if y = 0 then undefined ((div) :: uint64 ==> _) x (0 :: uint64) else Abs_uint64 (Rep_uint64 x sdiv Rep_uint64 y))"
definition div0_uint64 :: "uint64 ==> uint64" where [code del]: "div0_uint64 x = undefined ((div) :: uint64 ==> _) x (0 :: uint64)" declare [[code abort: div0_uint64]]
definition mod0_uint64 :: "uint64 ==> uint64" where [code del]: "mod0_uint64 x = undefined ((mod) :: uint64 ==> _) x (0 :: uint64)" declare [[code abort: mod0_uint64]]
lemma uint64_divmod_code [code]: "uint64_divmod x y = (if 0x8000000000000000 ≤ y then if x < y then (0, x) else (1, x - y) else if y = 0 then (div0_uint64 x, mod0_uint64 x) else let q = push_bit 1 (uint64_sdiv (drop_bit 1 x) y); r = x - q * y in if r ≥ y then (q + 1, r - y) else (q, r))"
including undefined_transfer unfolding uint64_divmod_def uint64_sdiv_def div0_uint64_def mod0_uint64_def
less_eq_uint64.rep_eq apply transfer apply (simp add: divmod_via_sdivmod push_bit_eq_mult) done
lemma uint64_sdiv_code [code]: "Rep_uint64 (uint64_sdiv x y) = (if y = 0 then Rep_uint64 (undefined ((div) :: uint64 ==> _) x (0 :: uint64)) else Rep_uint64 x sdiv Rep_uint64 y)" unfolding uint64_sdiv_def by(simp add: Abs_uint64_inverse)
text‹
Note that we only need a translation for signed division, but not for the remainder
because @{thm uint64_divmod_code} computes both with division only. ›
global_interpretation uint64: word_type_copy_target_language Abs_uint64 Rep_uint64 signed_drop_bit_uint64
uint64_of_nat nat_of_uint64 uint64_of_int int_of_uint64 Uint64 integer_of_uint64 64 set_bits_aux_uint64 6463 defines uint64_test_bit = uint64.test_bit and uint64_shiftl = uint64.shiftl and uint64_shiftr = uint64.shiftr and uint64_sshiftr = uint64.sshiftr by standard simp_all
code_printing constant uint64_test_bit ⇀
(SML) "Uint64.test'_bit"and
(Haskell) "Data'_Bits.testBitBounded"and
(OCaml) "Uint64.test'_bit"and
(Scala) "Uint64.test'_bit"and
(Eval) "(fn x => fn i => if i < 0 orelse i >= 64 then raise (Fail \"argument to uint64'_test'_bit out of bounds\") else Uint64.test'_bit x i)"
code_printing constant uint64_shiftl ⇀
(SML) "Uint64.shiftl"and
(Haskell) "Data'_Bits.shiftlBounded"and
(OCaml) "Uint64.shiftl"and
(Scala) "Uint64.shiftl"and
(Eval) "(fn w => fn i => if i < 0 orelse i >= 64 then raise (Fail \"argument to uint64'_shiftl out of bounds\") else Uint64.shiftl w i)"
code_printing constant uint64_shiftr ⇀
(SML) "Uint64.shiftr"and
(Haskell) "Data'_Bits.shiftrBounded"and
(OCaml) "Uint64.shiftr"and
(Scala) "Uint64.shiftr"and
(Eval) "(fn w => fn i => if i < 0 orelse i >= 64 then raise (Fail \"argument to uint64'_shiftr out of bounds\") else Uint64.shiftr w i)"
code_printing constant uint64_sshiftr ⇀
(SML) "Uint64.shiftr'_signed"and
(Haskell) "(Prelude.fromInteger (Prelude.toInteger (Data'_Bits.shiftrBounded (Prelude.fromInteger (Prelude.toInteger _) :: Uint64.Int64) _)) :: Uint64.Word64)"and
(OCaml) "Uint64.shiftr'_signed"and
(Scala) "Uint64.shiftr'_signed"and
(Eval) "(fn w => fn i => if i < 0 orelse i >= 64 then raise (Fail \"argument to uint64'_shiftr'_signed out of bounds\") else Uint64.shiftr'_signed w i)"
context includes bit_operations_syntax begin
lemma uint64_msb_test_bit: "msb x ⟷ bit (x :: uint64) 63" by transfer (simp add: msb_word_iff_bit)
lemma msb_uint64_code [code]: "msb x ⟷ uint64_test_bit x 63" by (simp add: uint64.test_bit_def uint64_msb_test_bit)
lemma uint64_of_int_code [code]: "uint64_of_int i = Uint64 (integer_of_int i)"
including integer.lifting by transfer simp
lemma int_of_uint64_code [code]: "int_of_uint64 x = int_of_integer (integer_of_uint64 x)"
including integer.lifting by transfer simp
lemma uint64_of_nat_code [code]: "uint64_of_nat = uint64_of_int ∘ int" by transfer (simp add: fun_eq_iff)
lemma nat_of_uint64_code [code]: "nat_of_uint64 x = nat_of_integer (integer_of_uint64 x)" unfolding integer_of_uint64_def including integer.lifting by transfer simp
definition integer_of_uint64_signed :: "uint64 ==> integer" where "integer_of_uint64_signed n = (if bit n 63 then undefined integer_of_uint64 n else integer_of_uint64 n)"
lemma integer_of_uint64_signed_code [code]: "integer_of_uint64_signed n = (if bit n 63 then undefined integer_of_uint64 n else integer_of_int (uint (Rep_uint64' n)))" by (simp add: integer_of_uint64_signed_def integer_of_uint64_def)
lemma integer_of_uint64_code [code]: "integer_of_uint64 n = (if bit n 63 then integer_of_uint64_signed (n AND 0x7FFFFFFFFFFFFFFF) OR 0x8000000000000000 else integer_of_uint64_signed n)" proof - have‹integer_of_uint64_signed (n AND 0x7FFFFFFFFFFFFFFF) OR 0x8000000000000000 = Bit_Operations.set_bit 63 (integer_of_uint64_signed (take_bit 63 n))› by (simp add: take_bit_eq_mask set_bit_eq_or push_bit_eq_mult mask_eq_exp_minus_1) moreoverhave‹integer_of_uint64 n = Bit_Operations.set_bit 63 (integer_of_uint64 (take_bit 63 n))›if‹bit n 63› proof (rule bit_eqI) fix m from that show‹bit (integer_of_uint64 n) m = bit (Bit_Operations.set_bit 63 (integer_of_uint64 (take_bit 63 n))) m›for m
including integer.lifting by transfer (auto simp add: bit_simps dest: bit_imp_le_length) qed ultimatelyshow ?thesis by simp (simp add: integer_of_uint64_signed_def bit_simps) qed