# Path: python/sudoku.py
# class Sudoku:
#     def __init__(self, line):
#         self.data = self.from_str(line)
#     def from_str(self, line):
#         rows = (line[i:i + BOARD_SIZE] for i in range(0, len(line), BOARD_SIZE))
#         return [list(map(int, row)) for row in rows]
#     def next_empty(self):
#         empty_slots = ((i, j)
#             for i in range(0, BOARD_SIZE)
#             for j in range(0, BOARD_SIZE)
#             if self.data[i][j] == 0)
#         return next(empty_slots, None)
#     def can_put(self, x, y, val):
#         if any(val in (self.data[i][y], self.data[x][i]) for i in range(0, BOARD_SIZE)):
#             return False
#         sq_x = x - (x % 3)
#         sq_y = y - (y % 3)
#         if any(self.data[i][j] == val for i in range(sq_x, sq_x + 3) for j in range(sq_y, sq_y + 3)):
#             return False
#         return True
#     def solve(self):
#         spot = self.next_empty()
#         if spot is None:
#             return
#         x, y = spot
#         for i in range(1, 10):
#             if self.can_put(x, y, i):
#                 self.data[x][y] = i
#                 self.solve()
#                 if self.next_empty() is None:
#                     return #found solution
# 
#         self.data[x][y] = 0  #solution not found, backtrack
#     def __str__(self):
#         return '\n'.join(' '.join(map(str, row)) for row in self.data)
# 
# class Process:
#     "Iterate over sudokus solving one by one"
#     def __init__(self, sudokus):
#         self.sudokus = sudokus
#         self.index = 0
#         self.total_steps = len(self.sudokus)
#         self.resolution = 100
#         self.width = 50
#     def __next__(self):
#         try:
#             s = Sudoku(self.sudokus[self.index])
#             s.solve()
#             self.index += 1
#             self.show_progress_bar()
#             return s
#         except IndexError:
#             self.index = 0
#             raise StopIteration from IndexError
#     def __iter__(self):
#         return self
#     def show_progress_bar(self):
#         "Update progress bar"
#         if self.total_steps / self.resolution == 0:
#             return
#         step = self.index
#         ratio = step / float(self.total_steps)
#         count = int(ratio * self.width)
#         percent = ratio * 100
#         print(f'{percent:.2f}% [', end='', flush=True)
#         for _ in range(0, count):
#             print('=', end='', flush=True)
#         for _ in range(count, self.width):
#             print(' ', end='', flush=True)
#         print(']\r', end='', flush=True)


import unittest
import io
from .sudoku import Sudoku, Process
                   [4, 3, 1, 8, 2, 9, 5, 7, 6],
                   [6, 8, 5, 7, 3, 4, 2, 9, 1],
                   [7, 9, 2, 1, 5, 6, 3, 8, 4]]

SUDOKU_SOLUTION_FORMATED = """2 7 3 4 8 1 9 6 5
9 1 6 2 7 5 4 3 8
5 4 8 6 9 3 1 2 7
8 5 9 3 4 7 6 1 2
3 6 7 5 1 2 8 4 9
1 2 4 9 6 8 7 5 3
4 3 1 8 2 9 5 7 6
6 8 5 7 3 4 2 9 1
7 9 2 1 5 6 3 8 4"""

class TestSudoku(unittest.TestCase):
    def test_from_str(self):
        s = Sudoku(SUDOKU_SAMPLE_LINE)
        self.assertEqual(SUDOKU_SAMPLE, s.data)

    def test_to_str(self):
        s = Sudoku(SUDOKU_SAMPLE_LINE)
        self.assertEqual(SUDOKU_SAMPLE_FORMATED.strip(), str(s).strip())

    def test_solve(self):
        s = Sudoku(SUDOKU_SAMPLE_LINE)
        s.solve()
        self.assertEqual(SUDOKU_SOLUTION, s.data)

    def test_process(self):
        results = []

# Path: python/sudoku.py
# class Sudoku:
#     def __init__(self, line):
#         self.data = self.from_str(line)
#     def from_str(self, line):
#         rows = (line[i:i + BOARD_SIZE] for i in range(0, len(line), BOARD_SIZE))
#         return [list(map(int, row)) for row in rows]
#     def next_empty(self):
#         empty_slots = ((i, j)
#             for i in range(0, BOARD_SIZE)
#             for j in range(0, BOARD_SIZE)
#             if self.data[i][j] == 0)
#         return next(empty_slots, None)
#     def can_put(self, x, y, val):
#         if any(val in (self.data[i][y], self.data[x][i]) for i in range(0, BOARD_SIZE)):
#             return False
#         sq_x = x - (x % 3)
#         sq_y = y - (y % 3)
#         if any(self.data[i][j] == val for i in range(sq_x, sq_x + 3) for j in range(sq_y, sq_y + 3)):
#             return False
#         return True
#     def solve(self):
#         spot = self.next_empty()
#         if spot is None:
#             return
#         x, y = spot
#         for i in range(1, 10):
#             if self.can_put(x, y, i):
#                 self.data[x][y] = i
#                 self.solve()
#                 if self.next_empty() is None:
#                     return #found solution
# 
#         self.data[x][y] = 0  #solution not found, backtrack
#     def __str__(self):
#         return '\n'.join(' '.join(map(str, row)) for row in self.data)
# 
# class Process:
#     "Iterate over sudokus solving one by one"
#     def __init__(self, sudokus):
#         self.sudokus = sudokus
#         self.index = 0
#         self.total_steps = len(self.sudokus)
#         self.resolution = 100
#         self.width = 50
#     def __next__(self):
#         try:
#             s = Sudoku(self.sudokus[self.index])
#             s.solve()
#             self.index += 1
#             self.show_progress_bar()
#             return s
#         except IndexError:
#             self.index = 0
#             raise StopIteration from IndexError
#     def __iter__(self):
#         return self
#     def show_progress_bar(self):
#         "Update progress bar"
#         if self.total_steps / self.resolution == 0:
#             return
#         step = self.index
#         ratio = step / float(self.total_steps)
#         count = int(ratio * self.width)
#         percent = ratio * 100
#         print(f'{percent:.2f}% [', end='', flush=True)
#         for _ in range(0, count):
#             print('=', end='', flush=True)
#         for _ in range(count, self.width):
#             print(' ', end='', flush=True)
#         print(']\r', end='', flush=True)


# Path: python/test_sudoku.py
import unittest
import io
from .sudoku import Sudoku, Process

                   [4, 3, 1, 8, 2, 9, 5, 7, 6],
                   [6, 8, 5, 7, 3, 4, 2, 9, 1],
                   [7, 9, 2, 1, 5, 6, 3, 8, 4]]

SUDOKU_SOLUTION_FORMATED = """2 7 3 4 8 1 9 6 5
9 1 6 2 7 5 4 3 8
5 4 8 6 9 3 1 2 7
8 5 9 3 4 7 6 1 2
3 6 7 5 1 2 8 4 9
1 2 4 9 6 8 7 5 3
4 3 1 8 2 9 5 7 6
6 8 5 7 3 4 2 9 1
7 9 2 1 5 6 3 8 4"""

class TestSudoku(unittest.TestCase):
    def test_from_str(self):
        s = Sudoku(SUDOKU_SAMPLE_LINE)
        self.assertEqual(SUDOKU_SAMPLE, s.data)

    def test_to_str(self):
        s = Sudoku(SUDOKU_SAMPLE_LINE)
        self.assertEqual(SUDOKU_SAMPLE_FORMATED.strip(), str(s).strip())

    def test_solve(self):
        s = Sudoku(SUDOKU_SAMPLE_LINE)
        s.solve()
        self.assertEqual(SUDOKU_SOLUTION, s.data)

    def test_process(self):
        results = []

        for solved_sudoku in Process([SUDOKU_SAMPLE_LINE]):