advent-of-code/2022/lib.nix

118 lines
3.7 KiB
Nix

{ pkgs, lib }: with lib; rec {
# Transpose a square grid
#
# [[a]] -> [[a]]
transpose = grid:
genList (n: map ((flip elemAt) n) grid) (length grid);
# Checks if there are any duplicate items in the list,
# in which case it returns false.
#
# [a] -> Bool
allUnique = list: list == []
|| !(elem (head list) (tail list)) && allUnique (tail list);
# Takes items until either a predicate fails, or the list is empty.
#
# (a -> Bool) -> [a] -> Int
takeWhile = pred: xs:
if xs == [] || !(stopPred (head xs))
then []
else [(head xs)] + (takeWhile pred (tail xs));
# Counts items until either a predicate fails, or the list is empty
#
# (a -> Bool) -> [a] -> Int
countWhile = pred: xs: length (takeWhile head xs);
# Like foldl, but keeps all intermediate values
#
# (b -> a -> b) -> b -> [a] -> [b]
scanl = f: x1: list: let
x2 = head list;
x1' = f x1 x2;
in if list == [] then [] else [x1'] ++ (scanl f x1' (tail list));
# Like scanl, but uses the first element as its start element.
#
# (a -> a -> a) -> [a] -> [a]
scanl1 = f: list:
if list == [] then [] else scanl f (head list) (tail list);
# See https://github.com/NixOS/nixpkgs/pull/205457
toInt = str:
let
inherit (builtins) match fromJSON;
# RegEx: Match any leading whitespace, possibly a '-', one or more digits,
# and finally match any trailing whitespace.
strippedInput = match "[[:space:]]*(-?[[:digit:]]+)[[:space:]]*" str;
# RegEx: Match a leading '0' then one or more digits.
isLeadingZero = match "0[[:digit:]]+" (head strippedInput) == [];
# Attempt to parse input
parsedInput = fromJSON (head strippedInput);
generalError = "toInt: Could not convert ${escapeNixString str} to int.";
octalAmbigError = "toInt: Ambiguity in interpretation of ${escapeNixString str}"
+ " between octal and zero padded integer.";
in
# Error on presence of non digit characters.
if strippedInput == null
then throw generalError
# Error on presence of leading zero/octal ambiguity.
else if isLeadingZero
then throw octalAmbigError
# Error if parse function fails.
else if !isInt parsedInput
then throw generalError
# Return result.
else parsedInput;
# Trivial function to wrap around the multiplication operation.
#
# Number -> Number -> Number
multiply = x: y: x * y;
# Trivial function to take the absolute value of a number
#
# Number -> Number
abs = x: if x < 0 then -x else x;
# Generate a list by repeating an element n times.
#
# a -> Int -> [a]
repeat = item: times: map (const item) (range 1 times);
# Compare two items, return either 1, 0, or -1 depending on whether
# one is bigger than the other.
#
# Ord a => a -> a -> Int
cmp = x: y: if x > y then 1 else if x < y then -1 else 0;
# Take 1 item, and skip the n-1 next items continuosly until the list is empty.
#
# Int -> [a] -> [a]
takeWithStride = n: l:
if l == [] then [] else [(head l)] ++ takeWithStride n (drop n l);
# Split a list at every n items.
#
# Int -> [a] -> [[a]]
chunksOf = n: list: if list == []
then []
else [(take n list)] ++ (chunksOf n (drop n list));
# Map something orderable through a function f if it is between min and max.
#
# Ord a => a -> a -> (a -> b) -> a -> Either a b
mapRange = min: max: f: o: if min <= o && o < max then f o else o;
# Shift a number n by an offset if it is between min and max.
#
# Number -> Number -> Number -> Number -> Number
transformRange = min: max: offset: n: mapRange min max (x: x + offset) n;
}