diff --git a/run.sh b/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..35ab6c2bda16374ddf95079a87083576d36f94c0
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+cp $1 build/graph.graph
+python3 src/part1/unified.py
+java -classpath src/part2/GraphCompress/out/production/GraphCompress Main
+python3 src/part3/encode.py
+# python3 src/part4/draw.py
diff --git a/src/part1/slashburn.py b/src/part1/slashburn.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc05b2f0b109ec48106f58351d24ff260e371d95
--- /dev/null
+++ b/src/part1/slashburn.py
@@ -0,0 +1,60 @@
+import networkx as nx
+from math import sqrt
+
+def add(l,x, key=lambda x:x):
+    m = min([key(x) for x in l])
+    if key(x) <= m:
+        return
+    for i in range(len(l)):
+        if key(l[i]) == m:
+            l[i] = x
+            return
+
+
+def k_max(l,k, key=lambda x:x):
+    if len(l) <= k:
+        return l
+    ans = []
+    for x in l:
+        if len(ans) < k:
+            ans.append(x)
+        else:
+            add(ans,x,key)
+    return ans
+
+def k_struct(H, k, adaptative=False):
+    G = H.copy()
+    while G.order() > k:
+        while G and nx.is_connected(G):
+            sorted_degree = sorted(G.degree, key = lambda x: x[1], reverse=True)
+            k_center = sorted_degree[:k]
+            # k_center = k_max(G.degree, k, key = lambda x: x[1])
+            center = []
+            for v, d in k_center:
+                center.append(v)
+                G.remove_node(v)
+            print(">>> removing center (%d nodes), %d remaining" % (k, G.order()))
+            yield set(center)
+        if G:
+            sorted_comp = sorted(list(nx.connected_components(G)), key = len)
+            bcc = sorted_comp[-1]
+            spokes = set()
+            for component in sorted_comp[:-1]:
+                spokes = spokes.union(component)
+            yield spokes
+
+            # G.remove_nodes_from([n for n in G if n not in set(bcc)])
+            to_remove = [n for n in G if n in spokes]
+            G.remove_nodes_from(to_remove)
+            print(">>> removing spokes (%d nodes), %d remaining" % (len(to_remove), G.order()))
+            if adaptative:
+                k = max(10,G.order()//1000)
+
+def k_slashburn(*args, **kwargs):
+    last = []
+    for s in k_struct(*args, **kwargs):
+        last.append(s)
+    return last
+
+def slashburn(G):
+    return k_slashburn(G, max(10,G.order()//1000), adaptative=True)
diff --git a/src/part1/unified.py b/src/part1/unified.py
new file mode 100644
index 0000000000000000000000000000000000000000..9c89ff951cf3f07255c67e0e5b5cb2f39e7752fc
--- /dev/null
+++ b/src/part1/unified.py
@@ -0,0 +1,103 @@
+import sys, os
+
+# Disable print
+sys.stdout = open(os.devnull, 'w')
+
+import networkx as nx
+from cdlib import algorithms, readwrite
+from json import loads
+from math import sqrt
+
+import matplotlib.cm as cm
+import matplotlib.pyplot as plt
+import community as community_louvain
+import metis
+
+from slashburn import k_slashburn, slashburn
+
+# Restore print
+sys.stdout = sys.__stdout__
+
+PATH = "build/"
+GRAPH = PATH + "graph.graph"
+OUT = PATH + "struct.txt"
+
+def myprint(f,x):
+    print(x)
+    f.write(x + '\n')
+
+def list_list_to_string(ll, spec="fc"):
+    return '\n'.join(map(lambda l: spec + " " + ' '.join(map(str, l)), ll))
+
+def map_list_list(f, ll):
+    return [list(map(f,l)) for l in ll]
+
+def prune_commu(ll, k):
+    return [l for l in ll if len(l) >= k]
+
+def partition_to_commu(p):
+    n = 1+max(p)
+    commu = [[] for _ in range(n)]
+    for i, u in enumerate(p):
+        commu[u].append(i)
+    return commu
+
+########################
+
+def decorate_cdlib(algo):
+    def f(G):
+        return loads(algo(G).to_json())["communities"]
+    return f
+
+@decorate_cdlib
+def big_clam(G):
+    return algorithms.big_clam(G)
+
+@decorate_cdlib
+def louvain(G):
+    return algorithms.louvain(G)
+
+@decorate_cdlib
+def kcut(G):
+    return algorithms.kcut(G)
+
+def sqrt_metis(G):
+    k = int(sqrt(G.order()/2))
+    _, parts = metis.part_graph(G, k)
+    return partition_to_commu(parts)
+
+def trivial(G):
+    return [list(range(G.order()))]
+
+def unified():
+    with open(GRAPH, 'r') as f:
+        lines = f.readlines()
+    G = nx.parse_edgelist(lines, nodetype=int, delimiter=",", data=False)
+    G = nx.convert_node_labels_to_integers(G, label_attribute="old")
+    threshold = 1
+    '''
+    Each function in flist takes a Graph G created with networkx such that V = [0..n-1] for some n,
+    and returns a list of communities. A community is a list of numbers in [0..n-1]
+    '''
+    flist = [
+        (slashburn, [], "sl"),
+        (big_clam, [], "bc"),
+        (louvain, [], "lv"),
+        (sqrt_metis, [], "mt"),
+    ]
+    print("Part 1")
+    i = 0
+    with open(OUT, 'w') as f:
+        for algo,args,spec in flist:
+            commu = algo(G, *args)
+            pruned = prune_commu(commu, threshold)
+            reverse = map_list_list(lambda x: G.nodes[x]["old"], pruned)
+            formated = list_list_to_string(reverse, spec=spec)
+            f.write(formated + '\n')
+            i += 1
+            print("> Algorithm %d/%d done" % (i, len(flist)))
+
+def main():
+    unified()
+
+main()
diff --git a/src/part2/GraphCompress/.idea/.gitignore b/src/part2/GraphCompress/.idea/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..26d33521af10bcc7fd8cea344038eaaeb78d0ef5
--- /dev/null
+++ b/src/part2/GraphCompress/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/src/part2/GraphCompress/.idea/.name b/src/part2/GraphCompress/.idea/.name
new file mode 100644
index 0000000000000000000000000000000000000000..0c1a9a637b731cbe38c4402e3509ddb2adeab829
--- /dev/null
+++ b/src/part2/GraphCompress/.idea/.name
@@ -0,0 +1 @@
+GraphCompress
\ No newline at end of file
diff --git a/src/part2/GraphCompress/.idea/misc.xml b/src/part2/GraphCompress/.idea/misc.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e0844bc7be0a8ca77dcee74dfc4059ee4a9d4a1d
--- /dev/null
+++ b/src/part2/GraphCompress/.idea/misc.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/out" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/src/part2/GraphCompress/.idea/modules.xml b/src/part2/GraphCompress/.idea/modules.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6596e23472d0cb37680807ac92557c4aaf26855b
--- /dev/null
+++ b/src/part2/GraphCompress/.idea/modules.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/GraphCompress.iml" filepath="$PROJECT_DIR$/GraphCompress.iml" />
+    </modules>
+  </component>
+</project>
diff --git a/src/part2/GraphCompress/.idea/uiDesigner.xml b/src/part2/GraphCompress/.idea/uiDesigner.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2b63946d5b31084bbb7dda418ceb3d75eb686373
--- /dev/null
+++ b/src/part2/GraphCompress/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Palette2">
+    <group name="Swing">
+      <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
+      </item>
+      <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
+      </item>
+      <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
+        <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
+        <initial-values>
+          <property name="text" value="Button" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="RadioButton" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="CheckBox" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="Label" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+          <preferred-size width="200" height="200" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+          <preferred-size width="200" height="200" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
+      </item>
+      <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
+          <preferred-size width="-1" height="20" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
+      </item>
+      <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
+      </item>
+    </group>
+  </component>
+</project>
\ No newline at end of file
diff --git a/src/part2/GraphCompress/.idea/vcs.xml b/src/part2/GraphCompress/.idea/vcs.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f1f0a09377b046f00df0ea33544740f0f8688a4c
--- /dev/null
+++ b/src/part2/GraphCompress/.idea/vcs.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$/../../../../.." vcs="Git" />
+    <mapping directory="$PROJECT_DIR$/../../../../../" vcs="Git" />
+    <mapping directory="$PROJECT_DIR$/../../.." vcs="Git" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/src/part2/GraphCompress/GraphCompress.iml b/src/part2/GraphCompress/GraphCompress.iml
new file mode 100644
index 0000000000000000000000000000000000000000..0181b11c83c3476dfca7f9230ed6ce514c93123d
--- /dev/null
+++ b/src/part2/GraphCompress/GraphCompress.iml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/20.1.0/annotations-20.1.0.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+  </component>
+</module>
\ No newline at end of file
diff --git a/src/part2/GraphCompress/out/production/GraphCompress/Block.class b/src/part2/GraphCompress/out/production/GraphCompress/Block.class
new file mode 100644
index 0000000000000000000000000000000000000000..0a3c7b1c93607eaa37af8a033e81436a44c2566c
Binary files /dev/null and b/src/part2/GraphCompress/out/production/GraphCompress/Block.class differ
diff --git a/src/part2/GraphCompress/out/production/GraphCompress/CompressMath.class b/src/part2/GraphCompress/out/production/GraphCompress/CompressMath.class
new file mode 100644
index 0000000000000000000000000000000000000000..3d105f92d4750af7187f08f6bac09f5d28e90515
Binary files /dev/null and b/src/part2/GraphCompress/out/production/GraphCompress/CompressMath.class differ
diff --git a/src/part2/GraphCompress/out/production/GraphCompress/Edge.class b/src/part2/GraphCompress/out/production/GraphCompress/Edge.class
new file mode 100644
index 0000000000000000000000000000000000000000..b54c57c9621ba73839a3a6b4bd9561ac191e9dd9
Binary files /dev/null and b/src/part2/GraphCompress/out/production/GraphCompress/Edge.class differ
diff --git a/src/part2/GraphCompress/out/production/GraphCompress/FileIO.class b/src/part2/GraphCompress/out/production/GraphCompress/FileIO.class
new file mode 100644
index 0000000000000000000000000000000000000000..1d9c352bded5941d01ee80b1810d290b33df5afc
Binary files /dev/null and b/src/part2/GraphCompress/out/production/GraphCompress/FileIO.class differ
diff --git a/src/part2/GraphCompress/out/production/GraphCompress/Graph.class b/src/part2/GraphCompress/out/production/GraphCompress/Graph.class
new file mode 100644
index 0000000000000000000000000000000000000000..d8fdfeee77e0c12474b959d8ce6f7dbf01ed5d77
Binary files /dev/null and b/src/part2/GraphCompress/out/production/GraphCompress/Graph.class differ
diff --git a/src/part2/GraphCompress/out/production/GraphCompress/Main.class b/src/part2/GraphCompress/out/production/GraphCompress/Main.class
new file mode 100644
index 0000000000000000000000000000000000000000..562dcf712292d0ff6b837971221fe8d9730c9bd0
Binary files /dev/null and b/src/part2/GraphCompress/out/production/GraphCompress/Main.class differ
diff --git a/src/part2/GraphCompress/out/production/GraphCompress/Partition.class b/src/part2/GraphCompress/out/production/GraphCompress/Partition.class
new file mode 100644
index 0000000000000000000000000000000000000000..98163a67d7dfad29adb6f5061f5f14044404e7b8
Binary files /dev/null and b/src/part2/GraphCompress/out/production/GraphCompress/Partition.class differ
diff --git a/src/part2/GraphCompress/out/production/GraphCompress/Positional.class b/src/part2/GraphCompress/out/production/GraphCompress/Positional.class
new file mode 100644
index 0000000000000000000000000000000000000000..d2551c43bdb924e5d6f84b074f78209eb8d5c2e9
Binary files /dev/null and b/src/part2/GraphCompress/out/production/GraphCompress/Positional.class differ
diff --git a/src/part2/GraphCompress/out/production/GraphCompress/SparseMatrix$1.class b/src/part2/GraphCompress/out/production/GraphCompress/SparseMatrix$1.class
new file mode 100644
index 0000000000000000000000000000000000000000..a4e264c6fbed5b8a237bc0ecf1c062f804e0752a
Binary files /dev/null and b/src/part2/GraphCompress/out/production/GraphCompress/SparseMatrix$1.class differ
diff --git a/src/part2/GraphCompress/out/production/GraphCompress/SparseMatrix.class b/src/part2/GraphCompress/out/production/GraphCompress/SparseMatrix.class
new file mode 100644
index 0000000000000000000000000000000000000000..3cb94e18a860849a539e7198f03971b28051aa1c
Binary files /dev/null and b/src/part2/GraphCompress/out/production/GraphCompress/SparseMatrix.class differ
diff --git a/src/part2/GraphCompress/out/production/GraphCompress/Structure.class b/src/part2/GraphCompress/out/production/GraphCompress/Structure.class
new file mode 100644
index 0000000000000000000000000000000000000000..721a8aadf2c419762861144c6bb27637ac6adb84
Binary files /dev/null and b/src/part2/GraphCompress/out/production/GraphCompress/Structure.class differ
diff --git a/src/part2/GraphCompress/src/Block.java b/src/part2/GraphCompress/src/Block.java
new file mode 100644
index 0000000000000000000000000000000000000000..bffd4d4612f8f16cc047bf202337be9099305a25
--- /dev/null
+++ b/src/part2/GraphCompress/src/Block.java
@@ -0,0 +1,88 @@
+import java.util.ArrayList;
+import java.util.Objects;
+
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+
+public class Block implements Positional{
+    private ArrayList<Edge> edgeList;
+    private int row;
+    private int column;
+    private int rowSize;
+    private int columnSize;
+
+    private int diskSpace;
+
+    public Block(ArrayList<Edge> edgeList, int row, int column, int rowSize, int columnSize) {
+        this.edgeList = edgeList;
+        this.row = max(row, column);
+        this.column =  min(row, column);
+        this.rowSize = rowSize;
+        this.columnSize = columnSize;
+        this.diskSpace = -1 ;
+    }
+
+    public boolean isDiagonal() {
+        return (this.row == this.column);
+    }
+
+    public boolean isEmpty() {
+        return (this.edgeList.size() == 0);
+    }
+
+    public void addEdge(Edge edge){
+        //if (!this.edgeList.contains((edge)))
+            this.edgeList.add(edge);
+        this.diskSpace = -1 ;
+    }
+
+    public ArrayList<Edge> getEdgeList() {
+        return edgeList;
+    }
+
+    public int getRow() {
+        return row;
+    }
+
+    public int getColumn() {
+        return column;
+    }
+
+    public int getDiskSpace(){
+        if (this.diskSpace == -1){
+            if (isDiagonal())
+                this.diskSpace = CompressMath.arithmeticSub(this.edgeList.size(), (long) this.rowSize * (long) this.columnSize / 2);
+            else
+                this.diskSpace = CompressMath.arithmeticSub(this.edgeList.size(), (long) this.rowSize * (long) this.columnSize);
+        }
+        return this.diskSpace;
+    }
+
+    public Block clone(){
+        return new Block(new ArrayList<Edge>(edgeList), row, column, rowSize, columnSize);
+    }
+
+    @Override
+    public String toString() {
+        String answer = "row: " + row + ", col: " + column + "; ";
+        answer += edgeList.size() + "/(" + rowSize + "*" + columnSize + ")";
+        /*
+        for (Edge edge : edgeList)
+            answer += edge.toString();
+         */
+        return  answer;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        Block block = (Block) o;
+        return row == block.row && column == block.column && rowSize == block.rowSize && columnSize == block.columnSize && edgeList.equals(block.edgeList);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(edgeList, row, column, rowSize, columnSize);
+    }
+}
diff --git a/src/part2/GraphCompress/src/CompressMath.java b/src/part2/GraphCompress/src/CompressMath.java
new file mode 100644
index 0000000000000000000000000000000000000000..8ebd7a8f193efc4893b35580afcf4104d9afcb06
--- /dev/null
+++ b/src/part2/GraphCompress/src/CompressMath.java
@@ -0,0 +1,60 @@
+import static java.lang.Math.log;
+import static java.lang.Math.min;
+import static java.lang.Math.ceil;
+
+public class CompressMath {
+    private static final double log2 = log(2);
+
+    public static int choose(int n, int k){
+        if (0 <= k && k <= n){
+            int nToken = 1;
+            int kToken = 1;
+            int bound = min(k, n-k);
+            for (int t=1; t <= bound; t++){
+                nToken *= n;
+                kToken *= t;
+                n -= 1;
+            }
+            return nToken / kToken;
+        }
+        return 0;
+    }
+
+    public static double entropy(double x){
+        return -(1-x)*log(1-x)/log2 - x*log(x)/log2;
+    }
+
+    public static double entropy(int p, long q){
+        double x = (double) p / (double) q;
+        return -(1-x)*log(1-x)/log2 - x*log(x)/log2;
+    }
+
+    public static double intSize(double n){
+        if (n < 1){
+            return 1;
+        }
+        return ceil(log(n)/log2);
+    }
+
+    public static int arithmetic(int n,int m){
+        // n = nbVertices
+        // m = nbEdges
+        double nCast = (double) n;
+        double mCast = (double) m;
+        double nSquare = (double) (n*n);
+        double x = 2 * mCast / nSquare;
+        int term1 = (int) (ceil(entropy(x) * nSquare / 2));
+        int term2 = (int) (intSize(nCast));
+        int term3 = (int) (2*intSize(intSize(nCast)));
+        return term1 + term2 + term3;
+    }
+
+    public static int arithmeticSub(int ones, long total){
+        double oneCast = (double) ones;
+        int term1 = (int) (ceil(entropy(ones, total) * total));
+        int term2 = (int) (intSize(oneCast));
+        int term3 = (int) (2*intSize(intSize(oneCast)));
+        return term1 + term2 + term3;
+    }
+
+}
diff --git a/src/part2/GraphCompress/src/Edge.java b/src/part2/GraphCompress/src/Edge.java
new file mode 100644
index 0000000000000000000000000000000000000000..c9460c5348dad377458b74e7709b1b85a945193b
--- /dev/null
+++ b/src/part2/GraphCompress/src/Edge.java
@@ -0,0 +1,78 @@
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Objects;
+import java.util.Scanner;
+
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+
+public class Edge {
+    private final int u;
+    private final int v;
+
+    public Edge(int u, int v) {
+        this.u = max(u,v);
+        this.v = min(u,v);
+    }
+
+    public int getU() {
+        return u;
+    }
+
+    public int getV() {
+        return v;
+    }
+
+    public static ArrayList<Edge> parseString(String edgeString){
+        ArrayList<Edge> edgeList = new ArrayList<Edge>();
+        String[] separated = edgeString.split(";");
+        for (String edge : separated){
+            String[] cut = edge.split(",");
+            int u = Integer.parseInt(cut[0]);
+            int v = Integer.parseInt(cut[1]);
+            edgeList.add(new Edge(u,v));
+        }
+        return edgeList;
+    }
+
+    public static ArrayList<Edge> readFile(String filename){
+        ArrayList<Edge> edgeList = new ArrayList<Edge>();
+        try {
+            File myObj = new File(filename);
+            Scanner myReader = new Scanner(myObj);
+            while (myReader.hasNextLine()) {
+                String data = myReader.nextLine();
+                if (!data.isEmpty()) {
+                    String[] cut = data.split(",");
+                    int u = Integer.parseInt(cut[0]);
+                    int v = Integer.parseInt(cut[1]);
+                    edgeList.add(new Edge(u, v));
+                }
+            }
+            myReader.close();
+        } catch (FileNotFoundException e) {
+            System.out.println("An error occurred.");
+            e.printStackTrace();
+        }
+        return edgeList;
+    }
+
+    @Override
+    public String toString() {
+        return "(" + u + "," + v + ')';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        Edge edge = (Edge) o;
+        return u == edge.u && v == edge.v;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(u, v);
+    }
+}
diff --git a/src/part2/GraphCompress/src/FileIO.java b/src/part2/GraphCompress/src/FileIO.java
new file mode 100644
index 0000000000000000000000000000000000000000..a71135010c26f2757c6390327894959b16fcb3f1
--- /dev/null
+++ b/src/part2/GraphCompress/src/FileIO.java
@@ -0,0 +1,22 @@
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+public class FileIO {
+    public static void write(String data, String pathname) {
+        File file = new File(pathname);
+        FileWriter f = null;
+        try {
+            f = new FileWriter(file);
+            f.write(data);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            try {
+                f.close();
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+}
diff --git a/src/part2/GraphCompress/src/Graph.java b/src/part2/GraphCompress/src/Graph.java
new file mode 100644
index 0000000000000000000000000000000000000000..16442b06d12321c0cfb7fdc5e961299b427ddf0c
--- /dev/null
+++ b/src/part2/GraphCompress/src/Graph.java
@@ -0,0 +1,143 @@
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+
+public class Graph {
+    private final int nbVertices;
+    private Partition partition;
+    private SparseMatrix<Block> blockMatrix;
+    private Partition copyPartition;
+
+    public Graph(int nbVertices){
+        this.nbVertices = nbVertices;
+        this.partition = null;
+        this.blockMatrix = null;
+        this.copyPartition = null;
+    }
+    public Graph(ArrayList<Edge> edgeList, int nbVertices){
+        Block block = new Block(edgeList, 0, 0, nbVertices, nbVertices);
+        Structure[] structList = new Structure[0];
+
+        this.nbVertices = nbVertices;
+        this.partition = new Partition(nbVertices, structList, this);
+        this.blockMatrix = new SparseMatrix<Block>();
+        this.copyPartition = null;
+        this.blockMatrix.scale(1);
+        this.blockMatrix.set(0,0,block);
+    }
+
+    public static Graph fromFile(String filename){
+        ArrayList<Edge> edgeList = Edge.readFile(filename);
+        int n = 0;
+        for(Edge edge: edgeList){
+            n = max(n, max(edge.getU(), edge.getV()));
+        }
+        return new Graph(edgeList, n+1);
+    }
+
+    /*
+    The graph clones creates a deep copy of the partition and a shallow copy of the blockMatrix.
+    Hence, methods like addStructure operates on the new partition and on the original blockMatrix.
+    This is not a problem, as SparseMatrix<E> is given undo operations to undo those method calls.
+     */
+    public Graph clone(){
+        Graph graph = new Graph(this.nbVertices);
+        graph.partition = this.partition.clone(graph);
+        graph.blockMatrix = this.blockMatrix;
+        graph.copyPartition = null;
+        return graph;
+    }
+
+    public void addStructure(Structure structure){
+        this.partition.addStructure(structure);
+    }
+
+    @Override
+    public String toString() {
+        String answer = this.partition.toString() + "\n";
+        String matrix = this.blockMatrix.toString();
+        return answer + matrix;
+    }
+
+    private void deleteBlock(int row, int column){
+        this.blockMatrix.delete(row, column);
+    }
+
+    private void addEdge(Edge edge){
+        int u = edge.getU();
+        int v = edge.getV();
+        int row = this.partition.getVertexAssignment(u);
+        int column = this.partition.getVertexAssignment(v);
+        if(this.blockMatrix.hasValue(row, column)){
+            this.blockMatrix.get(row, column).addEdge(edge);
+        } else {
+            ArrayList<Edge> edgeList = new ArrayList<Edge>();
+            edgeList.add(edge);
+            Block block = new Block(edgeList, row, column, partition.getPartSize(u), partition.getPartSize(v));
+            this.blockMatrix.set(row, column, block);
+        }
+    }
+
+    public void split(int oldRowIndex, int targetSize) {
+        int n = this.blockMatrix.getSize();
+        int columnIndex;
+        this.blockMatrix.scale(targetSize);
+        for (columnIndex = 0; columnIndex < n; columnIndex++) {
+            if (blockMatrix.hasValue(oldRowIndex, columnIndex)) {
+                Block block = blockMatrix.get(oldRowIndex, columnIndex);
+                this.deleteBlock(oldRowIndex, columnIndex);
+                for (Edge edge : block.getEdgeList()) {
+                    this.addEdge(edge);
+                }
+            }
+        }
+    }
+
+    public int diskSpace(){
+        int answer = 0;
+        int nbBlock = 0;
+        for (Block block : this.blockMatrix) {
+            answer += block.getDiskSpace();
+            nbBlock++;
+        }
+        answer += CompressMath.arithmetic(this.blockMatrix.getSize(), nbBlock);
+        return answer;
+    }
+
+    public void undoInit(){
+        this.blockMatrix.undoInit();
+        this.copyPartition = this.partition.clone(this);
+    }
+
+    public void undoAction(){
+        this.blockMatrix.undoAction();
+        this.partition = this.copyPartition;
+        this.copyPartition = null;
+    }
+
+    public boolean check(ArrayList<Edge> edgeList){
+        int i = 0;
+        int x, y;
+        for(Block block: this.blockMatrix)
+            for(Edge edge: block.getEdgeList()) {
+                i++;
+                x = this.partition.getVertexAssignment(edge.getU());
+                y = this.partition.getVertexAssignment(edge.getV());
+                if (block.getRow() != max(x,y)) {return false;}
+                if (block.getColumn() != min(x,y)) {return false;}
+                if(!edgeList.contains(edge)) {return false;}
+            }
+        return (i == edgeList.size());
+    }
+
+    public Partition getPartition() {
+        return partition;
+    }
+
+    public SparseMatrix<Block> getBlockMatrix() {
+        return blockMatrix;
+    }
+}
diff --git a/src/part2/GraphCompress/src/Main.java b/src/part2/GraphCompress/src/Main.java
new file mode 100644
index 0000000000000000000000000000000000000000..5fff43decefda6edc08a0b55273c566072fc7703
--- /dev/null
+++ b/src/part2/GraphCompress/src/Main.java
@@ -0,0 +1,58 @@
+import java.io.File;
+import java.sql.SQLOutput;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+
+public class Main {
+    public static void greedy(){
+        String path;
+        path = "build/";
+        String graphFilename = path + "graph.graph";
+        String structFilename = path + "struct.txt";
+        String partFilename = path + "partition.txt";
+        Graph graph = Graph.fromFile(graphFilename);
+        ArrayList<Structure> structList = Structure.listFromFile(structFilename);
+        int i, n;
+        n = structList.size();
+        Structure struct;
+        ArrayList<Integer> bestStructList= new ArrayList<Integer>();
+        int bestCompression = 10*graph.diskSpace();
+        int bestStructure;
+        int currentDiskSpace;
+        System.out.println("Part 2");
+        System.out.println("> Without structure, estimate size is " + graph.diskSpace() + " bits");
+        int j = 0;
+        while (true) {
+            bestStructure = -1;
+            for (i = 0; i < n; i++) {
+                if (!bestStructList.contains(i)) {
+                    struct = structList.get(i);
+                    graph.undoInit();
+                    graph.addStructure(struct);
+                    currentDiskSpace = graph.diskSpace();
+                    if (currentDiskSpace < bestCompression) {
+                        bestCompression = currentDiskSpace;
+                        bestStructure = i;
+                    }
+                    graph.undoAction();
+                }
+            }
+            if(bestStructure >= 0){
+                bestStructList.add(bestStructure);
+                graph.addStructure(structList.get(bestStructure));
+                System.out.println("> Selecting structure " + bestStructure + ", estimate size is " + graph.diskSpace() + " bits");
+                j++;
+            } else {
+                break;
+            }
+        }
+
+        FileIO.write(graph.getPartition().toFormattedString(), partFilename);
+
+    }
+
+    public static void main(String[] args) {
+        greedy();
+    }
+}
\ No newline at end of file
diff --git a/src/part2/GraphCompress/src/Partition.java b/src/part2/GraphCompress/src/Partition.java
new file mode 100644
index 0000000000000000000000000000000000000000..d24546e492576bf8e0da157a37a65ece213191c1
--- /dev/null
+++ b/src/part2/GraphCompress/src/Partition.java
@@ -0,0 +1,99 @@
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class Partition {
+    private int nbVertices;
+    private int[] vertexAssignmentList;
+    private ArrayList<Structure> partList; //assert: partList is a partition of [nbVertices] into Structures
+    private Graph graph;
+
+    /*
+    k in partList.get(i).getList() <=> vertexAssignmentList[k] == i
+     */
+
+
+    public Partition(){
+        this.nbVertices = 0;
+        this.vertexAssignmentList = null;
+        this.partList = null;
+        this.graph = null;
+    }
+    public Partition(int nbVertices, Structure[] structureList, Graph graph) {
+        this.graph = graph;
+        this.nbVertices = nbVertices;
+        this.partList = new ArrayList<Structure>();
+        this.vertexAssignmentList = new int[nbVertices];
+
+        this.partList.add(new Structure(nbVertices));
+        for(Structure structure: structureList){
+            this.addStructure(structure);
+        }
+    }
+
+    public Partition clone(Graph graph){
+        int n = this.vertexAssignmentList.length;
+        Partition partition = new Partition();
+        partition.nbVertices = this.nbVertices;
+        partition.vertexAssignmentList = new int[n];
+        partition.partList = new ArrayList<Structure>();
+        partition.graph = graph;
+        System.arraycopy(this.vertexAssignmentList, 0, partition.vertexAssignmentList, 0, n);
+        for(Structure structure: this.partList)
+            partition.partList.add(structure.clone());
+        return partition;
+    }
+
+    public void addStructure(Structure structure){
+        int n = this.partList.size();
+        int i, m;
+        Structure part;
+        ArrayList<Integer> todo = new ArrayList<Integer>();
+        for(i=0; i<n; i++){
+            part = this.partList.get(i);
+            Structure intersection = new Structure(0);
+            Structure difference = new Structure(0);
+            part.split(structure, intersection, difference);
+            if (!intersection.isEmpty()){
+                this.partList.set(i, intersection);
+                if (!difference.isEmpty()){
+                    todo.add(i);
+                    this.partList.add(difference);
+                }
+            }
+        }
+        m = this.partList.size();
+        for(i=n; i<m; i++){
+            part = this.partList.get(i);
+            for(int vertex: part.getList()){
+                this.vertexAssignmentList[vertex] = i;
+            }
+        }
+        for(int j=0; j<todo.size(); j++){
+            this.graph.split(todo.get(j),m);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return Arrays.toString(vertexAssignmentList) + "\n" + partList.toString();
+    }
+
+    public String toFormattedString() {
+        String ans = "";
+        int n = this.partList.size();
+        int i;
+        Structure part;
+        for(i=0; i<n; i++) {
+            part = this.partList.get(i);
+            ans += part.toFormattedString() + "\n";
+        }
+        return ans;
+    }
+
+    public int getVertexAssignment(int i) { return vertexAssignmentList[i]; }
+    public int getPartSize(int i) { return partList.get(vertexAssignmentList[i]).size(); }
+
+    public ArrayList<Structure> getPartList() {
+        return partList;
+    }
+}
diff --git a/src/part2/GraphCompress/src/Positional.java b/src/part2/GraphCompress/src/Positional.java
new file mode 100644
index 0000000000000000000000000000000000000000..97192734f21d2b3d88170807c01181e95f0847af
--- /dev/null
+++ b/src/part2/GraphCompress/src/Positional.java
@@ -0,0 +1,4 @@
+public interface Positional {
+    public int getRow();
+    public int getColumn();
+}
diff --git a/src/part2/GraphCompress/src/SparseMatrix.java b/src/part2/GraphCompress/src/SparseMatrix.java
new file mode 100644
index 0000000000000000000000000000000000000000..0ae4989e5068f5f0a565c554838c236471f1bdb3
--- /dev/null
+++ b/src/part2/GraphCompress/src/SparseMatrix.java
@@ -0,0 +1,98 @@
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+
+public class SparseMatrix<E extends Positional> implements Iterable<E> {
+    private int size;
+    private ArrayList<ArrayList<E>> matrix;
+    private int undoSize;
+    private ArrayList<E> undoList;
+
+    public SparseMatrix() {
+        this.size = 0;
+        this.undoSize = -1;
+        this.matrix = new ArrayList<ArrayList<E>>();
+        this.undoList = new ArrayList<E>();
+    }
+
+    // Guaranty that the matrix has size at least n
+    // No guaranty on the exact size
+    public void scale(int n){
+        this.size = n;
+        while(this.matrix.size() < n)
+            this.matrix.add(new ArrayList<E>(Collections.nCopies(1 + this.matrix.size(), null)));
+    }
+
+    public E get(int i, int j){
+        return this.matrix.get(max(i,j)).get(min(i,j));
+    }
+    public boolean hasValue(int i, int j){return this.get(max(i,j), min(i,j)) != null;}
+
+    public void set(int i, int j, E element){
+        this.matrix.get(max(i,j)).set(min(i,j), element);
+    }
+    public void delete(int i, int j){
+        this.undoList.add(this.get(i,j));
+        this.set(i,j, null);
+    }
+
+    public int getSize() {
+        return size;
+    }
+
+    public void undoInit(){
+        this.undoSize = this.size;
+        this.undoList = new ArrayList<E>();
+    }
+
+    public void undoAction(){
+        // Undo actions in reverse order
+        for(int i = this.undoList.size() - 1; i>= 0; i--){
+            E element = this.undoList.get(i);
+            this.set(element.getRow(), element.getColumn(), element);
+        }
+        for(int i = this.size; i > this.undoSize; i--)
+            this.matrix.remove(i-1);
+        this.size = this.undoSize;
+        this.undoSize = -1;
+    }
+
+
+    public Iterator<E> iterator() {
+        return new Iterator<E> () {
+
+            private int i = 0;
+            private int j = -1;
+
+            public boolean hasNext() {
+                while(true){
+                    j++;
+                    if (j>i) {
+                        j = 0;
+                        i++;
+                        if (i >= SparseMatrix.this.matrix.size())
+                            return false;
+                    }
+                    if (SparseMatrix.this.hasValue(i,j))
+                        return true;
+                }
+            }
+
+            public E next() {
+                if (!SparseMatrix.this.hasValue(i,j))
+                    System.out.println("Erreur impossible");
+                return SparseMatrix.this.get(i,j);
+            }
+        };
+    }
+    @Override
+    public String toString() {
+        String answer = "";
+        for(E element: this)
+            answer += element.toString() + "\n";
+        return answer;
+    }
+}
diff --git a/src/part2/GraphCompress/src/Structure.java b/src/part2/GraphCompress/src/Structure.java
new file mode 100644
index 0000000000000000000000000000000000000000..acdb87706d1e8d8fa4f3ef2f6756fc90a85eb158
--- /dev/null
+++ b/src/part2/GraphCompress/src/Structure.java
@@ -0,0 +1,121 @@
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Scanner;
+
+import static java.lang.Integer.parseInt;
+
+public class Structure {
+    private int[] list;
+
+    public Structure(int[] list) {
+        this.list = list;
+    }
+
+    public Structure(int n) {
+        this.list = new int[n];
+        for (int i=0; i<n; i++)
+            this.list[i]=i;
+    }
+
+    public Structure(ArrayList<Integer> list) {
+        int n = list.size();
+        this.list = new int[n];
+        for(int i=0; i<n; i++){
+            this.list[i] = list.get(i);
+        }
+    }
+
+    public Structure(String input){
+        String inputNoNewLine = input.replace("\n", "");
+        String inputNoFC = inputNoNewLine.substring(3);
+        String[] inputSplit = inputNoFC.split(" ");
+        int n = inputSplit.length;
+        this.list = new int[n];
+        int i = 0;
+        for (String vertex: inputSplit) {
+            this.list[i] = parseInt(vertex);
+            i++;
+        }
+    }
+
+    public int[] getList() {
+        return list;
+    }
+    public int size() {return list.length;}
+
+    public void setListFromArray(ArrayList<Integer> list) {
+        int n = list.size();
+        this.list = new int[n];
+        for (int i = 0; i < n; i++) {
+            this.list[i] = list.get(i);
+        }
+    }
+
+    public boolean contains(int vertex){
+        for (int u: this.getList()){
+            if (u == vertex)
+                return true;
+        }
+        return false;
+    }
+
+    public void split(Structure structure, Structure sIntersection, Structure sDifference){
+        ArrayList<Integer> intersection = new ArrayList(); // this inter structure
+        ArrayList<Integer> difference = new ArrayList();  // this minus structure
+        for (int vertex: this.getList()){
+            if (structure.contains(vertex)){
+                intersection.add(vertex);
+            } else {
+                difference.add(vertex);
+            }
+        }
+        sIntersection.setListFromArray(intersection);
+        sDifference.setListFromArray(difference);
+    }
+
+    public boolean isEmpty(){
+        return this.list.length == 0;
+    }
+
+    public static ArrayList<Structure> listFromFile(String filename){
+        ArrayList<Structure> structList = new ArrayList<Structure>();
+        try {
+            File myObj = new File(filename);
+            Scanner myReader = new Scanner(myObj);
+            while (myReader.hasNextLine()) {
+                String data = myReader.nextLine();
+                if (!data.isEmpty()) {
+                    structList.add(new Structure(data));
+                }
+            }
+            myReader.close();
+        } catch (FileNotFoundException e) {
+            System.out.println("An error occurred.");
+            e.printStackTrace();
+        }
+        return structList;
+    }
+
+    @Override
+    public String toString() {
+        return "Structure{" + Arrays.toString(list) + '}';
+    }
+
+    public String toFormattedString(){
+        String ans = "pt";
+        for (int k : this.list){
+            ans += " " + Integer.toString(k);
+        }
+        return ans;
+    }
+
+    public Structure clone(){
+        int n = this.size();
+        int[] list = new int[n];
+        System.arraycopy(this.list, 0, list, 0, n);
+        return new Structure(list);
+    }
+
+}
diff --git a/src/part3/encode.py b/src/part3/encode.py
new file mode 100644
index 0000000000000000000000000000000000000000..9084aa1f6b14bf2f07cfc1775fce2cee78f5ccc2
--- /dev/null
+++ b/src/part3/encode.py
@@ -0,0 +1,233 @@
+from math import ceil
+
+PATH = "build/"
+THRESHOLD = 8192
+
+def listMap(f,x): return list(map(f,x))
+
+def keySplit(s):
+    pq = s.split()
+    return (int(pq[0]), int(pq[1]))
+
+def stringToStruct(string):
+    string.replace('\n', '')
+    struct = string[3:]
+    vertexList = []
+    for vertexString in struct.split(' '):
+        if vertexString: vertexList.append(int(vertexString))
+    return vertexList
+
+def splitLongString(string, threshold):
+    count = 0
+    partIndex = [0]
+    for i in range(len(string)):
+        if string[i] == '1':
+            count += 1
+        if count > threshold:
+            partIndex.append(i)
+            count -= threshold
+    partIndex.append(len(string))
+    partList = []
+    for i in range(len(partIndex)-1):
+        start = partIndex[i]
+        end = partIndex[i+1]
+        partList.append(string[start:end])
+    return partList
+
+def getEdge(fileGraph):
+    with open(fileGraph, 'r') as f:
+        edgeList = f.readlines()
+    def stringToCouple(x):
+        [i,j,w] = x.replace('\n', '').split(',')
+        ni = min(int(i), int(j))
+        nj = max(int(i), int(j))
+        return(int(ni),int(nj))
+    edgeList = listMap(stringToCouple, edgeList)
+    return(edgeList)
+
+def getPartition(filePartition):
+    with open(filePartition, 'r') as f:
+        partList = f.readlines()
+    partList = listMap(stringToStruct, partList)
+    return(partList)
+
+def writeBinary(binary_string, file):
+    data = bytes(int(binary_string[i:i+8], 2) for i in range(0, len(binary_string), 8))
+    with open(file, 'wb') as f:
+        f.write(data)
+
+############
+
+def choose(n, k):
+    """
+    A fast way to calculate binomial coefficients by Andrew Dalke.
+    See http://stackoverflow.com/questions/3025162/statistics-combinations-in-python
+    """
+    if 0 <= k <= n:
+        ntok = 1
+        ktok = 1
+        for t in range(1, min(k, n - k) + 1):
+            ntok *= n
+            ktok *= t
+            n -= 1
+        if k % 100 == 0:
+            print(">>>", n, k)
+        return ntok // ktok
+    else:
+        return 0
+
+def ar_encode(list):
+    sum = 0
+    k = 1
+    for i, x in enumerate(list[::-1]):
+        if x in [1, '1']:
+            sum += choose(i,k)
+            k += 1
+    return sum
+
+def encodeBigInt(n):
+    a = bin(n)[2:]
+    b = bin(len(a))[2:]
+    n = ceil(len(b)/4)
+    c = '1' * n + '0'
+    return c + b.zfill(4*n) + a
+
+def encodeSmallInt(n):
+    a = bin(n)[2:]
+    n = ceil(len(a)/4)
+    b = '0' * n + '1'
+    return b + a.zfill(4*n)
+
+def encodeInt(n):
+    if n <= 2**25:
+        return encodeSmallInt(n)
+    return encodeBigInt(n)
+
+############
+
+class GraphEncode():
+    def __init__(self, edgeList, partList):
+        self.edgeList = edgeList
+        self.partList = partList
+        self.order = sum(listMap(len,partList))
+        self.vertexAssign = [] # if i = vertexAssign[vertex], then vertex is in partList[i]
+        self.vertexRank = [] # if i = vertexAssign[vertex] and j = vertexRank[vertex], then partList[i][j] == vertex
+        self.blocDict = {}
+
+        self._computeVertexPOV()
+        print("> Graph imported")
+        for edge in self.edgeList:
+            self._addEdge(edge)
+
+    def _computeVertexPOV(self):
+        n = self.order
+        self.vertexAssign = [0]*n
+        self.vertexRank = [0]*n
+        for i,part in enumerate(self.partList):
+            for j,vertex in enumerate(part):
+                self.vertexAssign[vertex] = i
+                self.vertexRank[vertex] = j
+
+    def _addEdge(self, edge):
+        part1 = self.vertexAssign[edge[0]]
+        part2 = self.vertexAssign[edge[1]]
+        if part1 > part2:
+            part1, part2 = part2, part1
+            edge = edge[::-1]
+        key = str(part1) + " " + str(part2)
+        if key in self.blocDict:
+            self.blocDict[key].append(edge)
+        else:
+            self.blocDict[key] = [edge]
+
+    def getPartSize(self, part):
+        return len(self.partList[part])
+
+    def getSortedBlocList(self):
+        return sorted(map(lambda x: (keySplit(x[0]), x[1]), self.blocDict.items()))
+
+    def metaMatrixToString(self):
+        n = len(self.partList)
+        print(">>> Data made of %d bits (%d ones)" % ((n*(n-1))//2, len(self.blocDict)))
+        ans = [[0] * n for _ in range(n)]
+        for key, value in self.blocDict.items():
+            p,q = keySplit(key)
+            ans[p][q] = 1
+        s = ""
+        for k,l in enumerate(ans):
+            for b in l[k:]:
+                s += str(b)
+        return s
+
+    def blockToString(self, i, j):
+        assert i <= j
+        key = str(i) + " " + str(j)
+        if key not in self.blocDict:
+            return ""
+        p = self.getPartSize(i)
+        q = self.getPartSize(j)
+        print(">>> Data made of %d bits (%d ones)" % (p*q, len(self.blocDict[key])))
+        ans = [[0] * q for _ in range(p)]
+        for edge in self.blocDict[key]:
+            x = self.vertexRank[edge[0]]
+            y = self.vertexRank[edge[1]]
+            ans[x][y] = 1
+        s = ""
+        if i == j:
+            for k,l in enumerate(ans):
+                for b in l[k+1:]:
+                    s += str(b)
+        else:
+            for l in ans:
+                for b in l:
+                    s += str(b)
+        return s
+
+    def encodeBlock(self, i, j):
+        s = ""
+        for part in splitLongString(self.blockToString(i, j), THRESHOLD):
+            s += encodeInt(ar_encode(part))
+        return s
+
+    def encodeMetaMatrix(self):
+        s = ""
+        for part in splitLongString(self.metaMatrixToString(), THRESHOLD):
+            s += encodeInt(ar_encode(part))
+        return s
+
+    def encode(self):
+        s = ""
+        s += encodeInt(self.order)
+        s += encodeInt(len(self.partList))
+        for i in range(len(self.partList)):
+            s += encodeInt(self.getPartSize(i))
+        print("> Part lenghs encoded")
+        s += encodeInt(len(self.blocDict))
+        s += self.encodeMetaMatrix()
+        print("> Meta-matrix encoded")
+        l = self.getSortedBlocList()
+        for kv in l:
+            value = kv[1]
+            s += encodeInt(len(value))
+        print("> Header encoded, current size is %d bits" % len(s))
+        for i, kv in enumerate(l):
+            key = kv[0]
+            s += self.encodeBlock(*key)
+            print("> Bloc %d/%d encoded, current size is %d bits" % (i, len(l), len(s)))
+        return s
+
+############
+
+def main():
+    print("Part 3")
+    fileGraph = PATH + "graph.graph"
+    filePartition = PATH + "partition.txt"
+    fileOut = PATH + "compress.out"
+    edgeList = getEdge(fileGraph)
+    partList = getPartition(filePartition)
+    graph = GraphEncode(edgeList, partList)
+    output = graph.encode()
+    writeBinary(output, fileOut)
+    print("> Encoding done in %d bits" % len(output))
+
+main()