diff --git a/src/Queue.mo b/src/Queue.mo index b78de6c0..38c349b3 100644 --- a/src/Queue.mo +++ b/src/Queue.mo @@ -43,19 +43,66 @@ import Iter "Iter"; import Order "Order"; import Types "Types"; +import PureQueue "pure/Queue"; module { public type Queue = Types.Queue.Queue; type Node = Types.Queue.Node; - // public func toPure(queue : Queue) : PureQueue.Queue { - // todo(); - // }; + /// Converts a mutable queue to an immutable, purely functional queue. + /// + /// Example: + /// ```motoko + /// import Queue "mo:base/Queue"; + /// + /// persistent actor { + /// let queue = Queue.fromIter([1, 2, 3].values()); + /// let pureQueue = Queue.toPure(queue); + /// } + /// ``` + /// + /// Runtime: O(n) + /// Space: O(n) + /// `n` denotes the number of elements stored in the queue. + public func toPure(queue : Queue) : PureQueue.Queue { + let pureQueue = PureQueue.empty(); + let iter = values(queue); + var current = pureQueue; + loop { + switch(iter.next()) { + case null { return current }; + case (?val) { current := PureQueue.pushBack(current, val) }; + }; + }; + }; - // public func fromPure(queue : PureQueue.Queue) : Queue { - // todo(); - // }; + /// Converts an immutable, purely functional queue to a mutable queue. + /// + /// Example: + /// ```motoko + /// import Queue "mo:base/Queue"; + /// import PureQueue "mo:base/pure/Queue"; + /// + /// persistent actor { + /// let pureQueue = PureQueue.fromIter([1, 2, 3].values()); + /// let queue = Queue.fromPure(pureQueue); + /// } + /// ``` + /// + /// Runtime: O(n) + /// Space: O(n) + /// `n` denotes the number of elements stored in the queue. + public func fromPure(pureQueue : PureQueue.Queue) : Queue { + let queue = empty(); + let iter = PureQueue.values(pureQueue); + loop { + switch(iter.next()) { + case null { return queue }; + case (?val) { pushBack(queue, val) }; + }; + }; + }; /// Create a new empty mutable double-ended queue. /// diff --git a/src/pure/Queue.mo b/src/pure/Queue.mo index 8d35ad5d..4b5b0521 100644 --- a/src/pure/Queue.mo +++ b/src/pure/Queue.mo @@ -25,7 +25,7 @@ import List "List"; import Order "../Order"; import Types "../Types"; -module Queue { +module { type List = Types.Pure.List; /// Double-ended queue data type. diff --git a/test/Queue.test.mo b/test/Queue.test.mo index a28893d8..3a26c690 100644 --- a/test/Queue.test.mo +++ b/test/Queue.test.mo @@ -2,6 +2,7 @@ import Suite "mo:matchers/Suite"; import T "mo:matchers/Testable"; import M "mo:matchers/Matchers"; import Queue "../src/Queue"; +import PureQueue "../src/pure/Queue"; import Iter "../src/Iter"; import Nat "../src/Nat"; import Runtime "../src/Runtime"; @@ -388,6 +389,94 @@ run( ) ); +run( + suite( + "pure queue conversions", + [ + test( + "empty to pure", + do { + let queue = Queue.empty(); + let pureQueue = Queue.toPure(queue); + PureQueue.isEmpty(pureQueue) + }, + M.equals(T.bool(true)) + ), + test( + "empty from pure", + do { + let pureQueue = PureQueue.empty(); + let queue = Queue.fromPure(pureQueue); + Queue.isEmpty(queue) + }, + M.equals(T.bool(true)) + ), + test( + "singleton to pure", + do { + let queue = Queue.singleton(1); + let pureQueue = Queue.toPure(queue); + Iter.toArray(PureQueue.values(pureQueue)) + }, + M.equals(T.array(T.natTestable, [1])) + ), + test( + "singleton from pure", + do { + let pureQueue = PureQueue.pushBack(PureQueue.empty(), 1); + let queue = Queue.fromPure(pureQueue); + Iter.toArray(Queue.values(queue)) + }, + M.equals(T.array(T.natTestable, [1])) + ), + test( + "multiple elements to pure", + do { + let queue = Queue.fromIter([1, 2, 3].vals()); + let pureQueue = Queue.toPure(queue); + Iter.toArray(PureQueue.values(pureQueue)) + }, + M.equals(T.array(T.natTestable, [1, 2, 3])) + ), + test( + "multiple elements from pure", + do { + var pureQueue = PureQueue.empty(); + pureQueue := PureQueue.pushBack(pureQueue, 1); + pureQueue := PureQueue.pushBack(pureQueue, 2); + pureQueue := PureQueue.pushBack(pureQueue, 3); + let queue = Queue.fromPure(pureQueue); + Iter.toArray(Queue.values(queue)) + }, + M.equals(T.array(T.natTestable, [1, 2, 3])) + ), + test( + "round trip mutable to pure to mutable", + do { + let original = Queue.fromIter([1, 2, 3].vals()); + let pureQueue = Queue.toPure(original); + let roundTrip = Queue.fromPure(pureQueue); + Iter.toArray(Queue.values(roundTrip)) + }, + M.equals(T.array(T.natTestable, [1, 2, 3])) + ), + test( + "round trip pure to mutable to pure", + do { + var original = PureQueue.empty(); + original := PureQueue.pushBack(original, 1); + original := PureQueue.pushBack(original, 2); + original := PureQueue.pushBack(original, 3); + let mutableQueue = Queue.fromPure(original); + let roundTrip = Queue.toPure(mutableQueue); + Iter.toArray(PureQueue.values(roundTrip)) + }, + M.equals(T.array(T.natTestable, [1, 2, 3])) + ) + ] + ) +); + // TODO: Use PRNG in new base library class Random(seed : Nat) { var number = seed; diff --git a/test/pure/Queue.test.mo b/test/pure/Queue.test.mo index 38ee1e5f..9a5c656f 100644 --- a/test/pure/Queue.test.mo +++ b/test/pure/Queue.test.mo @@ -35,23 +35,8 @@ func iterateBackward(queue : Queue.Queue) : Iter.Iter { } }; -func toText(queue : Queue.Queue) : Text { - var text = "["; - var isFirst = true; - for (element in iterateForward(queue)) { - if (not isFirst) { - text #= ", " - } else { - isFirst := false - }; - text #= debug_show (element) - }; - text #= "]"; - text -}; - func frontToText(t : (Nat, Queue.Queue)) : Text { - "(" # Nat.toText(t.0) # ", " # toText(t.1) # ")" + "(" # Nat.toText(t.0) # ", " # Queue.toText(t.1, Nat.toText) # ")" }; func frontEqual(t1 : (Nat, Queue.Queue), t2 : (Nat, Queue.Queue)) : Bool { @@ -59,7 +44,7 @@ func frontEqual(t1 : (Nat, Queue.Queue), t2 : (Nat, Queue.Queue)) : Bo }; func backToText(t : (Queue.Queue, Nat)) : Text { - "(" # toText(t.0) # ", " # Nat.toText(t.1) # ")" + "(" # Queue.toText(t.0, Nat.toText) # ", " # Nat.toText(t.1) # ")" }; func backEqual(t1 : (Queue.Queue, Nat), t2 : (Queue.Queue, Nat)) : Bool { diff --git a/validation/api/api.lock.json b/validation/api/api.lock.json index d41d737a..599cba2a 100644 --- a/validation/api/api.lock.json +++ b/validation/api/api.lock.json @@ -875,6 +875,7 @@ "public func filterMap(queue : Queue, project : T -> ?U) : Queue", "public func forEach(queue : Queue, operation : T -> ())", "public func fromIter(iter : Iter.Iter) : Queue", + "public func fromPure(pureQueue : PureQueue.Queue) : Queue", "public func isEmpty(queue : Queue) : Bool", "public func map(queue : Queue, project : T -> U) : Queue", "public func peekBack(queue : Queue) : ?T", @@ -886,6 +887,7 @@ "public type Queue", "public func singleton(element : T) : Queue", "public func size(queue : Queue) : Nat", + "public func toPure(queue : Queue) : PureQueue.Queue", "public func toText(queue : Queue, format : T -> Text) : Text", "public func values(queue : Queue) : Iter.Iter" ]