Sfoglia il codice sorgente

add support for Reddit dataset

Minh Tùng Trần 3 anni fa
parent
commit
43a2dbc9ce

+ 6 - 6
code/simulation/client/src/main/java/client/Client.java

@@ -19,16 +19,16 @@ public class Client {
     private final int serverPort = 1234;
     private int lineCount = 0;
     private volatile int lineDone = 0;
-    private static List<Long> ids = new ArrayList<>();
+    private static List<String> ids = new ArrayList<String>();
     private static Random rd = new Random();
 
-    private static final int clientNum = 1000000;
-    private static final int coverMessageNum = 0;//clientNum * 5;
+    private static final int clientNum = 1000000; // reddit-1638157
+    private static final int coverMessageNum = clientNum * 1000;//clientNum * 5;
     private static final int roundLength = 1800;
     private static final int roundNum = 30 * 24 * 3600 / roundLength;
     private static int coverMessageNumPerRound = coverMessageNum / roundNum;
 
-    private static final String dataPath = "/data/sorted_tweets_random_" + clientNum + ".txt";
+    private static final String dataPath = "/data/filtered_posts.txt"; //"/data/sorted_tweets_random_" + clientNum + ".txt";
     private static final String logPath = "/data/logs/";
 
     private PrintWriter out;
@@ -132,7 +132,7 @@ public class Client {
 
     class ClientThread implements Runnable {
         private final Object lock = new Object();
-        private long id;
+        private String id;
         private long timestamp;
         private String data;
         private String clientName;
@@ -141,7 +141,7 @@ public class Client {
         ClientThread(String data) throws ParseException {
             JSONObject obj = (JSONObject) new JSONParser().parse(data);
             this.timestamp = Long.parseLong(String.valueOf(obj.get("timestamp")));
-            this.id = Long.parseLong(String.valueOf(obj.get("user_id")));
+            this.id = String.valueOf(obj.get("user_id"));
             this.data = data;
             this.clientName = getClientName();
         }

+ 5 - 5
code/simulation/server/src/main/java/server/TCPServer.java

@@ -25,7 +25,7 @@ public class TCPServer {
     private ServerSocket server;
     private final String logPath = "/data/logs/";
     private final long startTime = System.currentTimeMillis();
-    private final long startDataTime = 1351742401;
+    private final long startDataTime = 1632896852; //Twitter-1351742401 Reddit-1632896852
 
     private final long roundLength = 1800;
     private int round = 1;
@@ -105,8 +105,8 @@ public class TCPServer {
 
                 JSONObject obj = (JSONObject) new JSONParser().parse(data);
                 long timestamp = Long.parseLong(String.valueOf(obj.get("timestamp")));
-                long user_id = Long.parseLong(String.valueOf(obj.get("user_id")));
-                JSONArray arr = (JSONArray) obj.get("hashtags");
+                String user_id = String.valueOf(obj.get("user_id"));
+                JSONArray arr = (JSONArray) obj.get("subreddit");//(JSONArray) obj.get("hashtags");
                 String[] hashtags = new String[arr.size()];
                 for(int i=0; i<arr.size(); i++)
                     hashtags[i]= String.valueOf(arr.get(i));
@@ -154,10 +154,10 @@ public class TCPServer {
         private String[] hashtags;
         private long timestamp;
         private String senderAddress;
-        private long id;
+        private String id;
 
 
-        Logger(long user_id, String[] hashtags, long timestamp, String senderAddress) {
+        Logger(String user_id, String[] hashtags, long timestamp, String senderAddress) {
             this.hashtags = hashtags;
             this.senderAddress = senderAddress;
             this.id = user_id;

+ 49 - 14
code/src/main/java/analyzer/Analyzer.java

@@ -22,19 +22,21 @@ public class Analyzer {
     public static final int roundLength = 1800;
     public static final int clientNo = 1000000;
 
+    public static final String datasetName = "reddit"; //;
+
     public static final boolean coverTraffic = false;
     public static final int delay = 0;
-    public static final String childPath = coverTraffic ? "ct" : delay > 0 ? "delay" : roundLength + "";
-    public static final String logType = "random_";    //"top-" "random_"
-    //public static final String logPath = "C:\\Users\\Admin\\Desktop\\Skripts\\Thesis\\Repo\\local\\vm-mount\\logs\\" + logType + clientNo + "\\" + roundLength;
-    public static final String logPath = "C:\\Users\\Admin\\Desktop\\Skripts\\Thesis\\Repo\\local\\vm-mount\\logs\\" + logType + clientNo + "\\" + childPath;
-    public static String clientLogPath = logPath + "\\clogs.txt";
-    public static String serverLogPath = logPath + "\\slogs.txt";
+
+    public static String childPath = "";
+    public static String logType = "";  //"top-" "random_"
+    public static String logPath = "";
+    public static String clientLogPath = "";
+    public static String serverLogPath = "";
 
     //TEST
     //private static final User toTraceUser = new User(306637085);
     private static final String toTraceHashtag = "InstantFollowBack";
-    private static final String intersectingClientID = "client-1034";
+    private static final String intersectingClientID = "client-937851";
     private static final long targetUserID = 398449825L;
 
 
@@ -45,6 +47,7 @@ public class Analyzer {
     }
 
     public Analyzer () throws InterruptedException {
+        pathConfig();
         Thread slogs = new Thread(() -> {
             try {
                 ServerLogParser.run();
@@ -63,6 +66,20 @@ public class Analyzer {
         //new ServerLogParser(serverLogPath).traceUser();
     }
 
+    private static void pathConfig() {
+        childPath = coverTraffic ? "ct" + "\\" + coverTraffic : delay > 0 ? "delay" + "\\" + delay : roundLength + "";
+        if(datasetName.equals("twitter")) {
+            logType = "twitter\\random_";
+            logPath = "C:\\Users\\Admin\\Desktop\\Skripts\\Thesis\\Repo\\local\\vm-mount\\logs\\" + logType + clientNo + "\\" + childPath;
+        }
+        else if(datasetName.equals("reddit")) {
+            logType = "reddit\\";
+            logPath = "C:\\Users\\Admin\\Desktop\\Skripts\\Thesis\\Repo\\local\\vm-mount\\logs\\" + logType + "\\" + "ct\\0\\";
+        }
+        clientLogPath = logPath + "\\clogs.txt";
+        serverLogPath = logPath + "\\slogs.txt";
+    }
+
     private static void traceClientUserLink() throws InterruptedException, ExecutionException {
         Analyzer privateInst = new Analyzer();
         int roundNum = privateInst.intersectUsers(ClientLogParser.clients.get(intersectingClientID), 1, true);
@@ -78,9 +95,9 @@ public class Analyzer {
         System.out.println();
         List<Map.Entry<Hashtag, Integer>> results = privateInst.calculatePointsGivenHashtags(ClientLogParser.clients.get(intersectingClientID), hashtags, 1, true);
         System.out.println("Result size: " + results.size());
-        User targetUser = ServerLogParser.users.get(targetUserID);
-        System.out.println();
-        targetUser.getHashtags().forEach((k, v) -> System.out.println(k.getName()));
+        //User targetUser = ServerLogParser.users.get(targetUserID);
+        //System.out.println();
+        //targetUser.getHashtags().forEach((k, v) -> System.out.println(k.getName()));
     }
 
     public void traceHashtag() throws InterruptedException, ExecutionException {
@@ -241,7 +258,10 @@ public class Analyzer {
         Map<String, Integer> results = new Hashtable<>();
 
         List<Round> targetRounds = new ArrayList<>(targetClient.getRounds().keySet());
+        targetRounds.sort(Comparator.comparingInt(Round::getNo));
         int targetNoRounds = targetRounds.size();
+        int lastRound = 1;
+        long lastResult = ServerLogParser.hashtags.size();
         for(int i = 0; i < targetNoRounds; i++) {
             results.forEach((k, v) -> results.replace(k, v - 1));
             //results.entrySet().removeIf(e -> (e.getValue() < 0));
@@ -254,7 +274,16 @@ public class Analyzer {
                 else
                     results.put(hashtag.getName(), -(i + 1) + 1);
             }
+            //long resultSize = Collections.frequency(results.values(), Collections.max(results.values(), Integer::compare));
+
+            //for (int x = lastRound + 1; x < targetRoundNo; x++) {
+            //    System.out.print(lastResult+ ",");
+            //}
+            //lastRound = targetRoundNo;
+            //lastResult = resultSize;
+            //System.out.print(resultSize + ",");
         }
+        //System.out.println();
         //results.entrySet().removeIf(e -> (e.getValue() < targetNoRound /2));
         List<Map.Entry<String, Integer>> sortedList = new ArrayList<>(results.entrySet());
         sortedList.sort(Map.Entry.comparingByValue((v1, v2) -> Integer.compare(v2, v1)));
@@ -305,16 +334,18 @@ public class Analyzer {
         List<Round> roundList = new ArrayList<>(ServerLogParser.rounds.values());
         roundList.sort(Comparator.comparingInt(Round::getNo));
         for(Round r: roundList) {
-            if(targetClient.getRounds().containsKey(r)) {
-                userList.removeIf(u -> !u.getRounds().containsKey(r) || !u.getRounds().get(r).equals(targetClient.getRounds().get(r)));
-            } else {
+            if(!targetClient.getRounds().containsKey(r)) {
                 userList.removeIf(u -> u.getRounds().containsKey(r));
+            } else {
+                userList.removeIf(u -> !u.getRounds().containsKey(r) || !u.getRounds().get(r).equals(targetClient.getRounds().get(r)));
             }
+            //System.out.print(userList.size() + ", ");
             if(userList.size() <= 1) {
                 roundProcessed = r.getNo();
                 break;
             }
         }
+        //System.out.println();
         if(userList.size() != 1) {
             roundProcessed = -1;
         }
@@ -362,6 +393,10 @@ public class Analyzer {
 
         @Override
         public void run() {
+            calculatePoint();
+        }
+
+        private void calculatePoint() {
             Map<Round, Integer> clone = new Hashtable<>(client.getRounds());
             AtomicInteger n = new AtomicInteger();
             clone.keySet().retainAll(this.hashtag.getRoundMap().keySet());
@@ -372,4 +407,4 @@ public class Analyzer {
                 hashtagMap.put(this.hashtag, n.get());
         }
     }
-}
+}

+ 2 - 2
code/src/main/java/analyzer/ClientLogParser.java

@@ -12,9 +12,9 @@ import java.util.*;
  * This class reads the client logs, creates objects and organizes them into data structures.
  * An instance of this class must run cooperatively with an instance of class ServerLogParser
  */
-public class ClientLogParser{
+public class ClientLogParser {
 
-    private static long startRoundTime = 1351742401;
+    private static long startRoundTime = 1632896852; //Twitter-1351742401 Reddit-1632896852
     public static volatile int clientRoundNo = 0;
     public static volatile boolean DONE = false;
 

+ 6 - 5
code/src/main/java/analyzer/Counter.java

@@ -15,9 +15,10 @@ public class Counter {
     private static String path = "C:\\Users\\Admin\\Desktop\\Skripts\\Thesis\\Repo\\local\\round_based_data_" + Analyzer.roundLength + ".txt";
 
     public static void main(String[] args) throws IOException, InterruptedException {
+        new Analyzer();
         countHashtag(10);
         System.out.println();
-        countClient(10000);
+        countClient(116000);
         //datasetStatistics();
         //trial();
     }
@@ -71,7 +72,7 @@ public class Counter {
     }
 
     private static void datasetStatistics() throws InterruptedException, IOException {
-        Analyzer.serverLogPath = path;
+        //Analyzer.serverLogPath = path;
         new Analyzer();
 
         //hashtagsPerUser();
@@ -83,8 +84,8 @@ public class Counter {
         //distribution2();
         //exportDataPopularity(new int[] {1, 10, 100, 1000, 10000, 100000});
         //exportDataLifetime(new int[] {1, 10, 100, 1000, 10000, 100000});
-        //numOfPostsEachRound();
-        activeTime();
+        numOfPostsEachRound();
+        //activeTime();
     }
 
     private static void hashtagsPerUser() {
@@ -406,7 +407,7 @@ public class Counter {
         entries.sort(Comparator.comparingInt(Map.Entry::getKey));
         for(Map.Entry<Integer, Round> e : entries) {
             int totalPosts = e.getValue().getUserMap().values().stream().mapToInt(v -> v).sum();
-            System.out.print(totalPosts + " ");
+            System.out.print(totalPosts + ",");
         }
     }
     public static void activeTime() {

+ 2 - 3
code/src/main/java/analyzer/ServerLogParser.java

@@ -1,6 +1,5 @@
 package analyzer;
 
-
 import analyzer.models.Hashtag;
 import analyzer.models.Round;
 import analyzer.models.User;
@@ -24,7 +23,7 @@ public class ServerLogParser {
     public static volatile int serverLineCounter = 0;
     public static volatile int lineTotal = 0;
 
-    public static volatile Map<Long, User> users = new Hashtable<>(Analyzer.clientNo, 1);
+    public static volatile Map<String, User> users = new Hashtable<>(Analyzer.clientNo, 1);
     public static volatile Map<Integer, Round> rounds = new Hashtable<>(1000);
     public static volatile Map<String, Hashtag> hashtags = new Hashtable<>(Analyzer.clientNo);
 
@@ -94,7 +93,7 @@ public class ServerLogParser {
             rounds.put(onRound, r);
         }
 
-        long id = Long.parseLong(elements[1]);
+        String id = String.valueOf(elements[1]);
         User u = users.get(id);
         if(u == null) {
             u = new User(id);

+ 3 - 3
code/src/main/java/analyzer/models/User.java

@@ -5,17 +5,17 @@ import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
 public class User {
-    private final long id;
+    private final String id;
     private Map<Hashtag, Integer> hashtagMap;
     private Map<Round, Integer> roundMap;
 
-    public User(long id) {
+    public User(String id) {
         this.id = id;
         hashtagMap = new Hashtable<>();
         roundMap = new Hashtable<>();
     }
 
-    public long getId() {
+    public String getId() {
         return this.id;
     }
 

+ 259 - 0
code/src/main/java/analyzer/tests/Accuracy.java

@@ -0,0 +1,259 @@
+package analyzer.tests;
+
+import analyzer.Analyzer;
+import analyzer.ClientLogParser;
+import analyzer.ServerLogParser;
+import analyzer.models.Client;
+import analyzer.models.Hashtag;
+import analyzer.models.User;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+/**
+ * This class evaluates results of the attacks on sets of subjects.
+ */
+public class Accuracy {
+    private static final int threadPoolSize = 8;
+
+    private static final int acceptRangeClientSet = 5;
+    private static final int acceptRangeHashtagSet = 5;
+
+    private static int counter;
+    private static int totalHashtags = 1000;
+    private static int totalClients = 1000;
+    private static final int clientThreshold = 5;
+
+    private static volatile int correct = 0;
+    private static volatile int averageRounds = 0;
+    private static volatile int averagePosts = 0;
+
+    public static final String boundnamesPath = "C:\\Users\\Admin\\Desktop\\Skripts\\Thesis\\Repo\\local\\vm-mount\\logs\\" + Analyzer.logType + Analyzer.clientNo + "\\boundnames.txt" ;
+    public static final List<String> identifiedLinks = new ArrayList<>();
+
+    private static Analyzer A;
+
+    public static void main(String[] args) throws InterruptedException, ExecutionException {
+        A = new Analyzer();
+        //hashtagTraceAccuracyUserUnknown();
+        clientTraceAccuracyUserUnknown();
+        //clientTraceAccuracyUserKnown();
+    }
+
+    //Step: most popular hashtags -> for each hashtag calculate points -> for each client intersect sets -> for each client check result
+    private static void hashtagTraceAccuracyUserUnknown() throws InterruptedException, ExecutionException {
+        List<Hashtag> hashtagList = new ArrayList<>(ServerLogParser.hashtags.values());
+        hashtagList.sort((h1, h2) -> Integer.compare(h2.getTotalPosts(), h1.getTotalPosts()));
+
+        ExecutorService executor = Executors.newFixedThreadPool(threadPoolSize);
+        List<Future<?>> tasks = new ArrayList<>();
+        totalHashtags = Math.min(totalHashtags, ServerLogParser.hashtags.values().size());
+        for(int i = 0; i < totalHashtags; i++) {
+            Future<?> task = executor.submit(new CheckHashtagTask(hashtagList.get(i)));
+            tasks.add(task);
+        }
+        for(Future<?> task : tasks)
+            task.get();
+        executor.shutdown();
+        printResult(false);
+    }
+
+    //Step: set intersection -> calculate point ->
+    private static void clientTraceAccuracyUserUnknown() throws InterruptedException, ExecutionException {
+        List<Client> clientList = createClientSet(totalClients);
+        ExecutorService executor = Executors.newFixedThreadPool(threadPoolSize);
+        List<Future<?>> tasks = new ArrayList<>();
+
+        totalClients = Math.min(totalClients, clientList.size());
+        for(int i = 0; i < totalClients; i++) {
+            Future<?> task = executor.submit(new CheckClientTask(clientList.get(i)));
+            tasks.add(task);
+        }
+        for(Future<?> task : tasks){
+            task.get();
+            counter++;
+            float progress = (float) Math.round(((float) counter / (float) totalHashtags) * 1000.0F) / 10.0F;
+            System.out.print("\r");
+            System.out.print(progress + " % done");
+        }
+        executor.shutdown();
+        printResult(false);
+    }
+
+    private static void clientTraceAccuracyUserKnown() throws InterruptedException, ExecutionException {
+        List<Client> clientList = createClientSet(totalClients);
+        System.out.println("clientList size " + clientList.size());
+        ExecutorService executor = Executors.newFixedThreadPool(threadPoolSize);
+        List<Future<?>> tasks = new ArrayList<>();
+
+        totalClients = Math.min(totalClients, clientList.size());
+        for(int i = 0; i < totalClients; i++) {
+            Future<?> task = executor.submit(new CheckClientTaskUserKnown(clientList.get(i)));
+            tasks.add(task);
+        }
+        for(Future<?> task : tasks){
+            task.get();
+            counter++;
+            float progress = (float) Math.round(((float) counter / (float) totalHashtags) * 1000.0F) / 10.0F;
+            System.out.print("\r");
+            System.out.print(progress + " % done");
+        }
+        executor.shutdown();
+        printResult(true);
+    }
+
+    private static boolean checkClientHashtagLink(Client client, Hashtag hashtag) throws IOException {
+        FileReader fr = new FileReader(boundnamesPath);
+        BufferedReader br= new BufferedReader(fr);
+        String line;
+        while((line = br.readLine()) != null) {
+            String[] elements = line.trim().split("\t");//userID client-ID
+
+            if(elements[1].equals(client.getId())) {
+                if(ServerLogParser.users.get(String.valueOf(elements[0])).getHashtags().containsKey(hashtag)) {
+                    br.close();
+                    identifiedLinks.add(client.getId() + "-" + hashtag.getName());
+                    return true;
+                }
+                break;
+            }
+        }
+        br.close();
+        return false;
+    }
+
+    private static boolean checkClientUserLink(Client client, User user) throws IOException {
+        FileReader fr = new FileReader(boundnamesPath);
+        BufferedReader br= new BufferedReader(fr);
+        String line;
+        while((line = br.readLine()) != null) {
+            String[] elements = line.trim().split("\t");//userID client-ID
+
+            if(String.valueOf(elements[0]).equals(user.getId()))
+                return elements[1].equals(client.getId());
+        }
+        System.out.println("User not found");
+        return false;
+    }
+
+    private static List<Client> createClientSet(int num) {
+        List<Client> result = new ArrayList<>();
+        for(Client c : ClientLogParser.clients.values()) {
+            if(result.size() == num)
+                break;
+            else {
+                if(c.getTotalPosts() >= Accuracy.clientThreshold)
+                    result.add(c);
+            }
+        }
+        if(result.size() < num)
+            totalClients = result.size();
+        return result;
+    }
+
+
+    private static void printResult(boolean userKnown) {
+        System.out.println();
+        if(!userKnown) {
+            //System.out.println("acceptRangeClientSet: " + acceptRangeClientSet);
+            //System.out.println("acceptRangeHashtagSet: " + acceptRangeHashtagSet);
+            System.out.println("clientThreshold: " + clientThreshold);
+            double result = ((double) correct / (double) totalClients) * 100.00;
+            System.out.println(result + " % of " + totalClients);
+            System.out.println("average most ranked: " + (double) averagePosts/(double)totalClients);
+            System.out.print(identifiedLinks.size() + " identified links: ");
+            for(String s: identifiedLinks) {
+                System.out.print(s + " ");
+            }
+        } else  {
+            double result = ((double) averageRounds / (double) totalClients);
+            System.out.println(result + " rounds of " + totalClients);
+        }
+        System.out.println();
+    }
+
+    private static synchronized void incrementCorrect() {
+        correct++;
+    }
+
+    private static synchronized void addToAverageRounds(int n) {
+        averageRounds += n;
+    }
+
+    private static synchronized void addToAveragePosts(int n) {
+        averagePosts += n;
+    }
+
+    private record CheckHashtagTask(Hashtag hashtag) implements Runnable {
+
+        @Override
+        public void run() {
+            try {
+                List<Map.Entry<Client, Integer>> lst = A.calculatePointsAllClients(hashtag, acceptRangeClientSet, false);
+                for(int i = 0; i < acceptRangeClientSet; i++) {
+                    for(Map.Entry<String, Integer> e: A.intersectHashtags(lst.get(i).getKey(), acceptRangeHashtagSet, false))
+                        if(e.getKey().equals(hashtag.getName())) {
+                            if(checkClientHashtagLink(lst.get(i).getKey(), hashtag)) {
+                                incrementCorrect();
+                                break;
+                            }
+                        }
+                }
+                counter++;
+                float progress = (float) Math.round(((float) counter / (float) totalHashtags) * 1000.0F) / 10.0F;
+                System.out.print("\r");
+                System.out.print(progress + " % done");
+            } catch (InterruptedException | IOException | ExecutionException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private record CheckClientTask(Client client) implements Runnable {
+
+        @Override
+        public void run() {
+            try {
+                List<Map.Entry<String, Integer>> intersection = A.intersectHashtags(client, 1, false);
+                intersection.removeIf(e -> e.getValue() < 0);
+                Hashtag[] hashtags = new Hashtag[intersection.size()];
+                for(int i = 0; i < hashtags.length; i++)
+                    hashtags[i] = ServerLogParser.hashtags.get(intersection.get(i).getKey());
+
+                List<Map.Entry<Hashtag, Integer>> points = A.calculatePointsGivenHashtags(client, hashtags, 1, false);
+                addToAveragePosts(points.size());
+                for(Map.Entry<Hashtag, Integer> e : points) {
+                    boolean identified = checkClientHashtagLink(client, e.getKey());
+                    if(identified){
+                        incrementCorrect();
+                        return;
+                    }
+                }
+
+            } catch (InterruptedException | ExecutionException | IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private record CheckClientTaskUserKnown(Client client) implements Runnable {
+
+        @Override
+        public void run() {
+            int rounds = A.intersectUsers(client, 1, false);
+            if (rounds == -1) {
+                totalClients--;
+            } else {
+                addToAverageRounds(rounds);
+            }
+        }
+    }
+}

+ 215 - 0
code/src/main/java/analyzer/tests/TestMain.java

@@ -0,0 +1,215 @@
+package analyzer.tests;
+
+import analyzer.Analyzer;
+import analyzer.ClientLogParser;
+import analyzer.ServerLogParser;
+import analyzer.models.Client;
+import analyzer.models.Hashtag;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+public class TestMain {
+    private static final int threadPoolSize = 8;
+
+    private static volatile int error = 0;
+    private static volatile double sum = 0.0;
+    private static volatile int max = Integer.MIN_VALUE, min = Integer.MAX_VALUE;
+
+    private static volatile int correct = 0;
+    private static volatile double avgSetSize = 0.0;
+
+    private static Analyzer A;
+
+    static List<Client> group1 = new ArrayList<>(); //>1000 messages
+    static List<Client> group2 = new ArrayList<>(); //100-1000 messages
+    static List<Client> group3 = new ArrayList<>(); //10-100 messages
+
+    public static void main(String[] args) throws InterruptedException, ExecutionException {
+        A = new Analyzer();
+        determineUserGroups();
+        testMethod1();
+        //testMethod2();
+    }
+
+    public static void determineUserGroups() {
+        int numOfClients = ClientLogParser.clients.values().size();
+        ClientLogParser.clients.values().forEach(c -> {
+            if(c.getTotalPosts() >= 10 && c.getTotalPosts() < 100)
+                group3.add(c);
+            else if(c.getTotalPosts() >= 100 && c.getTotalPosts() < 1000)
+                group2.add(c);
+            else if(c.getTotalPosts() >= 1000)
+                group1.add(c);
+        });
+        System.out.println(">= 1000: " + (double) group1.size()*100/numOfClients + "%" + " (" + group1.size() + " users)");
+        System.out.println("100-1000: " + (double) group2.size()*100/numOfClients + "%" + " (" + group2.size() + " users)");
+        System.out.println("10-100: " + (double) group3.size()*100/numOfClients + "%" + " (" + group3.size() + " users)");
+        System.out.println();
+    }
+
+    public static void testMethod1() throws ExecutionException, InterruptedException {
+        System.out.println("Method 1 on Group ");
+        System.out.print(">= 1000: ");
+        testMethodOneOnGroup(group1);
+        System.out.println();
+        System.out.print("100-1000: ");
+        testMethodOneOnGroup(group2);
+        System.out.println();
+        System.out.print("10-100: ");
+        testMethodOneOnGroup(group3);
+        System.out.println();
+    }
+
+    private static void testMethodOneOnGroup(List<Client> group) throws ExecutionException, InterruptedException {
+        int counter = 0;
+        int correct;
+
+        double averageGroup;
+        System.out.println("clientList size " + group.size());
+        ExecutorService executor = Executors.newFixedThreadPool(threadPoolSize);
+        List<Future<?>> tasks = new ArrayList<>();
+
+        for (Client client : group) {
+            Future<?> task = executor.submit(new ExcludeUsersTask(client));
+            tasks.add(task);
+        }
+        for(Future<?> task : tasks){
+            task.get();
+            counter++;
+            float progress = (float) Math.round(((float) counter / (float) group.size()) * 1000.0F) / 10.0F;
+            System.out.print("\r");
+            System.out.print(progress + " % done");
+        }
+        executor.shutdown();
+        correct = group.size() - error;
+        averageGroup = sum /correct;
+        System.out.println(" correct: " + correct + " \naverage no of rounds: " + averageGroup);
+        System.out.println("Min: " + min + "\nAvg: " + averageGroup + "\nMax: " + max);
+    }
+
+    public static void testMethod2() throws ExecutionException, InterruptedException {
+        System.out.println("Method 2 on Group ");
+        System.out.print(">= 1000: ");
+        testMethodTwoOnGroup(group1);
+        System.out.println();
+        System.out.print("100-1000: ");
+        testMethodTwoOnGroup(group2);
+        System.out.println();
+        System.out.print("10-100: ");
+        testMethodTwoOnGroup(group3);
+        System.out.println();
+    }
+
+    private static void testMethodTwoOnGroup(List<Client> group) throws ExecutionException, InterruptedException {
+        avgSetSize = 0.0;
+        correct = 0;
+        int counter = 0;
+        double averageGroup;
+        System.out.println("clientList size " + group.size());
+        ExecutorService executor = Executors.newFixedThreadPool(threadPoolSize);
+        List<Future<?>> tasks = new ArrayList<>();
+
+        for (Client client : group) {
+            Future<?> task = executor.submit(new RankTopicsTask(client));
+            tasks.add(task);
+        }
+        for(Future<?> task : tasks){
+            task.get();
+            counter++;
+            float progress = (float) Math.round(((float) counter / (float) group.size()) * 1000.0F) / 10.0F;
+            System.out.print("\r");
+            System.out.print(progress + " % done");
+        }
+        averageGroup = avgSetSize / group.size();
+        System.out.print(" correct: " + correct + " average set size: " + averageGroup);
+        executor.shutdown();
+    }
+
+    private record ExcludeUsersTask(Client client) implements Runnable {
+
+        private static synchronized void incrementError() {
+            error++;
+        }
+
+        private static synchronized void incrementAvg(int num) {
+            if(num > max)
+                max = num;
+            if(num < min)
+                min = num;
+            sum += num;
+        }
+
+        @Override
+        public void run() {
+            int rounds = A.intersectUsers(client, 1, false);
+            if (rounds == -1) {
+                incrementError();
+            } else {
+                incrementAvg(rounds);
+            }
+        }
+    }
+
+    private record RankTopicsTask(Client client) implements Runnable {
+        public static final String boundnamesPath = Analyzer.logPath + "\\boundnames.txt" ;
+
+        private static synchronized void incrementCorrect() {
+            correct++;
+        }
+
+        private static synchronized void incrementAvgSetSize(int num) {
+            avgSetSize += num;
+        }
+
+        @Override
+        public void run() {
+            List<Map.Entry<String, Integer>> intersection = A.intersectHashtags(client, 1000, false);
+            Hashtag[] hashtags = new Hashtag[intersection.size()];
+            for(int i = 0; i < hashtags.length; i++)
+                hashtags[i] = ServerLogParser.hashtags.get(intersection.get(i).getKey());
+            System.out.println();
+            List<Map.Entry<Hashtag, Integer>> results;
+            try {
+                results = A.calculatePointsGivenHashtags(client, hashtags, 1, false);
+                incrementAvgSetSize(results.size());
+                results.forEach(e -> {
+                    try {
+                        check(e.getKey());
+                    } catch (IOException ioException) {
+                        ioException.printStackTrace();
+                    }
+                });
+
+            } catch (InterruptedException | ExecutionException e) {
+                e.printStackTrace();
+            }
+        }
+
+        private void check(Hashtag hashtag) throws IOException {
+            FileReader fr = new FileReader(boundnamesPath);
+            BufferedReader br= new BufferedReader(fr);
+            String line;
+            while((line = br.readLine()) != null) {
+                String[] elements = line.trim().split("\t");//userID client-ID
+
+                if(elements[1].equals(client.getId())) {
+                    if(ServerLogParser.users.get(String.valueOf(elements[0])).getHashtags().containsKey(hashtag)) {
+                        br.close();
+                        incrementCorrect();
+                    }
+                    break;
+                }
+            }
+            br.close();
+        }
+    }
+}