## ~raph/xi2

502b06bc2e0990f42087f8384d09a99f83547f8c — Raph Levien 2 years ago
```Checkpoint generic HeightRope

I'm going to commit the generic version of the HeightRope as a
checkpoint. Right now, I think this will go easier if I take it
non-generic, as it's starting to feel pretty specialized.
```
```1 files changed, 88 insertions(+), 20 deletions(-)

M xi-druid/src/height_rope.rs
```
`M xi-druid/src/height_rope.rs => xi-druid/src/height_rope.rs +88 -20`
```@@ 8,11 8,64 @@ use std::marker::PhantomData;
#[derive(Clone)]
pub struct Vector<T: Clone>(Node<VectorInfo<T>>);

+/// A type representing a height measure.
+///
+/// Internally this is stored as `usize` using fixed point arithmetic,
+/// for two reasons. First, it lets the rope reuse the `Metric` mechanism.
+/// Second, it means that the monoid property is exact, which would not be
+/// the case for `f64`.
+///
+/// Currently, there are 8 bits of fraction. On 32 bit platforms, that
+/// means a maximum height of 16M, which should be good enough for most
+/// practical use but could be a limitation. Of course, on 64 bit platforms,
+/// the limit of 7.2e16 should never be a problem.
+#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
+pub struct Height(usize);
+
+    type Output = Self;
+
+    fn add(self, other: Self) -> Self {
+        Height(self.0 + other.0)
+    }
+}
+
+    fn add_assign(&mut self, other: Self) {
+        self.0 += other.0
+    }
+}
+
+
+impl Height {
+    /// The number of fractional bits in the representation.
+    pub const HEIGHT_FRAC_BITS: usize = 8;
+
+    /// The scale factor for converting from `f64`.
+    pub const SCALE_FACTOR: f64 = (1 << Self::HEIGHT_FRAC_BITS) as f64;
+
+    pub fn from_raw_frac(frac: usize) -> Height {
+        Height(frac)
+    }
+
+    pub fn as_raw_frac(self) -> usize {
+        self.0
+    }
+
+    pub fn from_f64(height: f64) -> Height {
+        Height((height * Self::SCALE_FACTOR).round() as usize)
+    }
+
+    pub fn to_f64(self) -> f64 {
+        self.0 as f64 * Self::SCALE_FACTOR.recip()
+    }
+}
+
// This technically doesn't have to be newtyped, we could impl leaf on
// Vec directly.
#[derive(Clone)]
pub struct VectorLeaf<T> {
-    data: Vec<T>,
+    data: Vec<(Height, T)>,
}

// Have to implement by hand because rust issue #26925

@@ 24,16 77,24 @@ impl<T> Default for VectorLeaf<T> {

#[derive(Clone)]
pub struct VectorInfo<T> {
+    /// The height of this section of rope.
+    height: Height,
phantom: PhantomData<T>,
}

impl<T: Clone> NodeInfo for VectorInfo<T> {
type L = VectorLeaf<T>;

-    fn accumulate(&mut self, _other: &Self) {}
+    fn accumulate(&mut self, other: &Self) {
+        self.height += other.height;
+    }

-    fn compute_info(_: &Self::L) -> Self {
-        VectorInfo { phantom: Default::default() }
+    fn compute_info(leaf: &Self::L) -> Self {
+        let mut height = Height::default();
+        for (leaf_height, _) in &leaf.data {
+            height += *leaf_height;
+        }
+        VectorInfo { height, phantom: Default::default() }
}
}

@@ 62,33 123,38 @@ impl<T: Clone> Leaf for VectorLeaf<T> {
}
}

-impl<T: Clone> From<Vec<T>> for Vector<T> {
-    fn from(v: Vec<T>) -> Self {
+impl<T: Clone> From<Vec<(Height, T)>> for Vector<T> {
+    fn from(v: Vec<(Height, T)>) -> Self {
Vector(Node::from_leaf(VectorLeaf { data: v }))
}
}

+// This probably shouldn't expose the internal representation as a pair. A deeper
+// question is whether it should even be generic.
+
impl<T: Clone> Vector<T> {
pub fn len(&self) -> usize {
self.0.len()
}

-    pub fn singleton(item: T) -> Vector<T> {
-        vec![item].into()
+    pub fn singleton(height: Height, item: T) -> Vector<T> {
+        vec![(height, item)].into()
}

-    pub fn get(&self, index: usize) -> Option<&T> {
+    pub fn get(&self, index: usize) -> Option<&(Height, T)> {
let cursor = Cursor::new(&self.0, index);
cursor.get_leaf().and_then(|(leaf, offset)| leaf.data.get(offset))
}

-    // Note: we can do get_mut too, but that requires mutable leaf access.
-
-    pub fn push(&mut self, item: T) {
+    pub fn push(&mut self, height: Height, item: T) {
+        let el = Self::singleton(height, item);
// This could be optimized more.
-        self.0 = Node::concat(self.0.clone(), Self::singleton(item).0)
+        self.0 = Node::concat(self.0.clone(), el.0)
}

+    // These mutation methods are not super-satisfying; for the general incremental
+    // algorithm case, we're going to want to expose builder methods.
+
pub fn remove(&mut self, index: usize) {
let mut b = TreeBuilder::new();
self.push_subseq(&mut b, Interval::new(0, index));

@@ 96,18 162,18 @@ impl<T: Clone> Vector<T> {
self.0 = b.build();
}

-    pub fn set(&mut self, index: usize, value: T) {
+    pub fn set(&mut self, index: usize, height: Height, value: T) {
let mut b = TreeBuilder::new();
self.push_subseq(&mut b, Interval::new(0, index));
-        b.push_leaf(VectorLeaf { data: vec![value]});
+        b.push_leaf(VectorLeaf { data: vec![(height, value)]});
self.push_subseq(&mut b, Interval::new(index + 1, self.len()));
self.0 = b.build();
}

-    pub fn insert(&mut self, index: usize, value: T) {
+    pub fn insert(&mut self, index: usize, height: Height, value: T) {
let mut b = TreeBuilder::new();
self.push_subseq(&mut b, Interval::new(0, index));
-        b.push_leaf(VectorLeaf { data: vec![value]});
+        b.push_leaf(VectorLeaf { data: vec![(height, value)]});
self.push_subseq(&mut b, Interval::new(index, self.len()));
self.0 = b.build();
}

@@ 126,7 192,9 @@ impl<T: Clone> Vector<T> {
}

impl<'a, T: Clone> IntoIterator for &'a Vector<T> {
-    type Item = &'a T;
+    // Maybe `(Height, &'a T)` would be better, not to expose the internal
+    // tuple, but it's a bit more work.
+    type Item = &'a (Height, T);

type IntoIter = std::iter::Flatten<ChunkIter<'a, T>>;

@@ 141,9 209,9 @@ pub struct ChunkIter<'a, T: Clone> {
}

impl<'a, T: Clone> Iterator for ChunkIter<'a, T> {
-    type Item = &'a [T];
+    type Item = &'a [(Height, T)];

-    fn next(&mut self) -> Option<&'a [T]> {
+    fn next(&mut self) -> Option<Self::Item> {
if self.cursor.pos() >= self.end {
return None;
}

```