<commit_before>#!/usr/local/bin/python3

from collections import deque
from functools import reduce
from itertools import permutations
import operator
import pathlib
import random

input_file = pathlib.Path(__file__).parent.parent.joinpath('day15_input.txt')


def parse_input(text):
    parsed = dict()
    for line in text.splitlines():
        ingredient, qualities = line.split(': ', maxsplit=1)
        parsed[ingredient] = dict()
        for qual_pair in qualities.split(', '):
            quality, magnitude = qual_pair.split()
            parsed[ingredient][quality] = int(magnitude)
    return parsed


def test_parse():
    sample_input = '''\
Butterscotch: capacity -1, durability -2, flavor 6, texture 3, calories 8
Cinnamon: capacity 2, durability 3, flavor -2, texture -1, calories 3'''
    expected = dict(
        Butterscotch=dict(
            capacity=-1, durability=-2, flavor=6, texture=3, calories=8),
        Cinnamon=dict(
            capacity=2, durability=3, flavor=-2, texture=-1, calories=3))
    assert parse_input(sample_input) == expected


def cookie_score(recipe, ingredient_dict):
    qual_scores = [
        [amount * magnitude
            for quality, magnitude in ingredient_dict[ingredient].items()]
        for ingredient, amount in recipe]

    qual_totals = map(sum, zip(*qual_scores))
    # Set negative numbers to zero
    qual_totals = list(map(lambda x: x if x > 0 else 0, qual_totals))
    calories = qual_totals.pop()
    cookie_score = reduce(operator.mul, qual_totals)
    return (cookie_score, calories)


def test_score():
    sample_data = dict(
        Butterscotch=dict(
            capacity=-1, durability=-2, flavor=6, texture=3, calories=8),
        Cinnamon=dict(
            capacity=2, durability=3, flavor=-2, texture=-1, calories=3))
    recipe = [('Butterscotch', 44), ('Cinnamon', 56)]
    assert cookie_score(recipe, sample_data) == 62842880


def test_score_zero():
    """Score is zero when any ingredients have negative total"""
    sample_data = dict(
        Butterscotch=dict(
            capacity=-1, durability=-2, flavor=6, texture=3, calories=8),
        Cinnamon=dict(
            capacity=2, durability=3, flavor=-2, texture=-1, calories=3))
    recipe = [('Butterscotch', 100)]
    assert cookie_score(recipe, sample_data) == 0


def add_lists(*lists):
    """Add two lists together without numpy

    For example, given lists:
        [1, 2]  [3, 4]
    The result is:
        [4, 6]

    Lists are sliced to prevent mutation.
    """
    lists = (l[:] for l in lists)
    return list(map(sum, zip(*lists)))


# This is more general but slower than the range-based version below
def combinations(length, total):
    """Return a tuple of given length containing integers with sum total"""
    if length == 1:
        yield (total,)
        return
    for n in range(total + 1):
        x = n
        for y in combinations(length - 1, total - x):
            yield (x,) + y


# This is about twice as fast as the general recursive version above<commit_msg>2015-15.py: Fix tests (account for extra return value)<commit_after>#!/usr/local/bin/python3

from collections import deque
from functools import reduce
from itertools import permutations
import operator
import pathlib
import random

input_file = pathlib.Path(__file__).parent.parent.joinpath('day15_input.txt')


def parse_input(text):
    parsed = dict()
    for line in text.splitlines():
        ingredient, qualities = line.split(': ', maxsplit=1)
        parsed[ingredient] = dict()
        for qual_pair in qualities.split(', '):
            quality, magnitude = qual_pair.split()
            parsed[ingredient][quality] = int(magnitude)
    return parsed


def test_parse():
    sample_input = '''\
Butterscotch: capacity -1, durability -2, flavor 6, texture 3, calories 8
Cinnamon: capacity 2, durability 3, flavor -2, texture -1, calories 3'''
    expected = dict(
        Butterscotch=dict(
            capacity=-1, durability=-2, flavor=6, texture=3, calories=8),
        Cinnamon=dict(
            capacity=2, durability=3, flavor=-2, texture=-1, calories=3))
    assert parse_input(sample_input) == expected


def cookie_score(recipe, ingredient_dict):
    qual_scores = [
        [amount * magnitude
            for quality, magnitude in ingredient_dict[ingredient].items()]
        for ingredient, amount in recipe]

    qual_totals = map(sum, zip(*qual_scores))
    # Set negative numbers to zero
    qual_totals = list(map(lambda x: x if x > 0 else 0, qual_totals))
    calories = qual_totals.pop()
    cookie_score = reduce(operator.mul, qual_totals)
    return (cookie_score, calories)


def test_score():
    sample_data = dict(
        Butterscotch=dict(
            capacity=-1, durability=-2, flavor=6, texture=3, calories=8),
        Cinnamon=dict(
            capacity=2, durability=3, flavor=-2, texture=-1, calories=3))
    recipe = [('Butterscotch', 44), ('Cinnamon', 56)]
    score, _ = cookie_score(recipe, sample_data)
    assert score == 62842880


def test_score_zero():
    """Score is zero when any ingredients have negative total"""
    sample_data = dict(
        Butterscotch=dict(
            capacity=-1, durability=-2, flavor=6, texture=3, calories=8),
        Cinnamon=dict(
            capacity=2, durability=3, flavor=-2, texture=-1, calories=3))
    recipe = [('Butterscotch', 100)]
    score, _ = cookie_score(recipe, sample_data)
    assert score == 0


def add_lists(*lists):
    """Add two lists together without numpy

    For example, given lists:
        [1, 2]  [3, 4]
    The result is:
        [4, 6]

    Lists are sliced to prevent mutation.
    """
    lists = (l[:] for l in lists)
    return list(map(sum, zip(*lists)))


# This is more general but slower than the range-based version below
def combinations(length, total):
    """Return a tuple of given length containing integers with sum total"""
    if length == 1:
        yield (total,)
        return
    for n in range(total + 1):
        x = n
        for y in combinations(length - 1, total - x):
            yield (x,) + y


# This is about twice as fast as the general recursive version above
{'fraction_non_alphanumeric': 0.11534081665853262, 'fraction_numerical': 0.02407678542378396, 'mean_word_length': 3.0078226857887875, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3789452', 'n_tokens_mistral': 1949, 'n_tokens_neox': 1831, 'n_words': 576}