Here's the overview of how I think it's done.
It starts in the Num.thy parse_translation
, at the ML function numeral_tr
.
In that function, there is the use of Lexicon.read_xnum
of lexicon.ML, which takes a string argument.
I don't know the details, but string "15"
is extracted from an expression like
"15 = n@N|(num.Bit1 (num.Bit1 (num.Bit1 num.One)))",
and fed to read_xnum
, where there is this equivalency:
Lexicon.read_xnum "15" = {leading_zeros = 0, radix = 10, value = 15}
In read_xnum
the function Library.read_radix_int
of library.ML is used, and it takes as arguments an integer radix and a list of digits, such as shown in this equivalency:
Library.read_radix_int 10 ["1","5"] = (15, []);
Next, the conversion from 15
to (num.Bit1 (num.Bit1 (num.Bit1 num.One)))
is a result of IntInf.quotRem
in the parse_translation
.
This takes me out of convenient HTML linking for isabelle.in.tum.de/repos.
IntInf.quotRem
is in Isabelle2013-2\contrib\polyml-5.5.1-1\src\basis\IntInf.sml, and is defined as
val quotRem: int*int->int*int = RunCall.run_call2C2 POLY_SYS_quotrem,
which leads to Isabelle2013-2\contrib\polyml-5.5.1-1\src\basis\RuntimeCalls.ML line 83:
val POLY_SYS_quotrem = 104 (* DCJM 05/03/10 *).
For Windows that takes me to Isabelle2013-2\contrib\polyml-5.5.1-1\src\libpolyml\x86asm.asm line 1660, though I could be leaving out some important details:
quotrem_really_long:
MOVL Reax,Redi
CALLMACRO CALL_IO POLY_SYS_quotrem
CALLMACRO RegMask quotrem,(M_Reax OR M_Redi OR M_Redx OR Mask_all).
I think that's enough of an overview to answer my question. A string "15" is converted to an ML integer, and then some assembly language level quotient/remainder division is used to convert 15 to binary Num.num
.
I'm actually only interested in how "15" is converted to 15 in read_radix_int
, and whether the details of that function will help me. I explain the application more below.
From here, I put in more detail for myself, to put in a nice form much of the information I collected.
What's sort of the deal
I start with a binary number as a bool list
, something like [True, True, True, True]
for binary 15, though here, I simplify it in some ways.
That then gets converted to something like [[True, False, True, False], [True, False, True]]
, which is decimal 15.
The real problem can be finding the right search phrase
Searching on something like "decimal to binary conversion" returns links to a lot of basic math algorithms, which aren't what I'm looking for.
Normal programming conversions aren't what I need either, which is just making explicit the underlying fact that integers are already in binary form:
Finally, other searches led me to the right word, "radix". Additionally, I resorted to doing searches on how things are done in C, where bit shifts are what I'm trying to tie into, though these may not be what I need:
Radix leading back to Num.thy
"Radix" took me back to Num.thy, which is where I thought the action might be, but hadn't seen anything that was obvious to me.
I include some source from Num.thy, lexicon.ML, and IntInf.sml:
(* THE TWO MAIN EXTERNAL FUNCTIONS IN THE TRANSLATIONS: read_xnum, quotRem *)
ML{*
Lexicon.read_xnum; (* string ->
{leading_zeros: int, radix: int, value: int}.*)
Lexicon.read_xnum "15"; (* {leading_zeros = 0, radix = 10, value = 15}.*)
Lexicon.read_xnum "15" = {leading_zeros = 0, radix = 10, value = 15};
IntInf.quotRem; (* int * int -> int * int.*)
IntInf.quotRem (5,3); (* (1, 2) *)
*}
parse_translation {* (* Num.thy(293) *)
let
fun num_of_int n =
if n > 0 then
(case IntInf.quotRem (n, 2) of
(0, 1) => Syntax.const @{const_name One}
| (n, 0) => Syntax.const @{const_name Bit0} $ num_of_int n
| (n, 1) => Syntax.const @{const_name Bit1} $ num_of_int n)
else raise Match
val pos = Syntax.const @{const_name numeral}
val neg = Syntax.const @{const_name neg_numeral}
val one = Syntax.const @{const_name Groups.one}
val zero = Syntax.const @{const_name Groups.zero}
fun numeral_tr [(c as Const (@{syntax_const "_constrain"}, _)) $ t $ u] =
c $ numeral_tr [t] $ u
| numeral_tr [Const (num, _)] =
let
val {value, ...} = Lexicon.read_xnum num;
in
if value = 0 then zero else
if value > 0
then pos $ num_of_int value
else neg $ num_of_int (~value)
end
| numeral_tr ts = raise TERM ("numeral_tr", ts);
in [("_Numeral", K numeral_tr)] end
*}
ML{* (* lexicon.ML(367) *)
(* read_xnum: hex/bin/decimal *)
local
val ten = ord "0" + 10;
val a = ord "a";
val A = ord "A";
val _ = a > A orelse raise Fail "Bad ASCII";
fun remap_hex c =
let val x = ord c in
if x >= a then chr (x - a + ten)
else if x >= A then chr (x - A + ten)
else c
end;
fun leading_zeros ["0"] = 0
| leading_zeros ("0" :: cs) = 1 + leading_zeros cs
| leading_zeros _ = 0;
in
fun read_xnum str =
let
val (sign, radix, digs) =
(case Symbol.explode (perhaps (try (unprefix "#")) str) of
"0" :: "x" :: cs => (1, 16, map remap_hex cs)
| "0" :: "b" :: cs => (1, 2, cs)
| "-" :: cs => (~1, 10, cs)
| cs => (1, 10, cs));
in
{radix = radix,
leading_zeros = leading_zeros digs,
value = sign * #1 (Library.read_radix_int radix digs)}
end;
end;
*}
ML{* (* IntInf.sml(42) *)
val quotRem: int*int->int*int = RunCall.run_call2C2 POLY_SYS_quotrem
*}
A big part of what I was looking for; radix again
(* THE FUNCTION WHICH TRANSLATES A LIST OF DIGITS/STRINGS TO A ML INTEGER *)
ML{*
Library.read_radix_int; (* int -> string list -> int * string list *)
Library.read_radix_int 10 ["1","5"]; (* (15, []): int * string list.*)
Library.read_radix_int 10 ["1","5"] = (15, []);
*}
ML{* (* library.ML(670) *)
fun read_radix_int radix cs =
let
val zero = ord "0";
val limit = zero + radix;
fun scan (num, []) = (num, [])
| scan (num, c :: cs) =
if zero <= ord c andalso ord c < limit then
scan (radix * num + (ord c - zero), cs)
else (num, c :: cs);
in scan (0, cs) end;
*}
The low-level division action
There's the high-level Integer.div_mod
in integer.ML, which wasn't used for the translation above:
fun div_mod x y = IntInf.divMod (x, y);
In Isabelle2013-2\contrib\polyml-5.5.1-1\src\basis\IntInf.sml, the higher-level divMod
can be compared to the lower-level quotRem
:
ML{* (* IntInf.sml(42) *)
val quotRem: int*int->int*int = RunCall.run_call2C2 POLY_SYS_quotrem
(* This should really be defined in terms of quotRem. *)
fun divMod(i, j) = (i div j, i mod j)
*}
With the low-level action for quotRem
apparently being done at the assembly language level:
ML{* (* RuntimeCalls.ML(83) *)
val POLY_SYS_quotrem = 104 (* DCJM 05/03/10 *)
*}
(* x86asm.asm(1660)
quotrem_really_long:
MOVL Reax,Redi
CALLMACRO CALL_IO POLY_SYS_quotrem
CALLMACRO RegMask quotrem,(M_Reax OR M_Redi OR M_Redx OR Mask_all)
*)
These various forms of div
and mod
are important to me.
I'm thinking that div
and mod
are to be avoided if possible, but tying into quotRem
would be the way to go, I think, if division is unavoidable.