import networkx as nx
import matplotlib.pyplot as plt

################################################################################
################################################################################
# Part 1: Bow-tie structure connectivity mining
################################################################################
################################################################################
# Read in the networks
D = nx.read_edgelist("out.link-dynamic-simplewiki.data", create_using=nx.DiGraph(), data=(("exists",int),("timestamp",int)))

################################################################################
# (a) Implement the forward-backward algorithm
# Inputs: G as a digraph, V as a list of root vertices
# Outputs:  SCC as all vertices reachable following out+in edges from V
#           IN as vertices reached following in edges from V and not in SCC
#           OUT as vertices reached following out edges from V and not in SCC
def FWBW(G, V):
  SCC = []
  IN = []
  OUT = []
  return (SCC, IN, OUT)


################################################################################
# (b) Perform a bow-tie decomposition
num_wcc = 0
num_scc = 0
num_in = 0
num_out = 0
num_tubes = 0
num_tendrils = 0

# TODO: identify large weak component, then use the FWBW algorithm to get the
#       number of vertices in each set of a bow-tie decomposition. Note, you
#       should only call your FWBW function to determine everything except
#       WCC. You can hard-code a root if you want to (though you actually don't 
#       need to do so).
# WCC: largest weak component in the input
# SCC, IN, OUT: defined as we did in class.
# Tubes: Vertices reached by IN and can reach OUT
# Tendrils: Everything else in large weak component

print("Num in WCC:", num_wcc)
print("Num in SCC:", num_scc)
print("Num in IN:", num_in)
print("Num in OUT:", num_out)
print("Num in tubes:", num_tubes)
print("Num in tendrils:", num_tendrils)

################################################################################
# Part 2: Real-world graph properties and measurements
################################################################################
################################################################################
# Read in the network
G = nx.read_edgelist("p2p-Gnutella31.data")

################################################################################
# (a) Create a biconnected components graph
# Vertices: each BiCC should become a single vertex. Recall that a single cut
#           edge is considered a biconnected component in this context.
# Edges:  Edges exist between vertices wherever the BiCCs that they represent
#         share a cut vertex. 
# E.g., BiCC A and BiCC B are connected via vertex v in the original graph - we
# would then create vertex A, vertex B, and edge (A, B) for our output graph

# TODO: compute a biconnectivity decomposition on the input G


# TODO: create all vertices and edges for G_bicc as defined above using the 
# biconnectivity decomposition.
G_bicc = nx.Graph()


print("G num verts:", G.order())
print("G num edges:", G.size())
print("G_bicc num verts:", G_bicc.order())
print("G_bicc num edges:", G_bicc.size())

################################################################################
# (b) Degree distributions
# Create and output the degree distributions for both G_bicc and G as plots.
# Use the methodology we've done in class. Are either power-law-ish looking?
# Next, explicitly estimate a power-law exponent using any method as well as
# output the Gini coefficient. See the PDF for links to methods to calculate 
# either.

# TODO: calculate degree distributions of the original graph as well as the
# constructed biconnectivity graph.


# TODO: output a plot containing both degree distributions


# TODO: Calculate an estimate for the power-law exponent as well as the Gini
# coefficient for both graphs.
G_pl = 0.0
G_bicc_pl = 0.0
G_gini = 0.0
G_bicc_gini = 0.0


print("G power-law exponent:", G_pl)
print("G_bicc power-law exponent:", G_bicc_pl)
print("G Gini coefficient:", G_gini)
print("G_bicc Gini coefficient:", G_bicc_gini)


################################################################################
# (c) Diameter 
# Compute the approximate diameters of both G and G_bicc using the BFS-based 
# algorithm defined in class. Use any root of your choosing. You can halt after
# the result doesn't increase for log(|V|) iterations.

# TODO: implement approximate diameter algorithm
# Inputs: G and graph, v as starting root.
def diameter(G, v):
  diameter = 0
  return diameter


# TODO: Actually call the above and output the result
G_dia = 0
G_bicc_dia = 0

print("G diameter:", G_dia)
print("G_bicc diameter:", G_bicc_dia)
