(B :
  sig
    type t
    val empty : t
    val length : t -> int
    val get : t -> int -> char
    val make : int -> char -> t
    val init : int -> f:(int -> char) -> t
    val compare : t -> t -> int
    val concat : sep:t -> t list -> t
    val iter : f:(char -> unit) -> t -> unit
    val iteri : f:(int -> char -> unit) -> t-> unit
    val map : f:(char -> char) -> t -> t
    val mapi : f:(int -> char -> char) -> t -> t
    val index_from : t -> int -> char -> int
    val rindex_from : t -> int -> char -> int
    val sub : t -> pos:int -> len:int -> t
    val of_buffer : Buffer.t -> t
    val string_for_output : t -> string
  end) = struct

  type character = char
  type t = B.t

  let max_string_length = Some Sys.max_string_length
  let empty = B.empty
  let compare = B.compare
  let is_empty t = (compare B.empty t = 0)

  let make = B.make
  let length = B.length

  let of_character = B.make 1

  let of_character_list cl =
    let r = ref cl in
    B.init (List.length cl) ~f:(fun _ ->
      let c = List.hd !r in
      r := List.tl !r;
      c)

  let to_character_list s =
    let res = ref [] in
    for i = length s - 1 downto 0 do
      res := (B.get s i) :: !res
    done;
    !res

  let get s ~index =
    try Some (B.get s index)
    with _ -> None

  (* Since our set always returns a copy! *)
  let set s ~index ~v =
    if index > length s - 1
    then None
    else Some (B.mapi (fun i c -> if i = index then v else c) s)

  let get_exn s ~index = B.get s index

  let set_exn s ~index ~v =
    match set s ~index ~v with None -> invalid_arg "set_exn" | Some s -> s

  let compare = B.compare

  let compare_substring (a, idxa, lena) (b, idxb, lenb) =
    let module With_exns = struct
      exception Return of int
      exception Left_out of int
      exception Right_out of int
      let f () =
        try
          let shortest = min lena lenb in
          for i = 0 to shortest - 1 do
            let ca = try B.get a (idxa + i) with _ -> raise (Left_out i) in
            let cb = try B.get b (idxb + i) with _ -> raise (Right_out i) in
            let c = Char.compare ca cb in
            if c <> 0
            then raise (Return c)
            else ()
          done;
          (Pervasives.compare (lena : int) lenb)
        with
        | Return c -> c
        | Left_out c -> (* a went out of bounds at 'c + idxa' *) -1
        | Right_out _ -> (* b went out of bounds at 'c + idxb' *)
          (* so, a is “longer” *) 1
    end in
    With_exns.f ()

  type s = t
  module T_length_and_compsub = struct
    type t = s
    let length = length
    let compare_substring = compare_substring
  end

  include Compare_substring_strict_of_loose(T_length_and_compsub)
  include Make_index_of_string(T_length_and_compsub)

  let concat ?(sep=B.empty) sl = B.concat ~sep sl

  let fold t ~init ~f =
    let res = ref init in
    for i = 0 to length t - 1 do
      res := f !res (B.get t i);
    done;
    !res

  let foldi t ~init ~f =
    let res = ref init in
    for i = 0 to length t - 1 do
      res := f i !res (B. get t i);
    done;
    !res

  let fold2_exn t1 t2 ~init ~f =
    let lgth1 = (length t1) in
    let lgth2 = (length t2) in
    match lgth1, lgth2 with
    | 0, 0 -> init
    | _, _ when lgth1 <> lgth2 -> invalid_arg "fold2_exn"
    | lgth1, lgth2 ->
        let res = ref init in
        for i = 0 to lgth1 - 1 do
          res := f !res (B.get t1 i) (B.get t2 i);
        done;
        !res

  let sub_exn t ~index ~length =
    if length = 0 then empty else B.sub t ~pos:index ~len:length

  let sub t ~index ~length =
    if length = 0 then Some empty else
      try Some (B.sub t ~pos:index ~len:length)
      with e -> None

  let slice_exn ?(start=0) ?finish t =
    let length_of_t = length t in
    let bound_check strict m x =
      let out_of_ub = if strict then x > length_of_t else x >= length_of_t in
      if x < 0 || (not (is_empty t) && out_of_ub) then
        Printf.ksprintf invalid_arg "slice_exn: invalid %s %d" m x
      else x
    in
    let _      = bound_check false "start" start
    and finish =
      match finish with
      | None   -> length_of_t
      | Some f -> bound_check true "finish" f
    in
    sub_exn t ~index:start ~length:(finish - start)

  let slice ?start ?finish t =
    try Some (slice_exn ?start ?finish t)
    with _ -> None

  let iter t ~f = B.iter t ~f
  let iteri t ~f = B.iteri t ~f
  let iter_reverse t ~f =
    for i = length t -1 downto 0 do
      f (get_exn t i)
    done

  let rev t =
    let lgth = length t in
    match lgth with
    | 0 -> empty
    | lgth ->
        let o = lgth - 1 in
        B.mapi ~f:(fun i _ -> B.get t (o - i)) t

  let map t ~f = B.map t ~f

  let map2_exn t1 t2 ~f =
    let lgth1 = (length t1) in
    let lgth2 = (length t2) in
    match lgth1, lgth2 with
    | 0, 0 -> empty
    | _, _ when lgth1 <> lgth2 -> invalid_arg "map2_exn"
    | lgth1, lgth2 ->
        B.mapi ~f:(fun i c -> f c (B.get t2 i)) t1

  let mapi t ~f = B.mapi t ~f

  let for_all t ~f =
    try (iter t (fun x -> if not (f x) then raise Not_found else ()); true)
    with Not_found -> false

  let exists t ~f =
    try (iter t (fun x -> if f x then raise Not_found else ()); false)
    with Not_found -> true

  let index_of_character t ?(from=0) c =
    let from = if from <= 0 then 0 else min (length t) from in
    try Some (B.index_from t from c)
    with _ -> None

  let index_of_character_reverse t ?from c =
    let from =
      let length_of_t = length t in
      match from with
      | None -> length_of_t - 1
      | Some s when s < 0 -> -1
      | Some s when s > length_of_t - 1 -> length_of_t - 1
      | Some s -> s
    in
    try Some (B.rindex_from t from c)
    with _ -> None

  let resize_from_length ~from ?length ~length_of_s =
    let from = if from <= 0 then 0 else min length_of_s from in
    let length =
      match length with
      | None -> length_of_s - from
      | Some lg when lg <= 0 -> 0
      | Some lg -> min (length_of_s - from) lg
    in
    (from, length)

  let find ?(from=0) ?length s ~f =
    let length_of_s = B.length s in
    let from, length = resize_from_length ~from ?length ~length_of_s in
    let found = ref None in
    let i = ref 0 in
    while !found = None && !i  < length do
      if f (get_exn s (!i + from))
      then found := Some (!i + from)
      else incr i
    done;
    !found

  let find_reverse ?from ?length s ~f =
    let length_of_s = B.length s in
    if length_of_s = 0 then None
    else begin
      let from =
        match from with
        | None -> length_of_s - 1
        | Some s when s < 0 -> -1
        | Some s when s >= length_of_s - 1 -> length_of_s - 1
        | Some s -> s
      in
      let length =
        match length with
        | None -> from + 1
        | Some l when l <= 0 -> 0
        | Some l when l >= from + 1 -> from + 1
        | Some l -> l
      in
      let found = ref None in
      let i = ref from in
      while !found = None && !i >= from - length + 1 do
        (* dbg "i: %d from: %d length: %d" !i from length; *)
        if f (get_exn s !i)
        then found := Some (!i)
        else decr i
      done;
      !found
    end

  let filter_map ?(from=0) ?length s ~f =
    let length_of_s = B.length s in
    let from, length = resize_from_length ~from ?length ~length_of_s in
    if length = 0 then empty
    else begin
      let b = Buffer.create length in
      for i = 0 to length - 1 do
        match f (get_exn s (i + from)) with
        | Some c -> Buffer.add_char b c
        | None -> ()
      done;
      B.of_buffer b
    end

  let filter ?from ?length s ~f =
      filter_map ?from ?length s ~f:(fun c -> if f c then Some c else None)

  include Make_strip_function (struct
      type t = s
      type character = char
      let empty = empty
      let length = length
      let sub_exn = sub_exn
      let find = find
      let find_reverse = find_reverse
      let is_whitespace = Native_character.is_whitespace
    end)

  include Make_split_function(struct
      type t = s
      type character = char
      let length = length
      let sub_exn = sub_exn
      let index_of_string = index_of_string
      let index_of_character = index_of_character
    end)

  include Make_prefix_suffix_array (struct
      type t = s
      type character = char
      let length = length
      let get = B.get
      let sub_exn = sub_exn
    end)

  include Make_split_at_index_functions(struct
      type t = s
      type character = char
      let empty = empty
      let length = length
      let sub_exn = sub_exn
    end)

  module Make_output (ModelApi.OUTPUT_MODEL) = struct

    let output chan t = Model.output chan (B.string_for_output t)

  end

  let take_while_with_index t ~f =
    let buf = Buffer.create (length t) in
    let rec loop idx =
      match get t idx with
      | Some c when f idx c -> Buffer.add_char buf c; loop (idx + 1)
      | _ -> ()
    in
    loop 0;
    B.of_buffer buf

  let take_while t ~f = take_while_with_index t ~f:(fun _ c -> f c)

end