import networkx as nx
import matplotlib.pyplot as plt 
from itertools import chain

# read in an visualize out dataset
G = nx.read_edgelist("out.moreno_zebra_zebra", comments="%")
nx.draw(G, with_labels=True)
plt.show()

# look at the connectivity structure using NetworkX functions
nx.is_connected(G)
nx.number_connected_components(G)
C = nx.connected_components(G)
for c in C:
	print(c)

nx.node_connected_component(G, '14')

# take the largest connected component and create a new graph from it
G1 = G.subgraph(nx.node_connected_component(G, '14'))
G1.order()
G1.size()
nx.is_connected(G1)
nx.draw(G1)
plt.show()

# load a directed graph (no multi-edge)
D = nx.read_edgelist("lec03.data", create_using=nx.DiGraph())
D.order()
D.size()
nx.draw(D, with_labels=True)
plt.show()

# load a directed graph w/multi-edges
D = nx.read_edgelist("lec03.data", create_using=nx.MultiDiGraph())
D.order()
D.size()
nx.draw(D, with_labels=True)
plt.show()

for u in D.neighbors('bob'):
	print(u)

for u in D.successors('bob'):
	print(u)

for u in D.predecessors('bob'):
	print(u)

# directed equivalent to 'all neighbors' for undirected graphs
for u in chain(D.predecessors('bob'), D.successors('bob')):
	print(u)

# out connectivity algorithm from lecture 1
def connectivity(G):
	counter = 0
	S = {}
	for v in G.nodes:
		S[v] = counter
		counter += 1
		
	updates = 1
	while updates > 0:
		updates = 0
		for v in G.nodes:
			for u in G.neighbors(v):
				if S[u] > S[v]:
					S[v] = S[u]
					updates += 1
	
	return S


# out connectivity algorithm from lecture 1
def weak_connectivity(G):
	counter = 0
	S = {}
	for v in G.nodes:
		S[v] = counter
		counter += 1
		
	updates = 1
	while updates > 0:
		updates = 0
		for v in G.nodes:
			for u in chain(G.successors(v),G.predecessors(v)):
				if S[u] > S[v]:
					S[v] = S[u]
					updates += 1
	
	return S

S = connectivity(G)
S = weak_connectivity(D)

# load in directed graph with edge data
D = nx.read_edgelist("out.link-dynamic-simplewiki", create_using=nx.DiGraph(), data=(("exists",int),("timestamp",int)))
D.order()
D.size()

# instead of visualizing the graph, visualize the degree distribution
out_degrees = {}
in_degrees = {}
for v in D.nodes():
	d = D.out_degree(v)
	if d not in out_degrees:
		out_degrees[d] = 1
	else:
		out_degrees[d] += 1
	d = D.in_degree(v)
	if d not in in_degrees:
		in_degrees[d] = 1
	else:
		in_degrees[d] += 1


sorted_out_degrees = sorted(out_degrees.items())
sorted_in_degrees = sorted(in_degrees.items())
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot([k for (k,v) in sorted_out_degrees], [v for (k, v) in sorted_out_degrees])
ax.plot([k for (k,v) in sorted_in_degrees], [v for (k, v) in sorted_in_degrees])
ax.set_xscale('log')
ax.set_yscale('log')
plt.show()


# call functions to look at the directivity structure of our graph
nx.is_strongly_connected(D)
nx.is_weakly_connected(D)
nx.number_strongly_connected_components(D)
nx.number_weakly_connected_components(D)

SCC = nx.strongly_connected_components(D)

def bfs_successors(G, root):
	levels = {}
	for v in G.nodes():
		levels[v] = -1
		
	levels[root] = 0
	level = 0
	
	Q = []
	Q.append(root)
	while len(Q) > 0:
		level += 1
		cur_num = len(Q)
		for i in range(0, cur_num):
			v = Q.pop()
			for u in G.successors(v):
				if levels[u] == -1:
					levels[u] = level
					Q.append(u)
					
	return levels

L = bfs_successors(D, '1')
O = []
for v in D.nodes():
	if L[v] != -1:
		O.append(v)
		
