/* * Copyright 2021 Jakob Schneider Villumsen * * Use of this source code is governed by the Apache 2.0 license * that can be found in the LICENSE.md file. */pubmod MutDeque {use Math.Shuffle////// Represents a mutable deque.////// Explanation of component types (from left to right):/// The 1st component is a reference the backing array./// The 2nd component is a reference to the front index./// The 3rd component is a reference to the back index.////// If front == back then the deque is empty./// Otherwise, the front index always points to an element (going counter-clockwise)/// and the back index always points to the first empty index (going clockwise).///pubstructMutDeque[a: Type, r: Region] { r: Region[r],mut values: Array[a, r],mut front: Int32,mut back: Int32 }instanceIterable[MutDeque[a, r]] {typeElm = atypeAef = rpubdefiterator(rc: Region[r1], md: MutDeque[a, r]): Iterator[a, r + r1, r1] \ (r + r1) = MutDeque.iterator(rc, md) }instanceForEach[MutDeque[a, r]] {typeElm = atypeAef = rpubdefforEach(f: a -> Unit \ ef, md: MutDeque[a, r]): Unit \ ef + r = MutDeque.forEach(f, md) }instanceFormattable[MutDeque[a, r]] withFormattable[a] {typeAef = Formattable.Aef[a] + rpubdefformat(x: MutDeque[a, r]): RichString \ (Formattable.Aef[a] + r) =use RichString.{fromString, joinWith};fromString("MutDeque#{") +joinWith(Formattable.format, fromString(", "), MutDeque.toList(x)) +fromString("}") }////// Constant denoting the minimum allowed capacity of the backing array.///defminCapacity(): Int32 = 8////// Constant denoting the smallest valid load factor.////// The load factor is the ratio between number of elements in the array and its total capacity./// I.e. `(number of elements) / capacity`.////// If the load factor falls below or is equal to `minLoadFactor` the array should be compressed.///defminLoadFactor(): Float32 = 1.0f32/4.0f32////// Constant denoting the largest valid load factor.////// The load factor is the ratio between number of elements in the array and its total capacity./// I.e. `(number of elements) / capacity`.////// If the load factor exceeds or is equal to `maxLoadFactor` the array should be expanded.///defmaxLoadFactor(): Float32 = 3.0f32/4.0f32////// Returns a string representation of the given MutDeque `d`.///pubdeftoString(d: MutDeque[a, r]): String \ rwithToString[a] = regionrc {"MutDeque#{"+ (MutDeque.iterator(rc, d) |> Iterator.join(", ")) +"}" }////// Returns an empty MutDeque.///pubdefempty(rc: Region[r]): MutDeque[a, r] \ r =emptyWithCapacity(rc, minCapacity())////// Returns an empty mutable deque with the given capacity rounded up to the/// default capacity.///pubdefemptyWithCapacity(rc: Region[r], capacity: Int32): MutDeque[a, r] \ r = {letflooredCapacity = Int32.max(capacity, minCapacity());new MutDeque @ rc {r = rc, values = Array.empty(rc, flooredCapacity), front = 0, back = 0} }////// Returns the number of elements in `d`.///pubdefsize(d: MutDeque[a, r]): Int32 \ r =computeSize(capacity(d), d->front, d->back)////// Returns a range of all valid indices of the mutable deque `d`.///pubdefindices(d: MutDeque[a, r]): Range[Int32] \ r = Range.Range(0, size(d))////// Returns the size of a MutDeque, where `l` = array length, `f` = front index, `b` = back index.///defcomputeSize(c: Int32, f: Int32, b: Int32): Int32 =if (f<=b)// The elements laid out without "wrapping around" the array.b-felsec- (f-b) // Subtract the complement of number of elements from the capacity////// Returns `true` if `d` is empty.///pubdefisEmpty(d: MutDeque[a, r]): Bool \ r =d->front ==d->back////// Returns `true` if `d` is non-empty.///pubdefnonEmpty(d: MutDeque[a, r]): Bool \ r = notisEmpty(d)////// Returns the sum of all elements in the deque `d`.///pubdefsum(d: MutDeque[Int32, r]): Int32 \ r =sumWith(identity, d)////// Returns the sum of all elements in the deque `d` according to the function `f`.///pubdefsumWith(f: a -> Int32 \ ef, d: MutDeque[a, r]): Int32 \ { ef, r } =foldLeft((acc, x) -> f(x) +acc, 0, d)////// Applies `f` to a start value `s` and all elements in `d` going from left to right.////// That is, the result is of the form: `f(...f(f(s, x1), x2)..., xn)`.///pubdeffoldLeft(f: (b, a) -> b \ ef, s: b, d: MutDeque[a, r]): b \ { ef, r } =letc = capacity(d) -1;defloop(i, e, acc) =if (i==e)accelseloop(Int32.bitwiseAnd(i+1, c), e, f(acc, Array.get(i, d->values)));loop(d->front, d->back, s)////// Applies `f` to a start value `s` and all elements in `d` going from right to left.////// That is, the result is of the form: `f(x1, ...f(xn-1, f(xn, s))...)`.///pubdeffoldRight(f: (a, b) -> b \ ef, s: b, d: MutDeque[a, r]): b \ { ef, r } =letc = capacity(d) -1;defloop(i, e, acc) =if (i==e)accelse {letj = Int32.bitwiseAnd(i-1, c);loop(j, e, f(Array.get(j, d->values), acc)) };loop(d->back, d->front, s)////// Returns the result of mapping each element and combining the results.///pubdeffoldMap(f: a -> b \ ef, d: MutDeque[a, r]): b \ { ef, r } withMonoid[b] =foldLeft((acc, x) -> Monoid.combine(acc, f(x)), Monoid.empty(), d)////// Returns `Some(x)` where `x` is the element at the front. Returns `None` if `d` is empty.///pubdefpopFront(d: MutDeque[a, r]): Option[a] \ r =if (isEmpty(d)) { None } else {// Get the element `x` at the front, update index, optionally compress array, return `Some(x)`.letx = Array.get(d->front, d->values);d->front = (d->front +1) `Int32.modulo`capacity(d);compress(d); Some(x) }////// Returns `Some(x)` where `x` is the element at the back. Returns `None` if `d` is empty.///pubdefpopBack(d: MutDeque[a, r]): Option[a] \ r =if (isEmpty(d)) { None } else {// Update index such that back points to a valid element `x`, get element, optionally compress array, return `Some(x)`.letb1 = (d->back -1) `Int32.modulo`capacity(d);letx = Array.get(b1, d->values);d->back = b1;compress(d); Some(x) }////// Pushes `x` to the front of `d`.///pubdefpushFront(x: a, d: MutDeque[a, r]): Unit \ r =// Update index such that it points to an empty index. This will never overlap with the back index.letf1 = (d->front -1) `Int32.modulo`capacity(d);// Store `x` in the array.Array.put(x, f1, d->values);// Update the front index reference.d->front = f1;// Optionally expand `d`.expand(d)////// Pushes `x` to the back of `d`.///pubdefpushBack(x: a, d: MutDeque[a, r]): Unit \ r =// Store `x` in the array.Array.put(x, d->back, d->values);// Update back index reference to point to next empty index.d->back = (d->back +1) `Int32.modulo`capacity(d);// Optionally expand `d`.expand(d)////// Optionally returns the front element. Does *not* remove it.///pubdefpeekFront(d: MutDeque[a, r]): Option[a] \ r =letf1 = d->front;letb1 = d->back;if (f1==b1) Noneelse Some(Array.get(f1, d->values))////// Optionally returns the back element. Does *not* remove it.///pubdefpeekBack(d: MutDeque[a, r]): Option[a] \ r =letf1 = d->front;letb1 = d->back;if (f1==b1) Noneelseletc = capacity(d) -1;leti = Int32.bitwiseAnd(b1-1, c); Some(Array.get(i, d->values))////// Doubles the capacity of `d` if the load factor is greater than or equal to `maxLoadFactor`.///defexpand(d: MutDeque[a, r]): Unit \ r =if (shouldExpand(d)) { grow(d) }////// Returns `true` if the load factor is greater than or equal to `maxLoadFactor`.///defshouldExpand(d: MutDeque[a, r]): Bool \ r =loadFactorOf(size(d), capacity(d)) >=maxLoadFactor()////// Doubles the capacity of `d`.///defgrow(d: MutDeque[a, r]): Unit \ r =letc = capacity(d);// Allocate empty array `arr` with double the capacity of `a`.letarr = Array.empty(d->r, Int32.leftShift(c, 1));// Copy elements from old array `a` to empty array `arr`.copyElements(d->r, d->front, d->back, d->values, arr);// Update references.d->values = arr;d->back = computeSize(c, d->front, d->back);d->front = 0////// Compresses MutDeque `d` if the load factor is less than or equal to `minLoadFactor`.///defcompress(d: MutDeque[a, r]): Unit \ r =if (shouldCompress(d)) { shrink(d) }////// Returns `true` if the load factor is less than or equal to 1 / 4.///defshouldCompress(d: MutDeque[a, r]): Bool \ r =loadFactorOf(size(d), capacity(d)) <=minLoadFactor()////// Shrinks MutDeque `d` to half its size but never below `minCapacity`.///defshrink(d: MutDeque[a, r]): Unit \ r =letmc = minCapacity();letc = capacity(d);if (c>mc) {// Prevent the backing array from shrinking below `minCapacity`.// Allocate empty array `arr` with half the capacity of `a`.letarr = Array.empty(d->r, Int32.rightShift(c, 1));// Copy elements from old array `a` to empty array `arr`.copyElements(d->r, d->front, d->back, d->values, arr);// Update references.d->values = arr;d->back = computeSize(c, d->front, d->back);d->front = 0 } else {() }////// Copies the elements from `a` to `a1`. Mutates the array `a1`.///defcopyElements(rc2: Region[r2], f: Int32, b: Int32, a: Array[a, r1], a1: Array[a, r2]): Unit \ { r1, r2 } =letc = Array.length(a);if (f<b) {// If this predicate is true the elements do not "wrap around" in the array, i.e. the elements are laid out sequentially from [0 .. b].Array.updateSequence(0, Array.slice(rc2, start = f, end = b, a), a1) } else {// Copy the front elements of `a` to a1[0 .. (c - f)].Array.updateSequence(0, Array.slice(rc2, start = f, end = c, a), a1);// Copy the back elements of `a` to a1[(c - f) .. b].Array.updateSequence(c-f, Array.slice(rc2, start = 0, end = b, a), a1) }////// Returns the load factor, given size `s` and capacity `c`.///defloadFactorOf(s: Int32, c: Int32): Float32 =Int32.toFloat32(s) /Int32.toFloat32(c)////// Returns the capacity of `d`.///defcapacity(d: MutDeque[a, r]): Int32 \ r =Array.length(d->values)////// Returns `true` if `MutDeque`s `a` and `b` have the same elements in the same order, i.e. are structurally equal.///pubdefsameElements(d1: MutDeque[t, r1], d2: MutDeque[t, r2]): Bool \ { r1, r2 } withEq[t] = regionrc3 {letaSize = size(d1);letbSize = size(d2);if (aSize==bSize) {leta1 = Array.empty(rc3, aSize);letb1 = Array.empty(rc3, bSize);copyElements(rc3, d1->front, d1->back, d1->values, a1);copyElements(rc3, d2->front, d2->back, d2->values, b1);Array.sameElements(a1, b1) } elsefalse }////// Returns `d` as a `List`.///pubdeftoList(d: MutDeque[a, r]): List[a] \ r =foldRight((x, acc) -> x :: acc, Nil, d)////// Returns `d` as an array.///pubdeftoArray(rc1: Region[r1], d: MutDeque[a, r2]): Array[a, r1] \ { r2, r1 } =letlen = MutDeque.capacity(d);leti = d->front;letj = d->back;if (i==j) Array#{} @ rc1elseif (i<j)Array.copyOfRange(rc1, i, j, d->values)elseArray.append(rc1, Array.copyOfRange(rc1, i, len, d->values), Array.copyOfRange(rc1, 0, j, d->values))////// Returns `d` as a vector.///pubdeftoVector(d: MutDeque[a, r]): Vector[a] \ r = regionrc {letarr = Array.empty(rc, size(d));forEachWithIndex((i, x) -> Array.put(x, i, arr), d);Array.toVector(arr) }////// Returns the concatenation of the string representation/// of each element in `d` with `sep` inserted between each element.///pubdefjoin(sep: String, d: MutDeque[a, r]): String \ rwithToString[a] = regionrc {MutDeque.iterator(rc, d) |> Iterator.join(sep) }////// Returns the concatenation of the string representation/// of each element in `d` according to `f` with `sep` inserted between each element.///pubdefjoinWith(f: a -> String \ ef, sep: String, d: MutDeque[a, r]): String \ { ef, r } = regionrc {MutDeque.iterator(rc, d) |> Iterator.joinWith(f, sep) }////// Returns an iterator over `d`.////// Modifying `d` while using an iterator has undefined behavior and is dangerous.///pubdefiterator(rc: Region[r1], d: MutDeque[a, r2]): Iterator[a, r1 + r2, r1] \ { r1, r2 } =leti = Ref.fresh(rc, d->front);letnext = () -> {if (Ref.get(i) <d->back) {letx = Array.get(Ref.get(i), d->values);Ref.put(Ref.get(i) +Int32.bitwiseAnd(1, capacity(d) -1), i); Some(x) } else { None } };Iterator.unfoldWithIter(rc, next)////// Apply the effectful function `f` to all the elements in the MutDeque `d`.///pubdefforEach(f: a -> Unit \ ef, d: MutDeque[a, r]): Unit \ { ef, r } =letc = capacity(d) -1;defloop(i) = {if (i==d->back) {() } else {matchArray.nth(i, d->values) {case Some(x) => f(x)case None => bug!("An error occurred in MutDeque.forEach!") };loop(Int32.bitwiseAnd(i+1, c)) } };loop(d->front)////// Apply the effectful function `f` to all the elements in the MutDeque `d`/// along with that element's index.///pubdefforEachWithIndex(f: (Int32, a) -> Unit \ ef, d: MutDeque[a, r]): Unit \ { ef, r } = regionrc {letix = Ref.fresh(rc, 0);forEach(x -> { leti = Ref.get(ix); f(i, x); Ref.put(i+1, ix) }, d) }////// Shuffles a copy of `d` using the Fisher–Yates shuffle.///pubdefshuffle(rc1: Region[r1], d: MutDeque[a, r2]): MutDeque[a, r1] \ { r2, r1, Shuffle } = regionrc3 {letarr = toArray(rc3, d) !> Array.shuffle;letresult = empty(rc1);Array.forEach(x -> pushBack(x, result), arr);result }}