import { Ok, Error, toList, Empty as $Empty, prepend as listPrepend, CustomType as $CustomType, makeError, divideFloat, isEqual, } from "../gleam.mjs"; import * as $dict from "../gleam/dict.mjs"; import * as $float from "../gleam/float.mjs"; import * as $int from "../gleam/int.mjs"; import * as $order from "../gleam/order.mjs"; const FILEPATH = "src/gleam/list.gleam"; export class Continue extends $CustomType { constructor($0) { super(); this[0] = $0; } } export const ContinueOrStop$Continue = ($0) => new Continue($0); export const ContinueOrStop$isContinue = (value) => value instanceof Continue; export const ContinueOrStop$Continue$0 = (value) => value[0]; export class Stop extends $CustomType { constructor($0) { super(); this[0] = $0; } } export const ContinueOrStop$Stop = ($0) => new Stop($0); export const ContinueOrStop$isStop = (value) => value instanceof Stop; export const ContinueOrStop$Stop$0 = (value) => value[0]; class Ascending extends $CustomType {} class Descending extends $CustomType {} const min_positive = 2.2250738585072014e-308; function length_loop(loop$list, loop$count) { while (true) { let list = loop$list; let count = loop$count; if (list instanceof $Empty) { return count; } else { let list$1 = list.tail; loop$list = list$1; loop$count = count + 1; } } } /** * Counts the number of elements in a given list. * * This function has to traverse the list to determine the number of elements, * so it runs in linear time. * * This function is natively implemented by the virtual machine and is highly * optimised. * * ## Examples * * ```gleam * assert length([]) == 0 * ``` * * ```gleam * assert length([1]) == 1 * ``` * * ```gleam * assert length([1, 2]) == 2 * ``` */ export function length(list) { return length_loop(list, 0); } function count_loop(loop$list, loop$predicate, loop$acc) { while (true) { let list = loop$list; let predicate = loop$predicate; let acc = loop$acc; if (list instanceof $Empty) { return acc; } else { let first$1 = list.head; let rest$1 = list.tail; let $ = predicate(first$1); if ($) { loop$list = rest$1; loop$predicate = predicate; loop$acc = acc + 1; } else { loop$list = rest$1; loop$predicate = predicate; loop$acc = acc; } } } } /** * Counts the number of elements in a given list satisfying a given predicate. * * This function has to traverse the list to determine the number of elements, * so it runs in linear time. * * ## Examples * * ```gleam * assert count([], fn(a) { a > 0 }) == 0 * ``` * * ```gleam * assert count([1], fn(a) { a > 0 }) == 1 * ``` * * ```gleam * assert count([1, 2, 3], int.is_odd) == 2 * ``` */ export function count(list, predicate) { return count_loop(list, predicate, 0); } /** * Reverses a list and prepends it to another list. * This function runs in linear time, proportional to the length of the list * to prepend. * * @ignore */ function reverse_and_prepend(loop$prefix, loop$suffix) { while (true) { let prefix = loop$prefix; let suffix = loop$suffix; if (prefix instanceof $Empty) { return suffix; } else { let first$1 = prefix.head; let rest$1 = prefix.tail; loop$prefix = rest$1; loop$suffix = listPrepend(first$1, suffix); } } } /** * Creates a new list from a given list containing the same elements but in the * opposite order. * * This function has to traverse the list to create the new reversed list, so * it runs in linear time. * * This function is natively implemented by the virtual machine and is highly * optimised. * * ## Examples * * ```gleam * assert reverse([]) == [] * ``` * * ```gleam * assert reverse([1]) == [1] * ``` * * ```gleam * assert reverse([1, 2]) == [2, 1] * ``` */ export function reverse(list) { return reverse_and_prepend(list, toList([])); } /** * Determines whether or not the list is empty. * * This function runs in constant time. * * ## Examples * * ```gleam * assert is_empty([]) * ``` * * ```gleam * assert !is_empty([1]) * ``` * * ```gleam * assert !is_empty([1, 1]) * ``` */ export function is_empty(list) { return isEqual(list, toList([])); } /** * Determines whether or not a given element exists within a given list. * * This function traverses the list to find the element, so it runs in linear * time. * * ## Examples * * ```gleam * assert !contains([], any: 0) * ``` * * ```gleam * assert [0] |> contains(any: 0) * ``` * * ```gleam * assert !contains([1], any: 0) * ``` * * ```gleam * assert !contains([1, 1], any: 0) * ``` * * ```gleam * assert [1, 0] |> contains(any: 0) * ``` */ export function contains(loop$list, loop$elem) { while (true) { let list = loop$list; let elem = loop$elem; if (list instanceof $Empty) { return false; } else { let first$1 = list.head; if (isEqual(first$1, elem)) { return true; } else { let rest$1 = list.tail; loop$list = rest$1; loop$elem = elem; } } } } /** * Gets the first element from the start of the list, if there is one. * * ## Examples * * ```gleam * assert first([]) == Error(Nil) * ``` * * ```gleam * assert first([0]) == Ok(0) * ``` * * ```gleam * assert first([1, 2]) == Ok(1) * ``` */ export function first(list) { if (list instanceof $Empty) { return new Error(undefined); } else { let first$1 = list.head; return new Ok(first$1); } } /** * Returns the list minus the first element. If the list is empty, `Error(Nil)` is * returned. * * This function runs in constant time and does not make a copy of the list. * * ## Examples * * ```gleam * assert rest([]) == Error(Nil) * ``` * * ```gleam * assert rest([0]) == Ok([]) * ``` * * ```gleam * assert rest([1, 2]) == Ok([2]) * ``` */ export function rest(list) { if (list instanceof $Empty) { return new Error(undefined); } else { let rest$1 = list.tail; return new Ok(rest$1); } } /** * Groups the elements from the given list by the given key function. * * Does not preserve the initial value order. * * ## Examples * * ```gleam * import gleam/dict * * assert * [Ok(3), Error("Wrong"), Ok(200), Ok(73)] * |> group(by: fn(i) { * case i { * Ok(_) -> "Successful" * Error(_) -> "Failed" * } * }) * |> dict.to_list * == [ * #("Failed", [Error("Wrong")]), * #("Successful", [Ok(73), Ok(200), Ok(3)]) * ] * ``` * * ```gleam * import gleam/dict * * assert group([1,2,3,4,5], by: fn(i) { i - i / 3 * 3 }) * |> dict.to_list * == [#(0, [3]), #(1, [4, 1]), #(2, [5, 2])] * ``` */ export function group(list, key) { return $dict.group(key, list); } function filter_loop(loop$list, loop$fun, loop$acc) { while (true) { let list = loop$list; let fun = loop$fun; let acc = loop$acc; if (list instanceof $Empty) { return reverse(acc); } else { let first$1 = list.head; let rest$1 = list.tail; let _block; let $ = fun(first$1); if ($) { _block = listPrepend(first$1, acc); } else { _block = acc; } let new_acc = _block; loop$list = rest$1; loop$fun = fun; loop$acc = new_acc; } } } /** * Returns a new list containing only the elements from the first list for * which the given functions returns `True`. * * ## Examples * * ```gleam * assert filter([2, 4, 6, 1], fn(x) { x > 2 }) == [4, 6] * ``` * * ```gleam * assert filter([2, 4, 6, 1], fn(x) { x > 6 }) == [] * ``` */ export function filter(list, predicate) { return filter_loop(list, predicate, toList([])); } function filter_map_loop(loop$list, loop$fun, loop$acc) { while (true) { let list = loop$list; let fun = loop$fun; let acc = loop$acc; if (list instanceof $Empty) { return reverse(acc); } else { let first$1 = list.head; let rest$1 = list.tail; let _block; let $ = fun(first$1); if ($ instanceof Ok) { let first$2 = $[0]; _block = listPrepend(first$2, acc); } else { _block = acc; } let new_acc = _block; loop$list = rest$1; loop$fun = fun; loop$acc = new_acc; } } } /** * Returns a new list containing only the elements from the first list for * which the given functions returns `Ok(_)`. * * ## Examples * * ```gleam * assert filter_map([2, 4, 6, 1], Error) == [] * ``` * * ```gleam * assert filter_map([2, 4, 6, 1], fn(x) { Ok(x + 1) }) == [3, 5, 7, 2] * ``` */ export function filter_map(list, fun) { return filter_map_loop(list, fun, toList([])); } function map_loop(loop$list, loop$fun, loop$acc) { while (true) { let list = loop$list; let fun = loop$fun; let acc = loop$acc; if (list instanceof $Empty) { return reverse(acc); } else { let first$1 = list.head; let rest$1 = list.tail; loop$list = rest$1; loop$fun = fun; loop$acc = listPrepend(fun(first$1), acc); } } } /** * Returns a new list containing the results of applying the supplied function to each element. * * ## Examples * * ```gleam * assert map([2, 4, 6], fn(x) { x * 2 }) == [4, 8, 12] * ``` */ export function map(list, fun) { return map_loop(list, fun, toList([])); } function map2_loop(loop$list1, loop$list2, loop$fun, loop$acc) { while (true) { let list1 = loop$list1; let list2 = loop$list2; let fun = loop$fun; let acc = loop$acc; if (list1 instanceof $Empty) { return reverse(acc); } else if (list2 instanceof $Empty) { return reverse(acc); } else { let a = list1.head; let as_ = list1.tail; let b = list2.head; let bs = list2.tail; loop$list1 = as_; loop$list2 = bs; loop$fun = fun; loop$acc = listPrepend(fun(a, b), acc); } } } /** * Combines two lists into a single list using the given function. * * If a list is longer than the other, the extra elements are dropped. * * ## Examples * * ```gleam * assert map2([1, 2, 3], [4, 5, 6], fn(x, y) { x + y }) == [5, 7, 9] * ``` * * ```gleam * assert map2([1, 2], ["a", "b", "c"], fn(i, x) { #(i, x) }) * == [#(1, "a"), #(2, "b")] * ``` */ export function map2(list1, list2, fun) { return map2_loop(list1, list2, fun, toList([])); } function map_fold_loop(loop$list, loop$fun, loop$acc, loop$list_acc) { while (true) { let list = loop$list; let fun = loop$fun; let acc = loop$acc; let list_acc = loop$list_acc; if (list instanceof $Empty) { return [acc, reverse(list_acc)]; } else { let first$1 = list.head; let rest$1 = list.tail; let $ = fun(acc, first$1); let acc$1 = $[0]; let first$2 = $[1]; loop$list = rest$1; loop$fun = fun; loop$acc = acc$1; loop$list_acc = listPrepend(first$2, list_acc); } } } /** * Similar to `map` but also lets you pass around an accumulated value. * * ## Examples * * ```gleam * assert * map_fold( * over: [1, 2, 3], * from: 100, * with: fn(memo, i) { #(memo + i, i * 2) } * ) * == #(106, [2, 4, 6]) * ``` */ export function map_fold(list, initial, fun) { return map_fold_loop(list, fun, initial, toList([])); } function index_map_loop(loop$list, loop$fun, loop$index, loop$acc) { while (true) { let list = loop$list; let fun = loop$fun; let index = loop$index; let acc = loop$acc; if (list instanceof $Empty) { return reverse(acc); } else { let first$1 = list.head; let rest$1 = list.tail; let acc$1 = listPrepend(fun(first$1, index), acc); loop$list = rest$1; loop$fun = fun; loop$index = index + 1; loop$acc = acc$1; } } } /** * Similar to `map`, but the supplied function will also be passed the index * of the element being mapped as an additional argument. * * The index starts at 0, so the first element is 0, the second is 1, and so * on. * * ## Examples * * ```gleam * assert index_map(["a", "b"], fn(x, i) { #(i, x) }) == [#(0, "a"), #(1, "b")] * ``` */ export function index_map(list, fun) { return index_map_loop(list, fun, 0, toList([])); } function try_map_loop(loop$list, loop$fun, loop$acc) { while (true) { let list = loop$list; let fun = loop$fun; let acc = loop$acc; if (list instanceof $Empty) { return new Ok(reverse(acc)); } else { let first$1 = list.head; let rest$1 = list.tail; let $ = fun(first$1); if ($ instanceof Ok) { let first$2 = $[0]; loop$list = rest$1; loop$fun = fun; loop$acc = listPrepend(first$2, acc); } else { return $; } } } } /** * Takes a function that returns a `Result` and applies it to each element in a * given list in turn. * * If the function returns `Ok(new_value)` for all elements in the list then a * list of the new values is returned. * * If the function returns `Error(reason)` for any of the elements then it is * returned immediately. None of the elements in the list are processed after * one returns an `Error`. * * ## Examples * * ```gleam * assert try_map([1, 2, 3], fn(x) { Ok(x + 2) }) == Ok([3, 4, 5]) * ``` * * ```gleam * assert try_map([1, 2, 3], fn(_) { Error(0) }) == Error(0) * ``` * * ```gleam * assert try_map([[1], [2, 3]], first) == Ok([1, 2]) * ``` * * ```gleam * assert try_map([[1], [], [2]], first) == Error(Nil) * ``` */ export function try_map(list, fun) { return try_map_loop(list, fun, toList([])); } /** * Returns a list that is the given list with up to the given number of * elements removed from the front of the list. * * If the list has less than the number of elements an empty list is * returned. * * This function runs in linear time but does not copy the list. * * ## Examples * * ```gleam * assert drop([1, 2, 3, 4], 2) == [3, 4] * ``` * * ```gleam * assert drop([1, 2, 3, 4], 9) == [] * ``` */ export function drop(loop$list, loop$n) { while (true) { let list = loop$list; let n = loop$n; let $ = n <= 0; if ($) { return list; } else { if (list instanceof $Empty) { return list; } else { let rest$1 = list.tail; loop$list = rest$1; loop$n = n - 1; } } } } function take_loop(loop$list, loop$n, loop$acc) { while (true) { let list = loop$list; let n = loop$n; let acc = loop$acc; let $ = n <= 0; if ($) { return reverse(acc); } else { if (list instanceof $Empty) { return reverse(acc); } else { let first$1 = list.head; let rest$1 = list.tail; loop$list = rest$1; loop$n = n - 1; loop$acc = listPrepend(first$1, acc); } } } } /** * Returns a list containing the first given number of elements from the given * list. * * If the list has less than the number of elements then the full list is * returned. * * This function runs in linear time. * * ## Examples * * ```gleam * assert take([1, 2, 3, 4], 2) == [1, 2] * ``` * * ```gleam * assert take([1, 2, 3, 4], 9) == [1, 2, 3, 4] * ``` */ export function take(list, n) { return take_loop(list, n, toList([])); } /** * Returns a new empty list. * * ## Examples * * ```gleam * assert new() == [] * ``` */ export function new$() { return toList([]); } /** * Returns the given item wrapped in a list. * * ## Examples * * ```gleam * assert wrap(1) == [1] * ``` * * ```gleam * assert wrap(["a", "b", "c"]) == [["a", "b", "c"]] * ``` * * ```gleam * assert wrap([[]]) == [[[]]] * ``` */ export function wrap(item) { return toList([item]); } function append_loop(loop$first, loop$second) { while (true) { let first = loop$first; let second = loop$second; if (first instanceof $Empty) { return second; } else { let first$1 = first.head; let rest$1 = first.tail; loop$first = rest$1; loop$second = listPrepend(first$1, second); } } } /** * Joins one list onto the end of another. * * This function runs in linear time, and it traverses and copies the first * list. * * ## Examples * * ```gleam * assert append([1, 2], [3]) == [1, 2, 3] * ``` */ export function append(first, second) { return append_loop(reverse(first), second); } /** * Prefixes an item to a list. This can also be done using the dedicated * syntax instead. * * ```gleam * let existing_list = [2, 3, 4] * assert [1, ..existing_list] == [1, 2, 3, 4] * ``` * * ```gleam * let existing_list = [2, 3, 4] * assert prepend(to: existing_list, this: 1) == [1, 2, 3, 4] * ``` */ export function prepend(list, item) { return listPrepend(item, list); } function flatten_loop(loop$lists, loop$acc) { while (true) { let lists = loop$lists; let acc = loop$acc; if (lists instanceof $Empty) { return reverse(acc); } else { let list = lists.head; let further_lists = lists.tail; loop$lists = further_lists; loop$acc = reverse_and_prepend(list, acc); } } } /** * Joins a list of lists into a single list. * * This function traverses all elements twice on the JavaScript target. * This function traverses all elements once on the Erlang target. * * ## Examples * * ```gleam * assert flatten([[1], [2, 3], []]) == [1, 2, 3] * ``` */ export function flatten(lists) { return flatten_loop(lists, toList([])); } /** * Maps the list with the given function into a list of lists, and then flattens it. * * ## Examples * * ```gleam * assert flat_map([2, 4, 6], fn(x) { [x, x + 1] }) == [2, 3, 4, 5, 6, 7] * ``` */ export function flat_map(list, fun) { return flatten(map(list, fun)); } /** * Reduces a list of elements into a single value by calling a given function * on each element, going from left to right. * * `fold([1, 2, 3], 0, add)` is the equivalent of * `add(add(add(0, 1), 2), 3)`. * * This function runs in linear time. */ export function fold(loop$list, loop$initial, loop$fun) { while (true) { let list = loop$list; let initial = loop$initial; let fun = loop$fun; if (list instanceof $Empty) { return initial; } else { let first$1 = list.head; let rest$1 = list.tail; loop$list = rest$1; loop$initial = fun(initial, first$1); loop$fun = fun; } } } /** * Reduces a list of elements into a single value by calling a given function * on each element, going from right to left. * * `fold_right([1, 2, 3], 0, add)` is the equivalent of * `add(add(add(0, 3), 2), 1)`. * * This function runs in linear time. * * Unlike `fold` this function is not tail recursive. Where possible use * `fold` instead as it will use less memory. */ export function fold_right(list, initial, fun) { if (list instanceof $Empty) { return initial; } else { let first$1 = list.head; let rest$1 = list.tail; return fun(fold_right(rest$1, initial, fun), first$1); } } function index_fold_loop(loop$over, loop$acc, loop$with, loop$index) { while (true) { let over = loop$over; let acc = loop$acc; let with$ = loop$with; let index = loop$index; if (over instanceof $Empty) { return acc; } else { let first$1 = over.head; let rest$1 = over.tail; loop$over = rest$1; loop$acc = with$(acc, first$1, index); loop$with = with$; loop$index = index + 1; } } } /** * Like `fold` but the folding function also receives the index of the current element. * * ## Examples * * ```gleam * assert ["a", "b", "c"] * |> index_fold("", fn(acc, item, index) { * acc <> int.to_string(index) <> ":" <> item <> " " * }) * == "0:a 1:b 2:c" * ``` * * ```gleam * assert [10, 20, 30] * |> index_fold(0, fn(acc, item, index) { acc + item * index }) * == 80 * ``` */ export function index_fold(list, initial, fun) { return index_fold_loop(list, initial, fun, 0); } /** * A variant of fold that might fail. * * The folding function should return `Result(accumulator, error)`. * If the returned value is `Ok(accumulator)` try_fold will try the next value in the list. * If the returned value is `Error(error)` try_fold will stop and return that error. * * ## Examples * * ```gleam * assert [1, 2, 3, 4] * |> try_fold(0, fn(acc, i) { * case i < 3 { * True -> Ok(acc + i) * False -> Error(Nil) * } * }) * == Error(Nil) * ``` */ export function try_fold(loop$list, loop$initial, loop$fun) { while (true) { let list = loop$list; let initial = loop$initial; let fun = loop$fun; if (list instanceof $Empty) { return new Ok(initial); } else { let first$1 = list.head; let rest$1 = list.tail; let $ = fun(initial, first$1); if ($ instanceof Ok) { let result = $[0]; loop$list = rest$1; loop$initial = result; loop$fun = fun; } else { return $; } } } } /** * A variant of fold that allows to stop folding earlier. * * The folding function should return `ContinueOrStop(accumulator)`. * If the returned value is `Continue(accumulator)` fold_until will try the next value in the list. * If the returned value is `Stop(accumulator)` fold_until will stop and return that accumulator. * * ## Examples * * ```gleam * assert [1, 2, 3, 4] * |> fold_until(0, fn(acc, i) { * case i < 3 { * True -> Continue(acc + i) * False -> Stop(acc) * } * }) * == 3 * ``` */ export function fold_until(loop$list, loop$initial, loop$fun) { while (true) { let list = loop$list; let initial = loop$initial; let fun = loop$fun; if (list instanceof $Empty) { return initial; } else { let first$1 = list.head; let rest$1 = list.tail; let $ = fun(initial, first$1); if ($ instanceof Continue) { let next_accumulator = $[0]; loop$list = rest$1; loop$initial = next_accumulator; loop$fun = fun; } else { let b = $[0]; return b; } } } } /** * Finds the first element in a given list for which the given function returns * `True`. * * Returns `Error(Nil)` if no such element is found. * * ## Examples * * ```gleam * assert find([1, 2, 3], fn(x) { x > 2 }) == Ok(3) * ``` * * ```gleam * assert find([1, 2, 3], fn(x) { x > 4 }) == Error(Nil) * ``` * * ```gleam * assert find([], fn(_) { True }) == Error(Nil) * ``` */ export function find(loop$list, loop$is_desired) { while (true) { let list = loop$list; let is_desired = loop$is_desired; if (list instanceof $Empty) { return new Error(undefined); } else { let first$1 = list.head; let rest$1 = list.tail; let $ = is_desired(first$1); if ($) { return new Ok(first$1); } else { loop$list = rest$1; loop$is_desired = is_desired; } } } } /** * Finds the first element in a given list for which the given function returns * `Ok(new_value)`, then returns the wrapped `new_value`. * * Returns `Error(Nil)` if no such element is found. * * ## Examples * * ```gleam * assert find_map([[], [2], [3]], first) == Ok(2) * ``` * * ```gleam * assert find_map([[], []], first) == Error(Nil) * ``` * * ```gleam * assert find_map([], first) == Error(Nil) * ``` */ export function find_map(loop$list, loop$fun) { while (true) { let list = loop$list; let fun = loop$fun; if (list instanceof $Empty) { return new Error(undefined); } else { let first$1 = list.head; let rest$1 = list.tail; let $ = fun(first$1); if ($ instanceof Ok) { return $; } else { loop$list = rest$1; loop$fun = fun; } } } } /** * Returns `True` if the given function returns `True` for all the elements in * the given list. If the function returns `False` for any of the elements it * immediately returns `False` without checking the rest of the list. * * ## Examples * * ```gleam * assert all([], fn(x) { x > 3 }) * ``` * * ```gleam * assert all([4, 5], fn(x) { x > 3 }) * ``` * * ```gleam * assert !all([4, 3], fn(x) { x > 3 }) * ``` */ export function all(loop$list, loop$predicate) { while (true) { let list = loop$list; let predicate = loop$predicate; if (list instanceof $Empty) { return true; } else { let first$1 = list.head; let rest$1 = list.tail; let $ = predicate(first$1); if ($) { loop$list = rest$1; loop$predicate = predicate; } else { return $; } } } } /** * Returns `True` if the given function returns `True` for any the elements in * the given list. If the function returns `True` for any of the elements it * immediately returns `True` without checking the rest of the list. * * ## Examples * * ```gleam * assert !any([], fn(x) { x > 3 }) * ``` * * ```gleam * assert any([4, 5], fn(x) { x > 3 }) * ``` * * ```gleam * assert any([4, 3], fn(x) { x > 4 }) * ``` * * ```gleam * assert any([3, 4], fn(x) { x > 3 }) * ``` */ export function any(loop$list, loop$predicate) { while (true) { let list = loop$list; let predicate = loop$predicate; if (list instanceof $Empty) { return false; } else { let first$1 = list.head; let rest$1 = list.tail; let $ = predicate(first$1); if ($) { return $; } else { loop$list = rest$1; loop$predicate = predicate; } } } } function zip_loop(loop$one, loop$other, loop$acc) { while (true) { let one = loop$one; let other = loop$other; let acc = loop$acc; if (one instanceof $Empty) { return reverse(acc); } else if (other instanceof $Empty) { return reverse(acc); } else { let first_one = one.head; let rest_one = one.tail; let first_other = other.head; let rest_other = other.tail; loop$one = rest_one; loop$other = rest_other; loop$acc = listPrepend([first_one, first_other], acc); } } } /** * Takes two lists and returns a single list of 2-element tuples. * * If one of the lists is longer than the other, the remaining elements from * the longer list are not used. * * ## Examples * * ```gleam * assert zip([], []) == [] * ``` * * ```gleam * assert zip([1, 2], [3]) == [#(1, 3)] * ``` * * ```gleam * assert zip([1], [3, 4]) == [#(1, 3)] * ``` * * ```gleam * assert zip([1, 2], [3, 4]) == [#(1, 3), #(2, 4)] * ``` */ export function zip(list, other) { return zip_loop(list, other, toList([])); } function strict_zip_loop(loop$one, loop$other, loop$acc) { while (true) { let one = loop$one; let other = loop$other; let acc = loop$acc; if (one instanceof $Empty) { if (other instanceof $Empty) { return new Ok(reverse(acc)); } else { return new Error(undefined); } } else if (other instanceof $Empty) { return new Error(undefined); } else { let first_one = one.head; let rest_one = one.tail; let first_other = other.head; let rest_other = other.tail; loop$one = rest_one; loop$other = rest_other; loop$acc = listPrepend([first_one, first_other], acc); } } } /** * Takes two lists and returns a single list of 2-element tuples. * * If one of the lists is longer than the other, an `Error` is returned. * * ## Examples * * ```gleam * assert strict_zip([], []) == Ok([]) * ``` * * ```gleam * assert strict_zip([1, 2], [3]) == Error(Nil) * ``` * * ```gleam * assert strict_zip([1], [3, 4]) == Error(Nil) * ``` * * ```gleam * assert strict_zip([1, 2], [3, 4]) == Ok([#(1, 3), #(2, 4)]) * ``` */ export function strict_zip(list, other) { return strict_zip_loop(list, other, toList([])); } function unzip_loop(loop$input, loop$one, loop$other) { while (true) { let input = loop$input; let one = loop$one; let other = loop$other; if (input instanceof $Empty) { return [reverse(one), reverse(other)]; } else { let rest$1 = input.tail; let first_one = input.head[0]; let first_other = input.head[1]; loop$input = rest$1; loop$one = listPrepend(first_one, one); loop$other = listPrepend(first_other, other); } } } /** * Takes a single list of 2-element tuples and returns two lists. * * ## Examples * * ```gleam * assert unzip([#(1, 2), #(3, 4)]) == #([1, 3], [2, 4]) * ``` * * ```gleam * assert unzip([]) == #([], []) * ``` */ export function unzip(input) { return unzip_loop(input, toList([]), toList([])); } function intersperse_loop(loop$list, loop$separator, loop$acc) { while (true) { let list = loop$list; let separator = loop$separator; let acc = loop$acc; if (list instanceof $Empty) { return reverse(acc); } else { let first$1 = list.head; let rest$1 = list.tail; loop$list = rest$1; loop$separator = separator; loop$acc = listPrepend(first$1, listPrepend(separator, acc)); } } } /** * Inserts a given value between each existing element in a given list. * * This function runs in linear time and copies the list. * * ## Examples * * ```gleam * assert intersperse([1, 1, 1], 2) == [1, 2, 1, 2, 1] * ``` * * ```gleam * assert intersperse([], 2) == [] * ``` */ export function intersperse(list, elem) { if (list instanceof $Empty) { return list; } else { let $ = list.tail; if ($ instanceof $Empty) { return list; } else { let first$1 = list.head; let rest$1 = $; return intersperse_loop(rest$1, elem, toList([first$1])); } } } function unique_loop(loop$list, loop$seen, loop$acc) { while (true) { let list = loop$list; let seen = loop$seen; let acc = loop$acc; if (list instanceof $Empty) { return reverse(acc); } else { let first$1 = list.head; let rest$1 = list.tail; let $ = $dict.has_key(seen, first$1); if ($) { loop$list = rest$1; loop$seen = seen; loop$acc = acc; } else { loop$list = rest$1; loop$seen = $dict.insert(seen, first$1, undefined); loop$acc = listPrepend(first$1, acc); } } } } /** * Removes any duplicate elements from a given list. * * This function returns in loglinear time. * * ## Examples * * ```gleam * assert unique([1, 1, 1, 4, 7, 3, 3, 4]) == [1, 4, 7, 3] * ``` */ export function unique(list) { return unique_loop(list, $dict.new$(), toList([])); } /** * This is exactly the same as merge_ascendings but mirrored: it merges two * lists sorted in descending order into a single list sorted in ascending * order according to the given comparator function. * * This reversing of the sort order is not avoidable if we want to implement * merge as a tail recursive function. We could reverse the accumulator before * returning it but that would end up being less efficient; so the merging * algorithm has to play around this. * * @ignore */ function merge_descendings(loop$list1, loop$list2, loop$compare, loop$acc) { while (true) { let list1 = loop$list1; let list2 = loop$list2; let compare = loop$compare; let acc = loop$acc; if (list1 instanceof $Empty) { let list = list2; return reverse_and_prepend(list, acc); } else if (list2 instanceof $Empty) { let list = list1; return reverse_and_prepend(list, acc); } else { let first1 = list1.head; let rest1 = list1.tail; let first2 = list2.head; let rest2 = list2.tail; let $ = compare(first1, first2); if ($ instanceof $order.Lt) { loop$list1 = list1; loop$list2 = rest2; loop$compare = compare; loop$acc = listPrepend(first2, acc); } else if ($ instanceof $order.Eq) { loop$list1 = rest1; loop$list2 = list2; loop$compare = compare; loop$acc = listPrepend(first1, acc); } else { loop$list1 = rest1; loop$list2 = list2; loop$compare = compare; loop$acc = listPrepend(first1, acc); } } } } /** * This is the same as merge_ascending_pairs but flipped for descending lists. * * @ignore */ function merge_descending_pairs(loop$sequences, loop$compare, loop$acc) { while (true) { let sequences = loop$sequences; let compare = loop$compare; let acc = loop$acc; if (sequences instanceof $Empty) { return reverse(acc); } else { let $ = sequences.tail; if ($ instanceof $Empty) { let sequence = sequences.head; return reverse(listPrepend(reverse(sequence), acc)); } else { let descending1 = sequences.head; let descending2 = $.head; let rest$1 = $.tail; let ascending = merge_descendings( descending1, descending2, compare, toList([]), ); loop$sequences = rest$1; loop$compare = compare; loop$acc = listPrepend(ascending, acc); } } } } /** * Merges two lists sorted in ascending order into a single list sorted in * descending order according to the given comparator function. * * This reversing of the sort order is not avoidable if we want to implement * merge as a tail recursive function. We could reverse the accumulator before * returning it but that would end up being less efficient; so the merging * algorithm has to play around this. * * @ignore */ function merge_ascendings(loop$list1, loop$list2, loop$compare, loop$acc) { while (true) { let list1 = loop$list1; let list2 = loop$list2; let compare = loop$compare; let acc = loop$acc; if (list1 instanceof $Empty) { let list = list2; return reverse_and_prepend(list, acc); } else if (list2 instanceof $Empty) { let list = list1; return reverse_and_prepend(list, acc); } else { let first1 = list1.head; let rest1 = list1.tail; let first2 = list2.head; let rest2 = list2.tail; let $ = compare(first1, first2); if ($ instanceof $order.Lt) { loop$list1 = rest1; loop$list2 = list2; loop$compare = compare; loop$acc = listPrepend(first1, acc); } else if ($ instanceof $order.Eq) { loop$list1 = list1; loop$list2 = rest2; loop$compare = compare; loop$acc = listPrepend(first2, acc); } else { loop$list1 = list1; loop$list2 = rest2; loop$compare = compare; loop$acc = listPrepend(first2, acc); } } } } /** * Given a list of ascending lists, it merges adjacent pairs into a single * descending list, halving their number. * It returns a list of the remaining descending lists. * * @ignore */ function merge_ascending_pairs(loop$sequences, loop$compare, loop$acc) { while (true) { let sequences = loop$sequences; let compare = loop$compare; let acc = loop$acc; if (sequences instanceof $Empty) { return reverse(acc); } else { let $ = sequences.tail; if ($ instanceof $Empty) { let sequence = sequences.head; return reverse(listPrepend(reverse(sequence), acc)); } else { let ascending1 = sequences.head; let ascending2 = $.head; let rest$1 = $.tail; let descending = merge_ascendings( ascending1, ascending2, compare, toList([]), ); loop$sequences = rest$1; loop$compare = compare; loop$acc = listPrepend(descending, acc); } } } } /** * Given some some sorted sequences (assumed to be sorted in `direction`) it * merges them all together until we're left with just a list sorted in * ascending order. * * @ignore */ function merge_all(loop$sequences, loop$direction, loop$compare) { while (true) { let sequences = loop$sequences; let direction = loop$direction; let compare = loop$compare; if (sequences instanceof $Empty) { return sequences; } else if (direction instanceof Ascending) { let $ = sequences.tail; if ($ instanceof $Empty) { let sequence = sequences.head; return sequence; } else { let sequences$1 = merge_ascending_pairs(sequences, compare, toList([])); loop$sequences = sequences$1; loop$direction = new Descending(); loop$compare = compare; } } else { let $ = sequences.tail; if ($ instanceof $Empty) { let sequence = sequences.head; return reverse(sequence); } else { let sequences$1 = merge_descending_pairs(sequences, compare, toList([])); loop$sequences = sequences$1; loop$direction = new Ascending(); loop$compare = compare; } } } } /** * Given a list it returns slices of it that are locally sorted in ascending * order. * * Imagine you have this list: * * ``` * [1, 2, 3, 2, 1, 0] * ^^^^^^^ ^^^^^^^ This is a slice in descending order * | * | This is a slice that is sorted in ascending order * ``` * * So the produced result will contain these two slices, each one sorted in * ascending order: `[[1, 2, 3], [0, 1, 2]]`. * * - `growing` is an accumulator with the current slice being grown * - `direction` is the growing direction of the slice being grown, it could * either be ascending or strictly descending * - `prev` is the previous element that needs to be added to the growing slice * it is carried around to check whether we have to keep growing the current * slice or not * - `acc` is the accumulator containing the slices sorted in ascending order * * @ignore */ function sequences( loop$list, loop$compare, loop$growing, loop$direction, loop$prev, loop$acc ) { while (true) { let list = loop$list; let compare = loop$compare; let growing = loop$growing; let direction = loop$direction; let prev = loop$prev; let acc = loop$acc; let growing$1 = listPrepend(prev, growing); if (list instanceof $Empty) { if (direction instanceof Ascending) { return listPrepend(reverse(growing$1), acc); } else { return listPrepend(growing$1, acc); } } else { let new$1 = list.head; let rest$1 = list.tail; let $ = compare(prev, new$1); if (direction instanceof Ascending) { if ($ instanceof $order.Lt) { loop$list = rest$1; loop$compare = compare; loop$growing = growing$1; loop$direction = direction; loop$prev = new$1; loop$acc = acc; } else if ($ instanceof $order.Eq) { loop$list = rest$1; loop$compare = compare; loop$growing = growing$1; loop$direction = direction; loop$prev = new$1; loop$acc = acc; } else { let _block; if (direction instanceof Ascending) { _block = listPrepend(reverse(growing$1), acc); } else { _block = listPrepend(growing$1, acc); } let acc$1 = _block; if (rest$1 instanceof $Empty) { return listPrepend(toList([new$1]), acc$1); } else { let next = rest$1.head; let rest$2 = rest$1.tail; let _block$1; let $1 = compare(new$1, next); if ($1 instanceof $order.Lt) { _block$1 = new Ascending(); } else if ($1 instanceof $order.Eq) { _block$1 = new Ascending(); } else { _block$1 = new Descending(); } let direction$1 = _block$1; loop$list = rest$2; loop$compare = compare; loop$growing = toList([new$1]); loop$direction = direction$1; loop$prev = next; loop$acc = acc$1; } } } else if ($ instanceof $order.Lt) { let _block; if (direction instanceof Ascending) { _block = listPrepend(reverse(growing$1), acc); } else { _block = listPrepend(growing$1, acc); } let acc$1 = _block; if (rest$1 instanceof $Empty) { return listPrepend(toList([new$1]), acc$1); } else { let next = rest$1.head; let rest$2 = rest$1.tail; let _block$1; let $1 = compare(new$1, next); if ($1 instanceof $order.Lt) { _block$1 = new Ascending(); } else if ($1 instanceof $order.Eq) { _block$1 = new Ascending(); } else { _block$1 = new Descending(); } let direction$1 = _block$1; loop$list = rest$2; loop$compare = compare; loop$growing = toList([new$1]); loop$direction = direction$1; loop$prev = next; loop$acc = acc$1; } } else if ($ instanceof $order.Eq) { let _block; if (direction instanceof Ascending) { _block = listPrepend(reverse(growing$1), acc); } else { _block = listPrepend(growing$1, acc); } let acc$1 = _block; if (rest$1 instanceof $Empty) { return listPrepend(toList([new$1]), acc$1); } else { let next = rest$1.head; let rest$2 = rest$1.tail; let _block$1; let $1 = compare(new$1, next); if ($1 instanceof $order.Lt) { _block$1 = new Ascending(); } else if ($1 instanceof $order.Eq) { _block$1 = new Ascending(); } else { _block$1 = new Descending(); } let direction$1 = _block$1; loop$list = rest$2; loop$compare = compare; loop$growing = toList([new$1]); loop$direction = direction$1; loop$prev = next; loop$acc = acc$1; } } else { loop$list = rest$1; loop$compare = compare; loop$growing = growing$1; loop$direction = direction; loop$prev = new$1; loop$acc = acc; } } } } /** * Sorts from smallest to largest based upon the ordering specified by a given * function. * * ## Examples * * ```gleam * import gleam/int * * assert sort([4, 3, 6, 5, 4, 1, 2], by: int.compare) == [1, 2, 3, 4, 4, 5, 6] * ``` */ export function sort(list, compare) { if (list instanceof $Empty) { return list; } else { let $ = list.tail; if ($ instanceof $Empty) { return list; } else { let x = list.head; let y = $.head; let rest$1 = $.tail; let _block; let $1 = compare(x, y); if ($1 instanceof $order.Lt) { _block = new Ascending(); } else if ($1 instanceof $order.Eq) { _block = new Ascending(); } else { _block = new Descending(); } let direction = _block; let sequences$1 = sequences( rest$1, compare, toList([x]), direction, y, toList([]), ); return merge_all(sequences$1, new Ascending(), compare); } } } function repeat_loop(loop$item, loop$times, loop$acc) { while (true) { let item = loop$item; let times = loop$times; let acc = loop$acc; let $ = times <= 0; if ($) { return acc; } else { loop$item = item; loop$times = times - 1; loop$acc = listPrepend(item, acc); } } } /** * Builds a list of a given value a given number of times. * * ## Examples * * ```gleam * assert repeat("a", times: 0) == [] * ``` * * ```gleam * assert repeat("a", times: 5) == ["a", "a", "a", "a", "a"] * ``` */ export function repeat(a, times) { return repeat_loop(a, times, toList([])); } function split_loop(loop$list, loop$n, loop$taken) { while (true) { let list = loop$list; let n = loop$n; let taken = loop$taken; let $ = n <= 0; if ($) { return [reverse(taken), list]; } else { if (list instanceof $Empty) { return [reverse(taken), toList([])]; } else { let first$1 = list.head; let rest$1 = list.tail; loop$list = rest$1; loop$n = n - 1; loop$taken = listPrepend(first$1, taken); } } } } /** * Splits a list in two before the given index. * * If the list is not long enough to have the given index the before list will * be the input list, and the after list will be empty. * * ## Examples * * ```gleam * assert split([6, 7, 8, 9], 0) == #([], [6, 7, 8, 9]) * ``` * * ```gleam * assert split([6, 7, 8, 9], 2) == #([6, 7], [8, 9]) * ``` * * ```gleam * assert split([6, 7, 8, 9], 4) == #([6, 7, 8, 9], []) * ``` */ export function split(list, index) { return split_loop(list, index, toList([])); } function split_while_loop(loop$list, loop$f, loop$acc) { while (true) { let list = loop$list; let f = loop$f; let acc = loop$acc; if (list instanceof $Empty) { return [reverse(acc), toList([])]; } else { let first$1 = list.head; let rest$1 = list.tail; let $ = f(first$1); if ($) { loop$list = rest$1; loop$f = f; loop$acc = listPrepend(first$1, acc); } else { return [reverse(acc), list]; } } } } /** * Splits a list in two before the first element that a given function returns * `False` for. * * If the function returns `True` for all elements the first list will be the * input list, and the second list will be empty. * * ## Examples * * ```gleam * assert split_while([1, 2, 3, 4, 5], fn(x) { x <= 3 }) * == #([1, 2, 3], [4, 5]) * ``` * * ```gleam * assert split_while([1, 2, 3, 4, 5], fn(x) { x <= 5 }) * == #([1, 2, 3, 4, 5], []) * ``` */ export function split_while(list, predicate) { return split_while_loop(list, predicate, toList([])); } /** * Given a list of 2-element tuples, finds the first tuple that has a given * key as the first element and returns the second element. * * If no tuple is found with the given key then `Error(Nil)` is returned. * * This function may be useful for interacting with Erlang code where lists of * tuples are common. * * ## Examples * * ```gleam * assert key_find([#("a", 0), #("b", 1)], "a") == Ok(0) * ``` * * ```gleam * assert key_find([#("a", 0), #("b", 1)], "b") == Ok(1) * ``` * * ```gleam * assert key_find([#("a", 0), #("b", 1)], "c") == Error(Nil) * ``` */ export function key_find(keyword_list, desired_key) { return find_map( keyword_list, (keyword) => { let key = keyword[0]; let value = keyword[1]; let $ = isEqual(key, desired_key); if ($) { return new Ok(value); } else { return new Error(undefined); } }, ); } /** * Given a list of 2-element tuples, finds all tuples that have a given * key as the first element and returns the second element. * * This function may be useful for interacting with Erlang code where lists of * tuples are common. * * ## Examples * * ```gleam * assert key_filter([#("a", 0), #("b", 1), #("a", 2)], "a") == [0, 2] * ``` * * ```gleam * assert key_filter([#("a", 0), #("b", 1)], "c") == [] * ``` */ export function key_filter(keyword_list, desired_key) { return filter_map( keyword_list, (keyword) => { let key = keyword[0]; let value = keyword[1]; let $ = isEqual(key, desired_key); if ($) { return new Ok(value); } else { return new Error(undefined); } }, ); } function key_pop_loop(loop$list, loop$key, loop$checked) { while (true) { let list = loop$list; let key = loop$key; let checked = loop$checked; if (list instanceof $Empty) { return new Error(undefined); } else { let k = list.head[0]; if (isEqual(k, key)) { let rest$1 = list.tail; let v = list.head[1]; return new Ok([v, reverse_and_prepend(checked, rest$1)]); } else { let first$1 = list.head; let rest$1 = list.tail; loop$list = rest$1; loop$key = key; loop$checked = listPrepend(first$1, checked); } } } } /** * Given a list of 2-element tuples, finds the first tuple that has a given * key as the first element. This function will return the second element * of the found tuple and list with tuple removed. * * If no tuple is found with the given key then `Error(Nil)` is returned. * * ## Examples * * ```gleam * assert key_pop([#("a", 0), #("b", 1)], "a") == Ok(#(0, [#("b", 1)])) * ``` * * ```gleam * assert key_pop([#("a", 0), #("b", 1)], "b") == Ok(#(1, [#("a", 0)])) * ``` * * ```gleam * assert key_pop([#("a", 0), #("b", 1)], "c") == Error(Nil) * ``` */ export function key_pop(list, key) { return key_pop_loop(list, key, toList([])); } function key_set_loop(loop$list, loop$key, loop$value, loop$inspected) { while (true) { let list = loop$list; let key = loop$key; let value = loop$value; let inspected = loop$inspected; if (list instanceof $Empty) { return reverse(listPrepend([key, value], inspected)); } else { let k = list.head[0]; if (isEqual(k, key)) { let rest$1 = list.tail; return reverse_and_prepend(inspected, listPrepend([k, value], rest$1)); } else { let first$1 = list.head; let rest$1 = list.tail; loop$list = rest$1; loop$key = key; loop$value = value; loop$inspected = listPrepend(first$1, inspected); } } } } /** * Given a list of 2-element tuples, inserts a key and value into the list. * * If there was already a tuple with the key then it is replaced, otherwise it * is added to the end of the list. * * ## Examples * * ```gleam * assert key_set([#(5, 0), #(4, 1)], 4, 100) == [#(5, 0), #(4, 100)] * ``` * * ```gleam * assert key_set([#(5, 0), #(4, 1)], 1, 100) == [#(5, 0), #(4, 1), #(1, 100)] * ``` */ export function key_set(list, key, value) { return key_set_loop(list, key, value, toList([])); } /** * Calls a function for each element in a list, discarding the return value. * * Useful for calling a side effect for every item of a list. * * ```gleam * import gleam/io * * assert each(["1", "2", "3"], io.println) == Nil * // 1 * // 2 * // 3 * ``` */ export function each(loop$list, loop$f) { while (true) { let list = loop$list; let f = loop$f; if (list instanceof $Empty) { return undefined; } else { let first$1 = list.head; let rest$1 = list.tail; f(first$1); loop$list = rest$1; loop$f = f; } } } /** * Calls a `Result` returning function for each element in a list, discarding * the return value. If the function returns `Error` then the iteration is * stopped and the error is returned. * * Useful for calling a side effect for every item of a list. * * ## Examples * * ```gleam * assert * try_each( * over: [1, 2, 3], * with: function_that_might_fail, * ) * == Ok(Nil) * ``` */ export function try_each(loop$list, loop$fun) { while (true) { let list = loop$list; let fun = loop$fun; if (list instanceof $Empty) { return new Ok(undefined); } else { let first$1 = list.head; let rest$1 = list.tail; let $ = fun(first$1); if ($ instanceof Ok) { loop$list = rest$1; loop$fun = fun; } else { return $; } } } } function partition_loop(loop$list, loop$categorise, loop$trues, loop$falses) { while (true) { let list = loop$list; let categorise = loop$categorise; let trues = loop$trues; let falses = loop$falses; if (list instanceof $Empty) { return [reverse(trues), reverse(falses)]; } else { let first$1 = list.head; let rest$1 = list.tail; let $ = categorise(first$1); if ($) { loop$list = rest$1; loop$categorise = categorise; loop$trues = listPrepend(first$1, trues); loop$falses = falses; } else { loop$list = rest$1; loop$categorise = categorise; loop$trues = trues; loop$falses = listPrepend(first$1, falses); } } } } /** * Partitions a list into a tuple/pair of lists * by a given categorisation function. * * ## Examples * * ```gleam * import gleam/int * * assert [1, 2, 3, 4, 5] |> partition(int.is_odd) == #([1, 3, 5], [2, 4]) * ``` */ export function partition(list, categorise) { return partition_loop(list, categorise, toList([]), toList([])); } function permutation_prepend( loop$el, loop$permutations, loop$list_1, loop$list_2, loop$acc ) { while (true) { let el = loop$el; let permutations = loop$permutations; let list_1 = loop$list_1; let list_2 = loop$list_2; let acc = loop$acc; if (permutations instanceof $Empty) { return permutation_zip(list_1, list_2, acc); } else { let head = permutations.head; let tail = permutations.tail; loop$el = el; loop$permutations = tail; loop$list_1 = list_1; loop$list_2 = list_2; loop$acc = listPrepend(listPrepend(el, head), acc); } } } function permutation_zip(list, rest, acc) { if (list instanceof $Empty) { return reverse(acc); } else { let head = list.head; let tail = list.tail; return permutation_prepend( head, permutations(reverse_and_prepend(rest, tail)), tail, listPrepend(head, rest), acc, ); } } /** * Returns all the permutations of a list. * * ## Examples * * ```gleam * assert permutations([1, 2]) == [[1, 2], [2, 1]] * ``` */ export function permutations(list) { if (list instanceof $Empty) { return toList([toList([])]); } else { let l = list; return permutation_zip(l, toList([]), toList([])); } } function window_loop(loop$acc, loop$list, loop$n) { while (true) { let acc = loop$acc; let list = loop$list; let n = loop$n; let window$1 = take(list, n); let $ = length(window$1) === n; if ($) { loop$acc = listPrepend(window$1, acc); loop$list = drop(list, 1); loop$n = n; } else { return reverse(acc); } } } /** * Returns a list of sliding windows. * * ## Examples * * ```gleam * assert window([1,2,3,4,5], 3) == [[1, 2, 3], [2, 3, 4], [3, 4, 5]] * ``` * * ```gleam * assert window([1, 2], 4) == [] * ``` */ export function window(list, n) { let $ = n <= 0; if ($) { return toList([]); } else { return window_loop(toList([]), list, n); } } /** * Returns a list of tuples containing two contiguous elements. * * ## Examples * * ```gleam * assert window_by_2([1,2,3,4]) == [#(1, 2), #(2, 3), #(3, 4)] * ``` * * ```gleam * assert window_by_2([1]) == [] * ``` */ export function window_by_2(list) { return zip(list, drop(list, 1)); } /** * Drops the first elements in a given list for which the predicate function returns `True`. * * ## Examples * * ```gleam * assert drop_while([1, 2, 3, 4], fn (x) { x < 3 }) == [3, 4] * ``` */ export function drop_while(loop$list, loop$predicate) { while (true) { let list = loop$list; let predicate = loop$predicate; if (list instanceof $Empty) { return list; } else { let first$1 = list.head; let rest$1 = list.tail; let $ = predicate(first$1); if ($) { loop$list = rest$1; loop$predicate = predicate; } else { return listPrepend(first$1, rest$1); } } } } function take_while_loop(loop$list, loop$predicate, loop$acc) { while (true) { let list = loop$list; let predicate = loop$predicate; let acc = loop$acc; if (list instanceof $Empty) { return reverse(acc); } else { let first$1 = list.head; let rest$1 = list.tail; let $ = predicate(first$1); if ($) { loop$list = rest$1; loop$predicate = predicate; loop$acc = listPrepend(first$1, acc); } else { return reverse(acc); } } } } /** * Takes the first elements in a given list for which the predicate function returns `True`. * * ## Examples * * ```gleam * assert take_while([1, 2, 3, 2, 4], fn (x) { x < 3 }) == [1, 2] * ``` */ export function take_while(list, predicate) { return take_while_loop(list, predicate, toList([])); } function chunk_loop( loop$list, loop$f, loop$previous_key, loop$current_chunk, loop$acc ) { while (true) { let list = loop$list; let f = loop$f; let previous_key = loop$previous_key; let current_chunk = loop$current_chunk; let acc = loop$acc; if (list instanceof $Empty) { return reverse(listPrepend(reverse(current_chunk), acc)); } else { let first$1 = list.head; let rest$1 = list.tail; let key = f(first$1); let $ = isEqual(key, previous_key); if ($) { loop$list = rest$1; loop$f = f; loop$previous_key = key; loop$current_chunk = listPrepend(first$1, current_chunk); loop$acc = acc; } else { let new_acc = listPrepend(reverse(current_chunk), acc); loop$list = rest$1; loop$f = f; loop$previous_key = key; loop$current_chunk = toList([first$1]); loop$acc = new_acc; } } } } /** * Returns a list of chunks in which * the return value of calling `f` on each element is the same. * * ## Examples * * ```gleam * assert [1, 2, 2, 3, 4, 4, 6, 7, 7] |> chunk(by: fn(n) { n % 2 }) * == [[1], [2, 2], [3], [4, 4, 6], [7, 7]] * ``` */ export function chunk(list, f) { if (list instanceof $Empty) { return list; } else { let first$1 = list.head; let rest$1 = list.tail; return chunk_loop(rest$1, f, f(first$1), toList([first$1]), toList([])); } } function sized_chunk_loop( loop$list, loop$count, loop$left, loop$current_chunk, loop$acc ) { while (true) { let list = loop$list; let count = loop$count; let left = loop$left; let current_chunk = loop$current_chunk; let acc = loop$acc; if (list instanceof $Empty) { if (current_chunk instanceof $Empty) { return reverse(acc); } else { let remaining = current_chunk; return reverse(listPrepend(reverse(remaining), acc)); } } else { let first$1 = list.head; let rest$1 = list.tail; let chunk$1 = listPrepend(first$1, current_chunk); let $ = left > 1; if ($) { loop$list = rest$1; loop$count = count; loop$left = left - 1; loop$current_chunk = chunk$1; loop$acc = acc; } else { loop$list = rest$1; loop$count = count; loop$left = count; loop$current_chunk = toList([]); loop$acc = listPrepend(reverse(chunk$1), acc); } } } } /** * Returns a list of chunks containing `count` elements each. * * If the last chunk does not have `count` elements, it is instead * a partial chunk, with less than `count` elements. * * For any `count` less than 1 this function behaves as if it was set to 1. * * ## Examples * * ```gleam * assert [1, 2, 3, 4, 5, 6] |> sized_chunk(into: 2) * == [[1, 2], [3, 4], [5, 6]] * ``` * * ```gleam * assert [1, 2, 3, 4, 5, 6, 7, 8] |> sized_chunk(into: 3) * == [[1, 2, 3], [4, 5, 6], [7, 8]] * ``` */ export function sized_chunk(list, count) { return sized_chunk_loop(list, count, count, toList([]), toList([])); } /** * This function acts similar to fold, but does not take an initial state. * Instead, it starts from the first element in the list * and combines it with each subsequent element in turn using the given * function. The function is called as `fun(accumulator, current_element)`. * * Returns `Ok` to indicate a successful run, and `Error` if called on an * empty list. * * ## Examples * * ```gleam * assert [] |> reduce(fn(acc, x) { acc + x }) == Error(Nil) * ``` * * ```gleam * assert [1, 2, 3, 4, 5] |> reduce(fn(acc, x) { acc + x }) == Ok(15) * ``` */ export function reduce(list, fun) { if (list instanceof $Empty) { return new Error(undefined); } else { let first$1 = list.head; let rest$1 = list.tail; return new Ok(fold(rest$1, first$1, fun)); } } function scan_loop(loop$list, loop$accumulator, loop$accumulated, loop$fun) { while (true) { let list = loop$list; let accumulator = loop$accumulator; let accumulated = loop$accumulated; let fun = loop$fun; if (list instanceof $Empty) { return reverse(accumulated); } else { let first$1 = list.head; let rest$1 = list.tail; let next = fun(accumulator, first$1); loop$list = rest$1; loop$accumulator = next; loop$accumulated = listPrepend(next, accumulated); loop$fun = fun; } } } /** * Similar to `fold`, but yields the state of the accumulator at each stage. * * ## Examples * * ```gleam * assert scan(over: [1, 2, 3], from: 100, with: fn(acc, i) { acc + i }) * == [101, 103, 106] * ``` */ export function scan(list, initial, fun) { return scan_loop(list, initial, toList([]), fun); } /** * Returns the last element in the given list. * * Returns `Error(Nil)` if the list is empty. * * This function runs in linear time. * * ## Examples * * ```gleam * assert last([]) == Error(Nil) * ``` * * ```gleam * assert last([1, 2, 3, 4, 5]) == Ok(5) * ``` */ export function last(loop$list) { while (true) { let list = loop$list; if (list instanceof $Empty) { return new Error(undefined); } else { let $ = list.tail; if ($ instanceof $Empty) { let last$1 = list.head; return new Ok(last$1); } else { let rest$1 = $; loop$list = rest$1; } } } } /** * Return unique combinations of elements in the list. * * ## Examples * * ```gleam * assert combinations([1, 2, 3], 2) == [[1, 2], [1, 3], [2, 3]] * ``` * * ```gleam * assert combinations([1, 2, 3, 4], 3) * == [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]] * ``` */ export function combinations(items, n) { if (n === 0) { return toList([toList([])]); } else if (items instanceof $Empty) { return items; } else { let first$1 = items.head; let rest$1 = items.tail; let _pipe = rest$1; let _pipe$1 = combinations(_pipe, n - 1); let _pipe$2 = map( _pipe$1, (combination) => { return listPrepend(first$1, combination); }, ); let _pipe$3 = reverse(_pipe$2); return fold( _pipe$3, combinations(rest$1, n), (acc, c) => { return listPrepend(c, acc); }, ); } } function combination_pairs_loop(loop$items, loop$acc) { while (true) { let items = loop$items; let acc = loop$acc; if (items instanceof $Empty) { return reverse(acc); } else { let first$1 = items.head; let rest$1 = items.tail; let first_combinations = map( rest$1, (other) => { return [first$1, other]; }, ); let acc$1 = reverse_and_prepend(first_combinations, acc); loop$items = rest$1; loop$acc = acc$1; } } } /** * Return unique pair combinations of elements in the list. * * ## Examples * * ```gleam * assert combination_pairs([1, 2, 3]) == [#(1, 2), #(1, 3), #(2, 3)] * ``` */ export function combination_pairs(items) { return combination_pairs_loop(items, toList([])); } function take_firsts(loop$rows, loop$column, loop$remaining_rows) { while (true) { let rows = loop$rows; let column = loop$column; let remaining_rows = loop$remaining_rows; if (rows instanceof $Empty) { return [reverse(column), reverse(remaining_rows)]; } else { let $ = rows.head; if ($ instanceof $Empty) { let rest$1 = rows.tail; loop$rows = rest$1; loop$column = column; loop$remaining_rows = remaining_rows; } else { let rest_rows = rows.tail; let first$1 = $.head; let remaining_row = $.tail; let remaining_rows$1 = listPrepend(remaining_row, remaining_rows); loop$rows = rest_rows; loop$column = listPrepend(first$1, column); loop$remaining_rows = remaining_rows$1; } } } } function transpose_loop(loop$rows, loop$columns) { while (true) { let rows = loop$rows; let columns = loop$columns; if (rows instanceof $Empty) { return reverse(columns); } else { let $ = take_firsts(rows, toList([]), toList([])); let column = $[0]; let rest$1 = $[1]; if (column instanceof $Empty) { loop$rows = rest$1; loop$columns = columns; } else { loop$rows = rest$1; loop$columns = listPrepend(column, columns); } } } } /** * Transpose rows and columns of the list of lists. * * Notice: This function is not tail recursive, * and thus may exceed stack size if called, * with large lists (on the JavaScript target). * * ## Examples * * ```gleam * assert transpose([[1, 2, 3], [101, 102, 103]]) * == [[1, 101], [2, 102], [3, 103]] * ``` */ export function transpose(list_of_lists) { return transpose_loop(list_of_lists, toList([])); } /** * Make a list alternating the elements from the given lists * * ## Examples * * ```gleam * assert interleave([[1, 2], [101, 102], [201, 202]]) * == [1, 101, 201, 2, 102, 202] * ``` */ export function interleave(list) { let _pipe = list; let _pipe$1 = transpose(_pipe); return flatten(_pipe$1); } function shuffle_pair_unwrap_loop(loop$list, loop$acc) { while (true) { let list = loop$list; let acc = loop$acc; if (list instanceof $Empty) { return acc; } else { let elem_pair = list.head; let enumerable = list.tail; loop$list = enumerable; loop$acc = listPrepend(elem_pair[1], acc); } } } function do_shuffle_by_pair_indexes(list_of_pairs) { return sort( list_of_pairs, (a_pair, b_pair) => { return $float.compare(a_pair[0], b_pair[0]); }, ); } /** * Takes a list, randomly sorts all items and returns the shuffled list. * * This function uses `float.random` to decide the order of the elements. * * ## Example * * ```gleam * [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] |> shuffle * // -> [1, 6, 9, 10, 3, 8, 4, 2, 7, 5] * ``` */ export function shuffle(list) { let _pipe = list; let _pipe$1 = fold( _pipe, toList([]), (acc, a) => { return listPrepend([$float.random(), a], acc); }, ); let _pipe$2 = do_shuffle_by_pair_indexes(_pipe$1); return shuffle_pair_unwrap_loop(_pipe$2, toList([])); } function max_loop(loop$list, loop$compare, loop$max) { while (true) { let list = loop$list; let compare = loop$compare; let max = loop$max; if (list instanceof $Empty) { return max; } else { let first$1 = list.head; let rest$1 = list.tail; let $ = compare(first$1, max); if ($ instanceof $order.Lt) { loop$list = rest$1; loop$compare = compare; loop$max = max; } else if ($ instanceof $order.Eq) { loop$list = rest$1; loop$compare = compare; loop$max = max; } else { loop$list = rest$1; loop$compare = compare; loop$max = first$1; } } } } /** * Takes a list and a comparator, and returns the maximum element in the list * * ## Examples * * ```gleam * assert [1, 2, 3, 4, 5] |> list.max(int.compare) == Ok(5) * ``` * * ```gleam * assert ["a", "c", "b"] |> list.max(string.compare) == Ok("c") * ``` */ export function max(list, compare) { if (list instanceof $Empty) { return new Error(undefined); } else { let first$1 = list.head; let rest$1 = list.tail; return new Ok(max_loop(rest$1, compare, first$1)); } } function log_random() { let $ = $float.logarithm($float.random() + min_positive); let random; if ($ instanceof Ok) { random = $[0]; } else { throw makeError( "let_assert", FILEPATH, "gleam/list", 2244, "log_random", "Pattern match failed, no pattern matched the value.", { value: $, start: 55129, end: 55200, pattern_start: 55140, pattern_end: 55150 } ) } return random; } function sample_loop(loop$list, loop$reservoir, loop$n, loop$w) { while (true) { let list = loop$list; let reservoir = loop$reservoir; let n = loop$n; let w = loop$w; let _block; { let $ = $float.logarithm(1.0 - w); let log; if ($ instanceof Ok) { log = $[0]; } else { throw makeError( "let_assert", FILEPATH, "gleam/list", 2227, "sample_loop", "Pattern match failed, no pattern matched the value.", { value: $, start: 54690, end: 54736, pattern_start: 54701, pattern_end: 54708 } ) } _block = $float.round($float.floor(divideFloat(log_random(), log))); } let skip = _block; let $ = drop(list, skip); if ($ instanceof $Empty) { return reservoir; } else { let first$1 = $.head; let rest$1 = $.tail; let reservoir$1 = $dict.insert(reservoir, $int.random(n), first$1); let w$1 = w * $float.exponential( divideFloat(log_random(), $int.to_float(n)), ); loop$list = rest$1; loop$reservoir = reservoir$1; loop$n = n; loop$w = w$1; } } } function build_reservoir_loop(loop$list, loop$size, loop$reservoir) { while (true) { let list = loop$list; let size = loop$size; let reservoir = loop$reservoir; let reservoir_size = $dict.size(reservoir); let $ = reservoir_size >= size; if ($) { return [reservoir, list]; } else { if (list instanceof $Empty) { return [reservoir, toList([])]; } else { let first$1 = list.head; let rest$1 = list.tail; let reservoir$1 = $dict.insert(reservoir, reservoir_size, first$1); loop$list = rest$1; loop$size = size; loop$reservoir = reservoir$1; } } } } /** * Builds the initial reservoir used by Algorithm L. * This is a dictionary with keys ranging from `0` up to `n - 1` where each * value is the corresponding element at that position in `list`. * * This also returns the remaining elements of `list` that didn't end up in * the reservoir. * * @ignore */ function build_reservoir(list, n) { return build_reservoir_loop(list, n, $dict.new$()); } /** * Returns a random sample of up to n elements from a list using reservoir * sampling via [Algorithm L](https://en.wikipedia.org/wiki/Reservoir_sampling#Optimal:_Algorithm_L). * Returns an empty list if the sample size is less than or equal to 0. * * Order is not random, only selection is. * * ## Examples * * ```gleam * sample([1, 2, 3, 4, 5], 3) * // -> [2, 4, 5] // A random sample of 3 items * ``` */ export function sample(list, n) { let $ = build_reservoir(list, n); let reservoir = $[0]; let rest$1 = $[1]; let $1 = $dict.is_empty(reservoir); if ($1) { return toList([]); } else { let w = $float.exponential(divideFloat(log_random(), $int.to_float(n))); return $dict.values(sample_loop(rest$1, reservoir, n, w)); } }