#include <stdio.h>
#include <stdlib.h>
#include "mpi.h"
/*
after the user chooses the way of entering matrices and entering them:
convert every matrix to 1d array and second matrix is converted by column
then check if there's a remainder in the first matrix rows, the root will
do their multiplication and scatter the rest of rows to the process to
do
if there's not a remainder , the rows
*/
int main(int argc, char *argv[])
{
int rank;
int nprocess;
int rem;
double startTime;
double endTime;
double elapsedTime;
int matrix1Row=0,matrix1Col=-1;
int matrix2Row=-2,matrix2Col=0;
int** matrix1;
int** matrix2;
int* mat1;
int* mat2;
int* globalArr1;
int* globalArr2;
int choice=0;
int i=0,j=0,k=0,m=0;
int subSize1;
int localSubSize=0;
int* subResult;
int* transferedResult;
int* finalResult;
int* localList;
int otherProcessesPortion=0;
int sum=0;
char filename[15];
MPI_Status status;
MPI_Init( &argc , &argv );
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &nprocess);
if(rank==0)
{
printf("Welcome to vector Matrix multiplication program!!\n"); printf("if you want program input matrices from file Enter 1\n"); printf("if you want program input 2 matrices from console Enter 2\n");
if(choice==1)
{
printf("enter your filename(including .txt)\n"); FILE *file;
file
= fopen(filename
, "r");
if(file)
{
fscanf(file
,"%d",&matrix1Row
); fscanf(file
,"%d",&matrix1Col
);
fscanf(file
,"%d",&matrix2Row
); fscanf(file
,"%d",&matrix2Col
);
matrix1
=malloc(matrix1Row
*sizeof(int)); for(i=0;i<matrix1Row;i++)
{
matrix1
[i
]=malloc(matrix1Col
*sizeof(int)); }
for(i=0;i<matrix1Row;i++)
{
for(j=0;j<matrix1Col;j++)
{
fscanf(file
,"%d",&matrix1
[i
][j
]); }
}
matrix2
=malloc(matrix2Row
*sizeof(int)); for(i=0;i<matrix2Row;i++)
{
matrix2
[i
]=malloc(matrix2Col
*sizeof(int)); }
for(i=0;i<matrix2Row;i++)
{
for(j=0;j<matrix2Col;j++)
{
fscanf(file
,"%d",&matrix2
[i
][j
]); }
}
if(matrix1Col!=matrix2Row)
printf("matrices can't be multiplied\n"); }
}
else if(choice==2)
{
while(matrix1Col!=matrix2Row)
{
printf("enter matrix #1 dimension:\n"); matrix1
=malloc(matrix1Row
*sizeof(int));
printf("enter matrix #2 dimension:\n"); matrix2
=malloc(matrix2Row
*sizeof(int));
if(matrix1Col!=matrix2Row)
printf("matrices can't be multiplied\n"); }
for(i=0;i<matrix1Row;i++)
{
matrix1
[i
]=malloc(matrix1Col
*sizeof(int)); }
printf("enter matrix #1 elements:\n"); for(i=0;i<matrix1Row;i++)
{
for(j=0;j<matrix1Col;j++)
{
scanf("%d",&matrix1
[i
][j
]); }
}
for(i=0;i<matrix2Row;i++)
{
matrix2
[i
]=malloc(matrix2Col
*sizeof(int)); }
printf("enter matrix #2 elements:\n"); for(i=0;i<matrix2Row;i++)
{
for(j=0;j<matrix2Col;j++)
{
scanf("%d",&matrix2
[i
][j
]); }
}
}
startTime= MPI_Wtime();
printf("starting time of program: %f\n",rank
,startTime
);
//converting 2D matrices to 1D arrays
mat1
=malloc((matrix1Row
*matrix1Col
)*sizeof(int)); for(i=0;i<matrix1Row;i++)
{
for(j=0;j<matrix1Col;j++)
{
mat1[j+(matrix1Col*i)]=matrix1[i][j];
}
}
mat2
=malloc((matrix2Row
*matrix2Col
)*sizeof(int)); for(i=0;i<matrix2Col;i++)
{
for(j=0;j<matrix2Row;j++)
{
mat2[j+(matrix1Col*i)]=matrix2[j][i];
}
}
rem=matrix1Row%nprocess;
subSize1=matrix1Row/nprocess;
if(rem!=0 && matrix1Col==matrix2Row)
{
otherProcessesPortion=matrix1Row-rem;
for (i=0;i<rem;i++)
{
for (j=0;j<matrix2Col;j++)
{
for (k=0;k<matrix1Col;k++)
{
sum+=mat1[i*matrix1Col+k]*mat2[k+(j*matrix2Row)];
}
sum=0;
}
}
localSubSize=otherProcessesPortion/nprocess;
localList
=malloc((otherProcessesPortion
*matrix1Col
)*sizeof(int)); j=0;
for(i=(rem*matrix1Col);i<(matrix1Col*matrix1Row);i++)
{
localList[j]=mat1[i];
j++;
}
}
}
MPI_Bcast(&matrix1Row,1,MPI_INT,0,MPI_COMM_WORLD);
MPI_Bcast(&matrix1Col,1,MPI_INT,0,MPI_COMM_WORLD);
MPI_Bcast(&matrix2Row,1,MPI_INT,0,MPI_COMM_WORLD);
MPI_Bcast(&subSize1,1,MPI_INT,0,MPI_COMM_WORLD);
MPI_Bcast(&matrix2Col,1,MPI_INT,0,MPI_COMM_WORLD);
if(rank!=0)
mat2
= malloc(sizeof(int) * matrix2Col
*matrix2Row
);
MPI_Bcast(&mat2[0],(matrix2Col*matrix2Row),MPI_INT,0,MPI_COMM_WORLD);
if(matrix1Row%nprocess>0 && matrix1Col==matrix2Row)
{
MPI_Bcast(&otherProcessesPortion,1,MPI_INT,0,MPI_COMM_WORLD);
MPI_Bcast(&localSubSize,1,MPI_INT,0,MPI_COMM_WORLD);
globalArr2
=malloc((localSubSize
*matrix1Col
)*sizeof(int)); MPI_Scatter(localList,(localSubSize*matrix1Col),MPI_INT,globalArr2,(localSubSize*matrix1Col),MPI_INT,0,MPI_COMM_WORLD);
sum=0;
subResult
=malloc((localSubSize
*matrix2Col
)*sizeof(int)); for(i=0;i<localSubSize;i++)
{
for (j=0;j<matrix2Col;j++)
{
for (k=0;k<matrix1Col;k++)
{
sum+=globalArr2[i*matrix1Col+k]*mat2[k+(j*matrix2Row)];
}
subResult[i*matrix2Col+j]=sum;
sum=0;
}
}
transferedResult
=malloc((otherProcessesPortion
*matrix2Col
)*sizeof(int)); MPI_Gather(subResult,matrix2Col,MPI_INT,transferedResult,matrix2Col,MPI_INT,0,MPI_COMM_WORLD);
if(rank==0)
{
finalResult
=malloc((localSubSize
*matrix2Col
)*sizeof(int)); for(j=0;j<(otherProcessesPortion*matrix2Col);j++)
{
printf("%d ",transferedResult
[j
]); if((j+1)%matrix2Col==0)
}
}
}
else if(matrix1Row%nprocess==0 && matrix1Col==matrix2Row)
{
globalArr1
=malloc((matrix1Col
*subSize1
)*sizeof(int)); MPI_Scatter(mat1,(matrix1Col*subSize1),MPI_INT,globalArr1,(matrix1Col*subSize1),MPI_INT,0,MPI_COMM_WORLD);
j=0,k=0,i=0;
subResult
=malloc((subSize1
*matrix2Col
)*sizeof(int));
for (i=0;i<subSize1;i++)
{
for (j=0;j<matrix2Col;j++)
{
for (k=0;k<matrix1Col;k++)
{
sum+=globalArr1[i*matrix1Col+k]*mat2[k+(j*matrix2Row)];
}
subResult[i*matrix2Col+j]=sum;
sum=0;
}
}
transferedResult
=malloc((matrix1Row
*matrix2Col
)*sizeof(int)); MPI_Gather(subResult,matrix2Col,MPI_INT,transferedResult,matrix2Col,MPI_INT,0,MPI_COMM_WORLD);
if(rank==0)
{
finalResult
=malloc((matrix1Row
*matrix2Col
)*sizeof(int)); for(j=0;j<(matrix1Row*matrix2Col);j++)
{
printf("%d ",transferedResult
[j
]); if((j+1)==(matrix1Row*matrix2Col)/2)
}
}
}
endTime= MPI_Wtime();
elapsedTime=endTime-startTime;
printf("Ending time of program: %f\n",elapsedTime
); /* shutdown MPI */
MPI_Finalize();
return 0;
}
I2luY2x1ZGUgPHN0ZGlvLmg+CiNpbmNsdWRlIDxzdGRsaWIuaD4KI2luY2x1ZGUgIm1waS5oIgovKgogICAgYWZ0ZXIgdGhlIHVzZXIgY2hvb3NlcyB0aGUgd2F5IG9mIGVudGVyaW5nIG1hdHJpY2VzIGFuZCBlbnRlcmluZyB0aGVtOgogICAgY29udmVydCBldmVyeSBtYXRyaXggdG8gMWQgYXJyYXkgYW5kIHNlY29uZCBtYXRyaXggaXMgY29udmVydGVkIGJ5IGNvbHVtbgogICAgdGhlbiBjaGVjayBpZiB0aGVyZSdzIGEgcmVtYWluZGVyIGluIHRoZSBmaXJzdCBtYXRyaXggcm93cywgdGhlIHJvb3Qgd2lsbCAKICAgIGRvIHRoZWlyIG11bHRpcGxpY2F0aW9uIGFuZCBzY2F0dGVyIHRoZSByZXN0IG9mIHJvd3MgdG8gdGhlIHByb2Nlc3MgdG8KICAgIGRvIAoKICAgIGlmIHRoZXJlJ3Mgbm90IGEgcmVtYWluZGVyICwgdGhlIHJvd3MKCgoKKi8KaW50IG1haW4oaW50IGFyZ2MsIGNoYXIgKmFyZ3ZbXSkKewogICAgaW50IHJhbms7CiAgICBpbnQgbnByb2Nlc3M7CiAgICBpbnQgcmVtOwogICAgZG91YmxlIHN0YXJ0VGltZTsKICAgIGRvdWJsZSBlbmRUaW1lOwogICAgZG91YmxlIGVsYXBzZWRUaW1lOwogICAgaW50IG1hdHJpeDFSb3c9MCxtYXRyaXgxQ29sPS0xOwogICAgaW50IG1hdHJpeDJSb3c9LTIsbWF0cml4MkNvbD0wOwogICAgaW50KiogbWF0cml4MTsKICAgIGludCoqIG1hdHJpeDI7CiAgICBpbnQqIG1hdDE7CiAgICBpbnQqIG1hdDI7CiAgICBpbnQqIGdsb2JhbEFycjE7CiAgICBpbnQqIGdsb2JhbEFycjI7CiAgICBpbnQgY2hvaWNlPTA7CiAgICBpbnQgaT0wLGo9MCxrPTAsbT0wOwogICAgaW50IHN1YlNpemUxOwogICAgaW50IGxvY2FsU3ViU2l6ZT0wOwogICAgaW50KiBzdWJSZXN1bHQ7CiAgICBpbnQqIHRyYW5zZmVyZWRSZXN1bHQ7CiAgICBpbnQqIGZpbmFsUmVzdWx0OwogICAgaW50KiBsb2NhbExpc3Q7CiAgICBpbnQgb3RoZXJQcm9jZXNzZXNQb3J0aW9uPTA7CiAgICBpbnQgc3VtPTA7CiAgICBjaGFyIGZpbGVuYW1lWzE1XTsKICAgIE1QSV9TdGF0dXMgc3RhdHVzOwoKICAgIE1QSV9Jbml0KCAmYXJnYyAsICZhcmd2ICk7CiAgCU1QSV9Db21tX3JhbmsoTVBJX0NPTU1fV09STEQsICZyYW5rKTsKCU1QSV9Db21tX3NpemUoTVBJX0NPTU1fV09STEQsICZucHJvY2Vzcyk7CgogICAgaWYocmFuaz09MCkKICAgIHsKICAgICAgICBwcmludGYoIldlbGNvbWUgdG8gdmVjdG9yIE1hdHJpeCBtdWx0aXBsaWNhdGlvbiBwcm9ncmFtISFcbiIpOwogICAgICAgIHByaW50ZigiaWYgeW91IHdhbnQgcHJvZ3JhbSBpbnB1dCBtYXRyaWNlcyBmcm9tIGZpbGUgRW50ZXIgMVxuIik7CiAgICAgICAgcHJpbnRmKCJpZiB5b3Ugd2FudCBwcm9ncmFtIGlucHV0IDIgbWF0cmljZXMgZnJvbSBjb25zb2xlIEVudGVyIDJcbiIpOwogICAgICAgIHNjYW5mKCIlZCIsJmNob2ljZSk7CgogICAgICAgIGlmKGNob2ljZT09MSkKICAgICAgICB7CiAgICAgICAgICAgIHByaW50ZigiZW50ZXIgeW91ciBmaWxlbmFtZShpbmNsdWRpbmcgLnR4dClcbiIpOwogICAgICAgICAgICBzY2FuZigiJXMiLCZmaWxlbmFtZSk7CiAgICAgICAgICAgIEZJTEUgKmZpbGU7CiAgICAgICAgICAgIGZpbGUgPSBmb3BlbihmaWxlbmFtZSwgInIiKTsKCiAgICAgICAgICAgIGlmKGZpbGUpCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIGZzY2FuZihmaWxlLCIlZCIsJm1hdHJpeDFSb3cpOwogICAgICAgICAgICAgICAgZnNjYW5mKGZpbGUsIiVkIiwmbWF0cml4MUNvbCk7CgogICAgICAgICAgICAgICAgZnNjYW5mKGZpbGUsIiVkIiwmbWF0cml4MlJvdyk7CiAgICAgICAgICAgICAgICBmc2NhbmYoZmlsZSwiJWQiLCZtYXRyaXgyQ29sKTsKCiAgICAgICAgICAgICAgICBtYXRyaXgxPW1hbGxvYyhtYXRyaXgxUm93KnNpemVvZihpbnQpKTsKICAgICAgICAgICAgICAgIGZvcihpPTA7aTxtYXRyaXgxUm93O2krKykKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBtYXRyaXgxW2ldPW1hbGxvYyhtYXRyaXgxQ29sKnNpemVvZihpbnQpKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGZvcihpPTA7aTxtYXRyaXgxUm93O2krKykKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBmb3Ioaj0wO2o8bWF0cml4MUNvbDtqKyspCiAgICAgICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICAgICBmc2NhbmYoZmlsZSwiJWQiLCZtYXRyaXgxW2ldW2pdKTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgbWF0cml4Mj1tYWxsb2MobWF0cml4MlJvdypzaXplb2YoaW50KSk7CiAgICAgICAgICAgICAgICBmb3IoaT0wO2k8bWF0cml4MlJvdztpKyspCiAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgbWF0cml4MltpXT1tYWxsb2MobWF0cml4MkNvbCpzaXplb2YoaW50KSk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBmb3IoaT0wO2k8bWF0cml4MlJvdztpKyspCiAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgZm9yKGo9MDtqPG1hdHJpeDJDb2w7aisrKQogICAgICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAgICAgZnNjYW5mKGZpbGUsIiVkIiwmbWF0cml4MltpXVtqXSk7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIGlmKG1hdHJpeDFDb2whPW1hdHJpeDJSb3cpCiAgICAgICAgICAgICAgICAgICAgcHJpbnRmKCJtYXRyaWNlcyBjYW4ndCBiZSBtdWx0aXBsaWVkXG4iKTsKICAgICAgICAgICAgfQogICAgICAgICAgICBmY2xvc2UoZmlsZSk7CiAgICAgICAgfQogICAgICAgIGVsc2UgaWYoY2hvaWNlPT0yKQogICAgICAgIHsKICAgICAgICAgICAgd2hpbGUobWF0cml4MUNvbCE9bWF0cml4MlJvdykKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgcHJpbnRmKCJlbnRlciBtYXRyaXggIzEgZGltZW5zaW9uOlxuIik7CiAgICAgICAgICAgICAgICBzY2FuZigiJWQiLCZtYXRyaXgxUm93KTsKICAgICAgICAgICAgICAgIHNjYW5mKCIlZCIsJm1hdHJpeDFDb2wpOwogICAgICAgICAgICAgICAgbWF0cml4MT1tYWxsb2MobWF0cml4MVJvdypzaXplb2YoaW50KSk7CiAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgIHByaW50ZigiZW50ZXIgbWF0cml4ICMyIGRpbWVuc2lvbjpcbiIpOwogICAgICAgICAgICAgICAgc2NhbmYoIiVkIiwmbWF0cml4MlJvdyk7CiAgICAgICAgICAgICAgICBzY2FuZigiJWQiLCZtYXRyaXgyQ29sKTsKICAgICAgICAgICAgICAgIG1hdHJpeDI9bWFsbG9jKG1hdHJpeDJSb3cqc2l6ZW9mKGludCkpOyAgCgogICAgICAgICAgICAgICAgaWYobWF0cml4MUNvbCE9bWF0cml4MlJvdykKICAgICAgICAgICAgICAgICAgICBwcmludGYoIm1hdHJpY2VzIGNhbid0IGJlIG11bHRpcGxpZWRcbiIpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGZvcihpPTA7aTxtYXRyaXgxUm93O2krKykKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgbWF0cml4MVtpXT1tYWxsb2MobWF0cml4MUNvbCpzaXplb2YoaW50KSk7CiAgICAgICAgICAgIH0gICAgCiAgICAgICAgICAgIHByaW50ZigiZW50ZXIgbWF0cml4ICMxIGVsZW1lbnRzOlxuIik7CiAgICAgICAgICAgIGZvcihpPTA7aTxtYXRyaXgxUm93O2krKykKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgZm9yKGo9MDtqPG1hdHJpeDFDb2w7aisrKQogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgIHNjYW5mKCIlZCIsJm1hdHJpeDFbaV1bal0pOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CgogICAgICAgICAgICBmb3IoaT0wO2k8bWF0cml4MlJvdztpKyspCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIG1hdHJpeDJbaV09bWFsbG9jKG1hdHJpeDJDb2wqc2l6ZW9mKGludCkpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHByaW50ZigiZW50ZXIgbWF0cml4ICMyIGVsZW1lbnRzOlxuIik7CiAgICAgICAgICAgIGZvcihpPTA7aTxtYXRyaXgyUm93O2krKykKICAgICAgICAgICAgewogICAgICAgICAgICAgICBmb3Ioaj0wO2o8bWF0cml4MkNvbDtqKyspCiAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgc2NhbmYoIiVkIiwmbWF0cml4MltpXVtqXSk7CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSAgICAKICAgICAgICB9CiAgICAgICAgc3RhcnRUaW1lPSBNUElfV3RpbWUoKTsKICAgICAgICBwcmludGYoInN0YXJ0aW5nIHRpbWUgb2YgcHJvZ3JhbTogJWZcbiIscmFuayxzdGFydFRpbWUpOwogICAgICAgIAogICAgICAgIC8vY29udmVydGluZyAyRCBtYXRyaWNlcyB0byAxRCBhcnJheXMKICAgICAgICBtYXQxPW1hbGxvYygobWF0cml4MVJvdyptYXRyaXgxQ29sKSpzaXplb2YoaW50KSk7CiAgICAgICAgZm9yKGk9MDtpPG1hdHJpeDFSb3c7aSsrKQogICAgICAgIHsKICAgICAgICAgICAgZm9yKGo9MDtqPG1hdHJpeDFDb2w7aisrKQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgIG1hdDFbaisobWF0cml4MUNvbCppKV09bWF0cml4MVtpXVtqXTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICAKICAgICAgICBtYXQyPW1hbGxvYygobWF0cml4MlJvdyptYXRyaXgyQ29sKSpzaXplb2YoaW50KSk7CiAgICAgICAgZm9yKGk9MDtpPG1hdHJpeDJDb2w7aSsrKQogICAgICAgIHsKICAgICAgICAgICAgZm9yKGo9MDtqPG1hdHJpeDJSb3c7aisrKQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBtYXQyW2orKG1hdHJpeDFDb2wqaSldPW1hdHJpeDJbal1baV07CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIHJlbT1tYXRyaXgxUm93JW5wcm9jZXNzOwogICAgICAgIHN1YlNpemUxPW1hdHJpeDFSb3cvbnByb2Nlc3M7CiAgICAgICAgaWYocmVtIT0wICYmIG1hdHJpeDFDb2w9PW1hdHJpeDJSb3cpCiAgICAgICAgewogICAgICAgICAgICBvdGhlclByb2Nlc3Nlc1BvcnRpb249bWF0cml4MVJvdy1yZW07CiAgICAgICAgICAgIGZvciAoaT0wO2k8cmVtO2krKykKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgZm9yIChqPTA7ajxtYXRyaXgyQ29sO2orKykKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBmb3IgKGs9MDtrPG1hdHJpeDFDb2w7aysrKQogICAgICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAgICAgc3VtKz1tYXQxW2kqbWF0cml4MUNvbCtrXSptYXQyW2srKGoqbWF0cml4MlJvdyldOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICBwcmludGYoIiVkICIsc3VtKTsKICAgICAgICAgICAgICAgICAgICBzdW09MDsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHByaW50ZigiXG4iKTsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgbG9jYWxTdWJTaXplPW90aGVyUHJvY2Vzc2VzUG9ydGlvbi9ucHJvY2VzczsKICAgICAgICAgICAgbG9jYWxMaXN0PW1hbGxvYygob3RoZXJQcm9jZXNzZXNQb3J0aW9uKm1hdHJpeDFDb2wpKnNpemVvZihpbnQpKTsgCiAgICAgICAgICAgIGo9MDsKICAgICAgICAKICAgICAgICAgICAgZm9yKGk9KHJlbSptYXRyaXgxQ29sKTtpPChtYXRyaXgxQ29sKm1hdHJpeDFSb3cpO2krKykKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgbG9jYWxMaXN0W2pdPW1hdDFbaV07CiAgICAgICAgICAgICAgICBqKys7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9CiAgICBNUElfQmNhc3QoJm1hdHJpeDFSb3csMSxNUElfSU5ULDAsTVBJX0NPTU1fV09STEQpOwogICAgTVBJX0JjYXN0KCZtYXRyaXgxQ29sLDEsTVBJX0lOVCwwLE1QSV9DT01NX1dPUkxEKTsKICAgIE1QSV9CY2FzdCgmbWF0cml4MlJvdywxLE1QSV9JTlQsMCxNUElfQ09NTV9XT1JMRCk7CiAgICBNUElfQmNhc3QoJnN1YlNpemUxLDEsTVBJX0lOVCwwLE1QSV9DT01NX1dPUkxEKTsKICAgIE1QSV9CY2FzdCgmbWF0cml4MkNvbCwxLE1QSV9JTlQsMCxNUElfQ09NTV9XT1JMRCk7CiAgICBpZihyYW5rIT0wKQogICAgICAgIG1hdDIgPSBtYWxsb2Moc2l6ZW9mKGludCkgKiBtYXRyaXgyQ29sKm1hdHJpeDJSb3cpOwogICAgCiAgICBNUElfQmNhc3QoJm1hdDJbMF0sKG1hdHJpeDJDb2wqbWF0cml4MlJvdyksTVBJX0lOVCwwLE1QSV9DT01NX1dPUkxEKTsKICAgIAogICAgaWYobWF0cml4MVJvdyVucHJvY2Vzcz4wICYmIG1hdHJpeDFDb2w9PW1hdHJpeDJSb3cpCiAgICB7CiAgICAgICAgTVBJX0JjYXN0KCZvdGhlclByb2Nlc3Nlc1BvcnRpb24sMSxNUElfSU5ULDAsTVBJX0NPTU1fV09STEQpOwogICAgICAgIE1QSV9CY2FzdCgmbG9jYWxTdWJTaXplLDEsTVBJX0lOVCwwLE1QSV9DT01NX1dPUkxEKTsKCiAgICAgICAgZ2xvYmFsQXJyMj1tYWxsb2MoKGxvY2FsU3ViU2l6ZSptYXRyaXgxQ29sKSpzaXplb2YoaW50KSk7CiAgICAgICAgTVBJX1NjYXR0ZXIobG9jYWxMaXN0LChsb2NhbFN1YlNpemUqbWF0cml4MUNvbCksTVBJX0lOVCxnbG9iYWxBcnIyLChsb2NhbFN1YlNpemUqbWF0cml4MUNvbCksTVBJX0lOVCwwLE1QSV9DT01NX1dPUkxEKTsKCiAgICAgICAgc3VtPTA7CiAgICAgICAgc3ViUmVzdWx0PW1hbGxvYygobG9jYWxTdWJTaXplKm1hdHJpeDJDb2wpKnNpemVvZihpbnQpKTsKICAgICAgICBmb3IoaT0wO2k8bG9jYWxTdWJTaXplO2krKykKICAgICAgICB7CiAgICAgICAgICAgIGZvciAoaj0wO2o8bWF0cml4MkNvbDtqKyspCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIGZvciAoaz0wO2s8bWF0cml4MUNvbDtrKyspCiAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgc3VtKz1nbG9iYWxBcnIyW2kqbWF0cml4MUNvbCtrXSptYXQyW2srKGoqbWF0cml4MlJvdyldOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgc3ViUmVzdWx0W2kqbWF0cml4MkNvbCtqXT1zdW07CiAgICAgICAgICAgICAgICBzdW09MDsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICB0cmFuc2ZlcmVkUmVzdWx0PW1hbGxvYygob3RoZXJQcm9jZXNzZXNQb3J0aW9uKm1hdHJpeDJDb2wpKnNpemVvZihpbnQpKTsKICAgICAgICBNUElfR2F0aGVyKHN1YlJlc3VsdCxtYXRyaXgyQ29sLE1QSV9JTlQsdHJhbnNmZXJlZFJlc3VsdCxtYXRyaXgyQ29sLE1QSV9JTlQsMCxNUElfQ09NTV9XT1JMRCk7CgogICAgICAgIGlmKHJhbms9PTApCiAgICAgICAgewogICAgICAgICAgICBmaW5hbFJlc3VsdD1tYWxsb2MoKGxvY2FsU3ViU2l6ZSptYXRyaXgyQ29sKSpzaXplb2YoaW50KSk7CiAgICAgICAgICAgIGZvcihqPTA7ajwob3RoZXJQcm9jZXNzZXNQb3J0aW9uKm1hdHJpeDJDb2wpO2orKykKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgcHJpbnRmKCIlZCAiLHRyYW5zZmVyZWRSZXN1bHRbal0pOwogICAgICAgICAgICAgICAgaWYoKGorMSklbWF0cml4MkNvbD09MCkKICAgICAgICAgICAgICAgICAgICBwcmludGYoIlxuIik7CiAgICAgICAgICAgIH0gICAgICAgIAogICAgICAgICAgICBwcmludGYoIlxuIik7CiAgICAgICAgfSAgCiAgICB9CiAgICBlbHNlIGlmKG1hdHJpeDFSb3clbnByb2Nlc3M9PTAgJiYgbWF0cml4MUNvbD09bWF0cml4MlJvdykKICAgIHsKICAgICAgICBnbG9iYWxBcnIxPW1hbGxvYygobWF0cml4MUNvbCpzdWJTaXplMSkqc2l6ZW9mKGludCkpOwogICAgICAgIE1QSV9TY2F0dGVyKG1hdDEsKG1hdHJpeDFDb2wqc3ViU2l6ZTEpLE1QSV9JTlQsZ2xvYmFsQXJyMSwobWF0cml4MUNvbCpzdWJTaXplMSksTVBJX0lOVCwwLE1QSV9DT01NX1dPUkxEKTsgCiAgICAgICAgCiAgICAgICAgaj0wLGs9MCxpPTA7CiAgICAgICAgc3ViUmVzdWx0PW1hbGxvYygoc3ViU2l6ZTEqbWF0cml4MkNvbCkqc2l6ZW9mKGludCkpOwoKICAgICAgICBmb3IgKGk9MDtpPHN1YlNpemUxO2krKykKICAgICAgICB7CiAgICAgICAgICAgIGZvciAoaj0wO2o8bWF0cml4MkNvbDtqKyspCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIGZvciAoaz0wO2s8bWF0cml4MUNvbDtrKyspCiAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgc3VtKz1nbG9iYWxBcnIxW2kqbWF0cml4MUNvbCtrXSptYXQyW2srKGoqbWF0cml4MlJvdyldOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgc3ViUmVzdWx0W2kqbWF0cml4MkNvbCtqXT1zdW07CiAgICAgICAgICAgICAgICBzdW09MDsKICAgICAgICAgICAgfQogICAgICAgIH0gCiAgICAgICAgdHJhbnNmZXJlZFJlc3VsdD1tYWxsb2MoKG1hdHJpeDFSb3cqbWF0cml4MkNvbCkqc2l6ZW9mKGludCkpOwogICAgICAgIE1QSV9HYXRoZXIoc3ViUmVzdWx0LG1hdHJpeDJDb2wsTVBJX0lOVCx0cmFuc2ZlcmVkUmVzdWx0LG1hdHJpeDJDb2wsTVBJX0lOVCwwLE1QSV9DT01NX1dPUkxEKTsKCiAgICAgICAgaWYocmFuaz09MCkKICAgICAgICB7CiAgICAgICAgICAgIGZpbmFsUmVzdWx0PW1hbGxvYygobWF0cml4MVJvdyptYXRyaXgyQ29sKSpzaXplb2YoaW50KSk7CiAgICAgICAgICAgIGZvcihqPTA7ajwobWF0cml4MVJvdyptYXRyaXgyQ29sKTtqKyspCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIHByaW50ZigiJWQgIix0cmFuc2ZlcmVkUmVzdWx0W2pdKTsKICAgICAgICAgICAgICAgIGlmKChqKzEpPT0obWF0cml4MVJvdyptYXRyaXgyQ29sKS8yKQogICAgICAgICAgICAgICAgICAgIHByaW50ZigiXG4iKTsKICAgICAgICAgICAgfSAgICAgICAgCiAgICAgICAgICAgIHByaW50ZigiXG4iKTsKICAgICAgICB9ICAKICAgIH0KICAgIGVuZFRpbWU9IE1QSV9XdGltZSgpOwogICAgZWxhcHNlZFRpbWU9ZW5kVGltZS1zdGFydFRpbWU7CiAgICBwcmludGYoIkVuZGluZyB0aW1lIG9mIHByb2dyYW06ICVmXG4iLGVsYXBzZWRUaW1lKTsKICAvKiBzaHV0ZG93biBNUEkgKi8KICBNUElfRmluYWxpemUoKTsKICAgIHJldHVybiAwOwp9Cg==