diff --git a/deque/deque.mbt b/deque/deque.mbt index 24d8d0ef9..b8272b501 100644 --- a/deque/deque.mbt +++ b/deque/deque.mbt @@ -38,6 +38,22 @@ pub fn T::from_array[A](arr : Array[A]) -> T[A] { deq } +///| +/// Creates a new copy of this deque. +pub fn T::copy[A](self : T[A]) -> T[A] { + let len = self.len + let deq = T::{ + buf: UninitializedArray::make(len), + len, + head: 0, + tail: len - 1, + } + for i, x in self { + deq.buf[i] = x + } + deq +} + ///| pub fn T::of[A](arr : FixedArray[A]) -> T[A] { let deq = T::{ @@ -542,12 +558,53 @@ pub fn shrink_to_fit[A](self : T[A]) -> Unit { ///| pub fn iter[A](self : T[A]) -> Iter[A] { Iter::new(fn(yield_) { - for i in 0.. break x } + guard not(self.is_empty()) else { IterContinue } + let { head, buf, len, .. } = self + let cap = buf.length() + let head_len = cap - head + if head_len >= len { + for i in head..<(head + len) { + guard let IterContinue = yield_(buf[i]) else { x => return x } + } } else { - IterContinue + for i in head.. return x } + + } + for i in 0..<(len - head_len) { + guard let IterContinue = yield_(buf[i]) else { x => return x } + + } + } + IterContinue + }) +} + +///| +pub fn iter2[A](self : T[A]) -> Iter2[Int, A] { + Iter2::new(fn(yield_) { + guard not(self.is_empty()) else { IterContinue } + let { head, buf, len, .. } = self + let cap = buf.length() + let head_len = cap - head + let mut j = 0 + if head_len >= len { + for i in head..<(head + len) { + guard let IterContinue = yield_(j, buf[i]) else { x => return x } + j += 1 + } + } else { + for i in head.. return x } + j += 1 + } + for i in 0..<(len - head_len) { + guard let IterContinue = yield_(j, buf[i]) else { x => return x } + j += 1 + } } + IterContinue }) } diff --git a/deque/deque.mbti b/deque/deque.mbti index 0210006c9..16a67419a 100644 --- a/deque/deque.mbti +++ b/deque/deque.mbti @@ -9,6 +9,7 @@ impl T { capacity[A](Self[A]) -> Int clear[A](Self[A]) -> Unit contains[A : Eq](Self[A], A) -> Bool + copy[A](Self[A]) -> Self[A] each[A](Self[A], (A) -> Unit) -> Unit eachi[A](Self[A], (Int, A) -> Unit) -> Unit from_array[A](Array[A]) -> Self[A] @@ -16,6 +17,7 @@ impl T { front[A](Self[A]) -> A? is_empty[A](Self[A]) -> Bool iter[A](Self[A]) -> Iter[A] + iter2[A](Self[A]) -> Iter2[Int, A] length[A](Self[A]) -> Int map[A, U](Self[A], (A) -> U) -> Self[U] mapi[A, U](Self[A], (Int, A) -> U) -> Self[U] diff --git a/deque/deque_test.mbt b/deque/deque_test.mbt index e84493e73..131bff77a 100644 --- a/deque/deque_test.mbt +++ b/deque/deque_test.mbt @@ -203,6 +203,15 @@ test "from_array" { inspect!(@deque.of([1, 2, 3]), content="@deque.of([1, 2, 3])") } +test "copy" { + inspect!((@deque.new() : @deque.T[Int]).copy(), content="@deque.of([])") + let deq = @deque.of([1, 2, 3]) + let deq1 = deq.copy() + deq1[0] = 111 + inspect!(deq, content="@deque.of([1, 2, 3])") + inspect!(deq1, content="@deque.of([111, 2, 3])") +} + test "op_set" { let dv = @deque.of([1, 2, 3, 4, 5]) dv[1] = 3 @@ -365,3 +374,75 @@ test "from_iter empty iter" { let dq : @deque.T[Int] = @deque.T::from_iter(Iter::empty()) inspect!(dq, content="@deque.of([])") } + +test "from_array with single element" { + let arr = [42] + let deque = @deque.T::from_array(arr) + assert_eq!(deque.length(), 1) + assert_eq!(deque.front(), Some(42)) + assert_eq!(deque.back(), Some(42)) +} + +test "unsafe_pop_front after many push_front" { + let dq = @deque.new() + // First fill some elements to create a buffer with some capacity + for i = 0; i < 10; i = i + 1 { + dq.push_front(i) + } + // Pop all elements except one + for i = 0; i < 9; i = i + 1 { + dq.unsafe_pop_front() + } + // Now head should be at the end of buffer + // When we pop again, head should wrap around to 0 + inspect!(dq.front(), content="Some(0)") + dq.unsafe_pop_front() + inspect!(dq.is_empty(), content="true") +} + +test "unsafe_pop_back when tail needs to wrap around" { + let dq = @deque.new(capacity=2) + dq.push_back(1) // tail = 0 + dq.push_back(2) // tail = 1 + dq.unsafe_pop_front() // head = 1 + dq.push_back(3) // tail = 0 (wraps around) + dq.unsafe_pop_back() + inspect!(dq.back(), content="Some(2)") +} + +test "deque op_set wrapping case" { + let dq = @deque.new(capacity=4) + // Add elements until the deque wraps around + dq.push_back(1) // head=0, tail=0 + dq.push_back(2) // head=0, tail=1 + dq.push_back(3) // head=0, tail=2 + dq.push_back(4) // head=0, tail=3 + dq.push_front(0) // head=3, tail=3 + + // Now the deque is [0,1,2,3,4] with head=3, tail=3 + // Setting dq[2] should trigger the uncovered branch + dq[2] = 10 + + // Verify the change + inspect!(dq, content="@deque.of([0, 10, 2, 3, 4])") +} + +test "iter when tail needs to wrap around" { + let dq = @deque.new(capacity=2) + dq.push_back(1) // tail = 0 + dq.push_back(2) // tail = 1 + dq.unsafe_pop_front() // head = 1 + dq.push_back(3) // tail = 0 (wraps around) + inspect!(dq.iter().to_array(), content="[2, 3]") + inspect!(dq.iter().filter(fn(x) { x % 2 == 0 }).to_array(), content="[2]") + inspect!(dq.iter().filter(fn(x) { x % 2 != 0 }).to_array(), content="[3]") +} + +test "iter2 when tail needs to wrap around" { + let dq = @deque.new(capacity=2) + dq.push_back(1) // tail = 0 + dq.push_back(2) // tail = 1 + dq.unsafe_pop_front() // head = 1 + dq.push_back(3) // tail = 0 (wraps around) + inspect!(dq.iter2().to_array(), content="[(0, 2), (1, 3)]") +} diff --git a/string/string.mbt b/string/string.mbt index cced44b37..45dc16727 100644 --- a/string/string.mbt +++ b/string/string.mbt @@ -116,6 +116,16 @@ pub fn to_array(self : String) -> Array[Char] { } ///| +/// Returns an iterator over the Unicode characters in the string. +/// +/// Note: This iterator yields Unicode characters, not Utf16 code units. +/// As a result, the count of characters returned by `iter().count()` may not be equal to the length of the string returned by `length()`. +/// +/// ``` +/// let s = "Hello, World!🤣"; +/// assert_eq!(s.iter().count(), 14); // Unicode characters +/// assert_eq!(s.length(), 15); // Utf16 code units +/// ``` pub fn iter(self : String) -> Iter[Char] { Iter::new(fn(yield_) { let len = self.length() @@ -162,6 +172,8 @@ pub fn iter2(self : String) -> Iter2[Int, Char] { } ///| +/// Note: This method operates on Unicode characters, not Utf16 code units. +/// As a result, the count of characters passed to the folding function may not be equal to the length of the string. pub fn fold[A](self : String, init~ : A, f : (A, Char) -> A) -> A { let mut rv = init for c in self {