Skip to content

Commit f59f948

Browse files
authored
Rotate balance (#3)
rotato
1 parent c094750 commit f59f948

File tree

5 files changed

+153
-55
lines changed

5 files changed

+153
-55
lines changed

dev/benchmark.lisp

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
(in-package #:rope/dev)
2+
3+
;; (progn
4+
;; (sb-ext:restrict-compiler-policy 'speed 3 3)
5+
;; (sb-ext:restrict-compiler-policy 'debug 0 0)
6+
;; ;; (sb-ext:restrict-compiler-policy 'space 0 0)
7+
;; (sb-ext:restrict-compiler-policy 'safety 0 0))
8+
9+
10+
(defparameter *readme*
11+
(merge-pathnames "README.md" (asdf:system-source-directory :rope)))
12+
13+
(defun print-time (time reps length)
14+
(format t "rope size: ~a, ~a microseconds~%"
15+
length
16+
(* 1000000 (/ time reps))))
17+
18+
(defmacro time* (&body body)
19+
`(let ((time))
20+
(sb-ext:call-with-timing
21+
(lambda (&rest plist) (setf time (getf plist :real-time-ms)))
22+
(lambda () ,@body))
23+
(coerce (/ time 1000) 'float)))
24+
25+
(defun benchmark-insert (&optional (reps 1000000))
26+
(with-open-file (s *readme*)
27+
(let* ((starting-rope (split-rope (make-rope s) 1000))
28+
(rope starting-rope))
29+
(dotimes (i 9999)
30+
(setf rope (concat-rope rope starting-rope)))
31+
(setf starting-rope rope)
32+
(dotimes (i 100)
33+
(print-time
34+
(time*
35+
(dotimes (i reps)
36+
(insert-rope rope (random (rope-length rope)) "Hello, world!")))
37+
reps
38+
(rope-length rope))
39+
(force-output)
40+
(setf rope (concat-rope rope starting-rope))))))

dev/graphviz.lisp

+17-8
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
(defparameter *string* "In computer programming, a rope, or cord, is a data structure composed of smaller strings that is used to efficiently store and manipulate longer strings or entire texts.")
44

55
(defparameter rope::*long-leaf* 24)
6-
(defparameter rope::*short-leaf* 8)
6+
(defparameter rope::*short-leaf* 2)
77

88
(defclass root ()
99
((name :initarg :name :accessor root-name)
@@ -15,28 +15,28 @@
1515
:attributes `(:label ,(format nil "rope: ~a~%length: ~a~%depth: ~a"
1616
(root-name root)
1717
(rope-length obj)
18-
(rope-depth obj))
18+
(rope::rope-depth obj))
1919
:style :filled))))
2020

2121
(defmethod graph-object-points-to ((self (eql 'rope)) (obj root))
2222
(let ((obj (root-rope obj)))
2323
(graph-object-points-to self obj)))
2424

25-
(defmethod graph-object-node ((self (eql 'rope)) (obj branch))
25+
(defmethod graph-object-node ((self (eql 'rope)) (obj rope::branch))
2626
(make-instance 'node
2727
:attributes `(:label ,(format nil "length: ~a~%depth: ~a"
2828
(rope-length obj)
29-
(rope-depth obj)))))
29+
(rope::rope-depth obj)))))
3030

31-
(defmethod graph-object-points-to ((self (eql 'rope)) (obj branch))
31+
(defmethod graph-object-points-to ((self (eql 'rope)) (obj rope::branch))
3232
(list (make-instance 'attributed
33-
:object (branch-right obj)
33+
:object (rope::branch-right obj)
3434
:attributes `(:label "R"))
3535
(make-instance 'attributed
36-
:object (branch-left obj)
36+
:object (rope::branch-left obj)
3737
:attributes `(:label "L"))))
3838

39-
(defmethod graph-object-node ((self (eql 'rope)) (obj leaf))
39+
(defmethod graph-object-node ((self (eql 'rope)) (obj rope::leaf))
4040
(make-instance 'node
4141
:attributes `(:label ,(format nil "~a"
4242
(rope::leaf-string obj))
@@ -75,3 +75,12 @@
7575
(setf rope (append-rope rope " we can do it again"))
7676
(setf rope (append-rope rope " later!"))
7777
(graph-ropes (list rope)))
78+
79+
#+example
80+
(let ((rope (rope::make-rope "hello world")))
81+
(dotimes (i 15)
82+
(setf rope (rope:append-rope rope "!")))
83+
(dotimes (i 5)
84+
(setf rope (rope:insert-rope rope 8 "@")))
85+
(graph-ropes (list rope
86+
)))

dev/package.lisp

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
(defpackage #:rope/dev
22
(:use #:cl #:cl-dot #:rope)
33
(:export
4-
#:graph-ropes))
4+
;; graphviz.lisp
5+
#:graph-ropes
6+
;; benchmark.lisp
7+
#:benchmark-insert
8+
))

rope.asd

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@
2424
:depends-on (#:cl-dot #:rope)
2525
:components ((:module "dev"
2626
:components ((:file "package")
27-
(:file "graphviz")))))
27+
(:file "graphviz")
28+
(:file "benchmark")))))

rope.lisp

+89-45
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@
3535
;; Utils ;;
3636
;;-------;;
3737

38+
(defun branch-weight (branch)
39+
(rope-length (branch-left branch)))
40+
41+
(defun leaf-short-p (leaf)
42+
(>= *short-leaf* (rope-length leaf)))
43+
44+
(defun strcat (a b)
45+
(concatenate 'string a b))
46+
47+
(defun make-leaf (string &optional length)
48+
(make-instance 'leaf :string string :length (or length (length string))))
49+
3850
(defgeneric make-rope (source)
3951
(:documentation "Create a new rope from a string, stream, or pathname.")
4052
(:method ((source rope))
@@ -43,7 +55,7 @@
4355
(labels ((read-leaves (&optional acc)
4456
(let* ((string (make-string *long-leaf*))
4557
(length (read-sequence string source))
46-
(leaf (make-instance 'leaf :length length :string (subseq string 0 length))))
58+
(leaf (make-leaf (subseq string 0 length) length)))
4759
(if (= *long-leaf* length)
4860
(read-leaves (cons leaf acc))
4961
(cons leaf acc)))))
@@ -57,19 +69,7 @@
5769
(if (<= *long-leaf* length)
5870
(concat-rope (make-rope (subseq source 0 (round length 2)))
5971
(make-rope (subseq source (round length 2))))
60-
(make-instance 'leaf :length length :string source)))))
61-
62-
(defgeneric rope-weight (rope)
63-
(:method ((rope leaf))
64-
(rope-length rope))
65-
(:method ((rope branch))
66-
(rope-length (branch-left rope))))
67-
68-
(defun leaf-short-p (leaf)
69-
(>= *short-leaf* (rope-length leaf)))
70-
71-
(defun strcat (a b)
72-
(concatenate 'string a b))
72+
(make-leaf source length)))))
7373

7474
;;-----------;;
7575
;; Iteration ;;
@@ -101,6 +101,59 @@
101101
;; Balancing ;;
102102
;;-----------;;
103103

104+
(defgeneric balance-factor (rope)
105+
(:method ((rope leaf))
106+
0)
107+
(:method ((rope branch))
108+
(- (rope-depth (branch-left rope))
109+
(rope-depth (branch-right rope)))))
110+
111+
(defun rotate-left (rope)
112+
(with-slots (left right) rope
113+
(concat-rope*
114+
(concat-rope left (branch-left right))
115+
(branch-right right))))
116+
117+
(defun rotate-right (rope)
118+
(with-slots (left right) rope
119+
(concat-rope*
120+
(branch-left left)
121+
(concat-rope (branch-right left) right))))
122+
123+
(defun rotate-left-right (rope)
124+
(with-slots (left right) rope
125+
(rotate-right (concat-rope* (rotate-left left) right))))
126+
127+
(defun rotate-right-left (rope)
128+
(with-slots (left right) rope
129+
(rotate-left (concat-rope* left (rotate-right right)))))
130+
131+
(defun balance-children (rope)
132+
(with-slots (left right) rope
133+
(concat-rope* (balance-rope left)
134+
(balance-rope right))))
135+
136+
(defgeneric balance-rope (rope)
137+
(:method ((rope leaf))
138+
rope)
139+
(:method ((rope branch))
140+
(with-slots (left right) rope
141+
(let ((bf (balance-factor rope)))
142+
(cond ((< 0 bf)
143+
(if (minusp (balance-factor left))
144+
(rotate-left-right rope)
145+
(rotate-right rope)))
146+
((> 0 bf)
147+
(if (plusp (balance-factor right))
148+
(rotate-right-left rope)
149+
(rotate-left rope)))
150+
(t
151+
rope))))))
152+
153+
;;---------;;
154+
;; Rebuild ;;
155+
;;---------;;
156+
104157
(defun normalize-leaves (leaves &optional carry)
105158
(let ((leaf (car leaves)))
106159
(cond ((and carry (null leaf))
@@ -115,16 +168,6 @@
115168
(t
116169
(cons leaf (normalize-leaves (cdr leaves)))))))
117170

118-
(defgeneric balancedp (rope)
119-
(:documentation "Check if a rope is a height-balanced tree.")
120-
(:method ((rope leaf))
121-
t)
122-
(:method ((rope branch))
123-
(with-slots (left right) rope
124-
(and (balancedp left)
125-
(balancedp right)
126-
(>= 2 (abs (- (rope-depth left) (rope-depth right))))))))
127-
128171
(defun merge-leaves (leaves start end)
129172
(let ((range (- end start)))
130173
(case range
@@ -134,30 +177,28 @@
134177
(concat-rope (merge-leaves leaves start mid)
135178
(merge-leaves leaves mid end)))))))
136179

137-
(defun balance-rope (rope &optional forcep)
180+
(defun rebuild-rope (rope)
138181
"Balance a rope by reconstructing it from the bottom up."
139-
(if (and (balancedp rope) (not forcep))
140-
rope
141-
(let ((leaves (normalize-leaves (collect-rope rope))))
142-
(merge-leaves leaves 0 (length leaves)))))
182+
(let ((leaves (normalize-leaves (collect-rope rope))))
183+
(merge-leaves leaves 0 (length leaves))))
143184

144185
;;--------;;
145186
;; Insert ;;
146187
;;--------;;
147188

148189
(defgeneric prepend-rope (rope source)
149190
(:documentation "Return a new rope with a string or rope inserted at the beginning of a rope.")
150-
(:method (rope (source string))
151-
(concat-rope (make-rope source) rope))
152191
(:method (rope (source rope))
153-
(concat-rope source rope)))
192+
(concat-rope source rope))
193+
(:method (rope (source t))
194+
(concat-rope (make-rope source) rope)))
154195

155196
(defgeneric append-rope (rope source)
156197
(:documentation "Return a new rope with a string or rope inserted at the end of a rope.")
157-
(:method (rope (source string))
158-
(concat-rope rope (make-rope source)))
159198
(:method (rope (source rope))
160-
(concat-rope rope source)))
199+
(concat-rope rope source))
200+
(:method (rope (source t))
201+
(concat-rope rope (make-rope source))))
161202

162203
(defun insert-rope (rope index str)
163204
"Return a new rope with a string or rope inserted at the specified index of a rope."
@@ -175,7 +216,7 @@
175216
(:method ((rope leaf) index)
176217
(char (leaf-string rope) index))
177218
(:method ((rope branch) index)
178-
(let ((weight (rope-weight rope)))
219+
(let ((weight (branch-weight rope)))
179220
(if (< index weight)
180221
(index-rope (branch-left rope) index)
181222
(index-rope (branch-right rope) (- index weight))))))
@@ -192,14 +233,17 @@
192233
;; Concat ;;
193234
;;--------;;
194235

236+
(defun concat-rope* (left right)
237+
"Concatenates without balancing."
238+
(make-instance 'branch
239+
:length (+ (rope-length left) (rope-length right))
240+
:depth (1+ (max (rope-depth left) (rope-depth right)))
241+
:left left
242+
:right right))
243+
195244
(defun concat-rope (left right)
196245
"Returns a balanced concatenation of two ropes."
197-
(balance-rope
198-
(make-instance 'branch
199-
:length (+ (rope-length left) (rope-length right))
200-
:depth (1+ (max (rope-depth left) (rope-depth right)))
201-
:left left
202-
:right right)))
246+
(balance-rope (concat-rope* left right)))
203247

204248
;;-------;;
205249
;; Split ;;
@@ -212,16 +256,16 @@
212256
(make-rope (subseq (leaf-string rope) index))))
213257
(:method ((rope branch) index)
214258
(with-slots (left right) rope
215-
(let ((weight (rope-weight rope)))
259+
(let ((weight (branch-weight rope)))
216260
(cond ((= index weight)
217261
(values left right))
218262
((< index weight)
219263
(multiple-value-bind (ante post) (split-rope left index)
220264
(values (balance-rope ante)
221-
(balance-rope (concat-rope post right)))))
265+
(concat-rope post right))))
222266
((> index weight)
223267
(multiple-value-bind (ante post) (split-rope right (- index weight))
224-
(values (balance-rope (concat-rope left ante))
268+
(values (concat-rope left ante)
225269
(balance-rope post)))))))))
226270

227271
;;------;;

0 commit comments

Comments
 (0)