/* test generator uses testlib.h: https://github.com/MikeMirzayanov/testlib/blob/master/testlib.h
   arguments:
    - first - name of the test that will be generated
    - second - value of n
    - third - value of m
    - fourth - max value
    - fifth - type of numbers
        = 0 - random numbers
        = 1 - spiral numbers
        = 2 - snake numbers
    - sixth - number of saddles
        = 0 - one saddle point
        = 1 - a few saddle points
    - seventh, eighth - coordinates of saddle point (or 0 0)
    - ninth, ... - used for seed for registerGen */
#include "testlib.h"
#include<iostream>
#include<vector>
#include "test_generator.h"
#define MAXN 500
#define MAXM 500
#define MAXNUM 1000000
using namespace std;
int tab[MAXN][MAXM],n,m;
int time1[MAXN][MAXM];
int drow[]={ -1, -0, +0, +1 };
int dcol[]={ -0, -1, +1, +0 };
bool check_equal (int row, int col) {
    for (int i=0; i<4; i++) {
        int r=row+drow[i],c=col+dcol[i];
        if ((r<0)||(r>=n)||(c<0)||(c>=m)) continue;
        if ((tab[r][c]!=0)&&(tab[r][c]==tab[row][col])) return true;
    }
    return false;
}
bool check_saddle (int row, int col) {
    for (int i=0; i<4; i++) {
        int r=row+drow[i],c=col+dcol[i];
        if ((r<0)||(r>=n)||(c<0)||(c>=m)) continue;
        if ((tab[r][c]!=0)&&(tab[r][c]<=tab[row][col])) return false;
    }
    return true;
}
int main (int argc, char** argv) {
    if (argc==1) {
       cout << "The test generator needs arguments!\n";
       return 0;
    }
    check_tests(argc,argv);
    if (argc<5) {
       cout << "The test generator needs more arguments!\n";
       return 0;
    }
    registerGen(argc,argv,1);
    ofstream fout(argv[1]);
    n=atoll(argv[2]); m=atoll(argv[3]);
    int max1=atoll(argv[4]);
    int type1=atoll(argv[5]);
    if (type1==0) {
        int min1=1e9;
        for (int i=0; i<n; i++) {
            for (int j=0; j<m; j++) {
                for (;;) {
                    tab[i][j]=rnd.next(2,max1);
                    if (check_equal(i,j)==false) break;
                }
                min1=min(min1,tab[i][j]);
            }
        }
    }
    else {
        int type2=atoll(argv[6]);
        int r=atoll(argv[7])-1,c=atoll(argv[8])-1;
        if (r==-1) {
            r=rnd.next(n);
            c=rnd.next(m);
        }
        int min1=rnd.wnext(2,max1-3*n*m,-3);
        tab[r][c]=min1;
        if (type1==1) {
            int r1=r,r2=r,c1=c,c2=c;
            vector <pair <int, int> > cells;
            cells.reserve(2*(n+m));
            for (;;) {
                if ((r1==0)&&(r2==n-1)&&(c1==0)&&(c2==m-1)) break;
                r1--; r2++; c1--; c2++;
                if (r1<0) r1=0;
                if (r2>=n) r2=n-1;
                if (c1<0) c1=0;
                if (c2>=m) c2=m-1;
                //cout << r1 << " " << r2 << " " << c1 << " " << c2 << endl ;

                cells.clear();
                for (int j=c1; j<=c2; j++) {
                    if (tab[r1][j]!=0) continue;
                    cells.push_back({r1, j});
                }
                for (int i=r1+1; i<=r2; i++) {
                    if (tab[i][c2]!=0) continue;
                    cells.push_back({i, c2});
                }
                for (int j=c2-1; j>=c1; j--) {
                    if (tab[r2][j]!=0) continue;
                    cells.push_back({r2, j});
                }
                for (int i=r2-1; i>=r1+1; i--) {
                    if (tab[i][c1]!=0) continue;
                    cells.push_back({i, c1});
                }
                for (;;) {
                    int ind,num;
                    for (;;) {
                        ind=rnd.next(cells.size());
                        auto [row, col]=cells[ind];
                        num=1;
                        for (int i=0; i<4; i++) {
                            int row1=row+drow[i],col1=col+dcol[i];
                            if ((row1<0)||(row1>=n)||(col1<0)||(col1>=m)) continue;
                            num=tab[row1][col1]+1;
                            if (num!=1) break;
                        }
                        if (num!=1) break;
                    }
                    for (int i=ind; i<cells.size(); i++) {
                        auto [row, col]=cells[i];
                        //cout << row+1 << " " << col+1 << "  ";
                        tab[row][col]=num;
                        num+=rnd.next(1,3);
                    }
                    for (int i=0; i<ind; i++) {
                        auto [row, col]=cells[i];
                        //cout << row+1 << " " << col+1 << "  ";
                        tab[row][col]=num;
                        num+=rnd.next(1,3);
                    }
                    bool flag=true;
                    for (auto [row, col] : cells) {
                        if ((check_equal(row,col)==true)||(check_saddle(row,col)==true)) {
                            flag=false;
                            break;
                        }
                    }
                    if (flag==true) break;
                }
                /*for (int i=0; i<n; i++) {
                    for (int j=0; j<m; j++) {
                        cout << tab[i][j] << " ";
                    }
                    cout << endl ;
                }
                cout << endl ;*/
            }
        }
        else {
            int num=tab[r][c]+1;
            int r1=r,c1=c+1;
            for (int i=0; ; i++) {
                if (i%2==0) {
                    int lim=rnd.wnext(min(c1,m-1),m-1,4);
                    if (r1<=5) lim=m-1;
                    if (n-1-r1<=5) lim=m-1;
                    for (int j=c1; j<=lim; j++) {
                        tab[r1][j]=num;
                        num+=rnd.next(1,3);
                    }
                    c1=lim;
                }
                else {
                    int lim=rnd.wnext(0,c1,-4);
                    if (r1<=5) lim=0;
                    if (n-1-r1<=5) lim=0;
                    for (int j=c1; j>=lim; j--) {
                        tab[r1][j]=num;
                        num+=rnd.next(1,3);
                    }
                    c1=lim;
                }
                if (r1==n-1) break;
                int lim=rnd.wnext(min(r1+2,n-1),min(r1+5,n-1),-1);
                for (int row=r1+1; row<lim; row++) {
                    tab[row][c1]=num;
                    num+=rnd.next(1,3);
                }
                r1=lim;
            }
            num=tab[r][c]+1;
            r1=r; c1=c-1;
            for (int i=0; ; i++) {
                if (i%2==0) {
                    int lim=rnd.wnext(0,max(c1,0),-4);
                    if (r1<=5) lim=0;
                    if (n-1-r1<=5) lim=0;
                    for (int j=c1; j>=lim; j--) {
                        tab[r1][j]=num;
                        num+=rnd.next(1,3);
                    }
                    c1=lim;
                }
                else {
                    int lim=rnd.wnext(c1,m-1,4);
                    if (r1<=5) lim=m-1;
                    if (n-1-r1<=5) lim=m-1;
                    for (int j=c1; j<=lim; j++) {
                        tab[r1][j]=num;
                        num+=rnd.next(1,3);
                    }
                    c1=lim;
                }
                if (r1==0) break;
                int lim=rnd.wnext(max(r1-5,0),max(r1-2,0),1);
                for (int row=r1-1; row>=lim; row--) {
                    tab[row][c1]=num;
                    num+=rnd.next(1,3);
                }
                r1=lim;
            }
            /*for (int i=0; i<n; i++) {
                for (int j=0; j<m; j++) {
                    cout << tab[i][j] << " ";
                }
                cout << endl ;
            }*/
            for (int i=0; i<n; i++) {
                for (int j=0; j<m; j++) {
                    if (tab[i][j]==0) {
                        int maxv=0;
                        for (int h=0; h<4; h++) {
                            int row1=i+drow[h],col1=j+dcol[h];
                            if ((row1<0)||(row1>=n)||(col1<0)||(col1>=m)) continue;
                            maxv=max(maxv,tab[row1][col1]);
                        }
                        tab[i][j]=maxv+1;
                    }
                }
            }
        }

        if (type2==1) {
            int cnt=rnd.next(1,10);
            for (int i=1; i<=cnt; i++) {
                for (;;) {
                    int row=rnd.next(n),col=rnd.next(m);
                    if (check_saddle(row,col)==false) {
                        int minv=1e9;
                        for (int i=0; i<4; i++) {
                            int row1=row+drow[i],col1=col+dcol[i];
                            if ((row1<0)||(row1>=n)||(col1<0)||(col1>=m)) continue;
                            minv=min(minv,tab[row1][col1]);
                        }
                        tab[row][col]=rnd.next(1,minv-1);
                        break;
                    }
                }
            }
        }
    }

    fout << n << " " << m << endl ;
    for (int i=0; i<n; i++) {
        for (int j=0; j<m; j++) {
            if (j!=0) fout << " ";
            fout << tab[i][j] ;
        }
        fout << endl ;
    }
    fout.close();

    cout << "test successfully generated!!!" << endl ;
    validate_test(argc,argv);
    return 0;
}
