diff --git a/src/Arrays.ml b/src/Arrays.ml
new file mode 100644
index 0000000000000000000000000000000000000000..5c943ae7ae7b90defe49042debe04e31f0f5d189
--- /dev/null
+++ b/src/Arrays.ml
@@ -0,0 +1,88 @@
+module QArray = struct
+
+  (* Most functions are inherited from the module [Array]. *)
+
+  include Array
+
+  (* Types. *)
+
+  type mut
+  type immut
+  type ('m, 'a) qarray = 'a array
+  type ('m, 'a) t = ('m, 'a) qarray
+
+  (* In the future, we might wish to publish the type equality
+     [(mut, 'a) qarray = 'a array].
+     This could be done by exploiting an equality GADT.
+     However, the type [(_, _) eq] does not yet exist in OCaml's
+     standard library, so let us wait. *)
+
+  (* The type equality [(immut, 'a) qarray = 'a array]
+     must of course not be published. *)
+
+  (* The empty array. *)
+
+  let empty = [||]
+
+  (* Weak type equality witnesses. *)
+
+  let import a = a
+  let export a = a
+
+end
+
+(* The implementation of [IArray] exploits the type equality
+   [('m, 'a) qarray = 'a array], which is not part of the public
+   interface of [QArray]. For this reason, [QArray] and [IArray]
+   must be placed in the same file (or [QArray] must publish unsafe
+   operations). *)
+
+module IArray = struct
+
+  (* Most functions are inherited from the module [Array]. *)
+
+  include Array
+
+  (* The type ['a iarray]. *)
+
+  type 'a iarray = 'a array
+
+  (* The type ['a t], another name for the same type. *)
+
+  type 'a t = 'a iarray
+
+  (* The empty array. *)
+
+  let empty = [||]
+
+  (* Conversions between mutable arrays and immutable arrays.
+     In either direction, a copy is required; we cannot allow
+     a single array to be viewed both as mutable and immutable. *)
+
+  let to_array = Array.copy
+  let of_array = Array.copy
+
+  (* Sorting requires a copy, followed with an in-place sorting step.
+     It might be possible to propose a faster sorting algorithm, by
+     taking advantage of the fact that we are allowed to allocate an
+     auxiliary array. *)
+
+  let sort cmp a =
+    let a = copy a in
+    sort cmp a;
+    a
+
+  let stable_sort cmp a =
+    let a = copy a in
+    stable_sort cmp a;
+    a
+
+  let fast_sort cmp a =
+    let a = copy a in
+    fast_sort cmp a;
+    a
+
+  (* It would be nice if we could redefine the array access notation
+     [a.(i)], but OCaml does not allow this. *)
+
+end
diff --git a/src/Arrays.mli b/src/Arrays.mli
new file mode 100644
index 0000000000000000000000000000000000000000..69368f62417034b54a88fa03506bb22ac72e20dc
--- /dev/null
+++ b/src/Arrays.mli
@@ -0,0 +1,170 @@
+module QArray : sig
+
+  (**Mutability-polymorphic arrays. *)
+
+  (**{1 Types} *)
+
+  (**An array of type [(mut, 'a) qarray] is definitely mutable. It can be
+     read, written, and used in a context where a mutable array is expected.
+
+     An array of type [(immut, 'a) qarray] is definitely immutable. It can be
+     read, but not written. It can be used in a context where an immutable
+     array is expected.
+
+     An array of type [('q, 'a) qarray], where the qualifier ['q] is abstract,
+     could be mutable or immutable. It can be read, but not written, and
+     cannot be used in a context where a mutable array or an immutable array
+     is expected.
+
+     When a function takes an argument of type [(_, 'a) qarray], this function
+     does not care whether the array is mutable or immutable; this function
+     can only read this array.
+
+     When a function returns a result of type [(_, 'a) qarray], this function
+     creates a fresh array, and does not care whether the array is viewed by
+     the caller as mutable or immutable, but the caller must choose one of
+     these two possibilities: an array cannot be both mutable and immutable.
+
+     The function [copy] can be used to turn a mutable array into an immutable
+     array, and vice-versa. Indeed, [copy] can be applied to an arbitrary
+     array (mutable, immutable, or abstract) and the new array can be regarded
+     either as mutable or as immutable, as one wishes. *)
+
+  type mut
+  type immut
+  type ('q, 'a) qarray
+  type ('q, 'a) t = ('q, 'a) qarray
+
+  (** {1 New Operations} *)
+
+  (**[empty] is an array of length 0. It is both mutable and immutable. *)
+  val empty : (_, 'a) qarray
+
+  (**[import] is an identity function. *)
+  val import: 'a array -> (mut, 'a) qarray
+
+  (**[export] is an identity function. *)
+  val export: (mut, 'a) qarray -> 'a array
+
+  (** {1 Standard Operations} *)
+
+  val length : (_, 'a) qarray -> int
+  val get : (_, 'a) qarray -> int -> 'a
+  val set : (mut, 'a) qarray -> int -> 'a -> unit
+  val make : int -> 'a -> (_, 'a) qarray
+  val create_float : int -> (mut, float) qarray
+  val init : int -> (int -> 'a) -> (_, 'a) qarray
+  val make_matrix : int -> int -> 'a -> (mut, (mut, 'a) qarray) qarray
+  val append : (_, 'a) qarray -> (_, 'a) qarray -> (_, 'a) qarray
+  val concat : (_, 'a) qarray list -> (_, 'a) qarray
+  val sub : (_, 'a) qarray -> int -> int -> (_, 'a) qarray
+  val copy : (_, 'a) qarray -> (_, 'a) qarray
+  val fill : (mut, 'a) qarray -> int -> int -> 'a -> unit
+  val blit : (_, 'a) qarray -> int -> (mut, 'a) qarray -> int -> int -> unit
+  val to_list : (_, 'a) qarray -> 'a list
+  val of_list : 'a list -> (_, 'a) qarray
+  val iter : ('a -> unit) -> (_, 'a) qarray -> unit
+  val iteri : (int -> 'a -> unit) -> (_, 'a) qarray -> unit
+  val map : ('a -> 'b) -> (_, 'a) qarray -> (_, 'b) qarray
+  val mapi : (int -> 'a -> 'b) -> (_, 'a) qarray -> (_, 'b) qarray
+  val fold_left : ('a -> 'b -> 'a) -> 'a -> (_, 'b) qarray -> 'a
+  val fold_left_map : ('a -> 'b -> 'a * 'c) -> 'a -> (_, 'b) qarray -> 'a * (_, 'c) qarray
+  val fold_right : ('b -> 'a -> 'a) -> (_, 'b) qarray -> 'a -> 'a
+  val iter2 : ('a -> 'b -> unit) -> (_, 'a) qarray -> (_, 'b) qarray -> unit
+  val map2 : ('a -> 'b -> 'c) -> (_, 'a) qarray -> (_, 'b) qarray -> (_, 'c) qarray
+  val for_all : ('a -> bool) -> (_, 'a) qarray -> bool
+  val exists : ('a -> bool) -> (_, 'a) qarray -> bool
+  val for_all2 : ('a -> 'b -> bool) -> (_, 'a) qarray -> (_, 'b) qarray -> bool
+  val exists2 : ('a -> 'b -> bool) -> (_, 'a) qarray -> (_, 'b) qarray -> bool
+  val mem : 'a -> (_, 'a) qarray -> bool
+  val memq : 'a -> (_, 'a) qarray -> bool
+  val find_opt : ('a -> bool) -> (_, 'a) qarray -> 'a option
+  val find_map : ('a -> 'b option) -> (_, 'a) qarray -> 'b option
+  val split : (_, 'a * 'b) qarray -> (_, 'a) qarray * (_, 'b) qarray
+  val combine : (_, 'a) qarray -> (_, 'b) qarray -> (_, 'a * 'b) qarray
+  val sort : ('a -> 'a -> int) -> (mut, 'a) qarray -> unit
+  val stable_sort : ('a -> 'a -> int) -> (mut, 'a) qarray -> unit
+  val fast_sort : ('a -> 'a -> int) -> (mut, 'a) qarray -> unit
+  val to_seq : (_, 'a) qarray -> 'a Seq.t
+  val to_seqi : (_, 'a) qarray -> (int * 'a) Seq.t
+  val of_seq : 'a Seq.t -> (_, 'a) qarray
+
+end
+
+module IArray : sig
+
+  (**This module provides a type of immutable arrays and operations on
+     immutable arrays. An immutable array is internally represented exactly in
+     the same way as a mutable array, but the type system guarantees that an
+     immutable array cannot be modified.
+
+     This module offers a fragment of the functionality of the module
+     [QArray]. The type ['a iarray] is a synonym for [(immut, 'a) qarray], and
+     the operations provided by this module are a subset of the operations
+     provided by [QArray].
+
+     As a slight exception to this rule, the three [sort] functions provided
+     by [IArray] return a sorted copy of the original array, whereas the
+     [sort] functions in [QArray] sort a mutable array in place. *)
+
+  (** {1 Types} *)
+
+  open QArray (* useful on the next line *)
+  type 'a iarray = (immut, 'a) qarray
+  type 'a t = 'a iarray
+
+  (** {1 New Operations} *)
+
+  (**[empty] is an immutable array of length 0. *)
+  val empty : 'a iarray
+
+  (**[to_array a] creates a fresh mutable copy of the immutable array [a]. *)
+  val to_array : 'a iarray -> 'a array
+
+  (**[of_array a] is an immutable copy of the mutable array [a]. *)
+  val of_array : 'a array -> 'a iarray
+
+  (** {1 Standard Operations} *)
+
+  val length : 'a iarray -> int
+  val get : 'a iarray -> int -> 'a
+  (* no [set] *)
+  val make : int -> 'a -> 'a iarray
+  (* no [create_float] *)
+  val init : int -> (int -> 'a) -> 'a iarray
+  (* no [make_matrix] *)
+  val append : 'a iarray -> 'a iarray -> 'a iarray
+  val concat : 'a iarray list -> 'a iarray
+  val sub : 'a iarray -> int -> int -> 'a iarray
+  (* no [copy] *)
+  (* no [fill] *)
+  val blit : 'a iarray -> int -> 'a array -> int -> int -> unit
+  val to_list : 'a iarray -> 'a list
+  val of_list : 'a list -> 'a iarray
+  val iter : ('a -> unit) -> 'a iarray -> unit
+  val iteri : (int -> 'a -> unit) -> 'a iarray -> unit
+  val map : ('a -> 'b) -> 'a iarray -> 'b iarray
+  val mapi : (int -> 'a -> 'b) -> 'a iarray -> 'b iarray
+  val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b iarray -> 'a
+  val fold_left_map : ('a -> 'b -> 'a * 'c) -> 'a -> 'b iarray -> 'a * 'c iarray
+  val fold_right : ('b -> 'a -> 'a) -> 'b iarray -> 'a -> 'a
+  val iter2 : ('a -> 'b -> unit) -> 'a iarray -> 'b iarray -> unit
+  val map2 : ('a -> 'b -> 'c) -> 'a iarray -> 'b iarray -> 'c iarray
+  val for_all : ('a -> bool) -> 'a iarray -> bool
+  val exists : ('a -> bool) -> 'a iarray -> bool
+  val for_all2 : ('a -> 'b -> bool) -> 'a iarray -> 'b iarray -> bool
+  val exists2 : ('a -> 'b -> bool) -> 'a iarray -> 'b iarray -> bool
+  val mem : 'a -> 'a iarray -> bool
+  val memq : 'a -> 'a iarray -> bool
+  val find_opt : ('a -> bool) -> 'a iarray -> 'a option
+  val find_map : ('a -> 'b option) -> 'a iarray -> 'b option
+  val split : ('a * 'b) iarray -> 'a iarray * 'b iarray
+  val combine : 'a iarray -> 'b iarray -> ('a * 'b) iarray
+  val sort : ('a -> 'a -> int) -> 'a iarray -> 'a iarray
+  val stable_sort : ('a -> 'a -> int) -> 'a iarray -> 'a iarray
+  val fast_sort : ('a -> 'a -> int) -> 'a iarray -> 'a iarray
+  val to_seq : 'a iarray -> 'a Seq.t
+  val to_seqi : 'a iarray -> (int * 'a) Seq.t
+  val of_seq : 'a Seq.t -> 'a iarray
+
+end