#include /* ------------------------------------------------------------------------------------------------------------------ */ /* */ // Internal deep structural validation walker checking color partitions static void c_Bipartite_DFS_Internal(c_Bipartite_t* self, const c_Graph_t* G, c_VertexId_t v) { self->marked[v] = C_TRUE; c_AdjList_t* list = c_Graph_GetAdjList((c_Graph_t*)G, v); if (!list) return; // Cache-friendly sequential sweep over flat neighbor array blocks for (c_size_t i = 0; i < list->size; i++) { c_VertexId_t w = (c_VertexId_t)list->array[i]; // Short-circuit search operations if an odd cycle has already been populated if (self->cycle.size > 0) return; if (!self->marked[w]) { self->edge_to[w] = v; self->color[w] = !self->color[v]; // Assign alternative inverted color mapping c_Bipartite_DFS_Internal(self, G, w); } // If neighbor w is already marked and shares the same color, an odd cycle is confirmed else if (self->color[w] == self->color[v]) { self->is_bipartite = C_FALSE; // Reconstruct the odd-length cycle path using back-tracing loops c_VertexIdList_t temp_stack; if (c_VertexIdList_Init(&temp_stack, 8, &self->allocator) != C_SUCCESS) return; c_size_t x = v; while (x != w && x != G->V) { c_VertexIdList_Append(&temp_stack, (c_uint_t)x); x = self->edge_to[x]; } c_VertexIdList_Append(&temp_stack, (c_uint_t)w); c_VertexIdList_Append(&temp_stack, (c_uint_t)v); // Close cycle track frame // Invert elements to maintain correct chronological routing direction for (c_size_t j = temp_stack.size; j > 0; j--) { c_uint_t val; c_VertexIdList_Get(&temp_stack, j - 1, &val); c_VertexIdList_Append(&self->cycle, val); } c_VertexIdList_Destroy(&temp_stack); return; } } } /* ------------------------------------------------------------------------------------------------------------------ */ /* */ c_err_t c_Bipartite_Init(c_Bipartite_t* self, const c_Graph_t* G, c_Allocator_t* allocator) { if (!self || !G ) return C_ERR_PARAM; self->allocator = allocator?*allocator:c_DefaultAllocator; self->is_bipartite = C_TRUE; self->V = G->V; self->marked = NULL; self->color = NULL; self->edge_to = NULL; if (c_UIntArray_Init(&self->cycle, 0, &self->allocator) != C_SUCCESS) { return C_ERR_NOMEM; } if (G->V == 0) return C_SUCCESS; self->marked = (c_bool_t*)c_Allocator_Alloc(&self->allocator, G->V * sizeof(*self->marked)); self->color = (c_bool_t*)c_Allocator_Alloc(&self->allocator, G->V * sizeof(*self->color)); self->edge_to = (c_size_t*)c_Allocator_Alloc(&self->allocator, G->V * sizeof(*self->edge_to)); if (!self->marked || !self->color || !self->edge_to) { c_Bipartite_Destroy(self); return C_ERR_NOMEM; } memset(self->marked, 0, G->V * sizeof(*self->marked)); memset(self->color, 0, G->V * sizeof(*self->color)); for (c_size_t i = 0; i < G->V; i++) self->edge_to[i] = G->V; // Sentinel definition // Scan all clusters within graph partitions sequentially for (c_VertexId_t v = 0; v < G->V; v++) { if (!self->marked[v]) { self->color[v] = C_FALSE; // Default baseline color option setting c_Bipartite_DFS_Internal(self, G, v); } } return C_SUCCESS; } void c_Bipartite_Destroy(c_Bipartite_t* self) { if (!self) return; if (self->marked) c_Allocator_Free(&self->allocator, self->marked); if (self->color) c_Allocator_Free(&self->allocator, self->color); if (self->edge_to) c_Allocator_Free(&self->allocator, self->edge_to); c_UIntArray_Destroy(&self->cycle); self->marked = NULL; self->color = NULL; self->edge_to = NULL; self->is_bipartite = C_FALSE; self->V = 0; } c_bool_t c_Bipartite_IsBipartite(const c_Bipartite_t* self) { return self ? self->is_bipartite : C_FALSE; } c_bool_t c_Bipartite_Color(const c_Bipartite_t* self, c_VertexId_t v) { if (!self || v >= self->V || !self->color) return C_FALSE; return self->color[v]; } const c_VertexIdList_t* c_Bipartite_Cycle(const c_Bipartite_t* self) { return self ? &self->cycle : NULL; }