Here is my solution. As a side note, it was probably not the best idea to stick to the benchmarking code in the quiz, as it forced not the best design decisions (I'd rather not have default constructor for Rope, and for sure - no normalizing 'in place'). But - what's done is done. For slice variants I've decided to have two argument ones in #slice, and one arg - in #[] Results: String Build: 0.188000 0.032000 0.220000 ( 0.219000) Sort: 1.578000 0.843000 2.421000 ( 2.422000) Rope Build: 0.031000 0.000000 0.031000 ( 0.031000) Sort: 0.203000 0.000000 0.203000 ( 0.234000) Code: ------------------------------------------------------------------------------------------------------------------------- class NilClass def length; 0; end end class String def shift return nil if empty? res=self[0] self[0]="" res end end class Rope attr_reader :length, :left, :right def initialize(left=nil,right=nil) @left=left if left @right=right if right @length=left.length+right.length end def append(what) len=what.length if (len>0) @left=self.dup if @right.length>0 @right=what @length+=len end self end alias << append def prepend(what) len=what.length if (len>0) @right=self.dup if @left.length>0 @left=what @length+=len end self end def to_s @left.to_s+@right.to_s end def [](i) return i.match(self.to_s)[0] if i.kind_of? Regexp if i.kind_of? Range pos,last=i.first,i.last pos=@length+pos if pos<0 last=@length+last if last<0 return nil if pos<0 || last<0 return slice(pos,last-pos+1) end i=@length+i if i<0 return nil if i<0 || i>@length-1 llen=@left.length i<llen ? @left[i] : @right[i-llen] end def []=(i,val) #fixnum only i=@length+i if i<0 ""[i]=0 if i<0 || i>@length-1 @length+=val.length-1 llen=@left.length i<llen ? @left[i]=val : @right[i-llen]=val end def slice(pos,len) return pos.match(self.to_s)[len] if pos.kind_of? Regexp pos=@length+pos if pos<0 return nil if pos<0 || len<0 || pos>@length-1 llen=@left.length return @left.slice(pos,len) if pos+len<=llen return @right.slice(pos-llen, len) if pos>=llen Rope.new(@left.slice(pos,len),@right.slice(0,len+pos-llen)) end def shift return nil if @length==0 @length-=1 res=@left.length>0 ? @left.shift : @right.shift @left=nil if @left.length==0 @right=nil if @right.length==0 res end def normalize r=Rebalancer.new(@length) self.traverse { |str| r.append(str) } @left,@right=r.get_ropes end def traverse(&blck) @left.kind_of?(String) ? yield(@left) : @left.traverse(&blck) if @left @right.kind_of?(String) ? yield(@right) : @right.traverse(&blck) if @right end end class Rebalancer def initialize len @limits=[1,2] @slots=[] n=2 @limits<<n=@limits[-2]+@limits[-1] while n<len end def append str @slots[0]=@slots[0] ? Rope.new(@slots[0],str) : str i=0 while @slots[i].length>@limits[i] @slots[i+1]=@slots[i+1] ? Rope.new(@slots[i+1],@slots[i]) : @slots[i] @slots[i]=nil i+=1 end end def get_ropes @slots.compact! (@slots.length-1).times { |i| @slots[i+1]=@slots[i+1] ? Rope.new(@slots[i+1],@slots[i]) : @slots[i] @slots[i]=nil i+=1 } [@slots[-1].left,@slots[-1].right] end end