Execution in local of a c-mpi program fails - c

I want to run, on my local pc, this c-mpi program Receiver process have to print their portion of array in order of rank. This is my code:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "mpi.h"
#define MAX_DIM 22
#define NPROC 4
#define TRUE 1
#define FALSE 0
void print(int*,int*,int*,int*,int*,int*);
int main(int argc, char* argv[]){
int i,rank,*x,*y,nproc;
int partition=MAX_DIM/NPROC;
int sendcount[NPROC],offset[NPROC];
int k=MAX_DIM%NPROC;
for(i=0;i<NPROC;i++){
sendcount[i]=partition;
if(i<k)
sendcount[i]++;
}
offset[0]=0;
for(i=1;i<NPROC;i++)
offset[i]=offset[i-1]+sendcount[i-1];
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&nproc);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
if(rank==0){
srand(time(NULL));
x=(int*)malloc(sizeof(int)*MAX_DIM);
y=(int*)malloc(sizeof(int)*MAX_DIM);
for(i=0;i<MAX_DIM;i++){
x[i]=rand()%100+1;
y[i]=rand()%100+1;
}
printf("Sender[%d] => array <x>: \n",rank);
for(i=0;i<MAX_DIM;i++){
if(i%10==0)
printf("\n");
printf("%d ",x[i]);
}
printf("\nSender[%d] => array <y>: \n",rank);
for(i=0;i<MAX_DIM;i++){
if(i%10==0)
printf("\n");
printf("%d ",y[i]);
}
}
int* rvett1=(int*)malloc(sizeof(int)*sendcount[rank]);
int* rvett2=(int*)malloc(sizeof(int)*sendcount[rank]);
MPI_Scatterv(x,&sendcount[rank],&offset[rank],MPI_INT,rvett1,sendcount[rank],MPI_INT,0,MPI_COMM_WORLD);
MPI_Scatterv(y,&sendcount[rank],&offset[rank],MPI_INT,rvett2,sendcount[rank],MPI_INT,0,MPI_COMM_WORLD);
print(&rank,&nproc,rvett1,&sendcount[rank],rvett2,&sendcount[rank]);
if(rank==0){
free(x);
free(y);
}
free(rvett1);
free(rvett2);
MPI_Finalize();
printf("Exit program! \n");
return 0;
}
void print(int* rank,int* nproc,int* rvett1,int* dim1,int* rvett2,int* dim2){
int i,tag=0;
short int token=FALSE;
MPI_Status info;
if(*rank==0){
printf("\nReceiver[%d] => part of array <x>, dimension: %d \n",*rank,*dim1);
for(i=0;i<*dim1;i++){
if(i%10==0)
printf("\n");
printf("%d ",rvett1[i]);
}
printf("\nReceiver[%d] => part of array <y>, dimension: %d \n",*rank,*dim1);
for(i=0;i<*dim2;i++){
if(i%10==0)
printf("\n");
printf("%d ",rvett2[i]);
}
token=TRUE;
printf("\nStarter[%d] sends a print token \n",*rank);
MPI_Send(&token,1,MPI_SHORT_INT,1,tag+1,MPI_COMM_WORLD);
}
else{
for(i=1;i<*nproc;i++){
if(*rank==i){
MPI_Recv(&token,1,MPI_SHORT_INT,i-1,tag+i,MPI_COMM_WORLD,&info);
printf("Receiver[%d] => OK print \n ",i);
printf("\nReceiver[%d] => part of array <x>, dimension: %d \n",*rank,*dim1);
for(i=0;i<*dim1;i++){
if(i%10==0)
printf("\n");
printf("%d ",rvett1[i]);
}
printf("\nReceiver[%d] => part of array <y>, dimension: %d \n",*rank,*dim1);
for(i=0;i<*dim2;i++){
if(i%10==0)
printf("\n");
printf("%d ",rvett2[i]);
}
if(*rank<(*nproc)-1){
printf("Receiver[%d] sends next token \n",i);
MPI_Send(&token,1,MPI_SHORT_INT,i+1,tag+i+1,MPI_COMM_WORLD);
}
}
}
}
}
I use this 2 command to compile and execute the program:
mpicc -o sca_prod sca_prod.c
mpiexec -n 4 ./sca_prod
During the execution the program crash and it returns this error:
Sender[0] => array <x>:
92 37 80 73 68 24 42 72 88 26
47 25 24 98 47 92 72 100 34 20
76 97
Sender[0] => array <y>:
17 62 55 70 53 44 73 72 19 47
11 83 29 30 56 39 80 51 24 54
96 70
Receiver[0] => part of array <x>, dimension: 6
92 37 80 73 68 24
Receiver[0] => part of array <y>, dimension: 6
17 62 55 70 53 44
Starter[0] sends a print token
Receiver[1] => OK print
Receiver[1] => part of array <x>, dimension: 6
42 72 88 26 47 25
Receiver[1] => part of array <y>, dimension: 6
73 72 19 47 11 83 Receiver[6] sends next token
Fatal error in MPI_Send: Invalid rank, error stack:
MPI_Send(174): MPI_Send(buf=0x7ffe216fb636, count=1, MPI_SHORT_INT, dest=7, tag=7, MPI_COMM_WORLD) failed
MPI_Send(100): Invalid rank has value 7 but must be nonnegative and less than 4
I'm using MPICH3.2 with Hydra executor and my OS is Ubuntu 14.04; the machine has a quadcore i7 processor. Please, can you help me? Thank you so much!

You have two subtle issues:
MPI_SHORT_INT is the datatype in MPI for a struct { short, int }, for a short int use MPI_SHORT instead. This leads to overwriting memory.
You use i as loop variable in two nested loops.
In general your code could be structured more better, to avoid issues like the second one.
Declare variables as locally as possible, especially loop variables within the loop declaration (c99).
Use more descriptive variable names.
Hide things like printing an array in functions.
Format your code properly.
Also learn to use a (parallel) debugger.

Related

Odd behavior in my code for a certain test case

The program is supposed to swap neighbouring elements which don't have a common denominator, and an element can only be swapped once.
When i run the program, pretty much for any input works fine. Except for this one:
100 //input for number of elements
48 92 76 93 17 38 59 34 53 99 58 20 50 0 38 37 16 36 91 12 59 1 76 82 20 76 7 72 13 70 64 23 81 70 41 69 11 0 16 41 37 83 41 99 73 79 4 38 24 32 87 38 95 24 77 30 61 13 89 67 87 76 22 31 67 31 25 90 6 76 21 43 40 55 72 91 91 28 18 58 72 71 83 22 99 23 86 58 75 53 69 29 5 55 46 8 98 55 19 46 //the elements
For this input, the program hangs and prints nothing. Does someone know what is going on in this particular case?
#include <stdio.h>
int nzd(int a, int b)
{
if(a==b || b==0)
return a;
if(a>b)
return nzd(a-b, b);
return nzd(a, b-a);
}
int swap(int *niza, int i)
{
int temp;
temp=*(niza+i);
*(niza+i)=*(niza+i+1);
*(niza+i+1)=temp;
}
int main()
{
int a[100], n, i;
scanf("%d", &n);
for(i=0; i<n; i++)
{
scanf("%d", &a[i]);
}
for(i=0; i<n; i++)
{
if(i+1==n) continue;
if(nzd(a[i], a[i+1])==1)
{
swap(a, i);
i++;
}
}
for(i=0; i<n; i++)
{
printf("%d ", a[i]);
}
return 0;
}
Your gcd function checks for the case of b==0 but not the case for a==0. Because you skip that check, you end up calling nzd(0, b-0); which is exactly the same as the prior call. This puts you in an infinite recursion loop which will eventually cause a stack overflow.
Add the check for this case in your function:
if(a==b || b==0 || a == 0)
Also, a faster implementation of gcd, called Euclid's algorithm, is as follows:
int gcd(int a, int b)
{
if (b==0) {
return a;
} else {
return (b, a%b);
}
}
Your function nzd() fails to handle the case a == 0 correctly and gets stuck in an endless loop. You need to handle this case, too:
int nzd(int a, int b)
{
if(a==b || a==0 || b==0)
return a;
if(a>b)
return nzd(a-b, b);
return nzd(a, b-a);
}

SegmentFault on File pointer between functions

Before you say, yes I've checked nearly all the other postings, none are working.
My program has been giving me a segmentation error for hours and hours and nothing is fixing it. I debugged it to the point where I found it's in the file pointer. From what I know, it's because of the way I'm either using the file pointer in the 'makeArray' function or from the file closing statement. I don't really understand how it's not working because I used my last program as reference for this and it runs perfectly fine but this one won't.
#include <stdio.h>
#include <stdlib.h>
#define ROWS 12
#define COLS 8
void makeArray(FILE*, int [][COLS]);
int getScore(int [][COLS], int, int);
int getMonthMax(int [][COLS], int);
int getYearMax(int [][COLS]);
float getMonthAvg(int [][COLS], int);
float getYearAvg(int [][COLS]);
int toursMissed(int [][COLS]);
void displayMenu();
int processRequest(int [][COLS], int);
void printArray(int [][COLS]);
int main(){
int scoresArray[ROWS][COLS];
int choice, constant = 0;
FILE* inputPtr;
inputPtr = fopen("scores.txt", "r");
makeArray(inputPtr, scoresArray);
fclose(inputPtr);
while(constant == 0){
displayMenu();
scanf("%d", &choice);
processRequest(scoresArray, choice);
}
return 0;
}
void makeArray(FILE* inputPtr, int scoresArray[][COLS]){
int i, j;
for(i = 0; i < ROWS; i++){
for(j = 0; j < COLS; j++){
fscanf(inputPtr, "%d", &scoresArray[i][j]);
}
}
return;
}
I've tried moving the file pointers to every different spot in the code and nothing. I don't necessarily want you to just give me the answer but I want an explanation of why it's happening in this specific code because every other post I've checked and their results don't match up to mine.
Also the input file is
26 35 25 92 0 6 47 68 26 72 67 33 84 28
22 36 53 66 23 86 36 75 14 62 43 11 42 5
14 58 0 23 30 87 80 81 13 35 94 45 1 53
14 55 46 19 13 0 25 28 66 86 69 0 81 15
55 60 26 70 22 36 15 67 62 16 71 7 29 92
84 37 2 30 7 5 4 50 0 67 2 53 69 87
8 23 74 58 86 0 78 88 85 12 1 52 999
I wonder if your university compiler is picky about the input file - can you remove all new lines from a copy of your input file and try running with the copied modified input file --- so it is just a stream of numbers --- see if this sorts it out...
........ in my experience of scanf and fscanf these functions can be a bit fragile if the input does not run exactly the way you say it will in the format part - here "%d" does not tell fscanf about new line characters....

SIGXFSZ runtime error

I'm trying to submit the solution for Spoj - Prime Intervals problem. But I'm getting a runtime error SIGXFSZ. It is given that, it occurs due to exceeded file size. I have used the Sieve of Eratosthenes algorithm to find the prime numbers. I don't understand what's wrong with my code and this is bugging me from last the 2 days. Please help me with the submission. Here is my code...
#include<stdio.h>
#include<string.h>
#include<stdbool.h>
#include<math.h>
int main(){
int t, turn;
long i, l,u,k,j;
scanf("%d", &t);
/*Looping for t test cases*/
for(turn=0; turn<t; turn++){
scanf("%ld %ld", &l, &u);
bool arr[u-l+1];
/*Assigning whole array with true*/
memset(arr, true, u-l+1);
/*Sieve of Eratosthenes logic for assigning false to composite values*/
for(i=0; i<=(int)sqrt(u)-l; i++){
k=0;
j = i+l;
if(arr[i]==true){
while((j*j + k*j) <= u){
arr[(j*j + k*j) - l] = false;
k++;
}
}
}
/*Printing all the primes in the interval*/
for(i=0; i<u-l; i++){
if(arr[i]==true){
printf("%ld\n", i+l);
}
}
}
return 0;
}
Test Input:
2
2 10
2 100
Output:
2
3
5
7
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47
53
59
61
67
71
73
79
83
89
97
I ran the posted code. the results were far from correct.
Most of the numbers output are not primes and fails to check the last number is the range, as shown in the second set of results
Here are the results:
1 <-- 1 test case
20 100 <-- range 20...100
20 <-- the outputs
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
Note: using 1 as the low end of the range usually results with no output produced
here is another run
The output should have been 5 7 11
1 <-- test cases
5 11 <-- range
5 <-- outputs
6
7
8
9
10
The following code does not try to minimize the size of the arr[] array, and if the upper end of the range is less than 16k then could declare the arr[] as short rather than unsigned int
The lowest valid value for the low end of the input is 2, but the code is not checking for that low limit, you might want to add that check.
The code makes no effort to minimize the number of loops executed by checking for the square root of the upper limit, you might want to add that check.
The code compiles cleanly, handles the case when the upper limit is a prime and when the lower limit is a prime as well as when the limit values are not primes.
#include <stdio.h>
#include <string.h>
#include <math.h>
int main()
{
int numTestCases, testCase;
size_t i; // index
size_t lowLimit;
size_t upperLimit;
size_t k; // offset multiplier
scanf("%d", &numTestCases);
/*Looping for t test cases*/
for(testCase=0; testCase<numTestCases; testCase++)
{
scanf("%lu %lu", (unsigned long*)&lowLimit, (unsigned long*)&upperLimit);
unsigned arr[upperLimit+1];
/*Assigning whole array to indicate entry is a prime*/
memset(arr, 0x01, upperLimit+1);
/*Sieve of Eratosthenes logic for assigning false to composite values*/
//size_t sqrtUpperLimit = (size_t)ceil(sqrt(upperLimit));
for(i=2; i<= upperLimit; i++)
{
if(arr[i])
{
if( i >= lowLimit )
{
printf("%ld\n", i);
}
for( k=2; (i*k) <= upperLimit; k++)
{
arr[(i*k)] = 0;
}
}
}
}
return 0;
} // end function; main
here is an edited version of the code, with the addition of some instrumentation in the way of prompts to the user via calls to printf()
#include <stdio.h>
#include <string.h>
#include <math.h>
int main()
{
int numTestCases, testCase;
size_t i; // index
size_t lowLimit;
size_t upperLimit;
size_t k; // offset multiplier
printf("enter number of test cases\n");
scanf("%d", &numTestCases);
/*Looping for t test cases*/
for(testCase=0; testCase<numTestCases; testCase++)
{
printf( "enter lower limit upper limit limits\n");
scanf("%lu %lu", (unsigned long*)&lowLimit, (unsigned long*)&upperLimit);
unsigned arr[upperLimit+1];
/*Assigning whole array to indicate entry is a prime*/
memset(arr, 0x01, upperLimit+1);
/*Sieve of Eratosthenes logic for assigning false to composite values*/
//size_t sqrtUpperLimit = (size_t)ceil(sqrt(upperLimit));
for(i=2; i<= upperLimit; i++)
{
if(arr[i])
{
if( i >= lowLimit )
{
printf("%ld\n", i);
}
for( k=2; (i*k) <= upperLimit; k++)
{
arr[(i*k)] = 0;
}
}
}
}
return 0;
} // end function; main
Using the above instrumented code and the input of:
5 2 3 30 31 20 27 2 3 4 5
it worked perfectly.
This was the output:
enter number of test cases
5
enter upper/lower limits
2 3
sizeof arr[]: 4
2
3
enter upper/lower limits
30 31
sizeof arr[]: 32
31
enter upper/lower limits
20 27
sizeof arr[]: 28
23
enter upper/lower limits
2 3
sizeof arr[]: 4
2
3
enter upper/lower limits
4 5
sizeof arr[]: 6
5

deletion of first two element of array declared statically not working

I am under a situation in a c that suppose i have code below:
#include <stdio.h>
main()
{
int size=8;
int data[18]={12,9,1,7,4,5,3,11};
int i,newS;
printf("check1 \n");
newS= size+1;
data[newS]=data[0] +data[1];
printf("news %d \n",data[newS]);
printf("check2 \n");
for(i=0;i<21;i++)
{
if (data[i] <data[newS] )
{
printf("check3 \n");
data[newS+1]= data[newS] +data[i] ;
newS++;
}
else
{
printf("check4 \n");
}
}
for(i=0;i<21;i++)
{
printf("%d ",data[i]);
}
printf("\n");
}
I expect it to produce result like this : 12 9 1 7 4 5 3 11 21 33 42 43 50 54 59 62 73 18 18 8. But i don't know why it has one "0" just after "11".How to remove this zero ?
The output obtained by corresponding code is this (which is not expected):
12 9 1 7 4 5 3 11 0 21 33 42 43 50 54 59 62 73 18 18 8
Here is the solution i have made to my problem of removing zero : (below is the code for future reference of any user).
#include <stdio.h>
main()
{
int size=8;
int data[18]={12,9,1,7,4,5,3,11};
int i,newS;
printf("check1 \n");
newS= size;
data[newS]=data[0] +data[1];
printf("news %d \n",data[newS]);
printf("check2 \n");
for(i=0;i<21;i++)
{
if (data[i] <data[newS] )
{
printf("check3 \n");
data[newS+1]= data[newS] +data[i] ;
//break;
newS++;
}
else
{
printf("check4 \n");
}
}
for(i=0;i<21;i++)
{
printf("%d ",data[i]);
}
printf("\n");
}
1) The topic of the question keeps changing. This is needlessly frustrating. If you have two questions, either express them both or break them into two separate posts.
2) Hint: What's the index of the last element of an 8-value array?
3) For delection -- restoring my original answer -- shift all the higher-indexed values downward one space in the array, and decrease the length by one.

MPI partition matrix into blocks

I want to partition matrix into blocks (not stripes) and then distribute this blocks using MPI_Scatter.
I came up with solution which works, but I think it is far from "best practice". I have 8x8 matrix, filled with numbers from 0 to 63. Then I divide it into 4 4x4 blocks, using MPI_Type_vector and distribute it via MPI_Send, but this require some extra computation since i have to compute offsets for each block in big matrix.
If I use scatter, first (top left) block is transfered OK, but other blocks are not (wrong offset for start of block).
So is it possible to transfer blocks of matrix using MPI_Scatter, or what is the best way to do desired decomposition?
This is my code:
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
#define SIZE 8
int main(void) {
MPI_Init(NULL, NULL);
int p, rank;
MPI_Comm_size(MPI_COMM_WORLD, &p);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
char i;
char a[SIZE*SIZE];
char b[(SIZE/2)*(SIZE/2)];
MPI_Datatype columntype;
MPI_Datatype columntype2;
MPI_Type_vector(4, 4, SIZE, MPI_CHAR, &columntype2);
MPI_Type_create_resized( columntype2, 0, sizeof(MPI_CHAR), &columntype );
MPI_Type_commit(&columntype);
if(rank == 0) {
for( i = 0; i < SIZE*SIZE; i++) {
a[i] = i;
}
for(int rec=0; rec < p; rec++) {
int offset = (rec%2)*4 + (rec/2)*32;
MPI_Send (a+offset, 1, columntype, rec, 0, MPI_COMM_WORLD);
}
}
MPI_Recv (b, 16, MPI_CHAR, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
//MPI_Scatter(&a, 1, boki, &b, 16, MPI_CHAR , 0, MPI_COMM_WORLD);
printf("rank= %d b= \n%d %d %d %d\n%d %d %d %d\n%d %d %d %d\n%d %d %d %d\n", rank, b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]);
MPI_Finalize();
return 0;
}
What you've got is pretty much "best practice"; it's just a bit confusing until you get used to it.
Two things, though:
First, be careful with this: sizeof(MPI_CHAR) is, I assume, 4 bytes, not 1. MPI_CHAR is an (integer) constant that describes (to the MPI library) a character. You probably want sizeof(char), or SIZE/2*sizeof(char), or anything else convenient. But the basic idea of doing a resize is right.
Second, I think you're stuck using MPI_Scatterv, though, because there's no easy way to make the offset between each block the same size. That is, the first element in the first block is at a[0], the second is at a[SIZE/2] (jump of size/2), the next is at a[SIZE*(SIZE/2)] (jump of (SIZE-1)*(SIZE/2)). So you need to be able to manually generate the offsets.
The following seems to work for me (I generalized it a little bit to make it clearer when "size" means "number of rows" vs "number of columns", etc):
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
#define COLS 12
#define ROWS 8
int main(int argc, char **argv) {
MPI_Init(&argc, &argv);
int p, rank;
MPI_Comm_size(MPI_COMM_WORLD, &p);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
char i;
char a[ROWS*COLS];
const int NPROWS=2; /* number of rows in _decomposition_ */
const int NPCOLS=3; /* number of cols in _decomposition_ */
const int BLOCKROWS = ROWS/NPROWS; /* number of rows in _block_ */
const int BLOCKCOLS = COLS/NPCOLS; /* number of cols in _block_ */
if (rank == 0) {
for (int ii=0; ii<ROWS*COLS; ii++) {
a[ii] = (char)ii;
}
}
if (p != NPROWS*NPCOLS) {
fprintf(stderr,"Error: number of PEs %d != %d x %d\n", p, NPROWS, NPCOLS);
MPI_Finalize();
exit(-1);
}
char b[BLOCKROWS*BLOCKCOLS];
for (int ii=0; ii<BLOCKROWS*BLOCKCOLS; ii++) b[ii] = 0;
MPI_Datatype blocktype;
MPI_Datatype blocktype2;
MPI_Type_vector(BLOCKROWS, BLOCKCOLS, COLS, MPI_CHAR, &blocktype2);
MPI_Type_create_resized( blocktype2, 0, sizeof(char), &blocktype);
MPI_Type_commit(&blocktype);
int disps[NPROWS*NPCOLS];
int counts[NPROWS*NPCOLS];
for (int ii=0; ii<NPROWS; ii++) {
for (int jj=0; jj<NPCOLS; jj++) {
disps[ii*NPCOLS+jj] = ii*COLS*BLOCKROWS+jj*BLOCKCOLS;
counts [ii*NPCOLS+jj] = 1;
}
}
MPI_Scatterv(a, counts, disps, blocktype, b, BLOCKROWS*BLOCKCOLS, MPI_CHAR, 0, MPI_COMM_WORLD);
/* each proc prints it's "b" out, in order */
for (int proc=0; proc<p; proc++) {
if (proc == rank) {
printf("Rank = %d\n", rank);
if (rank == 0) {
printf("Global matrix: \n");
for (int ii=0; ii<ROWS; ii++) {
for (int jj=0; jj<COLS; jj++) {
printf("%3d ",(int)a[ii*COLS+jj]);
}
printf("\n");
}
}
printf("Local Matrix:\n");
for (int ii=0; ii<BLOCKROWS; ii++) {
for (int jj=0; jj<BLOCKCOLS; jj++) {
printf("%3d ",(int)b[ii*BLOCKCOLS+jj]);
}
printf("\n");
}
printf("\n");
}
MPI_Barrier(MPI_COMM_WORLD);
}
MPI_Finalize();
return 0;
}
Running:
$ mpirun -np 6 ./matrix
Rank = 0
Global matrix:
0 1 2 3 4 5 6 7 8 9 10 11
12 13 14 15 16 17 18 19 20 21 22 23
24 25 26 27 28 29 30 31 32 33 34 35
36 37 38 39 40 41 42 43 44 45 46 47
48 49 50 51 52 53 54 55 56 57 58 59
60 61 62 63 64 65 66 67 68 69 70 71
72 73 74 75 76 77 78 79 80 81 82 83
84 85 86 87 88 89 90 91 92 93 94 95
Local Matrix:
0 1 2 3
12 13 14 15
24 25 26 27
36 37 38 39
Rank = 1
Local Matrix:
4 5 6 7
16 17 18 19
28 29 30 31
40 41 42 43
Rank = 2
Local Matrix:
8 9 10 11
20 21 22 23
32 33 34 35
44 45 46 47
Rank = 3
Local Matrix:
48 49 50 51
60 61 62 63
72 73 74 75
84 85 86 87
Rank = 4
Local Matrix:
52 53 54 55
64 65 66 67
76 77 78 79
88 89 90 91
Rank = 5
Local Matrix:
56 57 58 59
68 69 70 71
80 81 82 83
92 93 94 95

Resources