Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions builtin/builtin.mbti
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion builtin/uninitialized_array.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down
114 changes: 114 additions & 0 deletions deque/deque.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -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..<start_len {
set_null(self.buf, (start_idx + i) % cap)
}
for i in 0..<(len - start_len) {
set_null(self.buf, i)
}
} else {
deque.buf.unsafe_blit(0, self.buf, start_idx, len)
for i in 0..<len {
set_null(self.buf, start_idx + i)
}
}

// move self
// TODO: use blit
let new_head = self.head + len
for i = start - 1; i >= 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
}
2 changes: 2 additions & 0 deletions deque/deque.mbti
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
53 changes: 53 additions & 0 deletions deque/deque_test.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -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])")
}