diff --git a/modules/CPMplugin/README.md b/modules/CPMplugin/README.md
new file mode 100644
index 000000000..dfba5628e
--- /dev/null
+++ b/modules/CPMplugin/README.md
@@ -0,0 +1,4 @@
+## CPM plugin
+
+This README supports Markdown, see [syntax](https://help.github.com/articles/markdown-basics/)
+
diff --git a/modules/CPMplugin/pom.xml b/modules/CPMplugin/pom.xml
new file mode 100644
index 000000000..72b9191f7
--- /dev/null
+++ b/modules/CPMplugin/pom.xml
@@ -0,0 +1,78 @@
+
+
+ 4.0.0
+
+ gephi-plugin-parent
+ org.gephi
+ 0.10.0
+
+
+ my.company
+ cpm-plugin
+ 1.0.0
+ nbm
+
+ CPM plugin
+
+
+
+ org.gephi
+ gephi-toolkit
+ 0.10.0
+
+
+ org.netbeans.api
+ org-openide-util-lookup
+ RELEASE160
+
+
+ org.gephi
+ utils-longtask
+ 0.10.0
+
+
+ org.gephi
+ graph-api
+ 0.10.0
+
+
+ org.gephi
+ statistics-api
+ 0.10.0
+
+
+
+
+
+
+ org.apache.netbeans.utilities
+ nbm-maven-plugin
+
+ Apache 2.0
+ Ebrahim Shami
+ qsomeis@gmail.com
+
+ https://github.com/qfewzz/gephi-plugins
+
+
+
+
+
+
+
+
+
+
+
+ oss-sonatype
+ oss-sonatype
+ https://oss.sonatype.org/content/repositories/snapshots/
+
+ true
+
+
+
+
+
+
diff --git a/modules/CPMplugin/src/main/java/com/plugin/CPM.java b/modules/CPMplugin/src/main/java/com/plugin/CPM.java
new file mode 100644
index 000000000..dbf65fb96
--- /dev/null
+++ b/modules/CPMplugin/src/main/java/com/plugin/CPM.java
@@ -0,0 +1,277 @@
+package com.plugin;
+
+
+import org.gephi.graph.api.*;
+import org.gephi.utils.longtask.spi.LongTask;
+import org.gephi.utils.progress.ProgressTicket;
+import org.openide.util.Lookup;
+
+import java.util.*;
+
+
+public class CPM implements org.gephi.statistics.spi.Statistics, LongTask {
+
+ private String report = "";
+ private boolean cancel = false;
+
+ private ProgressTicket progressTicket;
+ private int k = 0;
+ private Set> Cliques = new HashSet>();
+ GenQueue> Bk = new GenQueue>();
+
+ public class SortByID implements Comparator {
+
+ public int compare(Node n1, Node n2) {
+ if (n1.getStoreId() > n2.getStoreId()) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+ }
+
+ //
+ public Object getLastElement(final Collection c) {
+ /*
+ final Iterator itr = c.iterator();
+ Object lastElement = itr.next();
+ while (itr.hasNext()) {
+ lastElement = itr.next();
+ }
+ return lastElement;
+ */
+ return null;
+ }
+
+ class GenQueue {
+
+ private LinkedList list = new LinkedList();
+
+ public void enqueue(E item) {
+ list.addLast(item);
+ }
+
+ public E dequeue() {
+ return list.pollFirst();
+ }
+
+ public boolean hasItems() {
+ return !list.isEmpty();
+ }
+
+ public int size() {
+ return list.size();
+ }
+
+ public void addItems(GenQueue extends E> q) {
+ while (q.hasItems()) {
+ list.addLast(q.dequeue());
+ }
+ }
+ }
+ //
+
+ private Vector getLargerIndexNodes(Graph g, Node vi) {
+ Vector output = new Vector();
+ for (Node n : g.getNodes()) {
+
+ boolean b1 = n.getStoreId() > vi.getStoreId(),
+ b2 = g.getEdge(n, vi) != null,
+ b3 = g.getEdge(vi, n) != null;
+
+ if (b1 && (b2 || b3)) {
+ output.addElement(n);
+ }
+ }
+
+ return output;
+ }
+
+ private boolean checkBk1IsClique(Graph g, TreeSet Bk1) {
+ for (Node firstNode : Bk1) {
+ for (Node secondNode : Bk1) {
+ if (firstNode == secondNode) {
+ continue;
+ }
+ if (g.getEdge(firstNode, secondNode) == null &&
+ g.getEdge(secondNode, firstNode) == null) { //One edge is missing in the Bk+1 clique
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ Random r = new Random();
+
+ @Override
+ public void execute(GraphModel gm) {
+ /*for (int i = 0; i < 2; i++) {
+ Graph graph = gm.getGraphVisible();
+ Node node = gm.factory().newNode();
+
+ node.setLabel(String.valueOf(r.nextInt()));
+ node.setX(r.nextInt(500));
+ node.setY(r.nextInt(500));
+ node.setSize(10f);
+ graph.addNode(node);
+ }*/
+
+
+ Graph g = gm.getGraphVisible();
+
+ g.readLock();
+
+ //Firstly add each node as an item in Bk
+ int count = 0;
+ TreeSet tmp;
+
+ for (Node n : g.getNodes()) {
+ count++;
+ //Trick: if the node's degree is less than k-1, it can not involve in k-clique
+ if (g.getDegree(n) >= k - 1) {
+ tmp = new TreeSet(new SortByID());
+ tmp.add(n);
+ Bk.enqueue(tmp); //Add the B1 (node itself) to the queue
+ }
+ }
+
+ //Now start the iterative process for finding cliques
+ tmp = Bk.dequeue();
+
+ while (tmp != null) {
+ if (cancel) {
+ //Empty variables
+ Bk.list.clear();
+ tmp.clear();
+ Cliques.clear();
+ return;
+ }
+
+ //Search for Bk+1
+ Node vi = tmp.last(); //(Node) getLastElement(tmp);
+ Vector largerIndexes = getLargerIndexNodes(g, vi);
+
+ for (Node vj : largerIndexes) {
+ TreeSet Bk1 = new TreeSet(new SortByID());
+ Bk1.addAll(tmp); //Clone current Bk into Bk+1
+ Bk1.add(vj);
+ if (Bk1.size() <= getK() && checkBk1IsClique(g, Bk1)) {
+
+ if (Bk1.size() == getK()) { //A clique of size k found. Finish expanding this Bk+1 here.
+ Cliques.add(Bk1);
+ } else if (Bk1.size() < getK()) {
+ Bk.enqueue(Bk1); //k should be checked for finding cliques of size k.
+ } else { //Clique with larger size will be omitted.
+ report += "
Larger Clique Found. It should not be here
";
+ }
+ }
+ }
+
+ tmp = Bk.dequeue(); //Check next item
+ }
+ g.readUnlock();
+
+ //Algorithm finished.
+ //Write the output
+ report += "Clique Detection started. Nodes with " + (k - 1) + " edges will not be included.";
+ report += "
";
+ report += "Found Cliques of size " + getK() + ".
Now making new graph ...
Clearing old graph ...";
+
+ //edit the graph
+ g.clear();
+ report += " [+]
Creating new nodes ...";
+
+ gm = Lookup.getDefault().lookup(GraphController.class).getGraphModel();
+ int nID = 0;
+ Set nodes = new HashSet();
+
+ for (Set firstClique : Cliques) { //Create the nodes
+ Node firstNode = gm.factory().newNode(String.valueOf(nID++));
+ firstNode.setX(-500 + r.nextInt(1000));
+ firstNode.setY(-500 + r.nextInt(1000));
+ firstNode.setSize(8f);
+
+ String nodeLabel = "";
+
+ for (Node n : firstClique) {
+ nodeLabel += n.getLabel() + ",";
+ }
+
+ nodeLabel = nodeLabel.substring(0, nodeLabel.length() - 1); //remove last ,
+ firstNode.setLabel(nodeLabel);
+
+ nodes.add(firstNode);
+ }
+
+ report += "[+]
Detecting and creating the edges ...";
+ HashSet edges = new HashSet();
+
+ for (Node vi : nodes) {
+ for (Node vj : nodes) {
+ if ((vi != vj) && (getSharedNodes(vi, vj) == k - 1)) {
+ if (g.isDirected()) {
+ edges.add(gm.factory().newEdge(vi, vj, true));
+ } else {
+ edges.add(gm.factory().newEdge(vi, vj, false));
+ }
+ }
+ }
+ }
+
+ report += "[+]
Redrawing new graph ...";
+ for (Node n : nodes) {
+ g.addNode(n);
+ }
+
+
+ for (Edge e : edges) {
+ g.addEdge(e);
+ }
+
+ report += "[+]
Done!
Palla, Gergely, Imre Derényi, Illés Farkas, and Tamás Vicsek. \"Uncovering the overlapping community structure of complex networks in nature and society.\" Nature 435, no. 7043 (2005): 814-818";
+
+ }
+
+ private int getSharedNodes(Node vi, Node vj) {
+ String[] firstCliqueNodes = vi.getLabel().split(",");
+ String[] secondCliqueNodes = vj.getLabel().split(",");
+
+ int sharedNodes = 0;
+
+ for (String n1 : firstCliqueNodes) {
+ for (String n2 : secondCliqueNodes) {
+ if (n1.equals(n2)) {
+ sharedNodes++;
+ }
+ }
+ }
+
+ return sharedNodes;
+ }
+
+ @Override
+ public String getReport() {
+ return report;
+ }
+
+ @Override
+ public boolean cancel() {
+ cancel = true;
+ return true;
+ }
+
+ @Override
+ public void setProgressTicket(ProgressTicket pt) {
+ this.progressTicket = pt;
+ }
+
+ public int getK() {
+ return k;
+ }
+
+ public void setK(int k) {
+ this.k = k;
+ }
+}
diff --git a/modules/CPMplugin/src/main/java/com/plugin/CPMBuilder.java b/modules/CPMplugin/src/main/java/com/plugin/CPMBuilder.java
new file mode 100644
index 000000000..0a9a70702
--- /dev/null
+++ b/modules/CPMplugin/src/main/java/com/plugin/CPMBuilder.java
@@ -0,0 +1,25 @@
+package com.plugin;
+
+import org.gephi.statistics.spi.Statistics;
+import org.gephi.statistics.spi.StatisticsBuilder;
+import org.openide.util.lookup.ServiceProvider;
+
+@ServiceProvider (service = StatisticsBuilder.class)
+public class CPMBuilder implements org.gephi.statistics.spi.StatisticsBuilder {
+
+ @Override
+ public String getName() {
+ return "Clique Percolation Method";
+ }
+
+ @Override
+ public Statistics getStatistics() {
+ return new CPM();
+ }
+
+ @Override
+ public Class extends Statistics> getStatisticsClass() {
+ return CPM.class;
+ }
+
+}
diff --git a/modules/CPMplugin/src/main/java/com/plugin/CPMPanel.java b/modules/CPMplugin/src/main/java/com/plugin/CPMPanel.java
new file mode 100644
index 000000000..aa7d1d68d
--- /dev/null
+++ b/modules/CPMplugin/src/main/java/com/plugin/CPMPanel.java
@@ -0,0 +1,77 @@
+package com.plugin;/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+
+import javax.swing.*;
+import java.awt.*;
+
+
+public class CPMPanel extends JPanel {
+
+ JTextField kvalue;
+
+ @SuppressWarnings("unchecked")
+ public CPMPanel() {
+ //this.setLayout(null);
+ JLabel jXHeader1 = new JLabel();
+
+ jXHeader1.setText("Enter the value for k (clique size, ex: k = 3 will find triangualrs). Higher values of k may take more time for computation. This algorithm is NP-Hard, so use it carefully."); // NOI18N
+// jXHeader1.setTitle("Clique Detector");
+
+ JLabel label = new JLabel("Enter value of k here:");
+ label.setFont(new Font("Times New Roman", Font.ROMAN_BASELINE, 13));
+ this.add(label);
+ kvalue = new JTextField();
+ this.add(kvalue);
+ Insets insets = this.getInsets();
+
+ Dimension size = label.getPreferredSize();
+ label.setBounds(20 + insets.left, 30 + insets.top, size.width, size.height);
+
+ Dimension size1 = kvalue.getPreferredSize();
+ kvalue.setBounds(20 + insets.left, 130 + insets.top, size1.width + 20, size1.height);
+
+ GroupLayout layout = new GroupLayout(this);
+ this.setLayout(layout);
+
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+ .addComponent(jXHeader1, GroupLayout.DEFAULT_SIZE, 536, Short.MAX_VALUE)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(label)
+ .addContainerGap(354, Short.MAX_VALUE))
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(kvalue)
+ .addContainerGap(382, Short.MAX_VALUE))
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jXHeader1, GroupLayout.PREFERRED_SIZE, 80, GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(label)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(kvalue)
+ .addContainerGap(187, Short.MAX_VALUE))
+ );
+ }
+
+ public int getK() {
+ int i = 0;
+ try {
+ i = Integer.parseInt(kvalue.getText());
+ } catch (Exception ex) {
+ return 0;
+ }
+ return i;
+ }
+
+ public void setK(int k) {
+ this.kvalue.setText(String.valueOf(k));
+ }
+}
diff --git a/modules/CPMplugin/src/main/java/com/plugin/CPMUI.java b/modules/CPMplugin/src/main/java/com/plugin/CPMUI.java
new file mode 100644
index 000000000..15e9009ac
--- /dev/null
+++ b/modules/CPMplugin/src/main/java/com/plugin/CPMUI.java
@@ -0,0 +1,71 @@
+package com.plugin;/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+import org.gephi.statistics.spi.Statistics;
+import org.gephi.statistics.spi.StatisticsUI;
+import org.openide.util.lookup.ServiceProvider;
+
+import javax.swing.*;
+
+@ServiceProvider(service = StatisticsUI.class)
+public class CPMUI implements StatisticsUI {
+
+ private CPMPanel panel;
+ private CPM myCliqueDetector;
+
+ @Override
+ public JPanel getSettingsPanel() {
+ panel = new CPMPanel();
+ return panel;
+ }
+
+ @Override
+ public void setup(Statistics ststcs) {
+ this.myCliqueDetector = (CPM) ststcs;
+ if (panel != null) {
+ panel.setK(myCliqueDetector.getK());
+ }
+ }
+
+ @Override
+ public void unsetup() {
+ if (panel != null) {
+ myCliqueDetector.setK(panel.getK());
+ }
+ panel = null;
+ }
+
+ @Override
+ public Class extends Statistics> getStatisticsClass() {
+ return CPM.class;
+ }
+
+ @Override
+ public String getValue() {
+ return null;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "Clique Percolation Method";
+ }
+
+ @Override
+ public String getShortDescription() {
+ return "Clique Percolation Method implementaion in gephi";
+ }
+
+ @Override
+ public String getCategory() {
+ return CATEGORY_NETWORK_OVERVIEW;
+ }
+
+ @Override
+ public int getPosition() {
+ return 800;
+ }
+
+}
diff --git a/modules/CPMplugin/src/main/nbm/manifest.mf b/modules/CPMplugin/src/main/nbm/manifest.mf
new file mode 100644
index 000000000..93ac04c07
--- /dev/null
+++ b/modules/CPMplugin/src/main/nbm/manifest.mf
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+OpenIDE-Module-Name: CPM plugin
+OpenIDE-Module-Short-Description: Clique Percolation Method implementation
+OpenIDE-Module-Long-Description: Simple algorithm for detecting overlapping communities based on node centeric method.
+OpenIDE-Module-Display-Category: Tool
diff --git a/pom.xml b/pom.xml
index afb8f2bf5..0e5165d3c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,6 +12,7 @@
+ modules/CPMplugin