<commit_before>/**
 * Copyright 2014 Open Connectome Project (http://openconnecto.me)
 * Written by <NAME> (<EMAIL>)
 *
 * This file is part of FlashGraph.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifdef PROFILER
#include <google/profiler.h>
#endif

#include "triangle_shared.h"

namespace {

struct undirected_runtime_data_t: public runtime_data_t
{
	vsize_t num_edge_reqs;
	vsize_t degree;

	undirected_runtime_data_t(vsize_t num_exist_triangles,
			vsize_t degree): runtime_data_t(degree, num_exist_triangles) {
		this->degree = degree;
		num_edge_reqs = 0;
	}
};

class undirected_triangle_vertex: public compute_vertex
{
	multi_func_value local_value;

	void inc_num_triangles(size_t num) {
		if (local_value.has_num_triangles())
			local_value.inc_num_triangles(num);
		else
			local_value.get_runtime_data()->num_triangles += num;
	}
public:
	undirected_triangle_vertex(vertex_id_t id): compute_vertex(id) {}

	int count_triangles(const page_vertex *v) const;

	int get_num_triangles() const {
		return local_value.get_num_triangles();
	}

	void run(vertex_program &prog) {
		vertex_id_t id = get_id();
		request_vertices(&id, 1);
	}

	void run(vertex_program &prog, const page_vertex &vertex) {
		if (vertex.get_id() == get_id())
			run_on_itself(prog, vertex);
		else
			run_on_neighbor(prog, vertex);
	}

	void run_on_itself(vertex_program &prog, const page_vertex &vertex);
	void run_on_neighbor(vertex_program &prog, const page_vertex &vertex);

	void run_on_message(vertex_program &prog, const vertex_message &msg) {
		inc_num_triangles(((count_msg &) msg).get_num());
	}

	void run_on_num_edges(vertex_id_t id, vsize_t num_edges);

	void destroy_runtime() {
		undirected_runtime_data_t *data
			= (undirected_runtime_data_t *) local_value.get_runtime_data();
		size_t num_curr_triangles = data->num_triangles;
		delete data;
		local_value.set_num_triangles(num_curr_triangles);
	}
};

void undirected_triangle_vertex::run_on_num_edges(vertex_id_t id, vsize_t num_edges)
{
	undirected_runtime_data_t *data
		= (undirected_runtime_data_t *) local_value.get_runtime_data();
	data->num_edge_reqs++;

	if ((num_edges < data->degree && id != get_id())
			|| (num_edges == data->degree && id < get_id())) {
		data->edges.push_back(id);
		data->num_required++;
	}
	if (data->num_edge_reqs == data->degree) {
		if (data->edges.empty()) {
			long ret = num_completed_vertices.inc(1);
			if (ret % 100000 == 0)
				printf("%ld completed vertices\n", ret);
			return;
		}
		data->finalize_init();
		// We now can request the neighbors.
		request_vertices(data->edges.data(), data->edges.size());
	}
}

void undirected_triangle_vertex::run_on_itself(vertex_program &prog,
		const page_vertex &vertex)
{
	assert(!local_value.has_runtime_data());

	long ret = num_working_vertices.inc(1);
	if (ret % 100000 == 0)
		printf("%ld working vertices\n", ret);
	// A vertex has to have in-edges and out-edges in order to form
	// a triangle. so we can simply skip the vertices that don't have
	// either of them.
	if (vertex.get_num_edges(edge_type::OUT_EDGE) == 0
			|| vertex.get_num_edges(edge_type::IN_EDGE) == 0) {
		long ret = num_completed_vertices.inc(1);
		if (ret % 100000 == 0)
			printf("%ld completed vertices\n", ret);

		destroy_runtime();
		return;
	}

	std::vector<vertex_id_t> edges(vertex.get_num_edges(edge_type::IN_EDGE));
	vertex.read_edges(edge_type::IN_EDGE, edges.data(), edges.size());

	local_value.set_runtime_data(new undirected_runtime_data_t(
				local_value.get_num_triangles(),
				vertex.get_num_edges(edge_type::IN_EDGE)));
	request_num_edges(edges.data(), edges.size());
}

void undirected_triangle_vertex::run_on_neighbor(vertex_program &prog,
		const page_vertex &vertex)
{
	assert(local_value.has_runtime_data());
	runtime_data_t *data = local_value.get_runtime_data();<commit_msg>[Graph]: fix a bug in undirected triangle counting.<commit_after>/**
 * Copyright 2014 Open Connectome Project (http://open<EMAIL>)
 * Written by <NAME> (<EMAIL>)
 *
 * This file is part of FlashGraph.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifdef PROFILER
#include <google/profiler.h>
#endif

#include "triangle_shared.h"

namespace {

struct undirected_runtime_data_t: public runtime_data_t
{
	vsize_t num_edge_reqs;
	vsize_t degree;

	undirected_runtime_data_t(vsize_t num_exist_triangles,
			vsize_t degree): runtime_data_t(degree, num_exist_triangles) {
		this->degree = degree;
		num_edge_reqs = 0;
	}
};

class undirected_triangle_vertex: public compute_vertex
{
	multi_func_value local_value;

	void inc_num_triangles(size_t num) {
		if (local_value.has_num_triangles())
			local_value.inc_num_triangles(num);
		else
			local_value.get_runtime_data()->num_triangles += num;
	}
public:
	undirected_triangle_vertex(vertex_id_t id): compute_vertex(id) {}

	int count_triangles(const page_vertex *v) const;

	int get_num_triangles() const {
		return local_value.get_num_triangles();
	}

	void run(vertex_program &prog) {
		vertex_id_t id = get_id();
		request_vertices(&id, 1);
	}

	void run(vertex_program &prog, const page_vertex &vertex) {
		if (vertex.get_id() == get_id())
			run_on_itself(prog, vertex);
		else
			run_on_neighbor(prog, vertex);
	}

	void run_on_itself(vertex_program &prog, const page_vertex &vertex);
	void run_on_neighbor(vertex_program &prog, const page_vertex &vertex);

	void run_on_message(vertex_program &prog, const vertex_message &msg) {
		inc_num_triangles(((count_msg &) msg).get_num());
	}

	void run_on_num_edges(vertex_id_t id, vsize_t num_edges);

	void destroy_runtime() {
		undirected_runtime_data_t *data
			= (undirected_runtime_data_t *) local_value.get_runtime_data();
		size_t num_curr_triangles = data->num_triangles;
		delete data;
		local_value.set_num_triangles(num_curr_triangles);
	}
};

void undirected_triangle_vertex::run_on_num_edges(vertex_id_t id, vsize_t num_edges)
{
	undirected_runtime_data_t *data
		= (undirected_runtime_data_t *) local_value.get_runtime_data();
	data->num_edge_reqs++;

	if ((num_edges < data->degree && id != get_id())
			|| (num_edges == data->degree && id < get_id())) {
		data->edges.push_back(id);
		data->num_required++;
	}
	if (data->num_edge_reqs == data->degree) {
		if (data->edges.empty()) {
			long ret = num_completed_vertices.inc(1);
			if (ret % 100000 == 0)
				printf("%ld completed vertices\n", ret);
			destroy_runtime();
			return;
		}
		data->finalize_init();
		// We now can request the neighbors.
		request_vertices(data->edges.data(), data->edges.size());
	}
}

void undirected_triangle_vertex::run_on_itself(vertex_program &prog,
		const page_vertex &vertex)
{
	assert(!local_value.has_runtime_data());

	long ret = num_working_vertices.inc(1);
	if (ret % 100000 == 0)
		printf("%ld working vertices\n", ret);
	// A vertex has to have in-edges and out-edges in order to form
	// a triangle. so we can simply skip the vertices that don't have
	// either of them.
	if (vertex.get_num_edges(edge_type::OUT_EDGE) == 0
			|| vertex.get_num_edges(edge_type::IN_EDGE) == 0) {
		long ret = num_completed_vertices.inc(1);
		if (ret % 100000 == 0)
			printf("%ld completed vertices\n", ret);
		return;
	}

	std::vector<vertex_id_t> edges(vertex.get_num_edges(edge_type::IN_EDGE));
	vertex.read_edges(edge_type::IN_EDGE, edges.data(), edges.size());

	local_value.set_runtime_data(new undirected_runtime_data_t(
				local_value.get_num_triangles(),
				vertex.get_num_edges(edge_type::IN_EDGE)));
	request_num_edges(edges.data(), edges.size());
}

void undirected_triangle_vertex::run_on_neighbor(vertex_program &prog,
		const page_vertex &vertex)
{
	assert(local_value.has_runtime_data());
	runtime_data_t *data = local_value.get_runtime_data();
{'fraction_non_alphanumeric': 0.11827586206896552, 'fraction_numerical': 0.008275862068965517, 'mean_word_length': 5.206134094151213, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 40, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3951754', 'n_tokens_mistral': 3277, 'n_tokens_neox': 2998, 'n_words': 686}