/***************************************************************************** * * mtdev - Multitouch Protocol Translation Library (MIT license) * * Copyright (C) 2010 Henrik Rydberg * Copyright (C) 2010 Canonical Ltd. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * ****************************************************************************/ #include "match.h" #include #include /** * Bitmap implementation of the hungarian algorithm (MIT license) * * Copyright (C) 2008 Henrik Rydberg * * Based on code released by Markus Buehren (2004) (BSD license) * * Copyright (C) 2004, Markus Buehren. All rights reserved. */ typedef unsigned col_t[1]; typedef unsigned mat_t[DIM_FINGER]; #define GET1(m, x) ((m[0] >> (x)) & 1U) #define SET1(m, x) (m[0] |= (1U << (x))) #define CLEAR1(m, x) (m[0] &= ~(1U << (x))) #define GET2(m, row, col) ((m[col] >> (row)) & 1U) #define SET2(m, row, col) (m[col] |= (1U << (row))) #define CLEAR2(m, row, col) (m[col] &= ~(1U << (row))) /********************************************************/ static void buildixvector(int *ix, mat_t mstar, int nrows, int ncols) { int row, col; for (row = 0; row < nrows; row++) { for (col = 0; col < ncols; col++) { if (GET2(mstar, row, col)) { ix[row] = col; break; } } } } /********************************************************/ static void step2a(int *ix, int *mdist, mat_t mstar, mat_t nmstar, mat_t mprime, col_t ccol, col_t crow, int nrows, int ncols, int dmin); static void step2b(int *ix, int *mdist, mat_t mstar, mat_t nmstar, mat_t mprime, col_t ccol, col_t crow, int nrows, int ncols, int dmin); static void step3(int *ix, int *mdist, mat_t mstar, mat_t nmstar, mat_t mprime, col_t ccol, col_t crow, int nrows, int ncols, int dmin); static void step4(int *ix, int *mdist, mat_t mstar, mat_t nmstar, mat_t mprime, col_t ccol, col_t crow, int nrows, int ncols, int dmin, int row, int col); static void step5(int *ix, int *mdist, mat_t mstar, mat_t nmstar, mat_t mprime, col_t ccol, col_t crow, int nrows, int ncols, int dmin); static void ixoptimal(int *ix, int *mdist, int nrows, int ncols) { int *mdistTemp, *mdistEnd, *columnEnd, value, minValue; int dmin, row, col; col_t ccol, crow; mat_t mstar, mprime, nmstar; memset(ccol, 0, sizeof(col_t)); memset(crow, 0, sizeof(col_t)); memset(mstar, 0, sizeof(mat_t)); memset(mprime, 0, sizeof(mat_t)); memset(nmstar, 0, sizeof(mat_t)); /* initialization */ for (row = 0; row < nrows; row++) ix[row] = -1; mdistEnd = mdist + nrows * ncols; /* preliminary steps */ if (nrows <= ncols) { dmin = nrows; for (row = 0; row < nrows; row++) { /* find the smallest element in the row */ mdistTemp = mdist + row; minValue = *mdistTemp; mdistTemp += nrows; while (mdistTemp < mdistEnd) { value = *mdistTemp; if (value < minValue) minValue = value; mdistTemp += nrows; } /* subtract the smallest element from each element of the row */ mdistTemp = mdist + row; while (mdistTemp < mdistEnd) { *mdistTemp -= minValue; mdistTemp += nrows; } } /* Steps 1 and 2a */ for (row = 0; row < nrows; row++) { for (col = 0; col < ncols; col++) { if (mdist[row + nrows * col] != 0) continue; if (GET1(ccol, col)) continue; SET2(mstar, row, col); SET1(ccol, col); break; } } } else { dmin = ncols; for (col = 0; col < ncols; col++) { /* find the smallest element in the column */ mdistTemp = mdist + nrows*col; columnEnd = mdistTemp + nrows; minValue = *mdistTemp++; while (mdistTemp < columnEnd) { value = *mdistTemp++; if (value < minValue) minValue = value; } /* subtract the smallest element from each element of the column */ mdistTemp = mdist + nrows*col; while (mdistTemp < columnEnd) *mdistTemp++ -= minValue; } /* Steps 1 and 2a */ for (col = 0; col < ncols; col++) { for (row = 0; row < nrows; row++) { if (mdist[row + nrows * col] != 0) continue; if (GET1(crow, row)) continue; SET2(mstar, row, col); SET1(ccol, col); SET1(crow, row); break; } } memset(crow, 0, sizeof(col_t)); } /* move to step 2b */ step2b(ix, mdist, mstar, nmstar, mprime, ccol, crow, nrows, ncols, dmin); } /********************************************************/ static void step2a(int *ix, int *mdist, mat_t mstar, mat_t nmstar, mat_t mprime, col_t ccol, col_t crow, int nrows, int ncols, int dmin) { int col, row; /* cover every column containing a starred zero */ for (col = 0; col < ncols; col++) { for (row = 0; row < nrows; row++) { if (!GET2(mstar, row, col)) continue; SET1(ccol, col); break; } } /* move to step 3 */ step2b(ix, mdist, mstar, nmstar, mprime, ccol, crow, nrows, ncols, dmin); } /********************************************************/ static void step2b(int *ix, int *mdist, mat_t mstar, mat_t nmstar, mat_t mprime, col_t ccol, col_t crow, int nrows, int ncols, int dmin) { int col, ncc; /* count covered columns */ ncc = 0; for (col = 0; col < ncols; col++) if (GET1(ccol, col)) ncc++; if (ncc == dmin) { /* algorithm finished */ buildixvector(ix, mstar, nrows, ncols); } else { /* move to step 3 */ step3(ix, mdist, mstar, nmstar, mprime, ccol, crow, nrows, ncols, dmin); } } /********************************************************/ static void step3(int *ix, int *mdist, mat_t mstar, mat_t nmstar, mat_t mprime, col_t ccol, col_t crow, int nrows, int ncols, int dmin) { int zerosFound; int row, col, cstar; zerosFound = 1; while (zerosFound) { zerosFound = 0; for (col = 0; col < ncols; col++) { if (GET1(ccol, col)) continue; for (row = 0; row < nrows; row++) { if (mdist[row + nrows * col] != 0) continue; if (GET1(crow, row)) continue; /* prime zero */ SET2(mprime, row, col); /* find starred zero in current row */ for (cstar = 0; cstar < ncols; cstar++) if (GET2(mstar, row, cstar)) break; if (cstar == ncols) { /* no starred zero */ /* move to step 4 */ step4(ix, mdist, mstar, nmstar, mprime, ccol, crow, nrows, ncols, dmin, row, col); return; } else { SET1(crow, row); CLEAR1(ccol, cstar); zerosFound = 1; break; } } } } /* move to step 5 */ step5(ix, mdist, mstar, nmstar, mprime, ccol, crow, nrows, ncols, dmin); } /********************************************************/ static void step4(int *ix, int *mdist, mat_t mstar, mat_t nmstar, mat_t mprime, col_t ccol, col_t crow, int nrows, int ncols, int dmin, int row, int col) { int rstar, cstar, primeRow, primeCol; /* generate temporary copy of mstar */ memcpy(nmstar, mstar, sizeof(mat_t)); /* star current zero */ SET2(nmstar, row, col); /* find starred zero in current column */ cstar = col; for (rstar = 0; rstar < nrows; rstar++) if (GET2(mstar, rstar, cstar)) break; while (rstar < nrows) { /* unstar the starred zero */ CLEAR2(nmstar, rstar, cstar); /* find primed zero in current row */ primeRow = rstar; for (primeCol = 0; primeCol < ncols; primeCol++) if (GET2(mprime, primeRow, primeCol)) break; /* star the primed zero */ SET2(nmstar, primeRow, primeCol); /* find starred zero in current column */ cstar = primeCol; for (rstar = 0; rstar < nrows; rstar++) if (GET2(mstar, rstar, cstar)) break; } /* use temporary copy as new mstar */ /* delete all primes, uncover all rows */ memcpy(mstar, nmstar, sizeof(mat_t)); memset(mprime, 0, sizeof(mat_t)); memset(crow, 0, sizeof(col_t)); /* move to step 2a */ step2a(ix, mdist, mstar, nmstar, mprime, ccol, crow, nrows, ncols, dmin); } /********************************************************/ static void step5(int *ix, int *mdist, mat_t mstar, mat_t nmstar, mat_t mprime, col_t ccol, col_t crow, int nrows, int ncols, int dmin) { int h = 0, value; int row, col, found = 0; /* find smallest uncovered element h */ for (row = 0; row < nrows; row++) { if (GET1(crow, row)) continue; for (col = 0; col < ncols; col++) { if (GET1(ccol, col)) continue; value = mdist[row + nrows * col]; if (!found || value < h) { h = value; found = 1; } } } /* where to go if nothing uncovered? */ if (!found) return; /* add h to each covered row */ for (row = 0; row < nrows; row++) { if (!GET1(crow, row)) continue; for (col = 0; col < ncols; col++) mdist[row + nrows * col] += h; } /* subtract h from each uncovered column */ for (col = 0; col < ncols; col++) { if (GET1(ccol, col)) continue; for (row = 0; row < nrows; row++) mdist[row + nrows * col] -= h; } /* move to step 3 */ step3(ix, mdist, mstar, nmstar, mprime, ccol, crow, nrows, ncols, dmin); } void mtdev_match(int ix[DIM_FINGER], int A[DIM2_FINGER], int nrow, int ncol) { ixoptimal(ix, A, nrow, ncol); }