Source file parallel_arrays_intf.ml

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
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
open! Base
open! Import

module type%template Get = sig @@ portable
  type ('a : any mod portable separable) t

  [@@@kind.default k = ks]
  [@@@mode.default m = (uncontended, shared)]

  (** To read a value from a parallel array, we must prove that it does not escape its
      capsule. This is the case if its type crosses contention, or if it is manipulated
      within a portable function. *)

  (** [get t i] reads the element at index [i]. Raises [Invalid_arg] if [i] is not in the
      range \[0..length t). *)
  val get : ('a : k mod contended portable separable). 'a t @ m -> int -> 'a @ m

  (** [unsafe_get t i] unsafely reads the element at index [i]. *)
  val unsafe_get : ('a : k mod contended portable separable). 'a t @ m -> int -> 'a @ m

  (** [extract t i f] applies [f] with the element read from index [i]. Raises
      [Invalid_arg] if [i] is not in the range \[0..length t). *)
  val extract
    : ('a : k mod portable separable).
    'a t @ m
    -> int
    -> ('a @ m -> 'b @ contended portable) @ local once portable
    -> 'b @ contended portable

  (** [unsafe_extract t i f] applies [f] with the element unsafely read from index [i]. *)
  val unsafe_extract
    : ('a : k mod portable separable).
    'a t @ m
    -> int
    -> ('a @ m -> 'b @ contended portable) @ local once portable
    -> 'b @ contended portable
end
[@@kind_set ks = (value_or_null, base_or_null)]

module type%template Set = sig @@ portable
  type ('a : any mod portable separable) t

  [@@@kind.default k = ks]

  (** To store a value in a parallel array, we must prove that it does not share
      unsynchronized state with any other elements. This is the case if its type crosses
      contention or it lives in a fresh capsule. *)

  (** [set t i a] stores the element [a] at index [i]. Raises [Invalid_arg] if [i] is not
      in the range \[0..length t). *)
  val set : ('a : k mod contended portable separable). 'a t -> int -> 'a -> unit

  (** [unsafe_set t i a] unsafely stores the element [a] at index [i]. *)
  val unsafe_set : ('a : k mod contended portable separable). 'a t -> int -> 'a -> unit

  (** [insert t i f] stores [f ()] at index [i]. Raises [Invalid_arg] if [i] is not in the
      range \[0..length t). *)
  val insert
    : ('a : k mod portable separable).
    'a t -> int -> (unit -> 'a) @ local once portable -> unit

  (** [unsafe_insert t i f] unsafely stores [f ()] at index [i]. *)
  val unsafe_insert
    : ('a : k mod portable separable).
    'a t -> int -> (unit -> 'a) @ local once portable -> unit
end
[@@kind_set ks = (value_or_null, base_or_null)]

module type%template Init = sig @@ portable
  type ('a : any mod portable separable) t
  type ('a : any) init

  [@@@kind.default k = ks]

  (** [init parallel n ~f] initializes an array with the result of [f] applied to the
      integers 0..n-1.

      If [t] is [array] or [iarray], [f] is not allowed to return boxed floats, as this
      would break the float array optimization. Prefer [float# t]. *)
  val init
    : ('a : k mod m portable).
    Parallel_kernel.t @ local
    -> 'a init
    -> f:(Parallel_kernel.t @ local -> int -> 'a) @ shareable
    -> 'a t
end
[@@modality m = (non_float, separable)] [@@kind_set ks = (value_or_null, base_or_null)]

module type%template Reduce = sig @@ portable
  type ('a : any mod portable separable) t

  [@@@kind.default k = ks]

  (** [fold parallel t ~init ~f ~combine] folds [combine] over the result of
      [map parallel t ~f]. [combine] must be associative and [combine init x] must equal
      [x]. *)
  val fold
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t @ shared
    -> init:(unit -> 'acc) @ portable
    -> f:(Parallel_kernel.t @ local -> 'acc -> 'a @ shared -> 'acc) @ shareable
    -> combine:(Parallel_kernel.t @ local -> 'acc -> 'acc -> 'acc) @ shareable
    -> 'acc

  (** [foldi parallel t ~init ~f ~combine] folds [combine] over the result of
      [mapi parallel t ~f]. [combine] must be associative and [combine init x] must equal
      [x]. *)
  val foldi
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t @ shared
    -> init:(unit -> 'acc) @ portable
    -> f:(Parallel_kernel.t @ local -> int -> 'acc -> 'a @ shared -> 'acc) @ shareable
    -> combine:(Parallel_kernel.t @ local -> 'acc -> 'acc -> 'acc) @ shareable
    -> 'acc

  [@@@mode.default m = (uncontended, shared)]

  (** [iter parallel t ~f] applies [f] to each element of [t]. *)
  val iter
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t @ m
    -> f:(Parallel_kernel.t @ local -> 'a @ m -> unit) @ shareable
    -> unit

  (** [iteri parallel t ~f] applies [f] to each element of [t] and its index. *)
  val iteri
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t @ m
    -> f:(Parallel_kernel.t @ local -> int -> 'a @ m -> unit) @ shareable
    -> unit

  (** [find parallel t ~f] returns the first element of [t] for which [f] returns [true],
      if it exists. *)
  val find
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t @ m
    -> f:(Parallel_kernel.t @ local -> 'a @ m -> bool) @ shareable
    -> ('a Option_u.t[@kind k]) @ m

  (** [findi parallel t ~f] returns the first element of [t] for which [f] returns [true],
      if it exists. *)
  val findi
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t @ m
    -> f:(Parallel_kernel.t @ local -> int -> 'a @ m -> bool) @ shareable
    -> ('a Option_u.t[@kind k]) @ m

  (** [reduce parallel t ~f] folds [f] over the elements of [t]. [f] must be associative.
      If [t] is empty, [reduce] returns [None]. *)
  val reduce
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t @ m
    -> f:(Parallel_kernel.t @ local -> 'a @ shared -> 'a @ shared -> 'a @ m) @ shareable
    -> ('a Option_u.t[@kind k]) @ m

  (** [min_elt parallel t ~compare] is the minimum element of [t] according to [compare].
      If [t] is empty, returns [None]. *)
  val min_elt
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t @ m
    -> compare:
         (Parallel_kernel.t @ local -> 'a @ local shared -> 'a @ local shared -> int)
       @ shareable
    -> ('a Option_u.t[@kind k]) @ m

  (** [max_elt parallel t ~compare] is the maximum element of [t] according to [compare].
      If [t] is empty, returns [None]. *)
  val max_elt
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t @ m
    -> compare:
         (Parallel_kernel.t @ local -> 'a @ local shared -> 'a @ local shared -> int)
       @ shareable
    -> ('a Option_u.t[@kind k]) @ m
end
[@@kind_set ks = (value_or_null, base_or_null)]

module type%template Map = sig @@ portable
  type ('a : any mod portable separable) t

  (** Mapping functions do not need to be templated over the mode of their output type. To
      work with a contended or shared ['b], return a ['b Modes.Contended.t] or
      ['b Modes.Shared.t]. *)

  include sig
    [@@@kind.default k1 = ks, k2 = ks]
    [@@@mode.default m = (uncontended, shared)]

    (** [map parallel t ~f] initializes an array with the result of [f] applied to each
        element of [t].

        [f] is not allowed to return boxed floats, as this would break the float array
        optimization. Prefer [float# t]. *)
    val map
      : ('a : k1 mod portable separable) ('b : k2 mod non_float portable).
      Parallel_kernel.t @ local
      -> 'a t @ m
      -> f:(Parallel_kernel.t @ local -> 'a @ m -> 'b) @ shareable
      -> 'b t

    (** [mapi parallel t ~f] initializes an array with the result of [f] applied to each
        element of [t] and its index.

        [f] is not allowed to return boxed floats, as this would break the float array
        optimization. Prefer [float# t]. *)
    val mapi
      : ('a : k1 mod portable separable) ('b : k2 mod non_float portable).
      Parallel_kernel.t @ local
      -> 'a t @ m
      -> f:(Parallel_kernel.t @ local -> int -> 'a @ m -> 'b) @ shareable
      -> 'b t
  end
end
[@@kind_set ks = (value_or_null, base_or_null)]

module type%template Sort = sig @@ portable
  type ('a : any mod portable separable) t

  [@@@kind.default k = ks]
  [@@@mode.default m = (uncontended, shared)]

  (** [sort parallel t ~compare] initializes an array with the contents of [t] unstably
      sorted with respect to [compare]. *)
  val sort
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t @ m
    -> compare:
         (Parallel_kernel.t @ local -> 'a @ local shared -> 'a @ local shared -> int)
       @ shareable
    -> 'a t @ m

  (** [stable_sort parallel t ~compare] initializes an array with the contents of [t]
      stably sorted with respect to [compare]. *)
  val stable_sort
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t @ m
    -> compare:
         (Parallel_kernel.t @ local -> 'a @ local shared -> 'a @ local shared -> int)
       @ shareable
    -> 'a t @ m
end
[@@kind_set ks = (value_or_null, base_or_null)]

module type%template Scan = sig @@ portable
  type ('a : any mod portable separable) t

  [@@@kind.default k = ks]
  [@@@mode.default m = (uncontended, shared)]

  (** [scan parallel t ~init ~f] initialises an array containing the exclusive prefix sums
      of [t] with respect to [f]. The first element is [init] and the full reduction of
      [t] is returned separately. [f] must be associative and [f init x] must equal [x]. *)
  val scan
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t @ m
    -> init:'a @ m
    -> f:(Parallel_kernel.t @ local -> 'a @ shared -> 'a @ shared -> 'a @ m) @ shareable
    -> #('a t * 'a) @ m

  (** [scan_inclusive parallel t ~init ~f] initialises an array containing the inclusive
      prefix sums of [t] with respect to [f]. The first element is the first element of
      [t]. [f] must be associative and [f init x] must equal [x]. *)
  val scan_inclusive
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t @ m
    -> init:'a @ m
    -> f:(Parallel_kernel.t @ local -> 'a @ shared -> 'a @ shared -> 'a @ m) @ shareable
    -> 'a t @ m
end
[@@kind_set ks = (value_or_null, base_or_null)]

module type%template Filter = sig @@ portable
  type ('a : any mod portable separable) t

  [@@@kind.default k = ks]
  [@@@mode.default m = (uncontended, shared)]

  (** [filter parallel t ~f] initialises an array containing the elements of [t] that
      satisfy the predicate [f]. *)
  val filter
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t @ m
    -> f:(Parallel_kernel.t @ local -> 'a @ m -> bool) @ shareable
    -> 'a t @ m

  (** [filteri parallel t ~f] initialises an array containing the elements of [t] that,
      alongside their index, satisfy the predicate [f]. *)
  val filteri
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t @ m
    -> f:(Parallel_kernel.t @ local -> int -> 'a @ m -> bool) @ shareable
    -> 'a t @ m
end
[@@kind_set ks = (value_or_null, base_or_null)]

module type%template Filter_map = sig @@ portable
  type ('a : any mod portable separable) t

  [@@@mode.default m = (uncontended, shared)]

  (** [filter_map parallel t ~f] initializes an array with the results of [f] applied to
      each element of [t], filtering out [Null]s. *)
  val filter_map
    : ('a : value_or_null mod portable separable) ('b : value mod non_float portable).
    Parallel_kernel.t @ local
    -> 'a t @ m
    -> f:(Parallel_kernel.t @ local -> 'a @ m -> 'b or_null) @ shareable
    -> 'b t

  (** [filter_mapi parallel t ~f] initializes an array with the result of [f] applied to
      each element of [t] and its index, filtering out [Null]s. *)
  val filter_mapi
    : ('a : value_or_null mod portable separable) ('b : value mod non_float portable).
    Parallel_kernel.t @ local
    -> 'a t @ m
    -> f:(Parallel_kernel.t @ local -> int -> 'a @ m -> 'b or_null) @ shareable
    -> 'b t
end

module type%template Inplace = sig @@ portable
  type ('a : any mod portable separable) t

  [@@@kind.default k = ks]

  (** [map_inplace parallel t ~f] overwrites an array with the result of [f] applied to
      each of its elements. *)
  val map_inplace
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t
    -> f:(Parallel_kernel.t @ local -> 'a -> 'a) @ shareable
    -> unit

  (** [mapi_inplace parallel t ~f] overwrites an array with the result of [f] applied to
      each of its elements and their indices. *)
  val mapi_inplace
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t
    -> f:(Parallel_kernel.t @ local -> int -> 'a -> 'a) @ shareable
    -> unit

  (** [init_inplace parallel t ~f] overwrites an array with the result of [f] applied to
      each array index. This can be much faster than using [mapi_inplace] since it does
      not need to read the array. *)
  val init_inplace
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t
    -> f:(Parallel_kernel.t @ local -> int -> 'a) @ shareable
    -> unit

  (** [sort_inplace parallel t ~compare] unstably sorts [t] with respect to [compare]. *)
  val sort_inplace
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t
    -> compare:
         (Parallel_kernel.t @ local -> 'a @ local shared -> 'a @ local shared -> int)
       @ shareable
    -> unit

  (** [stable_sort_inplace parallel t ~compare] stably sorts [t] with respect to
      [compare]. *)
  val stable_sort_inplace
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t
    -> compare:
         (Parallel_kernel.t @ local -> 'a @ local shared -> 'a @ local shared -> int)
       @ shareable
    -> unit

  (** [scan_inplace parallel t ~init ~f] overwrites [t] to contain the its exclusive
      prefix sums with respect to [f]. The first element becomes [init] and the full
      reduction of [t] is returned. [f] must be associative and [f init x] must equal [x]. *)
  val scan_inplace
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t
    -> init:'a
    -> f:(Parallel_kernel.t @ local -> 'a @ shared -> 'a @ shared -> 'a) @ shareable
    -> 'a

  (** [scan_inclusive_inplace parallel t ~init ~f] overwrites [t] to contain its inclusive
      prefix sums with respect to [f]. The first element is unchanged. [f] must be
      associative and [f init x] must equal [x]. *)
  val scan_inclusive_inplace
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> 'a t
    -> init:'a
    -> f:(Parallel_kernel.t @ local -> 'a @ shared -> 'a @ shared -> 'a) @ shareable
    -> unit
end
[@@kind_set ks = (value_or_null, base_or_null)]

module type%template Slice0 = sig @@ portable
  type ('a : any mod portable separable) array : value mod c portable with 'a

  (** Slices represent a contiguous portion of an array. *)
  type ('a : any mod portable separable) t : value mod c portable with 'a

  (** [length t] returns the number of elements in [t]. *)
  val length : ('a : any mod portable separable). 'a t @ contended local -> int

  [@@@kind.default k = ks]
  [@@@mode.default m = (uncontended, shared)]

  (** [slice ~i ~j array] is a slice representing [array\[i..j)]. *)
  val slice
    : ('a : k mod portable separable).
    ?i:int -> ?j:int -> 'a array @ m -> 'a t @ local m

  (** [sub ~i ~j slice] is a slice representing [slice\[i..j)]. *)
  val sub
    : ('a : k mod portable separable).
    ?i:int -> ?j:int -> 'a t @ local m -> 'a t @ local m

  (** To read a value from a parallel array, we must prove that it does not escape its
      capsule. This is the case if its type crosses contention, or if it is manipulated
      within a portable function. *)

  (** [get t i] reads the element at index [i]. Raises [Invalid_arg] if [i] is not in the
      range \[0..length t). *)
  val get : ('a : k mod contended portable separable). 'a t @ local m -> int -> 'a @ m

  (** [unsafe_get t i] unsafely reads the element at index [i]. *)
  val unsafe_get
    : ('a : k mod contended portable separable).
    'a t @ local m -> int -> 'a @ m

  (** [extract t i f] applies [f] with the element read from index [i]. Raises
      [Invalid_arg] if [i] is not in the range \[0..length t). *)
  val extract
    : ('a : k mod portable separable).
    'a t @ local m
    -> int
    -> ('a @ m -> 'b @ contended portable) @ local once portable
    -> 'b @ contended portable

  (** [unsafe_extract t i f] applies [f] with the element unsafely read from index [i]. *)
  val unsafe_extract
    : ('a : k mod portable separable).
    'a t @ local m
    -> int
    -> ('a @ m -> 'b @ contended portable) @ local once portable
    -> 'b @ contended portable

  (** [fork_join2 parallel ~pivot t f g] splits the slice [t] into two sub-slices
      representing [t\[0..pivot)] and [t\[pivot..length t)], respectively. The sub-slices
      are passed to [f] and [g], which run in parallel (refer to
      [{Parallel_kernel.fork_join2}]). *)
  val fork_join2
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> ?pivot:int
    -> 'a t @ local m
    -> (Parallel_kernel.t @ local -> 'a t @ local m -> 'b) @ forkable local once shareable
    -> (Parallel_kernel.t @ local -> 'a t @ local m -> 'c) @ once shareable
    -> #('b * 'c)

  (** [for_ parallel ~pivots t ~f] splits the slice [t] into multiple sub-slices
      representing the ranges [t\[0..pivots[0])], [t\[pivots[i]..pivots[i+1])], etc. The
      function [f] is evaluated for each sub-slice in parallel. [pivots] must be
      non-decreasing, but may have duplicate elements, resulting in empty sub-slices. *)
  val for_
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> pivots:int iarray
    -> 'a t @ local m
    -> f:(Parallel_kernel.t @ local -> 'a t @ local m -> unit) @ shareable
    -> unit

  (** [fori parallel ~pivots t ~f] splits the slice [t] into multiple sub-slices
      representing the ranges [t\[0..pivots[0])], [t\[pivots[i]..pivots[i+1])], etc. The
      function [f] is evaluated for each sub-slice and its index in parallel. [pivots]
      must be non-decreasing, but may have duplicate elements. *)
  val fori
    : ('a : k mod portable separable).
    Parallel_kernel.t @ local
    -> pivots:int iarray
    -> 'a t @ local m
    -> f:(Parallel_kernel.t @ local -> int -> 'a t @ local m -> unit) @ shareable
    -> unit
end
[@@kind_set ks = (value_or_null, base_or_null)] [@@mode c = (uncontended, contended)]

module type%template Islice = Slice0 [@mode contended] [@kind_set ks]
[@@kind_set ks = (value_or_null, base_or_null)]

module type%template Slice = sig @@ portable
  include Slice0 [@mode uncontended] [@kind_set ks] (** @inline *)

  [@@@kind.default k = ks]

  (** To store a value in a parallel array, we must prove that it does not share
      unsynchronized state with any other elements. This is the case if its type crosses
      contention or it lives in a fresh capsule. *)

  (** [set t i a] stores the element [a] at index [i]. Raises [Invalid_arg] if [i] is not
      in the range \[0..length t). *)
  val set : ('a : k mod contended portable separable). 'a t @ local -> int -> 'a -> unit

  (** [unsafe_insert t i f] unsafely stores [f a] at index [i]. *)
  val unsafe_set
    : ('a : k mod contended portable separable).
    'a t @ local -> int -> 'a -> unit

  (** [insert t i f] stores [f a] at index [i]. Raises [Invalid_arg] if [i] is not in the
      range \[0..length t). *)
  val insert
    : ('a : k mod portable separable).
    'a t @ local -> int -> (unit -> 'a) @ local once portable -> unit

  (** [unsafe_insert t i f] unsafely stores [f a] at index [i]. *)
  val unsafe_insert
    : ('a : k mod portable separable).
    'a t @ local -> int -> (unit -> 'a) @ local once portable -> unit
end
[@@kind_set ks = (value_or_null, base_or_null)]

module type Parallel_arrays = sig @@ portable
  include%template sig
    [@@@kind_set.default ks = (value_or_null, base_or_null)]

    module type Init = Init [@modality m] [@kind_set ks]
    [@@modality m = (non_float, separable)]

    module type Get = Get [@kind_set ks]
    module type Set = Set [@kind_set ks]
    module type Map = Map [@kind_set ks]
    module type Reduce = Reduce [@kind_set ks]
    module type Sort = Sort [@kind_set ks]
    module type Scan = Scan [@kind_set ks]
    module type Filter = Filter [@kind_set ks]
    module type Inplace = Inplace [@kind_set ks]
    module type Islice = Islice [@kind_set ks]
    module type Slice = Slice [@kind_set ks]
  end

  module type Filter_map = Filter_map

  (** The following "parallel array" types are distinct from normal arrays because their
      elements must always be portable and must never share unsynchronized state.

      This is the case when the element type ['a] crosses portability and contention, or
      when the array elements are portable and live in separate capsules (that is, were
      returned at portable uncontended from portable functions).

      To work with a pre-existing array whose element type does not cross portability and
      contention, the elements may be wrapped in [Modes.Portended.t] or [Capsule.Data.t]
      as applicable. *)

  module Array : sig
    type ('a : any mod portable separable) t : mutable_data with 'a

    (** [length t] returns the number of elements in [t]. *)
    external length
      : ('a : any mod portable separable).
      'a t @ contended -> int
      = "%array_length"
    [@@layout_poly]

    include%template sig
      [@@@mode.default m = (uncontended, shared)]

      val of_array : ('a : any mod contended portable separable). 'a array @ m -> 'a t @ m
      val to_array : ('a : any mod contended portable separable). 'a t @ m -> 'a array @ m
    end

    include Get [@kind_set base_or_null] with type ('a : any) t := 'a t (** @inline *)

    include Set [@kind_set base_or_null] with type ('a : any) t := 'a t (** @inline *)

    module Slice : Slice [@kind_set base_or_null] with type ('a : any) array := 'a t

    (** @inline *)
    include
      Init
      [@modality non_float] [@kind_set base_or_null]
      with type ('a : any) t := 'a t
       and type ('a : any) init = int

    include Map [@kind_set base_or_null] with type ('a : any) t := 'a t (** @inline *)

    include Reduce [@kind_set base_or_null] with type ('a : any) t := 'a t (** @inline *)

    include Sort [@kind_set base_or_null] with type ('a : any) t := 'a t (** @inline *)

    include Scan [@kind_set base_or_null] with type ('a : any) t := 'a t (** @inline *)

    include Filter [@kind_set base_or_null] with type ('a : any) t := 'a t (** @inline *)

    include Inplace [@kind_set base_or_null] with type ('a : any) t := 'a t (** @inline *)

    include Filter_map with type ('a : any) t := 'a t (** @inline *)
  end

  module Iarray : sig
    type ('a : any mod portable separable) t : immutable_data with 'a

    (** [length t] returns the number of elements in [t]. *)
    external length
      : ('a : any mod portable separable).
      'a t @ contended -> int
      = "%array_length"
    [@@layout_poly]

    include%template sig
      [@@@mode.default m = (uncontended, shared)]

      val of_iarray
        : ('a : any mod contended portable separable).
        'a iarray @ m -> 'a t @ m

      val to_iarray
        : ('a : any mod contended portable separable).
        'a t @ m -> 'a iarray @ m
    end

    include Get [@kind_set base_or_null] with type ('a : any) t := 'a t (** @inline *)

    module Slice : Islice [@kind_set base_or_null] with type ('a : any) array := 'a t

    (** @inline *)
    include
      Init
      [@modality non_float] [@kind_set base_or_null]
      with type ('a : any) t := 'a t
       and type ('a : any) init = int

    include Map [@kind_set base_or_null] with type ('a : any) t := 'a t (** @inline *)

    include Reduce [@kind_set base_or_null] with type ('a : any) t := 'a t (** @inline *)

    include Sort [@kind_set base_or_null] with type ('a : any) t := 'a t (** @inline *)

    include Scan [@kind_set base_or_null] with type ('a : any) t := 'a t (** @inline *)

    include Filter [@kind_set base_or_null] with type ('a : any) t := 'a t (** @inline *)

    include Filter_map with type ('a : any) t := 'a t (** @inline *)
  end

  module Bigstring : sig
    module Kind = Bigstring.Kind

    type ('a : any) t = 'a Bigstring.t = private
      { kind : 'a Kind.t
      ; data : Base_bigstring.t
      }
    [@@deriving sexp_of]

    val%template with_kind_exn : 'a Bigstring.Kind.t -> Base_bigstring.t @ m -> 'a t @ m
    [@@mode m = (uncontended, shared)]

    (** [length t] returns the number of elements in [t]. *)
    val length : 'a t @ contended -> int

    include Get with type ('a : any) t := 'a t (** @inline *)

    include Set with type ('a : any) t := 'a t (** @inline *)

    module Slice : Slice with type ('a : any) array := 'a t

    val%template of_slice : 'a Slice.t @ local m -> 'a t @ m
    [@@mode m = (uncontended, shared)]

    (** [of_string s] is a new character bigstring initialized by copying the contents of
        the string [s]. The optional arguments [pos] and [len] are forwarded to
        {!Base_bigstring.of_string} *)
    val of_string : ?pos:int -> ?len:int -> string -> char t

    (** @inline *)
    include
      Init
      [@modality separable]
      with type ('a : any) t := 'a t
       and type ('a : any) init := 'a Kind.t * int

    include Reduce with type ('a : any) t := 'a t (** @inline *)

    include Sort with type ('a : any) t := 'a t (** @inline *)

    include Scan with type ('a : any) t := 'a t (** @inline *)

    include Filter with type ('a : any) t := 'a t (** @inline *)

    include Inplace with type ('a : any) t := 'a t (** @inline *)
  end

  module Bigarray : sig
    module Kind : sig
      type ('a : any, 'k : any) t = ('a, 'k) Bigarray.kind =
        | Float32 : (float, Bigarray.float32_elt) t
        | Float64 : (float, Bigarray.float64_elt) t
        | Int8_signed : (int, Bigarray.int8_signed_elt) t
        | Int8_unsigned : (int, Bigarray.int8_unsigned_elt) t
        | Int16_signed : (int, Bigarray.int16_signed_elt) t
        | Int16_unsigned : (int, Bigarray.int16_unsigned_elt) t
        | Int32 : (int32, Bigarray.int32_elt) t
        | Int64 : (int64, Bigarray.int64_elt) t
        | Int : (int, Bigarray.int_elt) t
        | Nativeint : (nativeint, Bigarray.nativeint_elt) t
        | Complex32 : (Stdlib.Complex.t, Bigarray.complex32_elt) t
        | Complex64 : (Stdlib.Complex.t, Bigarray.complex64_elt) t
        | Char : (char, Bigarray.int8_unsigned_elt) t
        | Float16 : (float, Bigarray.float16_elt) t
    end

    module Layout : sig
      type ('a : any) t = 'a Bigarray.layout =
        | C_layout : Bigarray.c_layout t
        | Fortran_layout : Bigarray.fortran_layout t
    end

    module Spec : sig
      type ('a : any) t =
        | T : ('a : any) ('b : any) ('c : any). ('a, 'b) Kind.t * 'c Layout.t -> 'a t
    end

    type ('a : any) t =
      | T : ('a : any) ('b : any) ('c : any). ('a, 'b, 'c) Bigarray.Array1.t -> 'a t
    [@@unboxed]

    (** Wraps a [Bigarray.Array1.t] as a parallel bigarray. *)
    val%template of_bigarray
      : ('a : any) ('b : any) ('c : any).
      ('a, 'b, 'c) Bigarray.Array1.t @ m -> 'a t @ m
    [@@mode m = (uncontended, shared)]

    (** Wraps a [Bigarray.kind] and [Bigarray.layout] as a parallel bigarray spec. *)
    val kind : ('a, _) Kind.t -> _ Layout.t -> 'a Spec.t

    (** [length t] returns the number of elements in [t]. *)
    val length : 'a t @ contended -> int

    include Get with type ('a : any) t := 'a t (** @inline *)

    include Set with type ('a : any) t := 'a t (** @inline *)

    module Slice : Slice with type ('a : any) array := 'a t

    val%template of_slice : 'a Slice.t @ local m -> 'a t @ m
    [@@mode m = (uncontended, shared)]

    (** @inline *)
    include
      Init
      [@modality separable]
      with type ('a : any) t := 'a t
       and type ('a : any) init := 'a Spec.t * int

    include Reduce with type ('a : any) t := 'a t (** @inline *)

    include Sort with type ('a : any) t := 'a t (** @inline *)

    include Scan with type ('a : any) t := 'a t (** @inline *)

    include Filter with type ('a : any) t := 'a t (** @inline *)

    include Inplace with type ('a : any) t := 'a t (** @inline *)
  end
end