12) Refine blocking_tournament-max algorithm to work with any input size. You have to refine both parts of the algorithm. Take especial care on not exceeding the borders of arrays. The base algorithms will be available at the course web-page. 13) Refine the previous blocking_tournament-max algorithm work with any input size and any number of processors. The algorithm can refer the number of existing processors by variable P. The new version should use all available processors optimally. You have to refine both parts of the algorithm. What is the time complexity of the new algorithm as a function of N and P. function blocking_tournament-max(var A : array[0..N - 1]); blocks = P; block_size = roof(N/P); for i := 0 to blocks-1 pardo block_start = i*block_size; block_end = (i+1)*block_size-1; if (block_end >= N) then block_end := N-1; B[i] := A[block_start]; for j := block_start+1 to block_end do B[i] := max(B[i], A[j]); return tournament-max(B[0..blocks-1]); function tournament-max(var A : array[0..N - 1]); for i := roof(log(N)) - 1 to 0 do for j := 0 to 2^i - 1 pardo if (j*2+1 < N) then A[j] := max(A[j*2], A[j*2+1]) else if (j*2 < N) then A[j] := A[j*2]; return A[0]; T(N, P) = N/P + logP. 14) Write an algorithm for parallel "binary search". The algorithm is actually a (P+1)-ary search as it divides the input with P division points on every iteration. You can assume input size to be convenient with respect to P. # returns index, -1 of not found. function search(A : array[0..N-1]; x : element) : index; found : -1..N-1; first, last : 0..N-1; first := 0; last := N-1; found := -1; while ((found = -1) and (first <= last)) do nn := last - first + 1; block := roof(nn / P); for i := 0 to N-1 by block pardo if (A[i] = x) then found := i else if ((A[i] > x) and (A[i+block] <= x)) then first := i+1; last := i+block; return found;