Add files via upload

This commit is contained in:
Peter Norvig
2024-01-02 00:05:32 -08:00
committed by GitHub
parent 76c97b7f00
commit 280cf9a8e4
2 changed files with 4578 additions and 175 deletions

4410
ipynb/Advent-2023.ipynb Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -4,16 +4,18 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"<div style=\"text-align: right\" align=\"right\"><i>Peter Norvig<br>Decembers 20162021</i></div>\n", "<div style=\"text-align: right\" align=\"right\"><i>Peter Norvig<br>Decembers 20162023</i></div>\n",
"\n", "\n",
"# Advent of Code Utilities\n", "# Advent of Code Utilities\n",
"\n", "\n",
"Stuff I might need for [Advent of Code](https://adventofcode.com). First, some imports that I have used in past AoC years:" "Stuff I might need for [Advent of Code](https://adventofcode.com). \n",
"\n",
"First, some imports that I have used in past AoC years:"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 2, "execution_count": 5,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -26,7 +28,6 @@
"from math import ceil, floor, factorial, gcd, log, log2, log10, sqrt, inf\n", "from math import ceil, floor, factorial, gcd, log, log2, log10, sqrt, inf\n",
"\n", "\n",
"import matplotlib.pyplot as plt\n", "import matplotlib.pyplot as plt\n",
"\n",
"import ast\n", "import ast\n",
"import fractions\n", "import fractions\n",
"import functools\n", "import functools\n",
@@ -35,6 +36,7 @@
"import pathlib\n", "import pathlib\n",
"import re\n", "import re\n",
"import string\n", "import string\n",
"import sys\n",
"import time" "import time"
] ]
}, },
@@ -65,19 +67,21 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 3, "execution_count": 6,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"current_year = 2022 # Subdirectory name for input files\n", "current_year = 2023 # Subdirectory name for input files\n",
"\n",
"lines = str.splitlines # By default, split input text into lines\n", "lines = str.splitlines # By default, split input text into lines\n",
"\n",
"def paragraphs(text): \"Split text into paragraphs\"; return text.split('\\n\\n')\n", "def paragraphs(text): \"Split text into paragraphs\"; return text.split('\\n\\n')\n",
"\n", "\n",
"def parse(day_or_text:Union[int, str], parser:Callable=str, sections:Callable=lines, show=8) -> tuple:\n", "def parse(day_or_text:Union[int, str], parser=str, sections=lines, show=8) -> tuple:\n",
" \"\"\"Split the input text into `sections`, and apply `parser` to each.\n", " \"\"\"Split the input text into `sections`, and apply `parser` to each.\n",
" The first argument is either the text itself, or the day number of a text file.\"\"\"\n", " The first argument is either the text itself, or the day number of a text file.\"\"\"\n",
" if isinstance(day_or_text, str) and show == 8: \n", " if isinstance(day_or_text, str) and show == 8: \n",
" show = 0 # By default, don't show lines when parsing exampole text.\n", " show = 0 # By default, don't show lines when parsing example text.\n",
" start = time.time()\n", " start = time.time()\n",
" text = get_text(day_or_text)\n", " text = get_text(day_or_text)\n",
" show_items('Puzzle input', text.splitlines(), show)\n", " show_items('Puzzle input', text.splitlines(), show)\n",
@@ -86,7 +90,7 @@
" show_items('Parsed representation', records, show)\n", " show_items('Parsed representation', records, show)\n",
" return records\n", " return records\n",
"\n", "\n",
"def get_text(day_or_text:Union[int, str]) -> str:\n", "def get_text(day_or_text: Union[int, str]) -> str:\n",
" \"\"\"The text used as input to the puzzle: either a string or the day number,\n", " \"\"\"The text used as input to the puzzle: either a string or the day number,\n",
" which denotes the file 'AOC/year/input{day}.txt'.\"\"\"\n", " which denotes the file 'AOC/year/input{day}.txt'.\"\"\"\n",
" if isinstance(day_or_text, str):\n", " if isinstance(day_or_text, str):\n",
@@ -116,7 +120,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 4, "execution_count": 7,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -152,58 +156,6 @@
" return text.strip()" " return text.strip()"
] ]
}, },
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Helper functions:"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"def truncate(object, width=100, ellipsis=' ...') -> str:\n",
" \"\"\"Use elipsis to truncate `str(object)` to `width` characters, if necessary.\"\"\"\n",
" string = str(object)\n",
" return string if len(string) <= width else string[:width-len(ellipsis)] + ellipsis\n",
"\n",
"def mapt(function: Callable, *sequences) -> tuple:\n",
" \"\"\"`map`, with the result as a tuple.\"\"\"\n",
" return tuple(map(function, *sequences))\n",
"\n",
"def mapl(function: Callable, *sequences) -> list:\n",
" \"\"\"`map`, with the result as a list.\"\"\"\n",
" return list(map(function, *sequences))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Some tests for the functions above:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"assert parse(\"hello\\nworld\", show=0) == ('hello', 'world')\n",
"assert parse(\"123\\nabc7\", digits, show=0) == ((1, 2, 3), (7,))\n",
"assert truncate('hello world', 99) == 'hello world'\n",
"assert truncate('hello world', 8) == 'hell ...'\n",
"\n",
"assert atoms('hello, cruel_world! 24-7') == ('hello', 'cruel_world', 24, -7)\n",
"assert words('hello, cruel_world! 24-7') == ('hello', 'cruel', 'world')\n",
"assert digits('hello, cruel_world! 24-7') == (2, 4, 7)\n",
"assert ints('hello, cruel_world! 24-7') == (24, -7)\n",
"assert positive_ints('hello, cruel_world! 24-7') == (24, 7)"
]
},
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
@@ -215,32 +167,34 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 91, "execution_count": 105,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
"data": { "name": "stdout",
"text/plain": [ "output_type": "stream",
" .0000 seconds, answer: 3 INCORRECT!!!! Expected 2" "text": [
] " 0.1: .0000 seconds, answer unknown \n",
}, " 0.2: .0000 seconds, answer 549755813888 ok\n",
"execution_count": 91, " 0.3: .0000 seconds, answer 549755813889 WRONG; expected answer is 549755813888\n",
"metadata": {}, "10.4: .0000 seconds, answer 4 WRONG; expected answer is unknown\n"
"output_type": "execute_result" ]
} }
], ],
"source": [ "source": [
"answers = {} # `answers` is a dict of {puzzle_number: answer}\n", "answers = {} # `answers` is a dict of {puzzle_number: answer}\n",
"\n", "\n",
"unknown = 'unknown'\n",
"\n",
"class answer:\n", "class answer:\n",
" \"\"\"Verify that calling `code` computes the `solution` to `puzzle`. \n", " \"\"\"Verify that calling `code` computes the `solution` to `puzzle`. \n",
" Record results in the dict `answers`.\"\"\"\n", " Record results in the dict `answers`.\"\"\"\n",
" def __init__(self, puzzle, solution, code:callable):\n", " def __init__(self, puzzle: float, solution, code:callable=lambda:unknown):\n",
" self.solution, self.code = solution, code\n", " self.puzzle, self.solution, self.code = puzzle, solution, code\n",
" answers[puzzle] = self\n", " answers[puzzle] = self\n",
" self.check()\n", " self.check()\n",
" \n", " \n",
" def check(self):\n", " def check(self) -> bool:\n",
" \"\"\"Check if the code computes the correct solution; record run time.\"\"\"\n", " \"\"\"Check if the code computes the correct solution; record run time.\"\"\"\n",
" start = time.time()\n", " start = time.time()\n",
" self.got = self.code()\n", " self.got = self.code()\n",
@@ -248,12 +202,19 @@
" self.ok = (self.got == self.solution)\n", " self.ok = (self.got == self.solution)\n",
" return self.ok\n", " return self.ok\n",
" \n", " \n",
" def __repr__(self):\n", " def __repr__(self) -> str:\n",
" \"\"\"The repr of an answer shows what happened.\"\"\"\n", " \"\"\"The repr of an answer shows what happened.\"\"\"\n",
" def commas(x) -> str: return f'{x:,d}' if is_int(x) else f'{x}'\n", " secs = f'{self.secs:7.4f}'.replace(' 0.', ' .')\n",
" secs = f'{self.secs:7.4f} seconds'.replace(' 0.', ' .')\n", " comment = (f'' if self.got == unknown else\n",
" ok = '' if self.ok else f' !!!! INCORRECT !!!! Expected {commas(self.solution)}'\n", " f' ok' if self.ok else \n",
" return f'{secs}, answer: {commas(self.got)}{ok}'" " f' WRONG; expected answer is {self.solution}')\n",
" return f'Puzzle {self.puzzle:4.1f}: {secs} seconds, answer {self.got:<15}{comment}'\n",
"\n",
"def test_answer():\n",
" print(answer(0.1, unknown))\n",
" print(answer(0.2, 2**39, lambda: 2**39))\n",
" print(answer(0.3, 2**39, lambda: 2**39+1))\n",
" print(answer(10.4, unknown, lambda: 2 + 2))"
] ]
}, },
{ {
@@ -267,7 +228,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": 10,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -333,6 +294,10 @@
" \"Intersection of several sets; error if no sets.\"\n", " \"Intersection of several sets; error if no sets.\"\n",
" first, *rest = sets\n", " first, *rest = sets\n",
" return set(first).intersection(*rest)\n", " return set(first).intersection(*rest)\n",
"\n",
"def range_intersection(range1, range2) -> range:\n",
" \"\"\"Return a range that is the intersection of these two ranges.\"\"\"\n",
" return range(max(range1.start, range2.start), min(range1.stop, range2.stop))\n",
" \n", " \n",
"def naked_plot(points, marker='o', size=(10, 10), invert=True, square=False, **kwds):\n", "def naked_plot(points, marker='o', size=(10, 10), invert=True, square=False, **kwds):\n",
" \"\"\"Plot `points` without any axis lines or tick marks.\n", " \"\"\"Plot `points` without any axis lines or tick marks.\n",
@@ -353,45 +318,32 @@
" return {dic[x]: x for x in dic}\n", " return {dic[x]: x for x in dic}\n",
"\n", "\n",
"def walrus(name, value):\n", "def walrus(name, value):\n",
" \"\"\"If you're not in 3.8, and you can't do `x := val`,\n", " \"\"\"If you're not in 3.8 or more, and you can't do `x := val`,\n",
" then you can use `walrus('x', val)`.\"\"\"\n", " then you can use `walrus('x', val)`, if `x` is global.\"\"\"\n",
" globals()[name] = value\n", " globals()[name] = value\n",
" return value\n", " return value\n",
"\n", "\n",
"cat = ''.join\n", "def truncate(object, width=100, ellipsis=' ...') -> str:\n",
" \"\"\"Use elipsis to truncate `str(object)` to `width` characters, if necessary.\"\"\"\n",
" string = str(object)\n",
" return string if len(string) <= width else string[:width-len(ellipsis)] + ellipsis\n",
"\n",
"def mapt(function: Callable, *sequences) -> tuple:\n",
" \"\"\"`map`, with the result as a tuple.\"\"\"\n",
" return tuple(map(function, *sequences))\n",
"\n",
"def mapl(function: Callable, *sequences) -> list:\n",
" \"\"\"`map`, with the result as a list.\"\"\"\n",
" return list(map(function, *sequences))\n",
"\n",
"def cat(things: Collection) -> str:\n",
" \"\"\"Concatenate the things.\"\"\"\n",
" return ''.join(map(str, things))\n",
" \n",
"cache = functools.lru_cache(None)\n", "cache = functools.lru_cache(None)\n",
"Ø = frozenset() # empty set" "Ø = frozenset() # empty set"
] ]
}, },
{
"cell_type": "markdown",
"metadata": {},
"source": [
"More tests:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"assert multimap(((i % 3), i) for i in range(9)) == {0: [0, 3, 6], 1: [1, 4, 7], 2: [2, 5, 8]}\n",
"assert prod([2, 3, 5]) == 30\n",
"assert total(Counter('hello, world')) == 12\n",
"assert cover(3, 1, 4, 1, 5) == range(1, 6)\n",
"assert minmax([3, 1, 4, 1, 5, 9]) == (1, 9)\n",
"assert T([(1, 2, 3), (4, 5, 6)]) == [(1, 4), (2, 5), (3, 6)]\n",
"assert the({1}) == 1\n",
"assert split_at('hello, world', 6) == ('hello,', ' world')\n",
"assert is_int(-42) and not is_int('one')\n",
"assert sign(-42) == -1 and sign(0) == 0 and sign(42) == +1\n",
"assert union([{1, 2}, {3, 4}, {5, 6}]) == {1, 2, 3, 4, 5, 6}\n",
"assert intersection([{1, 2, 3}, {2, 3, 4}, {2, 4, 6, 8}]) == {2}\n",
"assert clock_mod(24, 12) == 12 and 24 % 12 == 0\n",
"assert cat(['hello', 'world']) == 'helloworld'"
]
},
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
@@ -403,7 +355,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 24, "execution_count": 7,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -459,32 +411,6 @@
" return next((x for x in iterable if x), default)" " return next((x for x in iterable if x), default)"
] ]
}, },
{
"cell_type": "markdown",
"metadata": {},
"source": [
"More tests:"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [],
"source": [
"assert quantify(words('This is a test'), str.islower) == 3\n",
"assert dotproduct([1, 2, 3, 4], [1000, 100, 10, 1]) == 1234\n",
"assert list(flatten([{1, 2, 3}, (4, 5, 6), [7, 8, 9]])) == [1, 2, 3, 4, 5, 6, 7, 8, 9]\n",
"assert append(([1, 2], [3, 4], [5, 6])) == [1, 2, 3, 4, 5, 6]\n",
"assert list(batched(range(11), 3)) == [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10)]\n",
"assert list(sliding_window('abcdefghi', 3)) == ['abc', 'bcd', 'cde', 'def', 'efg', 'fgh', 'ghi']\n",
"assert first('abc') == 'a'\n",
"assert first('') == None\n",
"assert last('abc') == 'c'\n",
"assert first_true([0, None, False, 42, 99]) == 42\n",
"assert first_true([0, None, '', 0.0]) == False"
]
},
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
@@ -496,7 +422,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 92, "execution_count": 8,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -571,22 +497,28 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 68, "execution_count": 9,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"class Grid(dict):\n", "class Grid(dict):\n",
" \"\"\"A 2D grid, implemented as a mapping of {(x, y): cell_contents}.\"\"\"\n", " \"\"\"A 2D grid, implemented as a mapping of {(x, y): cell_contents}.\"\"\"\n",
" def __init__(self, grid=(), directions=directions4, skip=(), default=KeyError):\n", " def __init__(self, grid=(), directions=directions4, skip=(), default=None):\n",
" \"\"\"Initialize with either (e.g.) `Grid({(0, 0): '#', (1, 0): '.', ...})`, or\n", " \"\"\"Initialize one of four ways: \n",
" `Grid([\"#..\", \"..#\"]) or `Grid(\"#..\\n..#\")`.\"\"\"\n", " `Grid({(0, 0): '#', (1, 0): '.', ...})`\n",
" `Grid(another_grid)\n",
" `Grid([\"#..\", \"..#\"])\n",
" `Grid(\"#..\\n..#\")`.\"\"\"\n",
" self.directions = directions\n", " self.directions = directions\n",
" self.skip = skip\n",
" self.default = default\n", " self.default = default\n",
" if isinstance(grid, abc.Mapping): \n", " if isinstance(grid, abc.Mapping): \n",
" self.update(grid) \n", " self.update(grid) \n",
" self.size = (len(cover(Xs(self))), len(cover(Ys(self))))\n",
" else:\n", " else:\n",
" if isinstance(grid, str): \n", " if isinstance(grid, str): \n",
" grid = grid.splitlines()\n", " grid = grid.splitlines()\n",
" self.size = (max(map(len, grid)), len(grid))\n",
" self.update({(x, y): val \n", " self.update({(x, y): val \n",
" for y, row in enumerate(grid) \n", " for y, row in enumerate(grid) \n",
" for x, val in enumerate(row)\n", " for x, val in enumerate(row)\n",
@@ -599,23 +531,33 @@
" else:\n", " else:\n",
" return self.default\n", " return self.default\n",
"\n", "\n",
" def copy(self): return Grid(self, directions=self.directions, default=self.default)\n", " def in_range(self, point) -> bool:\n",
" \"\"\"Is the point within the range of the grid's size?\"\"\"\n",
" return (0 <= X_(point) < X_(self.size) and\n",
" 0 <= Y_(point) < Y_(self.size))\n",
"\n",
" def copy(self): \n",
" return Grid(self, directions=self.directions, skip=self.skip, default=self.default)\n",
" \n", " \n",
" def neighbors(self, point) -> List[Point]:\n", " def neighbors(self, point) -> List[Point]:\n",
" \"\"\"Points on the grid that neighbor `point`.\"\"\"\n", " \"\"\"Points on the grid that neighbor `point`.\"\"\"\n",
" return [add2(point, Δ) for Δ in self.directions \n", " return [add2(point, Δ) for Δ in self.directions \n",
" if add2(point, Δ) in self or self.default != KeyError]\n", " if add2(point, Δ) in self or self.default not in (KeyError, None)]\n",
" \n", " \n",
" def neighbor_contents(self, point) -> Iterable:\n", " def neighbor_contents(self, point) -> Iterable:\n",
" \"\"\"The contents of the neighboring points.\"\"\"\n", " \"\"\"The contents of the neighboring points.\"\"\"\n",
" return (self[p] for p in self.neighbors(point))\n", " return (self[p] for p in self.neighbors(point))\n",
"\n",
" def findall(self, contents: Collection) -> List[Point]:\n",
" \"\"\"All points that contain one of the given contents, e.g. grid.findall('#').\"\"\"\n",
" return [p for p in self if self[p] in contents]\n",
" \n", " \n",
" def to_rows(self, xrange=None, yrange=None) -> List[List[object]]:\n", " def to_rows(self, xrange=None, yrange=None) -> List[List[object]]:\n",
" \"\"\"The contents of the grid, as a rectangular list of lists.\n", " \"\"\"The contents of the grid, as a rectangular list of lists.\n",
" You can define a window with an xrange and yrange; or they default to the whole grid.\"\"\"\n", " You can define a window with an xrange and yrange; or they default to the whole grid.\"\"\"\n",
" xrange = xrange or cover(Xs(self))\n", " xrange = xrange or cover(Xs(self))\n",
" yrange = yrange or cover(Ys(self))\n", " yrange = yrange or cover(Ys(self))\n",
" default = ' ' if self.default is KeyError else self.default\n", " default = ' ' if self.default in (KeyError, None) else self.default\n",
" return [[self.get((x, y), default) for x in xrange] \n", " return [[self.get((x, y), default) for x in xrange] \n",
" for y in yrange]\n", " for y in yrange]\n",
"\n", "\n",
@@ -637,28 +579,6 @@
" return [add(point, Δ) for Δ in directions]" " return [add(point, Δ) for Δ in directions]"
] ]
}, },
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here are some tests:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"p, q = (0, 3), (4, 0)\n",
"assert Y_(p) == 3 and X_(q) == 4\n",
"assert distance(p, q) == 5\n",
"assert taxi_distance(p, q) == 7\n",
"assert add(p, q) == (4, 3)\n",
"assert sub(p, q) == (-4, 3)\n",
"assert add(North, South) == (0, 0)"
]
},
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
@@ -676,7 +596,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": 10,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -704,7 +624,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": 11,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -781,7 +701,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": 12,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -810,7 +730,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": 13,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -825,7 +745,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 42, "execution_count": 14,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -839,7 +759,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 30, "execution_count": 15,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -850,6 +770,79 @@
" def __setattr__(self, attr, value):\n", " def __setattr__(self, attr, value):\n",
" self[attr] = value" " self[attr] = value"
] ]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Tests"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"def tests():\n",
" \"\"\"Run tests on utility functions. Also serves as usage examples.\"\"\"\n",
" \n",
" # PARSER\n",
"\n",
" assert parse(\"hello\\nworld\", show=0) == ('hello', 'world')\n",
" assert parse(\"123\\nabc7\", digits, show=0) == ((1, 2, 3), (7,))\n",
" assert truncate('hello world', 99) == 'hello world'\n",
" assert truncate('hello world', 8) == 'hell ...'\n",
"\n",
" assert atoms('hello, cruel_world! 24-7') == ('hello', 'cruel_world', 24, -7)\n",
" assert words('hello, cruel_world! 24-7') == ('hello', 'cruel', 'world')\n",
" assert digits('hello, cruel_world! 24-7') == (2, 4, 7)\n",
" assert ints('hello, cruel_world! 24-7') == (24, -7)\n",
" assert positive_ints('hello, cruel_world! 24-7') == (24, 7)\n",
"\n",
" # UTILITIES\n",
"\n",
" assert multimap(((i % 3), i) for i in range(9)) == {0: [0, 3, 6], 1: [1, 4, 7], 2: [2, 5, 8]}\n",
" assert prod([2, 3, 5]) == 30\n",
" assert total(Counter('hello, world')) == 12\n",
" assert cover(3, 1, 4, 1, 5) == range(1, 6)\n",
" assert minmax([3, 1, 4, 1, 5, 9]) == (1, 9)\n",
" assert T([(1, 2, 3), (4, 5, 6)]) == [(1, 4), (2, 5), (3, 6)]\n",
" assert the({1}) == 1\n",
" assert split_at('hello, world', 6) == ('hello,', ' world')\n",
" assert is_int(-42) and not is_int('one')\n",
" assert sign(-42) == -1 and sign(0) == 0 and sign(42) == +1\n",
" assert union([{1, 2}, {3, 4}, {5, 6}]) == {1, 2, 3, 4, 5, 6}\n",
" assert intersection([{1, 2, 3}, {2, 3, 4}, {2, 4, 6, 8}]) == {2}\n",
" assert clock_mod(24, 12) == 12 and 24 % 12 == 0\n",
" assert cat(['hello', 'world']) == 'helloworld'\n",
"\n",
" # ITERTOOL RECIPES\n",
"\n",
" assert quantify(words('This is a test'), str.islower) == 3\n",
" assert dotproduct([1, 2, 3, 4], [1000, 100, 10, 1]) == 1234\n",
" assert list(flatten([{1, 2, 3}, (4, 5, 6), [7, 8, 9]])) == [1, 2, 3, 4, 5, 6, 7, 8, 9]\n",
" assert append(([1, 2], [3, 4], [5, 6])) == [1, 2, 3, 4, 5, 6]\n",
" assert list(batched(range(11), 3)) == [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10)]\n",
" assert list(sliding_window('abcdefghi', 3)) == ['abc', 'bcd', 'cde', 'def', 'efg', 'fgh', 'ghi']\n",
" assert first('abc') == 'a'\n",
" assert first('') == None\n",
" assert last('abc') == 'c'\n",
" assert first_true([0, None, False, 42, 99]) == 42\n",
" assert first_true([0, None, '', 0.0]) == False\n",
"\n",
" # POINTS\n",
"\n",
" p, q = (0, 3), (4, 0)\n",
" assert Y_(p) == 3 and X_(q) == 4\n",
" assert distance(p, q) == 5\n",
" assert taxi_distance(p, q) == 7\n",
" assert add(p, q) == (4, 3)\n",
" assert sub(p, q) == (-4, 3)\n",
" assert add(North, South) == (0, 0)\n",
" \n",
"tests()"
]
} }
], ],
"metadata": { "metadata": {