diff --git a/builtin/builtin.mbti b/builtin/builtin.mbti index 4b4475fe6..3ae35e46c 100644 --- a/builtin/builtin.mbti +++ b/builtin/builtin.mbti @@ -308,6 +308,7 @@ fn[T] UninitializedArray::make(Int) -> Self[T] fn[T] UninitializedArray::op_as_view(Self[T], start~ : Int = .., end? : Int) -> ArrayView[T] fn[T] UninitializedArray::op_get(Self[T], Int) -> T fn[T] UninitializedArray::op_set(Self[T], Int, T) -> Unit +fn[T] UninitializedArray::unsafe_blit(Self[T], Int, Self[T], Int, Int) -> Unit #deprecated fn Bool::op_compare(Bool, Bool) -> Int diff --git a/builtin/uninitialized_array.mbt b/builtin/uninitialized_array.mbt index d907a03cf..64218afc1 100644 --- a/builtin/uninitialized_array.mbt +++ b/builtin/uninitialized_array.mbt @@ -98,7 +98,8 @@ pub fn[A] UninitializedArray::length(self : UninitializedArray[A]) -> Int { } ///| -fn[T] UninitializedArray::unsafe_blit( +#internal(unsafe, "For internal use only.") +pub fn[T] UninitializedArray::unsafe_blit( dst : UninitializedArray[T], dst_offset : Int, src : UninitializedArray[T], diff --git a/deque/deque.mbt b/deque/deque.mbt index 5fc46daab..6336e875a 100644 --- a/deque/deque.mbt +++ b/deque/deque.mbt @@ -1309,3 +1309,117 @@ pub impl[A : @json.FromJson] @json.FromJson for T[A] with from_json(json, path) } { len, buf, head, tail } } + +///| +/// Flattens a high-dimensional deque into a lower-dimensional deque +/// by concatenating all inner deques in order. +/// +/// Parameters: +/// +/// * `self` : The high-dimensional deque to flatten. +/// +/// Returns a new lower-dimensional deque containing all elements +/// from inner deques in sequence. +/// +/// Note: +/// - Uses the first inner deque as base and appends subsequent deques. +/// - Efficiently preserves element order across all inner deques. +/// +/// Example: +/// +/// ```moonbit +/// let deque = @deque.of([@deque.of([1,2,3]),@deque.of([4,5,6]),@deque.of([7,8])]) +/// let deque_test = deque.flatten() +/// inspect(deque_test, content="@deque.of([1, 2, 3, 4, 5, 6, 7, 8])") +/// ``` +pub fn[A] T::flatten(self : T[T[A]]) -> T[A] { + let mut len = 0 + for deque in self { + len += deque.length() + } + let target = T::{ + buf: UninitializedArray::make(len), + len, + head: 0, + tail: len - 1, + } + let mut i = 0 + for deque in self { + let cap = deque.buf.length() + let head_len = cap - deque.head + target.buf.unsafe_blit(i, deque.buf, deque.head, head_len) + if head_len < deque.len { + target.buf.unsafe_blit(i + head_len, deque.buf, 0, deque.len - head_len) + } + i += deque.len + } + target +} + +///| +/// Removes and returns elements in the specified range [begin, end) from the deque. +/// +/// Parameters: +/// +/// * `self` : The target deque (modified in-place). +/// * `start` : Start index of the range (inclusive). +/// * `len` : Length of the range to drain. +/// +/// Important: +/// - Returns a new deque containing the drained elements. +/// - Original deque retains elements outside [start, start + len) in original order. +/// +/// Example: +/// +/// ```moonbit +/// let deque = @deque.of([1,2,3,4,5,6,7,8,9]) +/// let deque_test = deque.drain(start=2, len=4) +/// inspect(deque_test, content="@deque.of([3, 4, 5, 6])") +/// inspect(deque, content="@deque.of([1, 2, 7, 8, 9])") +/// ``` +pub fn[A] T::drain(self : T[A], start~ : Int, len? : Int) -> T[A] { + let len = match len { + Some(l) => if l > self.len { self.len } else { l } + None => self.len - start + } + if len == 0 { + return new() + } + let deque = T::{ + buf: UninitializedArray::make(len), + len, + head: 0, + tail: len - 1, + } + let cap = self.buf.length() + let start_idx = (self.head + start) % cap + let start_len = cap - start_idx + + // copy to deque + if start_len < len { + deque.buf.unsafe_blit(0, self.buf, start_idx, start_len) + deque.buf.unsafe_blit(start_len, self.buf, 0, len - start_len) + for i in 0..= 0; i = i - 1 { + self.buf[(new_head + i) % cap] = self.buf[(self.head + i) % cap] + set_null(self.buf, (self.head + i) % cap) + } + self.head = new_head % cap + self.len -= len + deque +} diff --git a/deque/deque.mbti b/deque/deque.mbti index b08b129f4..45d835ab5 100644 --- a/deque/deque.mbti +++ b/deque/deque.mbti @@ -22,10 +22,12 @@ fn[A] T::capacity(Self[A]) -> Int fn[A] T::clear(Self[A]) -> Unit fn[A : Eq] T::contains(Self[A], A) -> Bool fn[A] T::copy(Self[A]) -> Self[A] +fn[A] T::drain(Self[A], start~ : Int, len? : Int) -> Self[A] fn[A] T::each(Self[A], (A) -> Unit) -> Unit fn[A] T::eachi(Self[A], (Int, A) -> Unit) -> Unit #deprecated fn[A] T::filter_map_inplace(Self[A], (A) -> A?) -> Unit +fn[A] T::flatten(Self[Self[A]]) -> Self[A] fn[A] T::front(Self[A]) -> A? fn[A] T::is_empty(Self[A]) -> Bool fn[A] T::iter(Self[A]) -> Iter[A] diff --git a/deque/deque_test.mbt b/deque/deque_test.mbt index c0ba3dee5..783cefb89 100644 --- a/deque/deque_test.mbt +++ b/deque/deque_test.mbt @@ -995,3 +995,56 @@ test "rev_iter2 when wrapped around" { content="[(0, 5), (1, 4), (2, 3), (3, 2), (4, 1), (5, 6)]", ) } + +///| +test "flatten with empty deque" { + let dq : @deque.T[@deque.T[Int]] = @deque.of([]) + inspect(dq.flatten(), content="@deque.of([])") + let dq : @deque.T[@deque.T[Int]] = @deque.of([@deque.of([]), @deque.of([])]) + inspect(dq.flatten(), content="@deque.of([])") + let dq : @deque.T[@deque.T[Int]] = @deque.of([ + @deque.of([1, 2, 3]), + @deque.of([]), + @deque.of([4, 5, 6]), + ]) + inspect(dq.flatten(), content="@deque.of([1, 2, 3, 4, 5, 6])") +} + +///| +test "flatten when wrapped around" { + let dq = of([1, 2, 3, 4, 5]) + dq.push_front(6) + inspect(dq.as_views(), content="([6], [1, 2, 3, 4, 5])") + let dqdq = of([dq, dq]) + inspect( + dqdq.flatten(), + content="@deque.of([6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5])", + ) +} + +///| +test "drain when wrapped around" { + // when the area to be drained is wrapped around + let dq = of([4, 5, 6]) + dq..push_front(3)..push_front(2)..push_front(1) + inspect(dq.as_views(), content="([1, 2, 3], [4, 5, 6])") + let dq2 = dq.drain(start=2, len=2) + inspect(dq.as_views(), content="([1], [2, 5, 6])") + inspect(dq2, content="@deque.of([3, 4])") + + // when the area to be drained is not wrapped around + let dq = of([4, 5, 6]) + dq..push_front(3)..push_front(2)..push_front(1) + inspect(dq.as_views(), content="([1, 2, 3], [4, 5, 6])") + let dq2 = dq.drain(start=1, len=2) + inspect(dq.as_views(), content="([1], [4, 5, 6])") + inspect(dq2, content="@deque.of([2, 3])") + + // when the start is larger than len + let dq = of([5, 6]) + dq..push_front(4)..push_front(3)..push_front(2)..push_front(1) + inspect(dq.as_views(), content="([1, 2], [3, 4, 5, 6])") + let dq2 = dq.drain(start=3, len=2) + inspect(dq.as_views(), content="([1, 2, 3, 6], [])") + inspect(dq2, content="@deque.of([4, 5])") +}