/*
 * Copyright (c) 2014, Sandia Corporation.
 * Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
 * the U.S. Government retains certain rights in this software.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *
 *     * Neither the name of Sandia Corporation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "smalloc.h" // for sfree, smalloc
#include "structs.h" // for vtx_data

/* Find a maximal matching in a graph using simple greedy algorithm. */
/* Randomly permute vertices, and then have each select an unmatched */
/* neighbor.  Choose the neighbor which results in coarsened vertex of */
/* minimum degree. */

int maxmatch9(struct vtx_data **graph,      /* array of vtx data for graph */
              int               nvtxs,      /* number of vertices in graph */
              int *             mflag,      /* flag indicating vtx selected or not */
              int               using_ewgts /* are edge weights being used? */
              )
{
  extern int HEAVY_MATCH;         /* use heavy-edge matching? */
  int *      order;               /* random ordering of vertices */
  int *      neighbors;           /* scatter array for neighbor list */
  int *      iptr, *jptr;         /* loops through integer arrays */
  float      ewgt;                /* edge weight */
  int        save;                /* neighbor vertex if only one active */
  int        vtx;                 /* vertex to process next */
  int        neighbor;            /* neighbor of a vertex */
  int        best;                /* best match found so far */
  float      same = 0, best_same; /* maximum # neighbors in common so far */
  float      best_ewgt;           /* edge weight of possible matching edge */
  int        nmerged;             /* number of edges in matching */
  int        i, j, k;             /* loop counters */

  void randomize();

  /* First, randomly permute the vertices. */
  neighbors = smalloc((nvtxs + 1) * sizeof(int));
  iptr = order = smalloc((nvtxs + 1) * sizeof(int));
  jptr         = mflag;
  for (i = 1; i <= nvtxs; i++) {
    *(++iptr)    = i;
    *(++jptr)    = 0;
    neighbors[i] = 0;
  }
  randomize(order, nvtxs);

  /*    if (!using_ewgts || !HEAVY_MATCH) { */

  nmerged = 0;
  ewgt    = 0;
  for (i = 1; i <= nvtxs; i++) {
    vtx = order[i];
    if (mflag[vtx] == 0) { /* Not already matched. */
      /* Add up sum of edge weights of neighbors. */
      save = -1;
      for (j = 1; j < graph[vtx]->nedges; j++) {
        neighbor            = graph[vtx]->edges[j];
        neighbors[neighbor] = i;
        if (mflag[neighbor] == 0) {
          /* Set flag for single possible neighbor. */
          if (save == -1)
            save = neighbor;
          else
            save = 0;
        }
        else {
          neighbors[mflag[neighbor]] = i;
        }
      }

      if (save != -1) { /* Does vertex have contractible edges? */
        nmerged++;
        if (save > 0) { /* Only one neighbor, easy special case. */
          mflag[vtx]  = save;
          mflag[save] = vtx;
        }
        else { /* Merge with best neighbor */
          best      = 0;
          best_same = -1;
          best_ewgt = -1;
          for (j = 1; j < graph[vtx]->nedges; j++) {
            neighbor = graph[vtx]->edges[j];
            if (mflag[neighbor] == 0) {
              if (using_ewgts && HEAVY_MATCH) {
                ewgt = graph[vtx]->ewgts[j];
              }
              if (ewgt > best_ewgt) {
                best      = neighbor;
                best_same = same;
                best_ewgt = ewgt;
              }
              else if (ewgt == best_ewgt) {
                /* break ties by larger same value */
                if (best_same == -1) {
                  /* Compute same value for current best vtx. */
                  best_same = 0;
                  for (k = 1; k < graph[best]->nedges; k++) {
                    if (neighbors[graph[best]->edges[k]] == i) {
                      if (using_ewgts) {
                        best_same += graph[best]->ewgts[k];
                      }
                      else
                        best_same += 1;
                    }
                  }
                }
                /* Now compute same value for candidate vtx. */
                same = 0;
                for (k = 1; k < graph[neighbor]->nedges; k++) {
                  if (neighbors[graph[neighbor]->edges[k]] == i) {
                    if (using_ewgts) {
                      same += graph[neighbor]->ewgts[k];
                    }
                    else
                      same += 1;
                  }
                }

                if (same > best_same) {
                  best      = neighbor;
                  best_same = same;
                  best_ewgt = ewgt;
                }
              }
            }
          }
          mflag[vtx]  = best;
          mflag[best] = vtx;
        }
      }
    }
  }

  sfree(order);
  sfree(neighbors);
  return (nmerged);
}
