File: dequeue.ml

package info (click to toggle)
janest-core 107.01-5
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 2,440 kB
  • sloc: ml: 26,624; ansic: 2,498; sh: 49; makefile: 29
file content (194 lines) | stat: -rw-r--r-- 7,110 bytes parent folder | download
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
(******************************************************************************
 *                             Core                                           *
 *                                                                            *
 * Copyright (C) 2008- Jane Street Holding, LLC                               *
 *    Contact: opensource@janestreet.com                                      *
 *    WWW: http://www.janestreet.com/ocaml                                    *
 *                                                                            *
 *                                                                            *
 * This library is free software; you can redistribute it and/or              *
 * modify it under the terms of the GNU Lesser General Public                 *
 * License as published by the Free Software Foundation; either               *
 * version 2 of the License, or (at your option) any later version.           *
 *                                                                            *
 * This library is distributed in the hope that it will be useful,            *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU          *
 * Lesser General Public License for more details.                            *
 *                                                                            *
 * You should have received a copy of the GNU Lesser General Public           *
 * License along with this library; if not, write to the Free Software        *
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  *
 *                                                                            *
 ******************************************************************************)

(* Double-ended index-able queue *)

(* the user's index is called the "conceptual" index (variable names are index or i),
   while the corresponding index into the physical array is called the "physical" index
   (variable names are pindex or p)
   the physical length is "Array.length buf.data" (variable name plength) while the
   conceptual length is buf.length (variable name length)
   invariant: physical_index = conceptual_index mod physical_length
   the "front" has smaller indices
   physical array lengths are always powers of two
   maybe I should insert dummy values into dropped indices to g.c. junk sooner? *)



open Std_internal


type 'a t = { mutable data: 'a array;
              mutable min_index: int; (* conceptual *)
              mutable length: int;    (* conceptual: max_index = min_index + length - 1 *)
              never_shrink: bool;
              dummy: 'a;
            }
 with sexp

type 'a sexpable = 'a t

let create ?(never_shrink=false) ?(initial_index=0) ~dummy () =
  { data = Array.create (1 lsl 3) dummy;  (* (1 lsl 3) = 8, must be power of 2! *)
    min_index = initial_index;
    length = 0;
    never_shrink = never_shrink;
    dummy = dummy;
  }

let length buf = buf.length

let is_empty buf = buf.length = 0

let front_index buf = buf.min_index

let back_index buf = buf.min_index + buf.length - 1

let is_full buf = buf.length = Array.length buf.data

let invariant buf =
  assert (buf.length <= Array.length buf.data)

let fast_double x = x lsl 1             (* x * 2 *)
let fast_half x = x asr 1               (* x / 2 *)
let fast_quarter x = x asr 2            (* x / 4 *)
let fast_mod x l = x land (l-1)         (* x % l (works when l is power of 2) *)
let fast_is_power_2 x = x land (x-1) = 0(* x=2^n for non-negative integer n or x=0 or -max_int-1*)

let check_index fname buf i =
  if i < buf.min_index || i >= buf.min_index + buf.length
  then invalid_arg (sprintf "Dequeue.%s: index %i is not in [%d, %d]"
                      fname i (front_index buf) (back_index buf)
                   )

let get buf i =
  check_index "get" buf i;
  buf.data.(fast_mod i (Array.length buf.data))

let get_front buf =
  get buf buf.min_index

let get_back buf =
  get buf (back_index buf)

let set buf i v =
  check_index "set" buf i;
  buf.data.(fast_mod i (Array.length buf.data)) <- v

let iteri ~f buf =
  for i=(front_index buf) to (back_index buf) do
    f i (get buf i)
  done

let iter ~f buf = iteri ~f:(fun _ x -> f x) buf

let foldi ~f ~init buf =
  let acc = ref init in
  iteri ~f:(fun i a -> acc := f !acc i a) buf;
  !acc

let fold ~f ~init buf = foldi ~f:(fun acc _ a -> f acc a) ~init buf

let copy_data buf new_plength =         (* plength = physical array length *)
  let old_plength = Array.length buf.data in
  (* these invariants are maintained -- let's make them explicit *)
  assert (new_plength >= buf.length);
  assert (fast_double new_plength = old_plength ||
          fast_double old_plength = new_plength);
  assert (fast_is_power_2 old_plength && fast_is_power_2 new_plength);
  let newdata = Array.create new_plength buf.dummy in
  let src_min_pindex = fast_mod buf.min_index old_plength in
  let dst_min_pindex = fast_mod buf.min_index new_plength in
  let first_copy_length =
    let small_plength = Int.min old_plength new_plength in
    let small_min_pindex = fast_mod buf.min_index small_plength in
    small_plength - small_min_pindex
  in
  Array.blit ~src:buf.data ~dst:newdata
    ~src_pos:src_min_pindex
    ~dst_pos:dst_min_pindex
    ~len:first_copy_length;
  if first_copy_length < buf.length
  then begin
    let second_copy_length = buf.length - first_copy_length in
    let second_copy_start_index = buf.min_index + first_copy_length in
    let src_start_pindex = fast_mod second_copy_start_index old_plength in
    let dst_start_pindex = fast_mod second_copy_start_index new_plength in
    Array.blit ~src:buf.data ~dst:newdata
      ~src_pos:src_start_pindex
      ~dst_pos:dst_start_pindex
      ~len:second_copy_length
  end;
  newdata

let swap_array buf new_plength =
  let newdata = copy_data buf new_plength in
  buf.data <- newdata

let maybe_expand buf =
  if is_full buf
  then swap_array buf (fast_double (Array.length buf.data))


let maybe_shrink buf =
  if not buf.never_shrink && buf.length < fast_quarter (Array.length buf.data)
  then swap_array buf (fast_half (Array.length buf.data))

let push_front buf v =
  maybe_expand buf;
  buf.min_index <- buf.min_index - 1;
  buf.length <- buf.length + 1;
  set buf buf.min_index v

let push_back buf v =
  maybe_expand buf;
  buf.length <- buf.length + 1;
  set buf (back_index buf) v

let drop_front ?(n=1) buf =
  if n > buf.length || n < 0 then invalid_arg "Dequeue.drop_front";
  buf.min_index <- buf.min_index + n;
  buf.length <- buf.length - n;
  maybe_shrink buf

let drop_back ?(n=1) buf =
  if n > buf.length || n < 0 then invalid_arg "Dequeue.drop_back";
  buf.length <- buf.length - n;
  maybe_shrink buf

let take_front buf =
  let v = get_front buf in
  drop_front buf;
  v

let take_back buf =
  let v = get_back buf in
  drop_back buf;
  v

let drop_indices_less_than buf i =
  drop_front ~n:(i - buf.min_index) buf

let drop_indices_greater_than buf i =
  drop_back ~n:(back_index buf - i) buf