from typing import List, Tuple, Set

data_structure: type = Tuple[int, int]


def parse_data(data: List[str]) -> data_structure:
    bounds: List[str] = data[0].split('-')
    return int(bounds[0]), int(bounds[1])


def meets_criteria(value: int, bounds: Tuple[int, int], extra_constraint: bool = False) -> bool:
    satisfies: bool = 100000 <= value <= 999999  # 6-digit
    satisfies &= bounds[0] <= value <= bounds[1]  # within range
    digits: List[int] = [10]
    working_number: int = value
    has_matching_adjacent_digits: bool = False
    for i in range(6):
        digits.append(working_number % 10)
        working_number = working_number // 10
        satisfies &= digits[i + 1] <= digits[i]  # non-strictly monotonically increasing digits from left-to-right
        has_matching_adjacent_digits |= digits[i + 1] == digits[i]
    satisfies &= has_matching_adjacent_digits  # has at least one pair of identical digits
    if extra_constraint and satisfies:
        unique_digits: Set[int] = set(digits)
        has_pair_of_matching_digits: bool = False
        for digit in unique_digits:
            has_pair_of_matching_digits |= digits.count(digit) == 2
        satisfies &= has_pair_of_matching_digits
    return satisfies


def part1(data: data_structure) -> int:
    return len([password for password in range(data[0], data[1] + 1) if meets_criteria(password, (data[0], data[1]))])


def part2(data: data_structure) -> int:
    return len([password for password in range(data[0], data[1] + 1)
                if meets_criteria(password, (data[0], data[1]), True)])


if __name__ == '__main__':
    raw_data = ['178416-676461']
    print(part1(parse_data(raw_data)))
    print(part2(parse_data(raw_data)))