Now model the core logic of a high-performance batch image processor for a multi-core machine. Real file I/O and actual parallel execution are not required; instead, simulate what an order-preserving process pool would produce. Each image task is independent. A fixed number of workers can process tasks in parallel. Tasks are submitted in input order, and each new task starts on the worker that becomes available first; if multiple workers are free at the same time, choose the smaller worker id. You must preserve output ordering in the returned list even though tasks finish at different times. A task failure must not affect other tasks. Use the same image metadata and operations as Part 1, but now invalid operations are possible. Processing cost rules are: grayscale costs current_width * current_height, scale costs current_width * current_height, and resize costs new_width * new_height. If scale has factor <= 0, resize has a non-positive target dimension, or the operation name is unknown, the task fails immediately before that operation and no output file is produced. Successful tasks still generate output paths using the same '_out' filename rule as Part 1.
Examples
Input: ([{'input_path': 'a.png', 'width': 10, 'height': 10, 'mode': 'RGB', 'operations': [('grayscale',), ('scale', 0.5)]}, {'input_path': 'b.jpg', 'width': 4, 'height': 5, 'mode': 'RGB', 'operations': [('resize', 2, 3)]}, {'input_path': 'c.bmp', 'width': 6, 'height': 6, 'mode': 'L', 'operations': []}], 'out', 2)
Expected Output: [('ok', 200, 'out/a_out.png', 5, 5, 'L', None), ('ok', 6, 'out/b_out.jpg', 2, 3, 'RGB', None), ('ok', 6, 'out/c_out.bmp', 6, 6, 'L', None)]
Explanation: Task 1 takes 100 + 100 = 200 time units. Task 2 takes 2 * 3 = 6. Task 3 has no operations, so once worker 1 becomes free at time 6, it finishes immediately at time 6.
Input: ([{'input_path': 'x.png', 'width': 8, 'height': 8, 'mode': 'RGB', 'operations': [('scale', 0.25)]}, {'input_path': 'y.png', 'width': 5, 'height': 5, 'mode': 'RGB', 'operations': [('resize', 0, 4)]}, {'input_path': 'z.png', 'width': 3, 'height': 7, 'mode': 'RGB', 'operations': [('grayscale',)]}], 'res', 2)
Expected Output: [('ok', 64, 'res/x_out.png', 2, 2, 'RGB', None), ('error', 0, None, None, None, None, 'invalid resize'), ('ok', 21, 'res/z_out.png', 3, 7, 'L', None)]
Explanation: The second task fails immediately, so its worker is free again at time 0 and can start the third task without affecting the first task.
Input: ([{'input_path': 'm.png', 'width': 2, 'height': 2, 'mode': 'RGB', 'operations': [('resize', 4, 4), ('scale', 0.5)]}, {'input_path': 'n', 'width': 1, 'height': 9, 'mode': 'RGB', 'operations': [('grayscale',), ('scale', 0.1)]}], 'out', 1)
Expected Output: [('ok', 32, 'out/m_out.png', 2, 2, 'RGB', None), ('ok', 50, 'out/n_out', 1, 1, 'L', None)]
Explanation: With one worker, tasks run sequentially. The first costs 16 + 16 = 32, so the second starts at time 32 and finishes at time 50.
Input: ([{'input_path': 'p.png', 'width': 5, 'height': 5, 'mode': 'RGB', 'operations': [('grayscale',), ('scale', -1.0)]}, {'input_path': 'q.png', 'width': 10, 'height': 1, 'mode': 'RGB', 'operations': [('resize', 1, 1)]}], 'tmp', 1)
Expected Output: [('error', 25, None, None, None, None, 'invalid scale'), ('ok', 26, 'tmp/q_out.png', 1, 1, 'RGB', None)]
Explanation: The first task spends 25 time units on grayscale, then fails before the invalid scale. The next task starts only after time 25 because there is just one worker.
Input: ([], 'out', 4)
Expected Output: []
Explanation: Edge case: no tasks means no results.