diff --git a/src/aig/gia/giaMulFind.c b/src/aig/gia/giaMulFind.c index b7bc0b0b6..5e7d8d7b2 100644 --- a/src/aig/gia/giaMulFind.c +++ b/src/aig/gia/giaMulFind.c @@ -661,15 +661,15 @@ Vec_Wec_t * Gia_ManMulFindBInputs( Gia_Man_t * p, Vec_Wec_t * vCuts4, Vec_Wec_t SeeAlso [] ***********************************************************************/ -Vec_Int_t * Gia_ManMulFindTfo( Gia_Man_t * p, Vec_Int_t * vIn0, Vec_Int_t * vIn1 ) +Vec_Int_t * Gia_ManMulFindTfo( Gia_Man_t * p, Vec_Int_t * vIn0, Vec_Int_t * vIn1, int fLits ) { Vec_Int_t * vTfo = Vec_IntAlloc( 100 ); Gia_Obj_t * pObj; int i, Obj; Gia_ManIncrementTravId( p ); Vec_IntForEachEntry( vIn0, Obj, i ) - Gia_ObjSetTravIdCurrentId( p, Obj ); + Gia_ObjSetTravIdCurrentId( p, fLits ? Abc_Lit2Var(Obj) : Obj ); Vec_IntForEachEntry( vIn1, Obj, i ) - Gia_ObjSetTravIdCurrentId( p, Obj ); + Gia_ObjSetTravIdCurrentId( p, fLits ? Abc_Lit2Var(Obj) : Obj ); Gia_ManForEachAnd( p, pObj, i ) { if ( Gia_ObjIsTravIdCurrentId(p, i) ) continue; @@ -678,15 +678,15 @@ Vec_Int_t * Gia_ManMulFindTfo( Gia_Man_t * p, Vec_Int_t * vIn0, Vec_Int_t * vIn1 } return vTfo; } -Vec_Wrd_t * Gia_ManMulFindSimCone( Gia_Man_t * p, Vec_Int_t * vIn0, Vec_Int_t * vIn1, Vec_Wrd_t * vSim0, Vec_Wrd_t * vSim1, Vec_Int_t * vTfo ) +Vec_Wrd_t * Gia_ManMulFindSimCone( Gia_Man_t * p, Vec_Int_t * vIn0, Vec_Int_t * vIn1, Vec_Wrd_t * vSim0, Vec_Wrd_t * vSim1, Vec_Int_t * vTfo, int fLits ) { Vec_Wrd_t * vRes = Vec_WrdAlloc( Vec_IntSize(vTfo) ); Vec_Wrd_t * vSims = Vec_WrdStart( Gia_ManObjNum(p) ); Gia_Obj_t * pObj; int i, Obj; Vec_IntForEachEntry( vIn0, Obj, i ) - Vec_WrdWriteEntry( vSims, Obj, Vec_WrdEntry(vSim0, i) ); + Vec_WrdWriteEntry( vSims, fLits ? Abc_Lit2Var(Obj) : Obj, (fLits && Abc_LitIsCompl(Obj)) ? ~Vec_WrdEntry(vSim0, i) : Vec_WrdEntry(vSim0, i) ); Vec_IntForEachEntry( vIn1, Obj, i ) - Vec_WrdWriteEntry( vSims, Obj, Vec_WrdEntry(vSim1, i) ); + Vec_WrdWriteEntry( vSims, fLits ? Abc_Lit2Var(Obj) : Obj, (fLits && Abc_LitIsCompl(Obj)) ? ~Vec_WrdEntry(vSim1, i) : Vec_WrdEntry(vSim1, i) ); Gia_ManForEachObjVec( vTfo, p, pObj, i ) { word Sim0 = Vec_WrdEntry(vSims, Gia_ObjFaninId0p(p, pObj) ); word Sim1 = Vec_WrdEntry(vSims, Gia_ObjFaninId1p(p, pObj) ); @@ -697,17 +697,17 @@ Vec_Wrd_t * Gia_ManMulFindSimCone( Gia_Man_t * p, Vec_Int_t * vIn0, Vec_Int_t * Vec_WrdFree( vSims ); return vRes; } -int Gia_ManMulFindGetArg( Vec_Wrd_t * vSim, int i, int fSigned ) +iword Gia_ManMulFindGetArg( Vec_Wrd_t * vSim, int i, int fSigned ) { - int w, Res = 0; word Word = 0; + int w; iword Res = 0; word Word = 0; Vec_WrdForEachEntry( vSim, Word, w ) if ( (Word >> i) & 1 ) - Res |= (1 << w); + Res |= ((iword)1 << w); if ( fSigned && ((Word >> i) & 1) ) - Res |= ~0 << Vec_WrdSize(vSim); + Res |= ~(iword)0 << Vec_WrdSize(vSim); return Res; } -void Gia_ManMulFindSetArg( Vec_Wrd_t * vSim, int i, int iNum ) +void Gia_ManMulFindSetArg( Vec_Wrd_t * vSim, int i, iword iNum ) { int w; word * pWords = Vec_WrdArray(vSim); for ( w = 0; w < Vec_WrdSize(vSim); w++ ) @@ -716,17 +716,17 @@ void Gia_ManMulFindSetArg( Vec_Wrd_t * vSim, int i, int iNum ) } Vec_Wrd_t * Gia_ManMulFindSim( Vec_Wrd_t * vSim0, Vec_Wrd_t * vSim1, int fSigned ) { - assert( Vec_WrdSize(vSim0) + Vec_WrdSize(vSim1) <= 30 ); + assert( Vec_WrdSize(vSim0) + Vec_WrdSize(vSim1) <= 62 ); Vec_Wrd_t * vRes = Vec_WrdStart( Vec_WrdSize(vSim0) + Vec_WrdSize(vSim1) ); for ( int i = 0; i < 64; i++ ) { - int a = Gia_ManMulFindGetArg( vSim0, i, fSigned ); - int b = Gia_ManMulFindGetArg( vSim1, i, fSigned ); + iword a = Gia_ManMulFindGetArg( vSim0, i, fSigned ); + iword b = Gia_ManMulFindGetArg( vSim1, i, fSigned ); Gia_ManMulFindSetArg( vRes, i, a * b ); } return vRes; } -void Gia_ManMulFindOutputs( Gia_Man_t * p, Vec_Wec_t * vTerms, int fVerbose ) +void Gia_ManMulFindOutputs( Gia_Man_t * p, Vec_Wec_t * vTerms, int fLits, int fVerbose ) { Abc_Random(1); for ( int m = 0; m < Vec_WecSize(vTerms)/3; m++ ) { @@ -737,8 +737,8 @@ void Gia_ManMulFindOutputs( Gia_Man_t * p, Vec_Wec_t * vTerms, int fVerbose ) Vec_Wrd_t * vSim1 = Vec_WrdStartRandom( Vec_IntSize(vIn1) ); Vec_Wrd_t * vSimU = Gia_ManMulFindSim( vSim0, vSim1, 0 ); Vec_Wrd_t * vSimS = Gia_ManMulFindSim( vSim0, vSim1, 1 ); - Vec_Int_t * vTfo = Gia_ManMulFindTfo( p, vIn0, vIn1 ); - Vec_Wrd_t * vSims = Gia_ManMulFindSimCone( p, vIn0, vIn1, vSim0, vSim1, vTfo ); + Vec_Int_t * vTfo = Gia_ManMulFindTfo( p, vIn0, vIn1, fLits ); + Vec_Wrd_t * vSims = Gia_ManMulFindSimCone( p, vIn0, vIn1, vSim0, vSim1, vTfo, fLits ); Vec_Int_t * vOutU = Vec_IntAlloc( 100 ); Vec_Int_t * vOutS = Vec_IntAlloc( 100 ); word Word; int w, iPlace; @@ -814,7 +814,7 @@ Vec_Wec_t * Gia_ManMulFindA( Gia_Man_t * p, Vec_Wec_t * vCuts3, int fVerbose ) Vec_Wec_t * vXors = Gia_ManMulFindXors( p, vCuts3, fVerbose ); Vec_Wec_t * vTerms = Gia_ManMulFindAInputs2( p, fVerbose ); if ( Vec_WecSize(vTerms) ) - Gia_ManMulFindOutputs( p, vTerms, fVerbose ); + Gia_ManMulFindOutputs( p, vTerms, 0, fVerbose ); Vec_WecFree( vXors ); return vTerms; } @@ -824,7 +824,7 @@ Vec_Wec_t * Gia_ManMulFindB( Gia_Man_t * p, Vec_Wec_t * vCuts4, Vec_Wec_t * vCut if ( Vec_WecSize(vCuts4) && Vec_WecSize(vCuts5) ) vTerms = Gia_ManMulFindBInputs2( p, vCuts4, vCuts5, fVerbose ); if ( Vec_WecSize(vTerms) ) - Gia_ManMulFindOutputs( p, vTerms, fVerbose ); + Gia_ManMulFindOutputs( p, vTerms, 0, fVerbose ); return vTerms; } void Gia_ManMulFindPrintSet( Vec_Int_t * vSet, int fLit, int fSkipLast ) diff --git a/src/aig/gia/giaMulFind3.c b/src/aig/gia/giaMulFind3.c new file mode 100644 index 000000000..52db2f120 --- /dev/null +++ b/src/aig/gia/giaMulFind3.c @@ -0,0 +1,48 @@ +/**CFile**************************************************************** + + FileName [giaMulFind3.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Scalable AIG package.] + + Synopsis [Multiplier detection.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: giaMulFind3.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include + +#include "gia.h" +#include "misc/vec/vecHsh.h" +#include "misc/util/utilTruth.h" + +ABC_NAMESPACE_IMPL_START + + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +void Gia_ManMulFindNew( Gia_Man_t * p, int nABits, int nFanLim, int fLits, int fVerbose ) +{ +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + +ABC_NAMESPACE_IMPL_END + diff --git a/src/aig/gia/module.make b/src/aig/gia/module.make index ebc39b1f6..dcb30e90a 100644 --- a/src/aig/gia/module.make +++ b/src/aig/gia/module.make @@ -58,6 +58,7 @@ SRC += src/aig/gia/giaAig.c \ src/aig/gia/giaMinLut.c \ src/aig/gia/giaMinLut2.c \ src/aig/gia/giaMulFind.c \ + src/aig/gia/giaMulFind3.c \ src/aig/gia/giaMuxes.c \ src/aig/gia/giaNf.c \ src/aig/gia/giaOf.c \ diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 2787c0a82..990ed118c 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -643,6 +643,7 @@ static int Abc_CommandAbc9FunAbs ( Abc_Frame_t * pAbc, int argc, cha static int Abc_CommandAbc9DsdInfo ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandAbc9FunTrace ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandAbc9MulFind ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandAbc9MulFind3 ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandAbc9BsFind ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandAbc9AndCare ( Abc_Frame_t * pAbc, int argc, char ** argv ); @@ -1469,7 +1470,8 @@ void Abc_Init( Abc_Frame_t * pAbc ) Cmd_CommandAdd( pAbc, "ABC9", "&funabs", Abc_CommandAbc9FunAbs, 0 ); Cmd_CommandAdd( pAbc, "ABC9", "&dsdinfo", Abc_CommandAbc9DsdInfo, 0 ); Cmd_CommandAdd( pAbc, "ABC9", "&funtrace", Abc_CommandAbc9FunTrace, 0 ); - Cmd_CommandAdd( pAbc, "ABC9", "&mulfind", Abc_CommandAbc9MulFind, 0 ); + Cmd_CommandAdd( pAbc, "ABC9", "&mulfind", Abc_CommandAbc9MulFind, 0 ); + Cmd_CommandAdd( pAbc, "ABC9", "&mulfind3", Abc_CommandAbc9MulFind3, 0 ); Cmd_CommandAdd( pAbc, "ABC9", "&bsfind", Abc_CommandAbc9BsFind, 0 ); Cmd_CommandAdd( pAbc, "ABC9", "&andcare", Abc_CommandAbc9AndCare, 0 ); @@ -57579,6 +57581,79 @@ usage: return 1; } +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandAbc9MulFind3( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + extern void Gia_ManMulFindNew( Gia_Man_t * p, int nABits, int nFanLim, int fLits, int fVerbose ); + int c, nABits = 0, nFanLim = 4, fLits = 0, fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "IFlvh" ) ) != EOF ) + { + switch ( c ) + { + case 'I': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-I\" should be followed by an integer.\n" ); + goto usage; + } + nABits = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nABits < 0 ) + goto usage; + break; + case 'F': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-F\" should be followed by an integer.\n" ); + goto usage; + } + nFanLim = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nFanLim < 0 ) + goto usage; + break; + case 'l': + fLits ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pAbc->pGia == NULL ) + { + Abc_Print( -1, "Abc_CommandAbc9MulFind(): There is no AIG.\n" ); + return 0; + } + Gia_ManMulFindNew( pAbc->pGia, nABits, nFanLim, fLits, fVerbose ); + return 0; + +usage: + Abc_Print( -2, "usage: &mulfind3 [-IF num] [-lvh]\n" ); + Abc_Print( -2, "\t detects multipliers in the given AIG\n" ); + Abc_Print( -2, "\t-I num : the bit-width of the first input if known [default = %d]\n", nABits ); + Abc_Print( -2, "\t-F num : the fanout limit [default = %d]\n", nFanLim ); + Abc_Print( -2, "\t-l : toggles using literals instead of nodes [default = %s]\n", fLits ? "yes": "no" ); + Abc_Print( -2, "\t-v : toggles printing verbose information [default = %s]\n", fVerbose ? "yes": "no" ); + Abc_Print( -2, "\t-h : print the command usage\n"); + return 1; +} + /**Function************************************************************* Synopsis [] diff --git a/src/misc/util/module.make b/src/misc/util/module.make index ef4baebf1..c3b797e7c 100644 --- a/src/misc/util/module.make +++ b/src/misc/util/module.make @@ -1,4 +1,5 @@ SRC += src/misc/util/utilBridge.c \ + src/misc/util/utilBipart.c \ src/misc/util/utilBSet.c \ src/misc/util/utilCex.c \ src/misc/util/utilColor.c \ diff --git a/src/misc/util/utilBipart.c b/src/misc/util/utilBipart.c new file mode 100644 index 000000000..8df16991c --- /dev/null +++ b/src/misc/util/utilBipart.c @@ -0,0 +1,1324 @@ +/**CFile**************************************************************** + + FileName [utilBipart.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Handling counter-examples.] + + Synopsis [Handling counter-examples.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: utilBipart.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "misc/util/abc_global.h" + +ABC_NAMESPACE_IMPL_START + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define List_ForEachSet(pList, pSet, s) for ((s) = 0, (pSet) = (pList) + 1; (s) < (pList)[0]; ++(s), (pSet) += (pSet)[0] + 1) +#define Set_ForEachNode(pSet, node, n) for ((n) = 1; (n) <= (pSet)[0] && (((node) = (pSet)[n]), 1); ++(n)) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct { + int *data; + int size; + int cap; +} IntVector; + +typedef struct { + int node_count; + int edge_count; + int *offset; + int *neighbors; + int *orig_ids; +} Graph; + +typedef struct { + int sizeA; + int sizeB; + int *nodesA; + int *nodesB; + uint64_t hashA; + uint64_t hashB; +} ComponentRecord; + +typedef struct { + ComponentRecord *data; + int size; + int cap; +} ComponentList; + +typedef struct { + int u; + int v; + int assigned; +} EdgeInfo; + +typedef struct { + uint64_t key; + int index; +} EdgeMapEntry; + +typedef struct { + IntVector nodesA; + IntVector nodesB; + IntVector edges; + int edge_count; +} WorkComponent; + +typedef struct { + Graph graph; + EdgeInfo *edges; + EdgeMapEntry *edge_map; + int edge_count; + WorkComponent *components; + int component_count; +} SolverContext; + +static void validate_edge_ownership(const SolverContext *ctx) { + int *owner; + int i; + if (ctx->edge_count == 0) { + return; + } + owner = (int *)malloc(ctx->edge_count * sizeof(int)); + assert(owner); + for (i = 0; i < ctx->edge_count; ++i) { + owner[i] = -1; + } + for (i = 0; i < ctx->component_count; ++i) { + const WorkComponent *comp = &ctx->components[i]; + int j; + for (j = 0; j < comp->edges.size; ++j) { + int idx = comp->edges.data[j]; + assert(idx >= 0 && idx < ctx->edge_count); + assert(owner[idx] == -1); + owner[idx] = i; + assert(ctx->edges[idx].assigned == i); + } + for (j = 1; j < comp->nodesA.size; ++j) { + assert(comp->nodesA.data[j - 1] < comp->nodesA.data[j]); + } + for (j = 1; j < comp->nodesB.size; ++j) { + assert(comp->nodesB.data[j - 1] < comp->nodesB.data[j]); + } + } + for (i = 0; i < ctx->edge_count; ++i) { + int assigned = ctx->edges[i].assigned; + if (assigned >= 0) { + assert(owner[i] == assigned); + } else { + assert(owner[i] == -1); + } + } + free(owner); +} + +static void vec_init(IntVector *v) { + v->data = NULL; + v->size = 0; + v->cap = 0; +} + +static void vec_reserve(IntVector *v, int need) { + if (need > v->cap) { + int cap = v->cap ? v->cap : 8; + while (cap < need) { + cap *= 2; + } + v->data = (int *)realloc(v->data, cap * sizeof(int)); + assert(v->data); + v->cap = cap; + } +} + +static void vec_push(IntVector *v, int value) { + vec_reserve(v, v->size + 1); + v->data[v->size++] = value; +} + +static void vec_clear(IntVector *v) { + v->size = 0; +} + +static void vec_free(IntVector *v) { + free(v->data); + v->data = NULL; + v->size = 0; + v->cap = 0; +} + +static void component_list_init(ComponentList *list) { + list->data = NULL; + list->size = 0; + list->cap = 0; +} + +static void component_list_push(ComponentList *list, ComponentRecord rec) { + if (list->size == list->cap) { + int cap = list->cap ? list->cap * 2 : 4; + list->data = (ComponentRecord *)realloc(list->data, cap * sizeof(ComponentRecord)); + assert(list->data); + list->cap = cap; + } + list->data[list->size++] = rec; +} + +static void component_list_free(ComponentList *list) { + int i; + for (i = 0; i < list->size; ++i) { + free(list->data[i].nodesA); + free(list->data[i].nodesB); + } + free(list->data); + list->data = NULL; + list->size = 0; + list->cap = 0; +} + +static int cmp_int(const void *a, const void *b) { + const int lhs = *(const int *)a; + const int rhs = *(const int *)b; + return (lhs > rhs) - (lhs < rhs); +} + +static int compare_sets(const int *a, int sizeA, const int *b, int sizeB) { + int i; + if (sizeA < sizeB) { + return -1; + } + if (sizeA > sizeB) { + return 1; + } + for (i = 0; i < sizeA; ++i) { + if (a[i] < b[i]) { + return -1; + } + if (a[i] > b[i]) { + return 1; + } + } + return 0; +} + +static int threshold_value(int percent, int count) { + return (percent * count + 99) / 100; +} + +static uint64_t hash_ids(const int *ids, int count) { + uint64_t h = 1469598103934665603ULL; + uint64_t prime = 1099511628211ULL; + int i; + for (i = 0; i < count; ++i) { + h ^= (uint64_t)(uint32_t)ids[i] + 0x9e3779b97f4a7c15ULL; + h *= prime; + } + return h; +} + +static int binary_search_id(const int *ids, int count, int value) { + int lo = 0; + int hi = count - 1; + while (lo <= hi) { + int mid = lo + (hi - lo) / 2; + if (ids[mid] == value) { + return mid; + } + if (ids[mid] < value) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return -1; +} + +static void graph_free(Graph *g) { + free(g->offset); + free(g->neighbors); + free(g->orig_ids); + g->offset = NULL; + g->neighbors = NULL; + g->orig_ids = NULL; + g->node_count = 0; + g->edge_count = 0; +} + +static void graph_build(Graph *g, int *pairs, int pair_count) { + int *ids; + int id_count = 0; + int unique_ids = 0; + int i; + int edge_capacity; + int *edges_u; + int *edges_v; + int mapped_edges = 0; + int *degree; + int *offset; + int *neighbors; + int total_adj; + int *cursor; + int *new_offsets; + int write_pos; + + g->node_count = 0; + g->edge_count = 0; + g->offset = NULL; + g->neighbors = NULL; + g->orig_ids = NULL; + + if (pair_count == 0) { + g->offset = (int *)calloc(1, sizeof(int)); + assert(g->offset); + return; + } + + assert((pair_count & 1) == 0); + edge_capacity = pair_count / 2; + + ids = (int *)malloc(pair_count * sizeof(int)); + assert(ids); + edges_u = (int *)malloc(edge_capacity * sizeof(int)); + edges_v = (int *)malloc(edge_capacity * sizeof(int)); + assert(edges_u && edges_v); + + for (i = 0; i < pair_count; i += 2) { + ids[id_count++] = pairs[i]; + ids[id_count++] = pairs[i + 1]; + } + + qsort(ids, id_count, sizeof(int), cmp_int); + for (i = 0; i < id_count; ++i) { + if (i == 0 || ids[i] != ids[i - 1]) { + ids[unique_ids++] = ids[i]; + } + } + + g->node_count = unique_ids; + g->orig_ids = (int *)malloc(unique_ids * sizeof(int)); + assert(g->orig_ids); + for (i = 0; i < unique_ids; ++i) { + g->orig_ids[i] = ids[i]; + } + + for (i = 0; i < pair_count; i += 2) { + int a = pairs[i]; + int b = pairs[i + 1]; + int u = binary_search_id(g->orig_ids, unique_ids, a); + int v = binary_search_id(g->orig_ids, unique_ids, b); + if (u == v) { + continue; + } + edges_u[mapped_edges] = u; + edges_v[mapped_edges] = v; + mapped_edges++; + } + + degree = (int *)calloc(unique_ids, sizeof(int)); + assert(degree); + for (i = 0; i < mapped_edges; ++i) { + int u = edges_u[i]; + int v = edges_v[i]; + degree[u]++; + degree[v]++; + } + + offset = (int *)malloc((unique_ids + 1) * sizeof(int)); + assert(offset); + offset[0] = 0; + for (i = 0; i < unique_ids; ++i) { + offset[i + 1] = offset[i] + degree[i]; + } + total_adj = offset[unique_ids]; + neighbors = (int *)malloc(total_adj * sizeof(int)); + assert(neighbors); + + cursor = (int *)malloc(unique_ids * sizeof(int)); + assert(cursor); + memcpy(cursor, offset, unique_ids * sizeof(int)); + for (i = 0; i < mapped_edges; ++i) { + int u = edges_u[i]; + int v = edges_v[i]; + neighbors[cursor[u]++] = v; + neighbors[cursor[v]++] = u; + } + free(cursor); + free(edges_u); + free(edges_v); + free(degree); + + new_offsets = (int *)malloc((unique_ids + 1) * sizeof(int)); + assert(new_offsets); + new_offsets[0] = 0; + write_pos = 0; + for (i = 0; i < unique_ids; ++i) { + int start = offset[i]; + int end = offset[i + 1]; + int len = end - start; + int j; + if (len > 1) { + qsort(neighbors + start, len, sizeof(int), cmp_int); + } + { + int last = -1; + for (j = start; j < end; ++j) { + int v = neighbors[j]; + if (v == i) { + continue; + } + if (last != -1 && v == last) { + continue; + } + neighbors[write_pos++] = v; + last = v; + } + } + new_offsets[i + 1] = write_pos; + } + + free(offset); + free(ids); + + g->offset = new_offsets; + g->neighbors = neighbors; + g->edge_count = write_pos / 2; +} + +static int count_edges_to_mark(const Graph *g, int node, const unsigned char *mark) { + int start = g->offset[node]; + int end = g->offset[node + 1]; + int count = 0; + int i; + for (i = start; i < end; ++i) { + int v = g->neighbors[i]; + if (mark[v]) { + count++; + } + } + return count; +} + +static int component_exists(const ComponentList *list, uint64_t hashA, uint64_t hashB, const int *nodesA, int sizeA, const int *nodesB, int sizeB) { + int i; + for (i = 0; i < list->size; ++i) { + const ComponentRecord *rec = &list->data[i]; + if (rec->sizeA != sizeA || rec->sizeB != sizeB) { + continue; + } + if (rec->hashA != hashA || rec->hashB != hashB) { + continue; + } + if (memcmp(rec->nodesA, nodesA, sizeA * sizeof(int)) != 0) { + continue; + } + if (memcmp(rec->nodesB, nodesB, sizeB * sizeof(int)) != 0) { + continue; + } + return 1; + } + return 0; +} + +static void detect_components(const Graph *g, int min_con, int min_part, ComponentList *out) { + unsigned char *markA; + unsigned char *markB; + int *counts; + IntVector setA; + IntVector setB; + IntVector touchedA; + IntVector touchedB; + IntVector touchedCounts; + int seed; + + if (g->node_count == 0) { + return; + } + + markA = (unsigned char *)calloc(g->node_count, sizeof(unsigned char)); + markB = (unsigned char *)calloc(g->node_count, sizeof(unsigned char)); + counts = (int *)calloc(g->node_count, sizeof(int)); + assert(markA && markB && counts); + + vec_init(&setA); + vec_init(&setB); + vec_init(&touchedA); + vec_init(&touchedB); + vec_init(&touchedCounts); + + for (seed = 0; seed < g->node_count; ++seed) { + int start = g->offset[seed]; + int end = g->offset[seed + 1]; + int iter = 0; + int valid = 1; + int changed; + int thresholdA; + int thresholdB; + int i; + + if (end - start == 0) { + continue; + } + + vec_clear(&setA); + vec_clear(&setB); + vec_clear(&touchedA); + vec_clear(&touchedB); + vec_clear(&touchedCounts); + + markA[seed] = 1; + vec_push(&touchedA, seed); + vec_push(&setA, seed); + + for (i = start; i < end; ++i) { + int v = g->neighbors[i]; + if (!markB[v]) { + markB[v] = 1; + vec_push(&touchedB, v); + vec_push(&setB, v); + } + } + + if (setB.size == 0) { + goto cleanup_seed; + } + + while (valid) { + changed = 0; + thresholdA = threshold_value(min_con, setB.size); + if (thresholdA == 0 && setB.size > 0) { + thresholdA = 1; + } + + for (i = 0; i < setA.size;) { + int node = setA.data[i]; + int cnt = count_edges_to_mark(g, node, markB); + if (cnt < thresholdA) { + markA[node] = 0; + setA.data[i] = setA.data[setA.size - 1]; + setA.size--; + changed = 1; + continue; + } + ++i; + } + + if (setA.size == 0) { + valid = 0; + break; + } + + thresholdB = threshold_value(min_con, setA.size); + if (thresholdB == 0 && setA.size > 0) { + thresholdB = 1; + } + + for (i = 0; i < setB.size;) { + int node = setB.data[i]; + int cnt = count_edges_to_mark(g, node, markA); + if (cnt < thresholdB) { + markB[node] = 0; + setB.data[i] = setB.data[setB.size - 1]; + setB.size--; + changed = 1; + continue; + } + ++i; + } + + if (setB.size == 0) { + valid = 0; + break; + } + + if (setB.size > 0 && thresholdA > 0) { + for (i = 0; i < setB.size; ++i) { + int node = setB.data[i]; + int n; + for (n = g->offset[node]; n < g->offset[node + 1]; ++n) { + int v = g->neighbors[n]; + if (markA[v] || markB[v]) { + continue; + } + if (counts[v] == 0) { + vec_push(&touchedCounts, v); + } + counts[v]++; + } + } + + for (i = 0; i < touchedCounts.size; ++i) { + int node = touchedCounts.data[i]; + if (counts[node] >= thresholdA) { + markA[node] = 1; + vec_push(&touchedA, node); + vec_push(&setA, node); + changed = 1; + } + counts[node] = 0; + } + vec_clear(&touchedCounts); + } + + thresholdB = threshold_value(min_con, setA.size); + if (thresholdB == 0 && setA.size > 0) { + thresholdB = 1; + } + + if (setA.size > 0 && thresholdB > 0) { + for (i = 0; i < setA.size; ++i) { + int node = setA.data[i]; + int n; + for (n = g->offset[node]; n < g->offset[node + 1]; ++n) { + int v = g->neighbors[n]; + if (markA[v] || markB[v]) { + continue; + } + if (counts[v] == 0) { + vec_push(&touchedCounts, v); + } + counts[v]++; + } + } + + for (i = 0; i < touchedCounts.size; ++i) { + int node = touchedCounts.data[i]; + if (counts[node] >= thresholdB) { + markB[node] = 1; + vec_push(&touchedB, node); + vec_push(&setB, node); + changed = 1; + } + counts[node] = 0; + } + vec_clear(&touchedCounts); + } + + if (!changed) { + break; + } + + ++iter; + if (iter > 64) { + break; + } + } + + if (valid && setA.size >= min_part && setB.size >= min_part) { + int sizeA = setA.size; + int sizeB = setB.size; + int64_t edges = 0; + int *idsA = (int *)malloc(sizeA * sizeof(int)); + int *idsB = (int *)malloc(sizeB * sizeof(int)); + uint64_t hashA; + uint64_t hashB; + + assert(idsA && idsB); + + for (i = 0; i < sizeA; ++i) { + int node = setA.data[i]; + edges += count_edges_to_mark(g, node, markB); + idsA[i] = node; + } + + for (i = 0; i < sizeB; ++i) { + idsB[i] = setB.data[i]; + } + + if (edges * 100 >= (int64_t)min_con * sizeA * sizeB) { + qsort(idsA, sizeA, sizeof(int), cmp_int); + qsort(idsB, sizeB, sizeof(int), cmp_int); + if (compare_sets(idsA, sizeA, idsB, sizeB) > 0) { + int *tmp = idsA; + int tmp_size = sizeA; + idsA = idsB; + idsB = tmp; + sizeA = sizeB; + sizeB = tmp_size; + } + hashA = hash_ids(idsA, sizeA); + hashB = hash_ids(idsB, sizeB); + if (!component_exists(out, hashA, hashB, idsA, sizeA, idsB, sizeB)) { + ComponentRecord rec; + rec.sizeA = sizeA; + rec.sizeB = sizeB; + rec.nodesA = idsA; + rec.nodesB = idsB; + rec.hashA = hashA; + rec.hashB = hashB; + component_list_push(out, rec); + } else { + free(idsA); + free(idsB); + } + } else { + free(idsA); + free(idsB); + } + } + +cleanup_seed: + for (i = 0; i < touchedA.size; ++i) { + markA[touchedA.data[i]] = 0; + } + for (i = 0; i < touchedB.size; ++i) { + markB[touchedB.data[i]] = 0; + } + for (i = 0; i < touchedCounts.size; ++i) { + counts[touchedCounts.data[i]] = 0; + } + } + + vec_free(&setA); + vec_free(&setB); + vec_free(&touchedA); + vec_free(&touchedB); + vec_free(&touchedCounts); + free(markA); + free(markB); + free(counts); +} + +static uint64_t make_edge_key(int a, int b) { + uint32_t ua = (uint32_t)a; + uint32_t ub = (uint32_t)b; + if (ua < ub) { + return ((uint64_t)ua << 32) | (uint64_t)ub; + } + return ((uint64_t)ub << 32) | (uint64_t)ua; +} + +static int cmp_edge_map(const void *a, const void *b) { + const EdgeMapEntry *ea = (const EdgeMapEntry *)a; + const EdgeMapEntry *eb = (const EdgeMapEntry *)b; + if (ea->key < eb->key) { + return -1; + } + if (ea->key > eb->key) { + return 1; + } + return 0; +} + +static int edge_map_find(const EdgeMapEntry *map, int size, int u, int v) { + uint64_t key = make_edge_key(u, v); + int lo = 0; + int hi = size - 1; + while (lo <= hi) { + int mid = lo + (hi - lo) / 2; + if (map[mid].key == key) { + return map[mid].index; + } + if (map[mid].key < key) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return -1; +} + +static void work_component_init(WorkComponent *comp) { + vec_init(&comp->nodesA); + vec_init(&comp->nodesB); + vec_init(&comp->edges); + comp->edge_count = 0; +} + +static void work_component_free(WorkComponent *comp) { + vec_free(&comp->nodesA); + vec_free(&comp->nodesB); + vec_free(&comp->edges); + comp->edge_count = 0; +} + +static int vector_contains(const IntVector *vec, int value) { + int lo = 0; + int hi = vec->size - 1; + while (lo <= hi) { + int mid = lo + (hi - lo) / 2; + int mid_val = vec->data[mid]; + if (mid_val == value) { + return 1; + } + if (mid_val < value) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return 0; +} + +static void vector_insert_sorted(IntVector *vec, int value) { + int lo = 0; + int hi = vec->size; + while (lo < hi) { + int mid = lo + (hi - lo) / 2; + if (vec->data[mid] < value) { + lo = mid + 1; + } else { + hi = mid; + } + } + vec_reserve(vec, vec->size + 1); + if (lo < vec->size) { + memmove(&vec->data[lo + 1], &vec->data[lo], (vec->size - lo) * sizeof(int)); + } + vec->data[lo] = value; + vec->size++; +} + +static EdgeInfo *build_edge_list(const Graph *g, int *out_count) { + int total = g->edge_count; + EdgeInfo *edges; + int idx = 0; + int u; + + if (total == 0) { + *out_count = 0; + return NULL; + } + + edges = (EdgeInfo *)malloc(total * sizeof(EdgeInfo)); + assert(edges); + + for (u = 0; u < g->node_count; ++u) { + int start = g->offset[u]; + int end = g->offset[u + 1]; + int i; + for (i = start; i < end; ++i) { + int v = g->neighbors[i]; + if (u < v) { + edges[idx].u = u; + edges[idx].v = v; + edges[idx].assigned = -1; + idx++; + } + } + } + + assert(idx == total); + *out_count = total; + return edges; +} + +static EdgeMapEntry *build_edge_map(const EdgeInfo *edges, int edge_count) { + EdgeMapEntry *map; + int i; + if (edge_count == 0) { + return NULL; + } + map = (EdgeMapEntry *)malloc(edge_count * sizeof(EdgeMapEntry)); + assert(map); + for (i = 0; i < edge_count; ++i) { + map[i].key = make_edge_key(edges[i].u, edges[i].v); + map[i].index = i; + } + qsort(map, edge_count, sizeof(EdgeMapEntry), cmp_edge_map); + return map; +} + +static int collect_edge_candidate(const EdgeInfo *edges, EdgeMapEntry *map, int map_size, int u, int v, int comp_idx, IntVector *list, int *assigned) { + int idx = edge_map_find(map, map_size, u, v); + if (idx < 0) { + return 0; + } + if (edges[idx].assigned == comp_idx) { + return 0; + } + if (edges[idx].assigned != -1) { + return 0; + } + vec_push(list, idx); + (*assigned)++; + return 1; +} + +static int evaluate_orientation(const EdgeInfo *edges, EdgeMapEntry *map, int map_size, WorkComponent *comp, int comp_idx, int nodeA, int nodeB, int min_con, IntVector *tmp_edges, int *addA, int *addB, int *num, int *den) { + int inA = vector_contains(&comp->nodesA, nodeA); + int inB = vector_contains(&comp->nodesB, nodeB); + int add_u = inA ? 0 : 1; + int add_v = inB ? 0 : 1; + int new_sizeA; + int new_sizeB; + int new_edges = 0; + int numerator; + + if (!inA && vector_contains(&comp->nodesB, nodeA)) { + return 0; + } + if (!inB && vector_contains(&comp->nodesA, nodeB)) { + return 0; + } + + new_sizeA = comp->nodesA.size + add_u; + new_sizeB = comp->nodesB.size + add_v; + if (new_sizeA == 0 || new_sizeB == 0) { + return 0; + } + + vec_clear(tmp_edges); + + if (add_u) { + int i; + for (i = 0; i < comp->nodesB.size; ++i) { + collect_edge_candidate(edges, map, map_size, nodeA, comp->nodesB.data[i], comp_idx, tmp_edges, &new_edges); + } + } + + if (add_v) { + int i; + for (i = 0; i < comp->nodesA.size; ++i) { + collect_edge_candidate(edges, map, map_size, comp->nodesA.data[i], nodeB, comp_idx, tmp_edges, &new_edges); + } + } + + if (add_u && add_v) { + collect_edge_candidate(edges, map, map_size, nodeA, nodeB, comp_idx, tmp_edges, &new_edges); + } + + if (!add_u && !add_v) { + collect_edge_candidate(edges, map, map_size, nodeA, nodeB, comp_idx, tmp_edges, &new_edges); + } + + numerator = comp->edge_count + new_edges; + *den = new_sizeA * new_sizeB; + *num = numerator; + + if ((int64_t)numerator * 100 < (int64_t)min_con * (*den)) { + return 0; + } + + *addA = add_u; + *addB = add_v; + return 1; +} + +static void assign_edges_to_component(EdgeInfo *edges, WorkComponent *comp, IntVector *edge_indices, int comp_idx) { + int i; + for (i = 0; i < edge_indices->size; ++i) { + int idx = edge_indices->data[i]; + edges[idx].assigned = comp_idx; + vec_push(&comp->edges, idx); + comp->edge_count++; + } +} + +static int compare_core_desc(const void *a, const void *b) { + const ComponentRecord *ra = (const ComponentRecord *)a; + const ComponentRecord *rb = (const ComponentRecord *)b; + int64_t ea = (int64_t)ra->sizeA * (int64_t)ra->sizeB; + int64_t eb = (int64_t)rb->sizeA * (int64_t)rb->sizeB; + if (ea > eb) { + return -1; + } + if (ea < eb) { + return 1; + } + return 0; +} + +static void build_core_components(EdgeInfo *edges, EdgeMapEntry *map, int map_size, int min_part_size, ComponentList *cores, WorkComponent **out_components, int *out_count) { + WorkComponent *components; + int comp_count = 0; + int capacity = 4; + int i; + + components = (WorkComponent *)malloc(capacity * sizeof(WorkComponent)); + assert(components); + + for (i = 0; i < cores->size; ++i) { + ComponentRecord *rec = &cores->data[i]; + int ok = 1; + int a; + if (rec->sizeA < min_part_size || rec->sizeB < min_part_size) { + continue; + } + for (a = 0; a < rec->sizeA && ok; ++a) { + int b; + for (b = 0; b < rec->sizeB; ++b) { + int idx = edge_map_find(map, map_size, rec->nodesA[a], rec->nodesB[b]); + if (idx < 0 || edges[idx].assigned != -1) { + ok = 0; + break; + } + } + } + if (!ok) { + continue; + } + if (comp_count == capacity) { + capacity *= 2; + components = (WorkComponent *)realloc(components, capacity * sizeof(WorkComponent)); + assert(components); + } + work_component_init(&components[comp_count]); + for (a = 0; a < rec->sizeA; ++a) { + vector_insert_sorted(&components[comp_count].nodesA, rec->nodesA[a]); + } + for (a = 0; a < rec->sizeB; ++a) { + vector_insert_sorted(&components[comp_count].nodesB, rec->nodesB[a]); + } + { + int ai; + for (ai = 0; ai < rec->sizeA; ++ai) { + int bi; + for (bi = 0; bi < rec->sizeB; ++bi) { + int idx = edge_map_find(map, map_size, rec->nodesA[ai], rec->nodesB[bi]); + assert(idx >= 0 && edges[idx].assigned == -1); + edges[idx].assigned = comp_count; + vec_push(&components[comp_count].edges, idx); + components[comp_count].edge_count++; + } + } + } + comp_count++; + } + + *out_components = components; + *out_count = comp_count; +} + +static void expand_components(EdgeInfo *edges, EdgeMapEntry *map, int map_size, WorkComponent *components, int comp_count, int edge_count, int min_con_value) { + IntVector tmp_edges; + IntVector best_edges; + int progress = 1; + + vec_init(&tmp_edges); + vec_init(&best_edges); + + while (progress) { + int e; + progress = 0; + for (e = 0; e < edge_count; ++e) { + int u; + int v; + int best_comp = -1; + int best_addA = 0; + int best_addB = 0; + int best_num = 0; + int best_den = 1; + int best_nodeA = -1; + int best_nodeB = -1; + int comp_idx; + + if (edges[e].assigned != -1) { + continue; + } + u = edges[e].u; + v = edges[e].v; + vec_clear(&best_edges); + + for (comp_idx = 0; comp_idx < comp_count; ++comp_idx) { + WorkComponent *comp = &components[comp_idx]; + int addA; + int addB; + int num; + int den; + if (evaluate_orientation(edges, map, map_size, comp, comp_idx, u, v, min_con_value, &tmp_edges, &addA, &addB, &num, &den)) { + int better = 0; + if ((int64_t)num * best_den > (int64_t)best_num * den) { + better = 1; + } else if ((int64_t)num * best_den == (int64_t)best_num * den && num > best_num) { + better = 1; + } + if (better) { + best_comp = comp_idx; + best_addA = addA; + best_addB = addB; + best_num = num; + best_den = den; + best_nodeA = u; + best_nodeB = v; + vec_clear(&best_edges); + vec_reserve(&best_edges, tmp_edges.size); + memcpy(best_edges.data, tmp_edges.data, tmp_edges.size * sizeof(int)); + best_edges.size = tmp_edges.size; + } + } + if (evaluate_orientation(edges, map, map_size, comp, comp_idx, v, u, min_con_value, &tmp_edges, &addA, &addB, &num, &den)) { + int better = 0; + if ((int64_t)num * best_den > (int64_t)best_num * den) { + better = 1; + } else if ((int64_t)num * best_den == (int64_t)best_num * den && num > best_num) { + better = 1; + } + if (better) { + best_comp = comp_idx; + best_addA = addA; + best_addB = addB; + best_num = num; + best_den = den; + best_nodeA = v; + best_nodeB = u; + vec_clear(&best_edges); + vec_reserve(&best_edges, tmp_edges.size); + memcpy(best_edges.data, tmp_edges.data, tmp_edges.size * sizeof(int)); + best_edges.size = tmp_edges.size; + } + } + } + + if (best_comp != -1) { + WorkComponent *comp = &components[best_comp]; + if (best_addA && !vector_contains(&comp->nodesA, best_nodeA)) { + vector_insert_sorted(&comp->nodesA, best_nodeA); + } + if (best_addB && !vector_contains(&comp->nodesB, best_nodeB)) { + vector_insert_sorted(&comp->nodesB, best_nodeB); + } + assign_edges_to_component(edges, comp, &best_edges, best_comp); + progress = 1; + } + } + } + + vec_free(&tmp_edges); + vec_free(&best_edges); +} + +static SolverContext * solver_run(int *pairs, int pair_count, int min_con_value, int min_part_size) { + SolverContext *ctx; + ComponentList cores; + ctx = (SolverContext *)calloc(1, sizeof(SolverContext)); + assert(ctx); + + graph_build(&ctx->graph, pairs, pair_count); + ctx->edges = build_edge_list(&ctx->graph, &ctx->edge_count); + ctx->edge_map = build_edge_map(ctx->edges, ctx->edge_count); + + component_list_init(&cores); + detect_components(&ctx->graph, 100, min_part_size, &cores); + if (cores.size > 1) { + qsort(cores.data, cores.size, sizeof(ComponentRecord), compare_core_desc); + } + build_core_components(ctx->edges, ctx->edge_map, ctx->edge_count, min_part_size, &cores, &ctx->components, &ctx->component_count); + expand_components(ctx->edges, ctx->edge_map, ctx->edge_count, ctx->components, ctx->component_count, ctx->edge_count, min_con_value); + validate_edge_ownership(ctx); + + component_list_free(&cores); + return ctx; +} + +static int * solver_build_result_array(const SolverContext *ctx) { + int total_sets = ctx->component_count * 2; + int total_ints = 1; + int *result; + int offset = 1; + int c; + + for (c = 0; c < ctx->component_count; ++c) { + total_ints += 1 + ctx->components[c].nodesA.size; + total_ints += 1 + ctx->components[c].nodesB.size; + } + + result = (int *)malloc(total_ints * sizeof(int)); + assert(result); + result[0] = total_sets; + + for (c = 0; c < ctx->component_count; ++c) { + int i; + result[offset++] = ctx->components[c].nodesA.size; + for (i = 0; i < ctx->components[c].nodesA.size; ++i) { + int node = ctx->components[c].nodesA.data[i]; + result[offset++] = ctx->graph.orig_ids[node]; + } + result[offset++] = ctx->components[c].nodesB.size; + for (i = 0; i < ctx->components[c].nodesB.size; ++i) { + int node = ctx->components[c].nodesB.data[i]; + result[offset++] = ctx->graph.orig_ids[node]; + } + } + + return result; +} + +static void solver_free_context(SolverContext *ctx) { + int i; + if (!ctx) { + return; + } + for (i = 0; i < ctx->component_count; ++i) { + work_component_free(&ctx->components[i]); + } + free(ctx->components); + free(ctx->edges); + free(ctx->edge_map); + graph_free(&ctx->graph); + free(ctx); +} + +static void print_components(const SolverContext *ctx) { + int c; + if (ctx->component_count == 0) { + printf("The number of components found is 0.\n"); + return; + } + printf("The number of components found is %d:\n", ctx->component_count); + for (c = 0; c < ctx->component_count; ++c) { + const WorkComponent *comp = &ctx->components[c]; + int i; + printf("Component %d:\n", c + 1); + printf(" { "); + for (i = 0; i < comp->nodesA.size; ++i) { + printf("%d ", ctx->graph.orig_ids[comp->nodesA.data[i]]); + } + printf("}\n"); + printf(" { "); + for (i = 0; i < comp->nodesB.size; ++i) { + printf("%d ", ctx->graph.orig_ids[comp->nodesB.data[i]]); + } + printf("}\n"); + + printf(" "); + for (i = 0; i < comp->nodesA.size; ++i) { + printf("%6d", ctx->graph.orig_ids[comp->nodesA.data[i]]); + } + printf("\n"); + { + int row; + for (row = 0; row < comp->nodesB.size; ++row) { + int nodeB = comp->nodesB.data[row]; + int col; + printf("%6d", ctx->graph.orig_ids[nodeB]); + for (col = 0; col < comp->nodesA.size; ++col) { + int nodeA = comp->nodesA.data[col]; + int idx = edge_map_find(ctx->edge_map, ctx->edge_count, nodeA, nodeB); + int mark = 0; + if (idx >= 0 && ctx->edges[idx].assigned == c) { + mark = 1; + } + printf("%6c", mark ? 'x' : ' '); + } + printf("\n"); + } + } + printf("\n"); + } +} + +int * compute_bipartite_subgraphs(int *pArray, int nSize, int min_con_value, int min_part_size, int fVerbose) { + SolverContext *ctx = solver_run(pArray, nSize, min_con_value, min_part_size); + int *result = solver_build_result_array(ctx); + if ( fVerbose ) print_components(ctx); + solver_free_context(ctx); + return result; +} + +/* + +static int * read_edge_list(const char *filename, int *out_size) { + FILE *fp = fopen(filename, "r"); + int capacity = 1024; + int count = 0; + int value; + int *data; + assert(fp); + + data = (int *)malloc(capacity * sizeof(int)); + assert(data); + while (fscanf(fp, "%d", &value) == 1) { + if (count == capacity) { + capacity *= 2; + data = (int *)realloc(data, capacity * sizeof(int)); + assert(data); + } + data[count++] = value; + } + fclose(fp); + assert((count & 1) == 0); + *out_size = count; + return data; +} + +static void usage(void) { + printf("Usage: bipart \n"); + printf(" or: bipart -C -M \n"); +} + +int main(int argc, char **argv) { + int min_con_value = 0; + int min_part_size = 0; + const char *filename = NULL; + int i; + int *edges_data; + int edge_count; + SolverContext *ctx; + clock_t start; + clock_t end; + double seconds; + int *result_array; + + if (argc == 4) { + min_con_value = atoi(argv[1]); + min_part_size = atoi(argv[2]); + filename = argv[3]; + } else { + for (i = 1; i < argc; ++i) { + if (strcmp(argv[i], "-C") == 0 && i + 1 < argc) { + min_con_value = atoi(argv[++i]); + } else if (strcmp(argv[i], "-M") == 0 && i + 1 < argc) { + min_part_size = atoi(argv[++i]); + } else if (argv[i][0] != '-') { + filename = argv[i]; + } + } + } + + if (!filename || min_con_value < 50 || min_con_value > 100 || min_part_size <= 0) { + usage(); + return 1; + } + + printf("Computing bi-partite components of the graph \"%s\".\n", filename); + printf("Assuming minimum connectivity %d%%.\n", min_con_value); + printf("Assuming minimum partition size %d nodes.\n\n", min_part_size); + + edges_data = read_edge_list(filename, &edge_count); + + start = clock(); + ctx = solver_run(edges_data, edge_count, min_con_value, min_part_size); + end = clock(); + seconds = (double)(end - start) / (double)CLOCKS_PER_SEC; + + printf("The problem is solved in %.2f sec.\n\n", seconds); + + print_components(ctx); + + result_array = solver_build_result_array(ctx); + free(result_array); + + solver_free_context(ctx); + free(edges_data); + return 0; +} +*/ + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + +ABC_NAMESPACE_IMPL_END +