/[flexichain]/flexichain/flexichain.lisp
ViewVC logotype

Contents of /flexichain/flexichain.lisp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.10 - (show annotations)
Tue Oct 5 05:05:06 2010 UTC (3 years, 6 months ago) by rstrandh
Branch: MAIN
CVS Tags: HEAD
Changes since 1.9: +20 -16 lines
Used REPLACE to implement insert-vector*.

Thanks to Cyrus Harmon for this improvement.
1 ;;; Flexichain
2 ;;; Flexichain data structure definition
3 ;;;
4 ;;; Copyright (C) 2003-2004 Robert Strandh (strandh@labri.fr)
5 ;;; Copyright (C) 2003-2004 Matthieu Villeneuve (matthieu.villeneuve@free.fr)
6 ;;; Copyright (C) 2010 Robert Strandh (strandh@labri.fr)
7 ;;;
8 ;;; This library is free software; you can redistribute it and/or
9 ;;; modify it under the terms of the GNU Lesser General Public
10 ;;; License as published by the Free Software Foundation; either
11 ;;; version 2.1 of the License, or (at your option) any later version.
12 ;;;
13 ;;; This library is distributed in the hope that it will be useful,
14 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 ;;; Lesser General Public License for more details.
17 ;;;
18 ;;; You should have received a copy of the GNU Lesser General Public
19 ;;; License along with this library; if not, write to the Free Software
20 ;;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
22
23 (in-package :flexichain)
24
25 (defclass flexichain ()
26 ((fill-element :initarg :fill-element)
27 (expand-factor :initarg :expand-factor :initform 1.5)
28 (min-size :initarg :min-size :initform 5))
29 (:documentation "The protocol class for flexichains."))
30
31 (defmethod initialize-instance :after ((chain flexichain) &rest initargs
32 &key initial-contents (element-type t))
33 (declare (ignore initargs initial-contents))
34 (with-slots (expand-factor min-size) chain
35 (assert (> expand-factor 1) ()
36 'flexichain-initialization-error
37 :cause "EXPAND-FACTOR should be greater than 1.")
38 (assert (> min-size 0) ()
39 'flexichain-initialization-error
40 :cause "MIN-SIZE should be greater than 0."))
41 (if (slot-boundp chain 'fill-element)
42 (with-slots (fill-element) chain
43 (assert (typep fill-element element-type) ()
44 'flexichain-initialization-error
45 :cause (format nil "FILL-ELEMENT ~A not of type ~S."
46 fill-element element-type)))
47 (multiple-value-bind (element foundp)
48 (find-if-2 (lambda (x)
49 (typep x element-type))
50 '(nil 0 #\a))
51 (if foundp
52 (setf (slot-value chain 'fill-element) element)
53 (error 'flexichain-initialization-error
54 :cause
55 "FILL-ELEMENT not provided, no default applicable.")))))
56
57 (define-condition flexi-error (simple-error)
58 ())
59
60 (define-condition flexi-initialization-error (flexi-error)
61 ((cause :reader flexi-initialization-error-cause
62 :initarg :cause :initform ""))
63 (:report (lambda (condition stream)
64 (format stream "Error initializing FLEXICHAIN (~S)"
65 (flexi-initialization-error-cause condition)))))
66
67 (define-condition flexi-position-error (flexi-error)
68 ((chain :reader flexi-position-error-chain
69 :initarg :chain :initform nil)
70 (position :reader flexi-position-error-position
71 :initarg :position :initform nil))
72 (:report (lambda (condition stream)
73 (format stream "Position ~D out of bounds in ~A"
74 (flexi-position-error-position condition)
75 (flexi-position-error-chain condition)))))
76
77 (define-condition flexi-incompatible-type-error (flexi-error)
78 ((chain :reader flexi-incompatible-type-error-chain
79 :initarg :chain :initform nil)
80 (element :reader flexi-incompatible-type-error-element
81 :initarg :element :initform nil))
82 (:report (lambda (condition stream)
83 (let ((element (flexi-incompatible-type-error-element
84 condition)))
85 (format stream "Element ~A of type ~A cannot be inserted in ~A"
86 element
87 (type-of element)
88 (flexi-incompatible-type-error-chain condition))))))
89
90 (defgeneric nb-elements (chain)
91 (:documentation "Returns the number of elements in the flexichain."))
92
93 (defgeneric flexi-empty-p (chain)
94 (:documentation "Checks whether CHAIN is empty or not."))
95
96 (defgeneric insert* (chain position object)
97 (:documentation "Inserts an object before the element at POSITION
98 in the chain. If POSITION is out of range (less than 0 or greater
99 than the length of CHAIN, the FLEXI-POSITION-ERROR condition will be
100 signaled."))
101
102 (defgeneric insert-vector* (chain position vector)
103 (:documentation "Inserts the elements of VECTOR before the
104 element at POSITION in the chain. If POSITION is out of
105 range (less than 0 or greater than the length of CHAIN, the
106 FLEXI-POSITION-ERROR condition will be signaled."))
107
108 (defgeneric delete* (chain position)
109 (:documentation "Deletes an element at POSITION of the chain.
110 If POSITION is out of range (less than 0 or greater than or equal
111 to the length of CHAIN, the FLEXI-POSITION-ERROR condition
112 will be signaled."))
113
114 (defgeneric delete-elements* (chain position n)
115 (:documentation "Delete N elements at POSITION of the chain. If
116 POSITION+N is out of range (less than 0 or greater than or equal
117 to the length of CHAIN, the FLEXI-POSITION-ERROR condition will
118 be signaled. N can be negative, in which case elements will be
119 deleted before POSITION."))
120
121 (defgeneric element* (chain position)
122 (:documentation "Returns the element at POSITION of the chain.
123 If POSITION is out of range (less than 0 or greater than or equal
124 to the length of CHAIN, the FLEXI-POSITION-ERROR condition
125 will be signaled."))
126
127 (defgeneric (setf element*) (object chain position)
128 (:documentation "Replaces the element at POSITION of CHAIN by OBJECT.
129 If POSITION if out of range (less than 0 or greater than or equal to
130 the length of CHAIN, the FLEXI-POSITION-ERROR condition will be signaled."))
131
132 (defgeneric push-start (chain object)
133 (:documentation "Inserts an object at the beginning of CHAIN."))
134
135 (defgeneric push-end (chain object)
136 (:documentation "Inserts an object at the end of CHAIN."))
137
138 (defgeneric pop-start (chain)
139 (:documentation "Pops and returns the element at the beginning of CHAIN."))
140
141 (defgeneric pop-end (chain)
142 (:documentation "Pops and returns the element at the end of CHAIN."))
143
144 (defgeneric rotate (chain &optional n)
145 (:documentation "Rotates the elements of CHAIN so that the element
146 that used to be at position N is now at position 0. With a negative
147 value of N, rotates the elements so that the element that used to be
148 at position 0 is now at position N."))
149
150 (defclass standard-flexichain (flexichain)
151 ((buffer)
152 (gap-start)
153 (gap-end)
154 (data-start))
155 (:documentation "The standard instantiable subclass of FLEXICHAIN."))
156
157 (defun required-space (chain nb-elements)
158 (with-slots (min-size expand-factor) chain
159 (+ 2 (max (ceiling (* nb-elements expand-factor))
160 min-size))))
161
162 (defmethod initialize-instance :after ((chain standard-flexichain)
163 &rest initargs
164 &key
165 initial-contents
166 (element-type t)
167 (initial-nb-elements 0)
168 (initial-element nil))
169 (declare (ignore initargs))
170 ;; Initialize slots
171 (with-slots (fill-element buffer) chain
172 (let* ((data-length (if (> (length initial-contents) initial-nb-elements)
173 (length initial-contents)
174 initial-nb-elements))
175 (size (required-space chain data-length))
176 (fill-size (- size data-length 2))
177 (sentinel-list (make-list 2 :initial-element fill-element))
178 (fill-list (make-list fill-size :initial-element fill-element)))
179 (setf buffer
180 (if initial-contents
181 (make-array size
182 :element-type element-type
183 :initial-contents (concatenate 'list
184 sentinel-list
185 initial-contents
186 fill-list))
187 (let ((arr (make-array size
188 :element-type element-type
189 :initial-element initial-element)))
190 (fill arr fill-element :end (length sentinel-list))
191 (fill arr fill-element
192 :start (+ (length sentinel-list) initial-nb-elements)
193 :end size))))
194 (with-slots (gap-start gap-end data-start) chain
195 (setf gap-start (+ 2 data-length)
196 gap-end 0
197 data-start 1)))))
198
199 (defmacro with-virtual-gap ((bl ds gs ge) chain &body body)
200 (let ((c (gensym)))
201 `(let* ((,c ,chain)
202 (,bl (length (slot-value ,c 'buffer)))
203 (,ds (slot-value ,c 'data-start))
204 (,gs (slot-value ,c 'gap-start))
205 (,ge (slot-value ,c 'gap-end)))
206 (declare (ignorable ,bl ,ds ,gs ,ge))
207 (when (< ,gs ,ds) (incf ,gs ,bl))
208 (when (< ,ge ,ds) (incf ,ge ,bl))
209 ,@body)))
210
211 (defmethod nb-elements ((chain standard-flexichain))
212 (with-virtual-gap (bl ds gs ge) chain
213 (- bl (- ge gs) 2)))
214
215 (defmethod flexi-empty-p ((chain standard-flexichain))
216 (zerop (nb-elements chain)))
217
218 (defun position-index (chain position)
219 "Returns the (0 indexed) index of the POSITION-th element
220 of the CHAIN in the buffer."
221 (with-virtual-gap (bl ds gs ge) chain
222 (let ((index (+ ds position 1)))
223 (when (>= index gs)
224 (incf index (- ge gs)))
225 (when (>= index bl)
226 (decf index bl))
227 index)))
228
229 (defun index-position (chain index)
230 "Returns the position corresponding to the INDEX in the CHAIN.
231 Note: the result is undefined if INDEX is not the index of a valid
232 element of the CHAIN."
233 (with-virtual-gap (bl ds gs ge) chain
234 (when (< index ds)
235 (incf index bl))
236 (when (>= index ge)
237 (decf index (- ge gs)))
238 (- index ds 1)))
239
240 (defun ensure-gap-position (chain position)
241 (move-gap chain (position-index chain position)))
242
243 (defun ensure-room (chain nb-elements)
244 (with-slots (buffer) chain
245 (when (> nb-elements (- (length buffer) 2))
246 (increase-buffer-size chain nb-elements))))
247
248 (defmethod insert* ((chain standard-flexichain) position object)
249 (with-slots (buffer gap-start) chain
250 (assert (<= 0 position (nb-elements chain)) ()
251 'flexi-position-error :chain chain :position position)
252 (ensure-gap-position chain position)
253 (ensure-room chain (1+ (nb-elements chain)))
254 (setf (aref buffer gap-start) object)
255 (incf gap-start)
256 (when (= gap-start (length buffer))
257 (setf gap-start 0))))
258
259 (defmethod insert-vector* ((chain standard-flexichain) position vector)
260 (with-slots (buffer gap-start) chain
261 (assert (<= 0 position (nb-elements chain)) ()
262 'flexi-position-error :chain chain :position position)
263 (ensure-gap-position chain position)
264 (ensure-room chain (+ (nb-elements chain) (length vector)))
265 (if (>= (+ gap-start (length vector)) (length buffer))
266 (progn
267 (replace buffer vector :start1 gap-start :end1 (length buffer))
268 (replace buffer vector
269 :start2 (- (length buffer) gap-start))
270 (setf gap-start (- (length vector) (- (length buffer) gap-start))))
271 (progn
272 (replace buffer vector :start1 gap-start :end1 (+ gap-start (length vector)))
273 (incf gap-start (length vector))))))
274
275 (defmethod delete* ((chain standard-flexichain) position)
276 (with-slots (buffer expand-factor min-size fill-element gap-end) chain
277 (assert (< -1 position (nb-elements chain)) ()
278 'flexi-position-error :chain chain :position position)
279 (ensure-gap-position chain position)
280 (setf (aref buffer gap-end) fill-element)
281 (incf gap-end)
282 (when (= gap-end (length buffer))
283 (setf gap-end 0))
284 (when (and (> (length buffer) (+ min-size 2))
285 (< (+ (nb-elements chain) 2) (/ (length buffer) (square expand-factor))))
286 (decrease-buffer-size chain))))
287
288 (defmethod delete-elements* ((chain standard-flexichain) position n)
289 (unless (zerop n)
290 (with-slots (buffer expand-factor min-size gap-end data-start) chain
291 (when (minusp n)
292 (incf position n)
293 (setf n (* -1 n)))
294 (assert (<= 0 (+ position n) (nb-elements chain)) ()
295 'flexi-position-error :chain chain :position position)
296 (ensure-gap-position chain position)
297 ;; Two cases to consider - one where position+n is wholly on
298 ;; this side of the gap in buffer, and one where part of it is
299 ;; "wrapped around" to the beginning of buffer.
300 (cond ((>= (length buffer) (+ gap-end n))
301 (fill-gap chain gap-end (+ gap-end n))
302 (incf gap-end n))
303 (t (let ((surplus-elements (- n (- (length buffer) gap-end))))
304 (fill-gap chain gap-end (length buffer))
305 (fill-gap chain 0 surplus-elements)
306 (setf gap-end surplus-elements))))
307 (when (= gap-end (length buffer))
308 (setf gap-end 0))
309 (when (and (> (length buffer) (+ min-size 2))
310 (< (+ (nb-elements chain) 2) (/ (length buffer) (square expand-factor))))
311 (decrease-buffer-size chain)))))
312
313 (defmethod element* ((chain standard-flexichain) position)
314 (with-slots (buffer) chain
315 (assert (< -1 position (nb-elements chain)) ()
316 'flexi-position-error :chain chain :position position)
317 (aref buffer (position-index chain position))))
318
319 (defmethod (setf element*) (object (chain standard-flexichain) position)
320 (with-slots (buffer) chain
321 (assert (< -1 position (nb-elements chain)) ()
322 'flexi-position-error :chain chain :position position)
323 (setf (aref buffer (position-index chain position)) object)))
324
325 (defmethod push-start ((chain standard-flexichain) object)
326 (insert* chain 0 object))
327
328 (defmethod push-end ((chain standard-flexichain) object)
329 (insert* chain (nb-elements chain) object))
330
331 (defmethod pop-start ((chain standard-flexichain))
332 (prog1
333 (element* chain 0)
334 (delete* chain 0)))
335
336 (defmethod pop-end ((chain standard-flexichain))
337 (let ((position (1- (nb-elements chain))))
338 (prog1
339 (element* chain position)
340 (delete* chain position))))
341
342 (defmethod rotate ((chain standard-flexichain) &optional (n 1))
343 (when (> (nb-elements chain) 1)
344 (cond ((plusp n) (loop repeat n do (push-start chain (pop-end chain))))
345 ((minusp n) (loop repeat (- n) do (push-end chain (pop-start chain))))
346 (t nil))))
347
348 (defun move-gap (chain hot-spot)
349 "Moves the elements and gap inside the buffer so that
350 the element currently at HOT-SPOT becomes the first element following
351 the gap, or does nothing if there are no elements."
352 (with-slots (gap-start gap-end) chain
353 (unless (= hot-spot gap-end)
354 (case (gap-location chain)
355 (:gap-empty (move-empty-gap chain hot-spot))
356 (:gap-left (move-left-gap chain hot-spot))
357 (:gap-right (move-right-gap chain hot-spot))
358 (:gap-middle (move-middle-gap chain hot-spot))
359 (:gap-non-contiguous (move-non-contiguous-gap chain hot-spot))))
360 (values gap-start gap-end)))
361
362 (defun move-empty-gap (chain hot-spot)
363 "Moves the gap. Handles the case where the gap is empty."
364 (with-slots (gap-start gap-end) chain
365 (setf gap-start hot-spot
366 gap-end hot-spot)))
367
368 (defun move-left-gap (chain hot-spot)
369 "Moves the gap. Handles the case where the gap is
370 on the left of the buffer."
371 (with-slots (buffer gap-start gap-end data-start) chain
372 (let ((buffer-size (length buffer)))
373 (cond ((< (- hot-spot gap-end) (- buffer-size hot-spot))
374 (push-elements-left chain (- hot-spot gap-end)))
375 ((<= (- buffer-size hot-spot) gap-end)
376 (hop-elements-left chain (- buffer-size hot-spot)))
377 (t
378 (hop-elements-left chain (- gap-end gap-start))
379 (push-elements-right chain (- gap-start hot-spot)))))))
380
381 (defun move-right-gap (chain hot-spot)
382 "Moves the gap. Handles the case where the gap is
383 on the right of the buffer."
384 (with-slots (buffer gap-start gap-end) chain
385 (let ((buffer-size (length buffer)))
386 (cond ((< (- gap-start hot-spot) hot-spot)
387 (push-elements-right chain (- gap-start hot-spot)))
388 ((<= hot-spot (- buffer-size gap-start))
389 (hop-elements-right chain hot-spot))
390 (t
391 (hop-elements-right chain (- buffer-size gap-start))
392 (push-elements-left chain (- hot-spot gap-end)))))))
393
394 (defun move-middle-gap (chain hot-spot)
395 "Moves the gap. Handles the case where the gap is
396 in the middle of the buffer."
397 (with-slots (buffer gap-start gap-end) chain
398 (let ((buffer-size (length buffer)))
399 (cond ((< hot-spot gap-start)
400 (cond ((<= (- gap-start hot-spot)
401 (+ (- buffer-size gap-end) hot-spot))
402 (push-elements-right chain (- gap-start hot-spot)))
403 (t
404 (push-elements-left chain (- buffer-size gap-end))
405 (move-right-gap chain hot-spot))))
406 (t
407 (cond ((< (- hot-spot gap-end)
408 (+ (- buffer-size hot-spot) gap-start))
409 (push-elements-left chain (- hot-spot gap-end)))
410 (t
411 (push-elements-right chain gap-start)
412 (move-left-gap chain hot-spot))))))))
413
414 (defun move-non-contiguous-gap (chain hot-spot)
415 "Moves the gap. Handles the case where the gap is in 2 parts,
416 on both ends of the buffer."
417 (with-slots (buffer gap-start gap-end) chain
418 (let ((buffer-size (length buffer)))
419 (cond ((< (- hot-spot gap-end) (- gap-start hot-spot))
420 (hop-elements-right chain (min (- buffer-size gap-start)
421 (- hot-spot gap-end)))
422 (let ((nb-left (- hot-spot gap-end)))
423 (unless (zerop nb-left)
424 (push-elements-left chain nb-left))))
425 (t
426 (hop-elements-left chain (min gap-end (- gap-start hot-spot)))
427 (let ((nb-right (- gap-start hot-spot)))
428 (unless (zerop nb-right)
429 (push-elements-right chain nb-right))))))))
430
431 (defgeneric move-elements (standard-flexichain to from start1 start2 end2)
432 (:documentation "move elements of a flexichain and adjust data-start"))
433
434 (defmethod move-elements ((fc standard-flexichain) to from start1 start2 end2)
435 (replace to from :start1 start1 :start2 start2 :end2 end2)
436 (with-slots (data-start) fc
437 (when (and (<= start2 data-start) (< data-start end2))
438 (incf data-start (- start1 start2)))))
439
440 (defgeneric fill-gap (standard-flexichain start end)
441 (:documentation "fill part of gap with the fill element"))
442
443 (defmethod fill-gap ((fc standard-flexichain) start end)
444 (with-slots (buffer fill-element) fc
445 (fill buffer fill-element :start start :end end)))
446
447 (defun push-elements-left (chain count)
448 "Pushes the COUNT elements of CHAIN at the right of the gap,
449 to the beginning of the gap. The gap must be continuous. Example:
450 PUSH-ELEMENTS-LEFT abcd-----efghijklm 2 => abcdef-----ghijklm"
451 (with-slots (buffer gap-start gap-end) chain
452 (move-elements chain buffer buffer gap-start gap-end (+ gap-end count))
453 (fill-gap chain (max gap-end (+ gap-start count)) (+ gap-end count))
454 (incf gap-start count)
455 (incf gap-end count)
456 (normalize-indices chain)))
457
458 (defun push-elements-right (chain count)
459 "Pushes the COUNT elements of CHAIN at the left of the gap,
460 to the end of the gap. The gap must be continuous. Example:
461 PUSH-ELEMENTS-RIGHT abcd-----efghijklm 2 => ab-----cdefghijklm"
462 (with-slots (buffer gap-start gap-end) chain
463 (let* ((buffer-size (length buffer))
464 (rotated-gap-end (if (zerop gap-end) buffer-size gap-end)))
465 (move-elements chain buffer buffer
466 (- rotated-gap-end count) (- gap-start count) gap-start)
467 (fill-gap chain (- gap-start count) (min gap-start (- rotated-gap-end count)))
468 (decf gap-start count)
469 (setf gap-end (- rotated-gap-end count))
470 (normalize-indices chain))))
471
472 (defun hop-elements-left (chain count)
473 "Moves the COUNT rightmost elements to the end of the gap,
474 on the left of the data. Example:
475 HOP-ELEMENTS-LEFT ---abcdefghijklm--- 2 => -lmabcdefghijk-----"
476 (with-slots (buffer gap-start gap-end) chain
477 (let* ((buffer-size (length buffer))
478 (rotated-gap-start (if (zerop gap-start) buffer-size gap-start)))
479 (move-elements chain buffer buffer
480 (- gap-end count) (- rotated-gap-start count) rotated-gap-start)
481 (fill-gap chain (- rotated-gap-start count) rotated-gap-start)
482 (setf gap-start (- rotated-gap-start count))
483 (decf gap-end count)
484 (normalize-indices chain))))
485
486 (defun hop-elements-right (chain count)
487 "Moves the COUNT leftmost elements to the beginning of the gap,
488 on the right of the data. Example:
489 HOP-ELEMENTS-RIGHT ---abcdefghijklm--- 2 => -----cdefghijklmab-"
490 (with-slots (buffer gap-start gap-end) chain
491 (move-elements chain buffer buffer gap-start gap-end (+ gap-end count))
492 (fill-gap chain gap-end (+ gap-end count))
493 (incf gap-start count)
494 (incf gap-end count)
495 (normalize-indices chain)))
496
497 (defun increase-buffer-size (chain nb-elements)
498 (resize-buffer chain (required-space chain nb-elements)))
499
500 (defun decrease-buffer-size (chain)
501 (resize-buffer chain (required-space chain (nb-elements chain))))
502
503 (defgeneric resize-buffer (standard-flexichain new-buffer-size)
504 (:documentation "allocate a new buffer with the size indicated"))
505
506 (defmethod resize-buffer ((fc standard-flexichain) new-buffer-size)
507 (with-slots (buffer gap-start gap-end
508 fill-element expand-factor) fc
509 (let ((buffer-size (length buffer))
510 (buffer-after (make-array new-buffer-size
511 :element-type (array-element-type buffer)
512 :initial-element fill-element)))
513 (case (gap-location fc)
514 ((:gap-empty :gap-middle)
515 (move-elements fc buffer-after buffer 0 0 gap-start)
516 (let ((gap-end-after (- new-buffer-size (- buffer-size gap-end))))
517 (move-elements fc buffer-after buffer gap-end-after gap-end buffer-size)
518 (setf gap-end gap-end-after)))
519 (:gap-right
520 (move-elements fc buffer-after buffer 0 0 gap-start))
521 (:gap-left
522 (let ((gap-end-after (- new-buffer-size (+ 2 (nb-elements fc)))))
523 (move-elements fc buffer-after buffer gap-end-after gap-end buffer-size)
524 (setf gap-end gap-end-after)))
525 (:gap-non-contiguous
526 (move-elements fc buffer-after buffer 0 gap-end gap-start)
527 (decf gap-start gap-end)
528 (setf gap-end 0)))
529 (setf buffer buffer-after)))
530 (normalize-indices fc))
531
532 (defun normalize-indices (chain)
533 "Sets gap limits to 0 if they are at the end of the buffer."
534 (with-slots (buffer gap-start gap-end data-start) chain
535 (let ((buffer-size (length buffer)))
536 (when (>= data-start buffer-size)
537 (setf data-start 0))
538 (when (>= gap-start buffer-size)
539 (setf gap-start 0))
540 (when (>= gap-end buffer-size)
541 (setf gap-end 0)))))
542
543 (defun gap-location (chain)
544 "Returns a keyword indicating the general location of the gap."
545 (with-slots (buffer gap-start gap-end) chain
546 (cond ((= gap-start gap-end) :gap-empty)
547 ((and (zerop gap-start) (>= gap-end 0)) :gap-left)
548 ((and (zerop gap-end) (> gap-start 0)) :gap-right)
549 ((> gap-end gap-start) :gap-middle)
550 (t :gap-non-contiguous))))
551

  ViewVC Help
Powered by ViewVC 1.1.5