1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
open Core
module Z = Zarith.Z
module T = struct
type t : value mod contended global portable = { global_ global : Z.t }
[@@unboxed] [@@unsafe_allow_any_mode_crossing] [@@deriving typerep ~abstract]
[%%template
[@@@mode.default m = (local, global)]
let[@inline] compare x y = Z.compare x.global y.global
let[@inline] equal x y = Z.equal x.global y.global]
let hash_fold_t state t = hash_fold_int state (Z.hash t.global)
let[@inline] hash t = Z.hash t.global
end
include T
let module_name = "Bigint"
let invariant (_ : t) = ()
module Stringable_t = struct
type nonrec t = t
let%template[@alloc a = (heap, stack)] [@inline] to_string { global = t } =
Z.to_string t [@exclave_if_stack a]
;;
let of_string_base str ~name ~of_string =
try { global = of_string str } with
| _ ->
failwithf "%s.%s: invalid argument %S" name module_name (globalize_string str) ()
;;
let of_string str = of_string_base str ~name:"of_string" ~of_string:Z.of_string
end
module Stable = struct
module V1 = struct
module Bin_rep = struct
open Stable_witness.Export
type t =
| Zero
| Pos of string
| Neg of string
[@@deriving bin_io ~localize, stable_witness]
end
module Bin_rep_conversion = struct
type nonrec t = t
let%template[@mode m = (global, local)] to_binable { global = t } =
let s = Z.sign t in
if s > 0
then (
let bits = Z.to_bits t in
Bin_rep.Pos bits [@exclave_if_local m])
else if s < 0
then (
let bits = Z.to_bits t in
Bin_rep.Neg bits [@exclave_if_local m])
else Bin_rep.Zero
;;
let of_binable = function
| Bin_rep.Zero -> { global = Z.zero }
| Bin_rep.Pos bits -> { global = Z.of_bits bits }
| Bin_rep.Neg bits -> { global = Z.of_bits bits |> Z.neg }
;;
end
type nonrec t = t [@@deriving compare ~localize, equal ~localize, hash]
include%template
Sexpable.Stable.Of_stringable.V1 [@alloc stack] [@modality portable] (Stringable_t)
include%template
Binable.Stable.Of_binable.V1 [@mode local] [@modality portable] [@alert "-legacy"]
(Bin_rep)
(Bin_rep_conversion)
let%expect_test "stable" =
print_endline [%bin_digest: t];
[%expect {| 2872d29354b873e996296d440fad2e52 |}]
;;
let stable_witness : t Stable_witness.t =
let (_bin_io : t Stable_witness.t) =
Stable_witness.of_serializable
Bin_rep.stable_witness
Bin_rep_conversion.of_binable
Bin_rep_conversion.to_binable
in
let (_sexp : t Stable_witness.t) = Stable_witness.assert_stable in
Stable_witness.assert_stable
;;
end
module V2 = struct
type nonrec t = t [@@deriving compare ~localize, equal ~localize]
include%template
Sexpable.Stable.Of_stringable.V1 [@modality portable] [@alloc stack] (Stringable_t)
let compute_size_in_bytes x =
let numbits = Z.numbits x in
Int.round_up ~to_multiple_of:8 numbits / 8
;;
let compute_tag ~size_in_bytes ~negative =
let open Int63 in
let sign_bit = if negative then one else zero in
shift_left (of_int size_in_bytes) 1 + sign_bit
;;
[%%template
[@@@mode.default m = (global, local)]
let bin_size_t : (t Bin_prot.Size.sizer[@mode local]) =
fun { global = x } ->
let size_in_bytes = compute_size_in_bytes x in
if size_in_bytes = 0
then Int63.bin_size_t Int63.zero
else (
let negative = Z.sign x = -1 in
let tag = compute_tag ~size_in_bytes ~negative in
Int63.bin_size_t tag + size_in_bytes)
;;
let bin_write_t : (t Bin_prot.Write.writer[@mode local]) =
fun buf ~pos { global = x } ->
let size_in_bytes = compute_size_in_bytes x in
if size_in_bytes = 0
then Int63.bin_write_t buf ~pos Int63.zero
else (
let bits = Z.to_bits x in
let negative = Z.sign x = -1 in
let tag = compute_tag ~size_in_bytes ~negative in
let pos = Int63.bin_write_t buf ~pos tag in
Bin_prot.Common.blit_string_buf bits ~dst_pos:pos buf ~len:size_in_bytes;
pos + size_in_bytes)
;;]
let bin_read_t : t Bin_prot.Read.reader =
fun buf ~pos_ref ->
let tag = Core.Int63.bin_read_t buf ~pos_ref in
if Int63.equal tag Int63.zero
then { global = Z.zero }
else (
let negative = Int63.(tag land one = one) in
let size_in_bytes = Int63.(to_int_exn (shift_right tag 1)) in
let bytes = Bytes.create size_in_bytes in
Bin_prot.Common.blit_buf_bytes ~src_pos:!pos_ref buf bytes ~len:size_in_bytes;
let abs =
Z.of_bits (Bytes.unsafe_to_string ~no_mutation_while_string_reachable:bytes)
in
pos_ref := !pos_ref + size_in_bytes;
{ global = (if negative then Z.neg abs else abs) })
;;
let module_name = "Bigint.Stable.V2.t"
let bin_writer_t : t Bin_prot.Type_class.writer =
{ size = bin_size_t; write = bin_write_t }
;;
let __bin_read_t__ _buf ~pos_ref _vint =
Bin_prot.Common.raise_variant_wrong_type module_name !pos_ref
;;
let bin_reader_t : t Bin_prot.Type_class.reader =
{ read = bin_read_t; vtag_read = __bin_read_t__ }
;;
let bin_shape_t : Bin_prot.Shape.t =
Bin_prot.Shape.basetype
(Bin_prot.Shape.Uuid.of_string "7a8cceb2-f3a2-11e9-b7cb-aae95a547ff6")
[]
;;
let bin_t : t Bin_prot.Type_class.t =
{ shape = bin_shape_t; writer = bin_writer_t; reader = bin_reader_t }
;;
let stable_witness : t Stable_witness.t =
let (_bin_io : t Stable_witness.t) =
Stable_witness.assert_stable
in
let (_sexp : t Stable_witness.t) = Stable_witness.assert_stable in
Stable_witness.assert_stable
;;
end
end
module Unstable = struct
include Stable.V1
include Stringable_t
let globalize { global = t } = { global = t }
let of_string_opt (local_ t) =
try Some (of_string t) with
| _ -> None
;;
let (t_sexp_grammar : t Sexplib.Sexp_grammar.t) = { untyped = Integer }
let of_zarith_bigint t = { global = t }
let to_zarith_bigint { global = t } = t
let ( /% ) (local_ x) (local_ y) =
if Z.sign y.global >= 0
then { global = Z.ediv x.global y.global }
else
failwithf
"%s.(%s /%% %s) : divisor must be positive"
module_name
(to_string x)
(to_string y)
()
;;
let ( % ) (local_ x) (local_ y) =
if Z.sign y.global >= 0
then { global = Z.erem x.global y.global }
else
failwithf
"%s.(%s %% %s) : divisor must be positive"
module_name
(to_string x)
(to_string y)
()
;;
let[@inline] ( - ) { global = x } { global = y } = { global = Z.( - ) x y }
let[@inline] ( + ) { global = x } { global = y } = { global = Z.( + ) x y }
let[@inline] ( * ) { global = x } { global = y } = { global = Z.( * ) x y }
let[@inline] ( / ) { global = x } { global = y } = { global = Z.( / ) x y }
let[@inline] rem { global = x } { global = y } = { global = Z.rem x y }
let[@inline] ( ~- ) { global = x } = { global = Z.( ~- ) x }
let[@inline] neg (local_ { global = x }) = { global = Z.neg x }
let[@inline] abs_local (local_ { global = x }) = { global = Z.abs x }
let abs = abs_local
let[@inline] succ (local_ { global = x }) = { global = Z.succ x }
let[@inline] pred (local_ { global = x }) = { global = Z.pred x }
let[@inline] equal__local (local_ { global = x }) (local_ { global = y }) = Z.equal x y
let[@inline] equal x y = equal__local x y
let ( = ) = equal
let[@inline] ( < ) { global = x } { global = y } = Z.lt x y
let[@inline] ( > ) { global = x } { global = y } = Z.gt x y
let[@inline] ( <= ) { global = x } { global = y } = Z.leq x y
let[@inline] ( >= ) { global = x } { global = y } = Z.geq x y
let[@inline] max { global = x } { global = y } = { global = Z.max x y }
let[@inline] min { global = x } { global = y } = { global = Z.min x y }
let ascending = compare
let[@inline] shift_right (local_ { global = t }) n = { global = Z.shift_right t n }
let[@inline] shift_left (local_ { global = t }) n = { global = Z.shift_left t n }
let[@inline] bit_not (local_ { global = x }) = { global = Z.lognot x }
let[@inline] bit_xor (local_ { global = x }) (local_ { global = y }) =
{ global = Z.logxor x y }
;;
let[@inline] bit_or (local_ { global = x }) (local_ { global = y }) =
{ global = Z.logor x y }
;;
let[@inline] bit_and (local_ { global = x }) (local_ { global = y }) =
{ global = Z.logand x y }
;;
let ( land ) = bit_and
let ( lor ) = bit_or
let ( lxor ) = bit_xor
let lnot = bit_not
let ( lsl ) = shift_left
let ( asr ) = shift_right
let[@inline] of_int x = { global = Z.of_int x }
let[@inline] of_int32 (local_ x) = { global = Z.of_int32 x }
let[@inline] of_int64 (local_ x) = { global = Z.of_int64 x }
let[@inline] of_nativeint (local_ x) = { global = Z.of_nativeint x }
let[@inline] of_float_unchecked (local_ x) = { global = Z.of_float x }
let of_float = of_float_unchecked
let of_int_exn = of_int
let of_int32_exn = of_int32
let of_local_int32_exn = of_int32
let of_int64_exn = of_int64
let of_local_int64_exn = of_int64
let of_nativeint_exn = of_nativeint
let of_local_nativeint_exn = of_nativeint
let[@inline] to_int_exn { global = t } = Z.to_int t
let[@inline] to_int32_exn (local_ { global = t }) = Z.to_int32 t
let[@inline] to_int64_exn (local_ { global = t }) = Z.to_int64 t
let[@inline] to_nativeint_exn (local_ { global = t }) = Z.to_nativeint t
let[@inline] to_float (local_ { global = t }) = Z.to_float t
let zero = { global = Z.zero }
let one = { global = Z.one }
let minus_one = { global = Z.minus_one }
let to_int { global = t } = if Z.fits_int t then Some (Z.to_int t) else None
let to_int32 { global = t } = if Z.fits_int32 t then Some (Z.to_int32 t) else None
let to_int64 { global = t } = if Z.fits_int64 t then Some (Z.to_int64 t) else None
let to_local_int32_exn = to_int32_exn
let to_local_int64 = to_int64
let to_local_int64_exn = to_int64_exn
let to_local_nativeint_exn = to_nativeint_exn
let to_nativeint { global = t } =
if Z.fits_nativeint t then Some (Z.to_nativeint t) else None
;;
let ( <> ) x y = not (equal x y)
let incr (local_ cell) = cell := succ !cell
let decr (local_ cell) = cell := pred !cell
let pow (local_ { global = x }) (local_ y) = { global = Z.pow x (to_int_exn y) }
let ( ** ) = pow
let[@inline] popcount (local_ { global = t }) = Z.popcount t |> of_int
end
module Local_comparisons = struct
let ( = ) = Unstable.equal__local
let[@inline] ( <> ) x y = not (x = y)
let[@inline] ( <= ) { global = x } { global = y } = Z.leq x y
let[@inline] ( >= ) { global = x } { global = y } = Z.geq x y
let[@inline] ( < ) { global = x } { global = y } = Z.lt x y
let[@inline] ( > ) { global = x } { global = y } = Z.gt x y
end
module T_math = Int_math.Make (struct
include Unstable
include Local_comparisons
end)
module T_conversions = Int_conversions.Make (Unstable)
module%template T_comparable_with_zero =
Comparable.With_zero [@modality portable] (Unstable)
module%template T_identifiable =
Identifiable.Make [@mode local] [@modality portable] (struct
let module_name = module_name
include Unstable
end)
module O = struct
include T_identifiable
include T_comparable_with_zero
include T_conversions
include T_math
include Unstable
end
include O
module Summable = struct
type nonrec t = t
let zero = zero
let[@inline] ( + ) x y = x + y
let[@inline] ( - ) x y = x - y
end
module Make_random (State : sig
@@ portable
type t
val bits : t -> int
val int : t -> int -> int
end) : sig
@@ portable
val random : state:State.t -> t -> t
end = struct
let bits_at_depth ~depth = Int.shift_left 30 depth
let range_at_depth ~depth = shift_left one (bits_at_depth ~depth)
let rec choose_bit_depth_for_range_from ~range ~depth =
if range_at_depth ~depth >= range
then depth
else choose_bit_depth_for_range_from ~range ~depth:(Int.succ depth)
;;
let choose_bit_depth_for_range ~range = choose_bit_depth_for_range_from ~range ~depth:0
let rec random_bigint_at_depth ~state ~depth =
if Int.equal depth 0
then of_int (State.bits state)
else (
let prev_depth = Int.pred depth in
let prefix = random_bigint_at_depth ~state ~depth:prev_depth in
let suffix = random_bigint_at_depth ~state ~depth:prev_depth in
bit_or (shift_left prefix (bits_at_depth ~depth:prev_depth)) suffix)
;;
let random_value_is_uniform_in_range ~range ~depth n =
let k = range_at_depth ~depth / range in
n < k * range
;;
let rec large_random_at_depth ~state ~range ~depth =
let result = random_bigint_at_depth ~state ~depth in
if random_value_is_uniform_in_range ~range ~depth result
then result % range
else large_random_at_depth ~state ~range ~depth
;;
let large_random ~state ~range =
let tolerance_factor = of_int 1_000 in
let depth = choose_bit_depth_for_range ~range:(range * tolerance_factor) in
large_random_at_depth ~state ~range ~depth
;;
let random ~state range =
if range <= zero
then
failwithf "Bigint.random: argument %s <= 0" (to_string_hum range) ()
else if range < shift_left one 30
then of_int (State.int state (to_int_exn range))
else large_random ~state ~range
;;
end
module Random_internal = Make_random (Random.State)
let random ?(state = Random.State.get_default ()) range =
Random_internal.random ~state range
;;
module For_quickcheck : sig @@ portable
include%template Quickcheckable.S_int [@mode portable] with type t := t
val gen_negative : t Quickcheck.Generator.t
val gen_positive : t Quickcheck.Generator.t
end = struct
module Generator = Base_quickcheck.Generator
open Generator.Portable.Let_syntax
module Uniform = Make_random (struct
type t = Splittable_random.t
let int t range = Splittable_random.int t ~lo:0 ~hi:(Int.pred range)
let bits t = int t (Int.shift_left 1 30)
end)
let random_uniform ~state lo hi = lo + Uniform.random ~state (succ (hi - lo))
let%template gen_uniform_incl lower_bound upper_bound =
if lower_bound > upper_bound
then
raise_s
[%message
"Bigint.gen_uniform_incl: bounds are crossed"
(lower_bound : t)
(upper_bound : t)];
(Generator.create [@mode portable]) (fun ~size:_ ~random:state ->
random_uniform ~state lower_bound upper_bound)
;;
let%template gen_incl lower_bound upper_bound =
(Generator.weighted_union [@mode portable])
[ 0.05, (Generator.return [@mode portable]) lower_bound
; 0.05, (Generator.return [@mode portable]) upper_bound
; 0.9, gen_uniform_incl lower_bound upper_bound
]
;;
let min_represented_by_n_bits n =
if Int.equal n 0 then zero else shift_left one (Int.pred n)
;;
let max_represented_by_n_bits n = pred (shift_left one n)
let gen_log_uniform_incl lower_bound upper_bound =
if lower_bound < zero || lower_bound > upper_bound
then
raise_s
[%message
"Bigint.gen_log_incl: invalid bounds" (lower_bound : t) (upper_bound : t)];
let min_bits = Z.numbits lower_bound.global in
let max_bits = Z.numbits upper_bound.global in
let%bind bits = Generator.int_uniform_inclusive min_bits max_bits in
gen_uniform_incl
(max lower_bound (min_represented_by_n_bits bits))
(min upper_bound (max_represented_by_n_bits bits))
;;
let%template gen_log_incl lower_bound upper_bound =
(Generator.weighted_union [@mode portable])
[ 0.05, (Generator.return [@mode portable]) lower_bound
; 0.05, (Generator.return [@mode portable]) upper_bound
; 0.9, gen_log_uniform_incl lower_bound upper_bound
]
;;
let%template gen_positive =
let%bind = Generator.size in
let num_bytes = Int.succ extra_bytes in
let num_bits = Int.( * ) num_bytes 8 in
gen_log_uniform_incl one (pred (shift_left one num_bits))
;;
let%template gen_negative = (Generator.map [@mode portable]) gen_positive ~f:neg
let%template quickcheck_generator =
(Generator.weighted_union [@mode portable])
[ 0.45, gen_positive
; 0.1, (Generator.return [@mode portable]) zero
; 0.45, gen_negative
]
;;
let%template quickcheck_observer =
(Quickcheck.Observer.create [@mode portable]) (fun t ~size:_ ~hash ->
hash_fold_t hash t)
;;
let quickcheck_shrinker = Quickcheck.Shrinker.empty ()
end
include For_quickcheck
module Hex = struct
type nonrec t = t [@@deriving bin_io ~localize, typerep]
module M = Base.Int_conversions.Make_hex (struct
type nonrec t = t [@@deriving hash, compare ~localize]
let to_string { global = i } = Z.format "%x" i
let of_hex_string str = Z.of_string_base 16 str
let of_string str =
of_string_base str ~name:"Hex.of_string" ~of_string:of_hex_string
;;
let ( < ) = Local_comparisons.( < )
let neg = neg
let zero = zero
let module_name = module_name ^ ".Hex"
end)
include (
M.Hex :
module type of struct
include M.Hex
end
with type t := t)
end
module Binary = struct
type nonrec t = t [@@deriving bin_io ~localize, compare ~localize, hash, typerep]
let chars_per_delimiter = 4
[%%template
[@@@alloc a = (heap, stack)]
let ( ^ ) = (String.append [@alloc a])
[@@@alloc.default a]
let to_string { global = t } = Z.format "%#b" t
let to_string_hum ?(delimiter = '_') { global = t } =
(let input = Z.format "%b" t in
"0b"
^ (Int_conversions.insert_delimiter_every [@alloc a])
input
~delimiter
~chars_per_delimiter)
[@exclave_if_stack a]
;;
let sexp_of_t t : Sexp.t = Atom (to_string t) [@exclave_if_stack a]]
module Hum = struct
let%template[@alloc a = (heap, stack)] to_string = (to_string_hum [@alloc a])
end
end
include T