void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) 
{
      unsigned int   *pBestErrorsImg32u, *pCodebookImgA32u, *pCodebookImgB32u, *pBestMapping32u, *pHashTableImgB32u, *pHashTableImgA32u;
      unsigned short *pHashsNoe16u, *pSequencyOrder16u, *pSequencyLevels16u;
      bool           *pDownwardFlag;
      
      int            *pCurrFiltImgs_A16s, *pCurrFiltImgs_B16s;
      int            nKernels , nKernelsTarget;
      unsigned int   nSingleChannelColNum;// Number of columns in single channel;
      
      
      unsigned int  nCurrTypeHashVal32u, nCurrCandMapping32u,nCurrCandMapping32u_2, nCurrTypeMapping32u;
      unsigned int nCurrLocA32u;
      
      int i, j; // Counters
      int m, n, mTarget, mHashTable, nHashTable;
      
      int nProjectionToTestDiffNoe; // Number of GCK projections in the input images to test differences between input iteration pixel to its candidates
      int nSequencyLevelsNum;       // Number of sequency levels
      
      int nHashTableCounter;
      
      int nFirstRowForward, nLastRowForward, nFirstColForward, nLastColForward; // Matlab indices
      int nFirstRowBack, nLastRowBack, nFirstColBack, nLastColBack;
      int nLeftRightOffset, nLeftRightTargetOffset, nTopBotOffset, nTopBotTargetOffset;  // Direction of propagation depending on current loop running direction (downwards/upwards)
      
      unsigned int nNumOfElements, nNumOfTargetElements, nNumOfElementsHashTable;
      unsigned int nCurIterStep, nCurIterOffset;
      unsigned int nHorizNeighbourMapping32u, nVerticalNeighbourMapping32u,CordConv;
      unsigned int CandidateBestMapping , CandidateBestMappingConv;
      int kOfKNN;
      int loc,ind , ind2;
      
      if (nrhs < 11 || nrhs > 13 || nlhs!=0)
                mexErrMsgTxt("Usage: GCKCodebookPropagation16s(nBestErrorsImg32u, nBestMapping32u, nCurrFiltImgs_A16s, nCurrFiltImgs_B16s, nCodebookImgA32u, nCodebookImgB32u, nHashTableImgB32u, nHashsNoe16u, nSequencyOrder16u, nSequencyLevels16u, bDownwardFlag, kOfKNN));\n");
      
      if (mxGetClassID(prhs[0]) != mxUINT32_CLASS ||
              mxGetClassID(prhs[1]) != mxUINT32_CLASS || mxGetClassID(prhs[2]) != mxINT32_CLASS  ||
              mxGetClassID(prhs[3]) != mxINT32_CLASS  || mxGetClassID(prhs[4]) != mxUINT32_CLASS ||
              mxGetClassID(prhs[5]) != mxUINT32_CLASS || mxGetClassID(prhs[6]) != mxUINT32_CLASS || mxGetClassID(prhs[7]) != mxUINT32_CLASS ||
              mxGetClassID(prhs[8]) != mxUINT16_CLASS  || mxGetClassID(prhs[9]) != mxUINT16_CLASS  ||
              mxGetClassID(prhs[10]) != mxUINT16_CLASS  || mxGetClassID(prhs[11]) != mxLOGICAL_CLASS || mxGetClassID(prhs[12]) != mxUINT32_CLASS)
                mexErrMsgTxt("Input best errors image must be a uint32 matrix, input linear mapping image must be a uint32 matrix, input and target GCK projections images must be int16 matrices inside a cell array, input and target codebook images must be a uint32 matrices, Hash table of target image must be a uint32 matrix, number of indices in each hash entry must be a uint8 scalar, sequency order of GCK kernels must be a uint8 vector, sequency levels of GCK kernels must be a uint8 vector and downwarads or upwards iteration must be a boolean!");
      
      
      // Get inputs
      pBestErrorsImg32u        = (unsigned int*)mxGetPr(prhs[0]);
      pBestMapping32u          = (unsigned int*)mxGetPr(prhs[1]);
      pCurrFiltImgs_A16s       = (int*)mxGetPr(prhs[2]);
      pCurrFiltImgs_B16s       = (int*)mxGetPr(prhs[3]);
      pCodebookImgA32u         = (unsigned int*)mxGetPr(prhs[4]);
      pCodebookImgB32u         = (unsigned int*)mxGetPr(prhs[5]);
      pHashTableImgA32u        = (unsigned int*)mxGetPr(prhs[6]);
      pHashTableImgB32u        = (unsigned int*)mxGetPr(prhs[7]);
      pHashsNoe16u             = (unsigned short*)mxGetPr(prhs[8]);
      pSequencyOrder16u        = (unsigned short*)mxGetPr(prhs[9]);
      pSequencyLevels16u       = (unsigned short*)mxGetPr(prhs[10]);
      pDownwardFlag            = (bool*)mxGetPr(prhs[11]);
      kOfKNN = *(unsigned int*)mxGetPr(prhs[12]);
      
      // Get sizes of variables (Opposite on purpose)//
      m = mxGetM(prhs[4]); // Width
      n = mxGetN(prhs[4]); // Height
      
      nSingleChannelColNum = n; // Num of elements in input image column
         
      nKernels       = mxGetM(prhs[2]);
      nKernelsTarget = mxGetM(prhs[3]);
          
      if (nKernels != nKernelsTarget)
         mexErrMsgTxt("Number of projection images for input and target images must be the same!");

      mTarget = mxGetM(prhs[5]); // Height of image B
      
      
      mHashTable = mxGetM(prhs[6]); // Width of hash table
      nHashTable = mxGetN(prhs[6]); // Height of hash table
      
      // Sequency params
      nProjectionToTestDiffNoe = mxGetM(prhs[9]);
      nSequencyLevelsNum       = mxGetM(prhs[10]);
      
      if (mxGetN(prhs[9])>1 || mxGetN(prhs[10])>1)
                mexErrMsgTxt("Input sequency order of GCK kernels and sequency levels of GCK kernels must be column vectors!");
      
      
      // Get input and target images num of elements
      nNumOfElements          = mxGetN(prhs[2]); // Get number of elements in projections matrix (source)
      nNumOfTargetElements    = mxGetN(prhs[3]); // Get number of elements in projections matrix (destination)
      nNumOfElementsHashTable = mHashTable * nHashTable;
     
      // Initialize filter parameters
      if (*pDownwardFlag) 
	  {		// Filter downwards
            nFirstRowForward = (int)1;
            nLastRowForward  = m;
            nFirstColForward = (int)1;
            nLastColForward  = nSingleChannelColNum;
            
            nLeftRightOffset       = m;
            nTopBotOffset          = 1;
            nLeftRightTargetOffset = mTarget;
            nTopBotTargetOffset    = 1;
      }
      else
	  {
            // Filter upards
            nLastRowBack  = (int)0;
            nFirstRowBack = m-(int)2;
            nLastColBack  = (int)0;
            nFirstColBack = nSingleChannelColNum-(int)2;
            
            nLeftRightOffset       = -m;
            nTopBotOffset          = -1;
            nLeftRightTargetOffset = -mTarget;
            nTopBotTargetOffset    = -1;
      }
      
      if (*pDownwardFlag) 
      {
            // Begin looping on input image pixels forwards
            for (j=nFirstColForward; j<nLastColForward; j++) 
			{
                  nCurIterStep = j*m;
                      
                  for (i=nFirstRowForward; i<nLastRowForward; i++) 
				  {
                        nCurIterOffset = nCurIterStep+i;
                       
                        // Collect candidates
                        // ## Type 1 ##
                        // Current pixel best mapping's entry in target image hash table
                        nCurrTypeHashVal32u = pCodebookImgA32u[nCurIterOffset]-1;
                        
                        nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u]-1;// -1 for matlab2C
                        CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
													 nCurrCandMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);
                        
						for (nHashTableCounter=1; nHashTableCounter<(*pHashsNoe16u); nHashTableCounter++) 
						{
                                  nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u +=  mHashTable]-1;// -1 for matlab2C;
                                  CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
															   nCurrCandMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);
                        }
                        
                        // ## NEW - Type A ##
                        nCurrTypeHashVal32u = pCodebookImgA32u[nCurIterOffset]-1; // index into hash table
                        nCurrLocA32u = pHashTableImgA32u[nCurrTypeHashVal32u]-1;// -1 for matlab2C // first A-candidate
                        
						CordConv = CONV_TO_LINEAR_MAPPING(nCurrLocA32u,kOfKNN);
                        for  (ind = 0; ind < kOfKNN; ind++)
                        {
                              nCurrCandMapping32u = pBestMapping32u[CordConv++];// its current best candidate
                              
                              CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
														   nCurrCandMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);
                        }
                        
                        for (nHashTableCounter=1; nHashTableCounter<(*pHashsNoe16u); nHashTableCounter++) 
						{
                              loc = pHashTableImgA32u[nCurrTypeHashVal32u +=  mHashTable]-1;
							  CordConv = CONV_TO_LINEAR_MAPPING(loc,kOfKNN);
                              for  (ind = 0; ind < kOfKNN; ind++) 
                              {         
                                    nCurrCandMapping32u = pBestMapping32u[CordConv++];// -1 for matlab2C; A index
                                    CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
												                 nCurrCandMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);
                              }

                        }

                        // ## Type 2 - lr ##
                        // Right of the left pixel best mapping's
						CordConv = CONV_TO_LINEAR_MAPPING(nCurIterOffset - nLeftRightOffset,kOfKNN);
                        for  (ind = 0; ind < kOfKNN; ind++) 
                        {
                              nHorizNeighbourMapping32u    = pBestMapping32u[CordConv++];
                              nCurrTypeMapping32u = nHorizNeighbourMapping32u + nLeftRightTargetOffset;
                              nCurrTypeHashVal32u = pCodebookImgB32u[nCurrTypeMapping32u]-1;
                              
                              // Test for original candidate
                              CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
														   nCurrTypeMapping32u, nCurIterOffset,nKernels,m,n, kOfKNN);
                              
                              // Test for cadidates in hash table with the same entry
                              nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u]-1;// -1 for matlab2C;
                              CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
														   nCurrCandMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);

							  for (nHashTableCounter=1; nHashTableCounter<(*pHashsNoe16u); nHashTableCounter++) 
							  {
                                    nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u +=  mHashTable]-1;// -1 for matlab2C;
                                    CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
																 nCurrCandMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);
                              }
                        }
                        
                        // ## Type 2 - tb ##
                        // Bottom of the top pixel best mapping's - Or otherwise
						CordConv = CONV_TO_LINEAR_MAPPING(nCurIterOffset - nTopBotOffset,kOfKNN);
                        for  (ind = 0; ind < kOfKNN; ind++) 
						{
                              nVerticalNeighbourMapping32u = pBestMapping32u[CordConv++];
                              nCurrTypeMapping32u = nVerticalNeighbourMapping32u + nTopBotTargetOffset;
                              nCurrTypeHashVal32u = pCodebookImgB32u[nCurrTypeMapping32u]-1;
                              // Test for original candidate
                              CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
														   nCurrTypeMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);
                              // Test for cadidats in hash table with the same entry
                              nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u]-1;// -1 for matlab2C;
                              CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
														   nCurrCandMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);
                              for (nHashTableCounter=1; nHashTableCounter<(*pHashsNoe16u); nHashTableCounter++) 
							  {
                                    nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u +=  mHashTable]-1;// -1 for matlab2C;
                                    CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
																 nCurrCandMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);
                              }
                        }
                        
                        // TYPE 4: if images are the same, check your candidates mapping
                        CordConv = CONV_TO_LINEAR_MAPPING(nCurIterOffset,kOfKNN);
                        for  (ind = 0; ind < kOfKNN; ind++) 
						{
                              CandidateBestMapping = pBestMapping32u[CordConv+ind];
                              if (CandidateBestMapping != nCurIterOffset)
                              {
                                  CandidateBestMappingConv = CONV_TO_LINEAR_MAPPING(CandidateBestMapping,kOfKNN);
                                  for (ind2 = 0 ; ind2 <  kOfKNN; ind2++) 
                                  {
                                         nCurrTypeMapping32u = pBestMapping32u[CandidateBestMappingConv + ind2];
										 if (nCurrTypeMapping32u != nCurIterOffset)
										 {
	                                        // Find the mapping candidates
		                                    CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
																		 nCurrTypeMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);
										 }
                                 }
                            }
                        }
                  }
            }
      }
      else 
	  {
            // Begin looping on input image pixels backwards
            for (j=nFirstColBack; j>=nLastColBack; j--)
			{
                  nCurIterStep = j*m;
                  for (i=nFirstRowBack; i>=nLastRowBack; i--) 
				  {
                        nCurIterOffset = nCurIterStep+i;
                        
                        // Collect candidates
                        // ## Type 1 ##
                        // Current pixel best mapping's entry in target image hash table
                        nCurrTypeHashVal32u = pCodebookImgA32u[nCurIterOffset]-1;
                        
                        nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u]-1;// -1 for matlab2C
                        CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
													 nCurrCandMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);
                        
                        for (nHashTableCounter=1; nHashTableCounter < (*pHashsNoe16u); nHashTableCounter++) 
						{
                              nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u +=  mHashTable]-1;// -1 for matlab2C;
                              CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
														   nCurrCandMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);
                        }
                        
                        // ## NEW - Type A ##
                        nCurrTypeHashVal32u = pCodebookImgA32u[nCurIterOffset]-1; // index into hash table
                        nCurrLocA32u = pHashTableImgA32u[nCurrTypeHashVal32u]-1;// -1 for matlab2C // first A-candidate
                        CordConv = CONV_TO_LINEAR_MAPPING(nCurrLocA32u,kOfKNN);
                        for  (ind = 0; ind < kOfKNN; ind++)
                        {
                              nCurrCandMapping32u = pBestMapping32u[CordConv++];// its current best candidate
                              CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
														   nCurrCandMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);
                        }
                        
                        for (nHashTableCounter=1; nHashTableCounter<(*pHashsNoe16u); nHashTableCounter++)
						{
                              loc = pHashTableImgA32u[nCurrTypeHashVal32u +=  mHashTable]-1;
							  CordConv = CONV_TO_LINEAR_MAPPING(loc,kOfKNN);
                              for  (ind = 0; ind < kOfKNN; ind++) 
                              {         
                                  nCurrCandMapping32u = pBestMapping32u[CordConv++];// -1 for matlab2C; A index
                                  CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
															   nCurrCandMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);
                              }
                        }
                        CordConv = CONV_TO_LINEAR_MAPPING(nCurIterOffset - nLeftRightOffset,kOfKNN);
                        // Right of the left pixel best mapping's
                        for  (ind = 0; ind < kOfKNN; ind++) 
                        {
							  nHorizNeighbourMapping32u    = pBestMapping32u[CordConv++];
                              
							  nCurrTypeMapping32u = nHorizNeighbourMapping32u + nLeftRightTargetOffset;
							  nCurrTypeHashVal32u = pCodebookImgB32u[nCurrTypeMapping32u]-1;
		                      
							  // ## Type 2 - lr ##
							  // Test for original candidate
							  CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
														   nCurrTypeMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);
		                      
							  // Test for cadidats in hash table with the same entry
							  nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u]-1;// -1 for matlab2C;
							  CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
														   nCurrCandMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);
							  
							  for (nHashTableCounter=1; nHashTableCounter<(*pHashsNoe16u); nHashTableCounter++) 
							  {
									nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u +=  mHashTable]-1;// -1 for matlab2C;
									CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
																 nCurrCandMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);
							  }
                        }
                        // ## Type 2 - tb ##
                        // Bottom of the top pixel best mapping's - Or otherwise
						CordConv = CONV_TO_LINEAR_MAPPING(nCurIterOffset - nTopBotOffset,kOfKNN);
                        for  (ind = 0; ind < kOfKNN; ind++) 
						{
                              nVerticalNeighbourMapping32u = pBestMapping32u[CordConv++];
                              nCurrTypeMapping32u = nVerticalNeighbourMapping32u + nTopBotTargetOffset;
                              nCurrTypeHashVal32u = pCodebookImgB32u[nCurrTypeMapping32u]-1;
                              
                              // Test for original candidate
                              CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
														   nCurrTypeMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);
                              // Test for cadidats in hash table with the same entry
                              nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u]-1;// -1 for matlab2C;
                              CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
					                                       nCurrCandMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);
                              
                              for (nHashTableCounter=1; nHashTableCounter<(*pHashsNoe16u); nHashTableCounter++) 
							  {
                                    nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u +=  mHashTable]-1;// -1 for matlab2C;
                                    CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
																 nCurrCandMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);
                              }
                        }

                        // TYPE 4: if images are the same, check your candidates mapping
                        CordConv = CONV_TO_LINEAR_MAPPING(nCurIterOffset,kOfKNN);
                        for  (ind = 0; ind < kOfKNN; ind++) 
						{
                              CandidateBestMapping = pBestMapping32u[CordConv+ind];
                              if (CandidateBestMapping != nCurIterOffset)
                              {
                                  CandidateBestMappingConv = CONV_TO_LINEAR_MAPPING(CandidateBestMapping,kOfKNN);
                                  for (ind2 = 0 ; ind2 <  kOfKNN; ind2++) 
                                  {
                                         nCurrTypeMapping32u = pBestMapping32u[CandidateBestMappingConv + ind2];
										 if (nCurrTypeMapping32u != nCurIterOffset)
										 {
			                                // Find the mapping candidates
	                                        CalcSqrDifferenceAndTakeBest(pCurrFiltImgs_A16s, pCurrFiltImgs_B16s, pBestErrorsImg32u, pBestMapping32u,
		                                                                 nCurrTypeMapping32u, nCurIterOffset, nKernels,m,n, kOfKNN);
										 }
                                 }
                            }
                        }
                  }
            }
      }
}
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {
    unsigned int   *pBestErrorsImg32u, *pCodebookImgA32u, *pCodebookImgB32u, *pBestMapping32u, *pHashTableImgB32u, *pHashTableImgA32u;
    unsigned short *pHashsNoe16u, *pSequencyOrder16u;
    bool           *pDownwardFlag;

    int          *mCurrFiltersImg, *mCurrFiltersImgTarget;

    unsigned int   *pCurrBestError32u, *pCurrBestMapping32u;


    unsigned int  nCurrTypeHashVal32u, nCurrCandMapping32u, nCurrTypeMapping32u;
    unsigned int nCurrLocA32u;


    int i, j; // Counters
    int m, n, mTarget, mHashTable, nHashTable, nKernels, nKernelsTarget; // Input size
    int nSingleChannelColNum;// Number of columns in single channel

    int nProjectionToTestDiffNoe; // Number of GCK projections in the input images to test differences between input iteration pixel to its candidates
    int nSequencyLevelsNum;       // Number of sequency levels

    int nHashTableCounter;

    int nFirstRowForward, nLastRowForward, nFirstColForward, nLastColForward; // Matlab indices
    int nFirstRowBack, nLastRowBack, nFirstColBack, nLastColBack;
    int nLeftRightOffset, nLeftRightTargetOffset, nTopBotOffset, nTopBotTargetOffset;  // Direction of propagation depending on current loop running direction (downwards/upwards)

    unsigned int nCurIterStep, nCurIterOffset;
    unsigned int nHorizNeighbourMapping32u, nVerticalNeighbourMapping32u;

    int advances = 0;
    unsigned int candBeforeLoop;

    if (nrhs < 11 || nrhs > 12 || nlhs!=0)
        mexErrMsgTxt("Usage: GCKCodebookPropagation16s(nBestErrorsImg32u, nBestMapping32u, nCurrFiltImgs_A16s, nCurrFiltImgs_B16s, nCodebookImgA32u, nCodebookImgB32u, nHashTableImgB32u, nHashsNoe16u, nSequencyOrder16u, nSequencyLevels16u, bDownwardFlag));\n");

    if (mxGetClassID(prhs[0]) != mxUINT32_CLASS ||
            mxGetClassID(prhs[1]) != mxUINT32_CLASS || mxGetClassID(prhs[2]) != mxINT32_CLASS   ||
            mxGetClassID(prhs[3]) != mxINT32_CLASS   || mxGetClassID(prhs[4]) != mxUINT32_CLASS ||
            mxGetClassID(prhs[5]) != mxUINT32_CLASS || mxGetClassID(prhs[6]) != mxUINT32_CLASS || mxGetClassID(prhs[7]) != mxUINT32_CLASS ||
            mxGetClassID(prhs[8]) != mxUINT16_CLASS  || mxGetClassID(prhs[9]) != mxUINT16_CLASS  ||
            mxGetClassID(prhs[10]) != mxUINT16_CLASS  || mxGetClassID(prhs[11]) != mxLOGICAL_CLASS)
    {
        mexErrMsgTxt("Input best errors image must be a uint32 matrix, input linear mapping image must be a uint32 matrix, input and target GCK projections images must be int16 matrices inside a cell array, input and target codebook images must be a uint32 matrices, Hash table of target image must be a uint32 matrix, number of indices in each hash entry must be a uint8 scalar, sequency order of GCK kernels must be a uint8 vector, sequency levels of GCK kernels must be a uint8 vector and downwarads or upwards iteration must be a boolean!");
    }

    // Get inputs
    pBestErrorsImg32u        = (unsigned int*)mxGetPr(prhs[0]);
    pBestMapping32u          = (unsigned int*)mxGetPr(prhs[1]);
    mCurrFiltersImg          = (int*)mxGetPr(prhs[2]);
    mCurrFiltersImgTarget    = (int*)mxGetPr(prhs[3]);
    pCodebookImgA32u         = (unsigned int*)mxGetPr(prhs[4]);
    pCodebookImgB32u         = (unsigned int*)mxGetPr(prhs[5]);
    pHashTableImgA32u        = (unsigned int*)mxGetPr(prhs[6]);
    pHashTableImgB32u        = (unsigned int*)mxGetPr(prhs[7]);
    pHashsNoe16u             = (unsigned char*)mxGetPr(prhs[8]);
    pSequencyOrder16u        = (unsigned char*)mxGetPr(prhs[9]);
    pDownwardFlag            = (bool*)mxGetPr(prhs[11]);

    // Get sizes of variables (Opposite on purpose)//
    m = mxGetM(prhs[0]); // Width
    n = mxGetN(prhs[0]); // Height
    nSingleChannelColNum = n; // Num of elements in input image column

    nKernels       = mxGetM(prhs[2]);


    nKernelsTarget = mxGetM(prhs[3]);


    if (nKernels != nKernelsTarget)
        mexErrMsgTxt("Number of projection images for input and target images must be the same!");

    mTarget = mxGetM(prhs[5]); // Height of image B

    mHashTable = mxGetM(prhs[6]); // Width of hash table
    nHashTable = mxGetN(prhs[6]); // Height of hash table

    // Sequency params
    nProjectionToTestDiffNoe = mxGetM(prhs[9]);
    nSequencyLevelsNum       = mxGetM(prhs[10]);

    // Initialize filter parameters
    if (*pDownwardFlag) {
        // Filter downwards
        nFirstRowForward = (int)1;
        nLastRowForward  = m;
        nFirstColForward = (int)1;
        nLastColForward  = nSingleChannelColNum;

        nLeftRightOffset       = m;
        nTopBotOffset          = 1;
        nLeftRightTargetOffset = mTarget;
        nTopBotTargetOffset    = 1;
    }
    else {
        // Filter upards
        nLastRowBack  = (int)0;
        nFirstRowBack = m-(int)2;
        nLastColBack  = (int)0;
        nFirstColBack = nSingleChannelColNum-(int)2;

        nLeftRightOffset       = -m;
        nTopBotOffset          = -1;
        nLeftRightTargetOffset = -mTarget;
        nTopBotTargetOffset    = -1;
    }

    if (*pDownwardFlag) {
        // Begin looping on input image pixels forwards
        for (j=nFirstColForward; j<nLastColForward; j++) {
            nCurIterStep = j*m;

            for (i=nFirstRowForward; i<nLastRowForward; i++) {
                nCurIterOffset = nCurIterStep+i;

                pCurrBestError32u   = &pBestErrorsImg32u[nCurIterOffset];
                pCurrBestMapping32u = &pBestMapping32u[nCurIterOffset];
                candBeforeLoop =  pCurrBestMapping32u[0] ;

                nHorizNeighbourMapping32u    = pBestMapping32u[nCurIterOffset - nLeftRightOffset];
                nVerticalNeighbourMapping32u = pBestMapping32u[nCurIterOffset - nTopBotOffset];
                // Collect candidates
                // ## Type 1 ##
                // Current pixel best mapping's entry in target image hash table
                nCurrTypeHashVal32u = pCodebookImgA32u[nCurIterOffset]-1;
                nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u]-1;// -1 for matlab2C
                CalcSqrDifferenceAndTakeBest(mCurrFiltersImg, mCurrFiltersImgTarget, &pCurrBestError32u,
                                             &pCurrBestMapping32u, nCurrCandMapping32u, nCurIterOffset,nKernels);

                for (nHashTableCounter=1; nHashTableCounter<(*pHashsNoe16u); nHashTableCounter++) {
                    nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u +=  mHashTable]-1;// -1 for matlab2C;
                    CalcSqrDifferenceAndTakeBest(mCurrFiltersImg, mCurrFiltersImgTarget, &pCurrBestError32u,
                                                 &pCurrBestMapping32u, nCurrCandMapping32u, nCurIterOffset,nKernels);
                }
                // ## NEW - Type A ##
                nCurrTypeHashVal32u = pCodebookImgA32u[nCurIterOffset]-1; // index into hash table
                nCurrLocA32u = pHashTableImgA32u[nCurrTypeHashVal32u]-1;// -1 for matlab2C // first A-candidate
                nCurrCandMapping32u = pBestMapping32u[nCurrLocA32u];// its current best candidate

                CalcSqrDifferenceAndTakeBest(mCurrFiltersImg, mCurrFiltersImgTarget, &pCurrBestError32u,
                                             &pCurrBestMapping32u, nCurrCandMapping32u, nCurIterOffset,nKernels);

                for (nHashTableCounter=1; nHashTableCounter<(*pHashsNoe16u); nHashTableCounter++) {
                    nCurrCandMapping32u = pBestMapping32u[pHashTableImgA32u[nCurrTypeHashVal32u +=  mHashTable]-1];// -1 for matlab2C; A index
                    CalcSqrDifferenceAndTakeBest(mCurrFiltersImg, mCurrFiltersImgTarget, &pCurrBestError32u,
                                                 &pCurrBestMapping32u, nCurrCandMapping32u, nCurIterOffset,nKernels);
                }

                // Right of the left pixel best mapping's
                nCurrTypeMapping32u = nHorizNeighbourMapping32u + nLeftRightTargetOffset;
                nCurrTypeHashVal32u = pCodebookImgB32u[nCurrTypeMapping32u]-1;

                // ## Type 2 - lr ##
                // Test for original candidate
                CalcSqrDifferenceAndTakeBest(mCurrFiltersImg, mCurrFiltersImgTarget,
                                             &pCurrBestError32u, &pCurrBestMapping32u, nCurrTypeMapping32u, nCurIterOffset,nKernels);

                // Test for cadidates in hash table with the same entry
                nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u]-1;// -1 for matlab2C;
                CalcSqrDifferenceAndTakeBest(mCurrFiltersImg, mCurrFiltersImgTarget,
                                             &pCurrBestError32u, &pCurrBestMapping32u, nCurrCandMapping32u, nCurIterOffset,nKernels);

                for (nHashTableCounter=1; nHashTableCounter<(*pHashsNoe16u); nHashTableCounter++) {
                    nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u +=  mHashTable]-1;// -1 for matlab2C;
                    CalcSqrDifferenceAndTakeBest(mCurrFiltersImg, mCurrFiltersImgTarget,
                                                 &pCurrBestError32u, &pCurrBestMapping32u, nCurrCandMapping32u, nCurIterOffset,nKernels);
                }
                // ## Type 2 - tb ##
                // Bottom of the top pixel best mapping's - Or otherwise
                nCurrTypeMapping32u = nVerticalNeighbourMapping32u + nTopBotTargetOffset;
                nCurrTypeHashVal32u = pCodebookImgB32u[nCurrTypeMapping32u]-1;

                // Test for original candidate
                CalcSqrDifferenceAndTakeBest(mCurrFiltersImg, mCurrFiltersImgTarget,
                                             &pCurrBestError32u, &pCurrBestMapping32u, nCurrTypeMapping32u, nCurIterOffset,nKernels);

                // Test for cadidats in hash table with the same entry
                nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u]-1;// -1 for matlab2C;
                CalcSqrDifferenceAndTakeBest(mCurrFiltersImg, mCurrFiltersImgTarget,
                                             &pCurrBestError32u, &pCurrBestMapping32u, nCurrCandMapping32u, nCurIterOffset,nKernels);

                for (nHashTableCounter=1; nHashTableCounter<(*pHashsNoe16u); nHashTableCounter++) {
                    nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u +=  mHashTable]-1;// -1 for matlab2C;
                    CalcSqrDifferenceAndTakeBest(mCurrFiltersImg, mCurrFiltersImgTarget,
                                                 &pCurrBestError32u, &pCurrBestMapping32u, nCurrCandMapping32u, nCurIterOffset,nKernels);
                }



            }
        }
        // mexPrintf("***********************************************\n");
        // mexPrintf("***** advances down: %d, out of: %d **********\n", advances, (nLastColForward - nFirstColForward)*(nLastRowForward - nFirstRowForward));
        // mexPrintf("***********************************************\n");

    }
    else {
        // Begin looping on input image pixels backwards
        for (j=nFirstColBack; j>=nLastColBack; j--) {
            nCurIterStep = j*m;

            for (i=nFirstRowBack; i>=nLastRowBack; i--) {
                nCurIterOffset = nCurIterStep+i;
                pCurrBestError32u   = &pBestErrorsImg32u[nCurIterOffset];
                pCurrBestMapping32u = &pBestMapping32u[nCurIterOffset];
                candBeforeLoop =  pCurrBestMapping32u[0] ;

                nHorizNeighbourMapping32u    = pBestMapping32u[nCurIterOffset - nLeftRightOffset];
                nVerticalNeighbourMapping32u = pBestMapping32u[nCurIterOffset - nTopBotOffset];

                // Collect candidates
                // ## Type 1 ##
                // Current pixel best mapping's entry in target image hash table
                nCurrTypeHashVal32u = pCodebookImgA32u[nCurIterOffset]-1;

                nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u]-1;// -1 for matlab2C

                CalcSqrDifferenceAndTakeBest(mCurrFiltersImg, mCurrFiltersImgTarget, &pCurrBestError32u,
                                             &pCurrBestMapping32u, nCurrCandMapping32u, nCurIterOffset,nKernels);

                for (nHashTableCounter=1; nHashTableCounter<(*pHashsNoe16u); nHashTableCounter++) {
                    nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u +=  mHashTable]-1;// -1 for matlab2C;
                    CalcSqrDifferenceAndTakeBest(mCurrFiltersImg, mCurrFiltersImgTarget, &pCurrBestError32u,
                                                 &pCurrBestMapping32u, nCurrCandMapping32u, nCurIterOffset,nKernels);
                }

                // ## NEW - Type A ##
                nCurrTypeHashVal32u = pCodebookImgA32u[nCurIterOffset]-1; // index into hash table
                nCurrLocA32u = pHashTableImgA32u[nCurrTypeHashVal32u]-1;// -1 for matlab2C // first A-candidate
                nCurrCandMapping32u = pBestMapping32u[nCurrLocA32u];// its current best candidate

                CalcSqrDifferenceAndTakeBest(mCurrFiltersImg, mCurrFiltersImgTarget, &pCurrBestError32u,
                                             &pCurrBestMapping32u, nCurrCandMapping32u, nCurIterOffset,nKernels);

                for (nHashTableCounter=1; nHashTableCounter<(*pHashsNoe16u); nHashTableCounter++) {
                    nCurrCandMapping32u = pBestMapping32u[pHashTableImgA32u[nCurrTypeHashVal32u +=  mHashTable]-1];// -1 for matlab2C; A index
                    CalcSqrDifferenceAndTakeBest(mCurrFiltersImg, mCurrFiltersImgTarget, &pCurrBestError32u,
                                                 &pCurrBestMapping32u, nCurrCandMapping32u, nCurIterOffset,nKernels);
                }

                // Right of the left pixel best mapping's
                nCurrTypeMapping32u = nHorizNeighbourMapping32u + nLeftRightTargetOffset;
                nCurrTypeHashVal32u = pCodebookImgB32u[nCurrTypeMapping32u]-1;

                // ## Type 2 - lr ##
                // Test for original candidate
                CalcSqrDifferenceAndTakeBest(mCurrFiltersImg, mCurrFiltersImgTarget,
                                             &pCurrBestError32u, &pCurrBestMapping32u, nCurrTypeMapping32u, nCurIterOffset,nKernels);
                // Test for cadidates in hash table with the same entry
                nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u]-1;// -1 for matlab2C;
                CalcSqrDifferenceAndTakeBest(mCurrFiltersImg, mCurrFiltersImgTarget,
                                             &pCurrBestError32u, &pCurrBestMapping32u, nCurrCandMapping32u, nCurIterOffset,nKernels);

                for (nHashTableCounter=1; nHashTableCounter<(*pHashsNoe16u); nHashTableCounter++) {
                    nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u +=  mHashTable]-1;// -1 for matlab2C;
                    CalcSqrDifferenceAndTakeBest(mCurrFiltersImg, mCurrFiltersImgTarget,
                                                 &pCurrBestError32u, &pCurrBestMapping32u, nCurrCandMapping32u, nCurIterOffset,nKernels);
                }
                // ## Type 2 - tb ##
                // Bottom of the top pixel best mapping's - Or otherwise
                nCurrTypeMapping32u = nVerticalNeighbourMapping32u + nTopBotTargetOffset;
                nCurrTypeHashVal32u = pCodebookImgB32u[nCurrTypeMapping32u]-1;

                // Test for original candidate
                CalcSqrDifferenceAndTakeBest(mCurrFiltersImg, mCurrFiltersImgTarget,
                                             &pCurrBestError32u, &pCurrBestMapping32u, nCurrTypeMapping32u, nCurIterOffset,nKernels);

                // Test for cadidats in hash table with the same entry
                nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u]-1;// -1 for matlab2C;
                CalcSqrDifferenceAndTakeBest(mCurrFiltersImg, mCurrFiltersImgTarget,
                                             &pCurrBestError32u, &pCurrBestMapping32u, nCurrCandMapping32u, nCurIterOffset,nKernels);

                for (nHashTableCounter=1; nHashTableCounter<(*pHashsNoe16u); nHashTableCounter++) {
                    nCurrCandMapping32u = pHashTableImgB32u[nCurrTypeHashVal32u +=  mHashTable]-1;// -1 for matlab2C;
                    CalcSqrDifferenceAndTakeBest(mCurrFiltersImg, mCurrFiltersImgTarget,
                                                 &pCurrBestError32u, &pCurrBestMapping32u, nCurrCandMapping32u, nCurIterOffset,nKernels);
                }
            }

        }
        // mexPrintf("***********************************************\n");
        // mexPrintf("***** advances up: %d, out of: %d **********\n", advances, (nLastColBack - nFirstColBack)*(nLastRowBack - nFirstRowBack));
        // mexPrintf("***********************************************\n");
    }


}