diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000000000000000000000000000000000000..64fea47fb75767bb9671849b7e4f3af0d4c52036
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+CluMATE
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 540fc7632975c9808478c027ddccb8f4b202adb6..af11a34e8c747c5226ae353e5c58d4cec3d82c11 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,4 +1,4 @@
-rootProject.name = 'Mascl'
+rootProject.name = 'CluMATE'
 
 // Uncomment the two next lines when building amak locally
 include ':amak'
diff --git a/src/main/java/fr/irit/smac/amas/Cluster.java b/src/main/java/fr/irit/smac/amas/Cluster.java
deleted file mode 100644
index ad97a75e9e0a4be8874d488aaa2958e41b7838dd..0000000000000000000000000000000000000000
--- a/src/main/java/fr/irit/smac/amas/Cluster.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package fr.irit.smac.amas;
-
-import lombok.Getter;
-import lombok.ToString;
-
-@ToString
-public class Cluster {
-	@Getter
-	private final DataPoint representative;
-	@Getter
-	private final int size;
-
-	public Cluster(DataPoint representative) {
-		this(representative, 1);
-	}
-
-	public Cluster(DataPoint representative, int size) {
-		this.representative = representative;
-		this.size = size;
-	}
-
-	public Cluster fuse(Cluster other) {
-		var newRepresentative = representative.fuse(other.representative);
-		var newSize = size + other.size;
-		return new Cluster(newRepresentative, newSize);
-	}
-
-	public Cluster fuse(DataPoint other) {
-		var newRepresentative = representative.fuse(other);
-		var newSize = size + 1;
-		return new Cluster(newRepresentative, newSize);
-	}
-}
diff --git a/src/main/java/fr/irit/smac/amas/DataPoint.java b/src/main/java/fr/irit/smac/amas/DataPoint.java
deleted file mode 100644
index 63f767e3b1dbe098d2b8dfec6d00492ad0c79a78..0000000000000000000000000000000000000000
--- a/src/main/java/fr/irit/smac/amas/DataPoint.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package fr.irit.smac.amas;
-
-public interface DataPoint {
-	DataPoint fuse(DataPoint representative);
-}
diff --git a/src/main/java/fr/irit/smac/amas/MASSettings.java b/src/main/java/fr/irit/smac/amas/MASSettings.java
deleted file mode 100644
index ebb31a8de70568551a87abeb170c647b420e51a7..0000000000000000000000000000000000000000
--- a/src/main/java/fr/irit/smac/amas/MASSettings.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package fr.irit.smac.amas;
-
-import java.util.EnumSet;
-
-public record MASSettings(SimilarityScoreMethod similarityScoreMethod, float fusionThreshold, EnumSet<AMASOption> amasOptions) {
-
-}
diff --git a/src/main/java/fr/irit/smac/amas/SimilarityScoreMethod.java b/src/main/java/fr/irit/smac/amas/SimilarityScoreMethod.java
deleted file mode 100644
index f2d3b3ccc0c4df9578f11b956623a5eeb90e8d79..0000000000000000000000000000000000000000
--- a/src/main/java/fr/irit/smac/amas/SimilarityScoreMethod.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package fr.irit.smac.amas;
-
-
-public interface SimilarityScoreMethod {
-	float apply(DataPoint dp1, DataPoint dp2);
-
-	boolean areRoughlySimilar(DataPoint dp1, DataPoint dp2);
-}
diff --git a/src/main/java/fr/irit/smac/amas/controller/ClusterAMASController.java b/src/main/java/fr/irit/smac/amas/controller/ClusterAMASController.java
deleted file mode 100644
index e42553f214754fca99dc090f7299cbee5f778273..0000000000000000000000000000000000000000
--- a/src/main/java/fr/irit/smac/amas/controller/ClusterAMASController.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package fr.irit.smac.amas.controller;
-
-import fr.irit.smac.amak.scheduling.Scheduler;
-import fr.irit.smac.amas.*;
-import fr.irit.smac.amas.controller.command.NewDataPointCommand;
-import fr.irit.smac.amas.controller.command.ShutdownCommand;
-import fr.irit.smac.amas.controller.command.SolveCommand;
-import fr.irit.smac.amas.controller.query.RetrieveClustersResultQuery;
-import lombok.Getter;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Executors;
-
-public class ClusterAMASController {
-	@Getter
-	private final ClusterEnvironment environment;
-	@Getter
-	private final MASSettings masSettings;
-	@Getter
-	private final ClusterAMAS amas;
-	@Getter
-	private final Scheduler scheduler;
-
-	public ClusterAMASController(MASSettings masSettings) {
-		this.masSettings = masSettings;
-		environment = new ClusterEnvironment();
-		amas = new ClusterAMAS(environment, masSettings);
-		scheduler = new Scheduler(Executors.newSingleThreadExecutor(), amas, environment);
-	}
-
-	public void handle(NewDataPointCommand newDataPointCommand) {
-		environment.addDataPoints(newDataPointCommand.newDataPoints());
-	}
-
-	public void handle(SolveCommand solveCommand) {
-		scheduler.startWithSleepSync(0);
-	}
-
-	public void handle(ShutdownCommand shutdownCommand) {
-		scheduler.stop();
-	}
-
-	public List<Cluster> handle(RetrieveClustersResultQuery retrieveClustersResultQuery) {
-		return amas.getAgents(ClusterAgent.class).stream().map(agent -> agent.getCluster()).toList();
-	}
-}
diff --git a/src/main/java/fr/irit/smac/amas/controller/command/Command.java b/src/main/java/fr/irit/smac/amas/controller/command/Command.java
deleted file mode 100644
index a9a67851fe89f5b3ea8e22e9ab77cef2a7953362..0000000000000000000000000000000000000000
--- a/src/main/java/fr/irit/smac/amas/controller/command/Command.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package fr.irit.smac.amas.controller.command;
-
-public interface Command {
-}
diff --git a/src/main/java/fr/irit/smac/amas/controller/command/NewDataPointCommand.java b/src/main/java/fr/irit/smac/amas/controller/command/NewDataPointCommand.java
deleted file mode 100644
index 257112b78f746c23824c800e1f810159323323d9..0000000000000000000000000000000000000000
--- a/src/main/java/fr/irit/smac/amas/controller/command/NewDataPointCommand.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package fr.irit.smac.amas.controller.command;
-
-import fr.irit.smac.amas.DataPoint;
-
-import java.util.List;
-
-public record NewDataPointCommand(List<DataPoint> newDataPoints) implements Command {
-}
diff --git a/src/main/java/fr/irit/smac/amas/controller/command/SolveCommand.java b/src/main/java/fr/irit/smac/amas/controller/command/SolveCommand.java
deleted file mode 100644
index c43da7da6159ae504dd1b4f41bd27d40f5b5a55f..0000000000000000000000000000000000000000
--- a/src/main/java/fr/irit/smac/amas/controller/command/SolveCommand.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package fr.irit.smac.amas.controller.command;
-
-public record SolveCommand() implements Command {
-}
diff --git a/src/main/java/fr/irit/smac/amas/controller/query/Query.java b/src/main/java/fr/irit/smac/amas/controller/query/Query.java
deleted file mode 100644
index 9093d158e99841481068c1159c75d1948bb9f476..0000000000000000000000000000000000000000
--- a/src/main/java/fr/irit/smac/amas/controller/query/Query.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package fr.irit.smac.amas.controller.query;
-
-public interface Query {
-}
diff --git a/src/main/java/fr/irit/smac/clumate/CluMATE.java b/src/main/java/fr/irit/smac/clumate/CluMATE.java
new file mode 100644
index 0000000000000000000000000000000000000000..d1c1d421559e1e16439b34c4e81d960b1d4033b2
--- /dev/null
+++ b/src/main/java/fr/irit/smac/clumate/CluMATE.java
@@ -0,0 +1,26 @@
+package fr.irit.smac.clumate;
+
+import fr.irit.smac.clumate.amas.MASSettings;
+import fr.irit.smac.clumate.amas.controller.CluMATEController;
+import fr.irit.smac.clumate.amas.controller.command.NewDataPointCommand;
+import fr.irit.smac.clumate.amas.controller.command.SolveCommand;
+import fr.irit.smac.clumate.amas.controller.query.Result;
+import fr.irit.smac.clumate.amas.controller.query.RetrieveClustersResultQuery;
+import fr.irit.smac.clumate.cluster.DataPoint;
+
+import java.util.List;
+
+public class CluMATE<T extends DataPoint> {
+	private final CluMATEController<T> controller;
+
+	public CluMATE(MASSettings<T> masSettings) {
+		this.controller = new CluMATEController<T>(masSettings);
+	}
+	public void fit(List<T> data) {
+		controller.handle(new NewDataPointCommand<T>(data));
+		controller.handle(new SolveCommand());
+	}
+	public Result<T> retrieveClusters() {
+		return controller.handle(new RetrieveClustersResultQuery());
+	}
+}
diff --git a/src/main/java/fr/irit/smac/amas/AMASOption.java b/src/main/java/fr/irit/smac/clumate/amas/AMASOption.java
similarity index 50%
rename from src/main/java/fr/irit/smac/amas/AMASOption.java
rename to src/main/java/fr/irit/smac/clumate/amas/AMASOption.java
index ac7316485185cdcb2564bfb473e9c722e7bb6c2d..8d83d0a0d4572f748a7c88fe58c0b4f99df3d6a0 100644
--- a/src/main/java/fr/irit/smac/amas/AMASOption.java
+++ b/src/main/java/fr/irit/smac/clumate/amas/AMASOption.java
@@ -1,4 +1,4 @@
-package fr.irit.smac.amas;
+package fr.irit.smac.clumate.amas;
 
 public enum AMASOption {
 	Forget
diff --git a/src/main/java/fr/irit/smac/amas/ClusterAMAS.java b/src/main/java/fr/irit/smac/clumate/amas/ClusterAMAS.java
similarity index 78%
rename from src/main/java/fr/irit/smac/amas/ClusterAMAS.java
rename to src/main/java/fr/irit/smac/clumate/amas/ClusterAMAS.java
index 08a950b17f00a33699c3e8fb415a82187832e3a6..ed241f05daf6b210094de354d3e6d5ba23ed71e7 100644
--- a/src/main/java/fr/irit/smac/amas/ClusterAMAS.java
+++ b/src/main/java/fr/irit/smac/clumate/amas/ClusterAMAS.java
@@ -1,24 +1,22 @@
-package fr.irit.smac.amas;
+package fr.irit.smac.clumate.amas;
 
 import fr.irit.smac.amak.Amas;
+import fr.irit.smac.clumate.cluster.DataPoint;
 import lombok.Getter;
 import lombok.Setter;
 
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.List;
 import java.util.function.Consumer;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-public class ClusterAMAS extends Amas<ClusterEnvironment> {
+public class ClusterAMAS<T extends DataPoint> extends Amas<ClusterEnvironment<T>> {
 	private static final Logger logger = Logger.getLogger(ClusterAMAS.class.getName());
 	@Setter
 	private Consumer<Integer> onCycleEnd;
 	@Getter
-	private final MASSettings masSettings;
+	private final MASSettings<T> masSettings;
 
-	public ClusterAMAS(ClusterEnvironment environment, MASSettings masSettings) {
+	public ClusterAMAS(ClusterEnvironment<T> environment, MASSettings<T> masSettings) {
 		super(environment, 1, ExecutionPolicy.TWO_PHASES);
 		this.masSettings = masSettings;
 	}
@@ -29,7 +27,7 @@ public class ClusterAMAS extends Amas<ClusterEnvironment> {
 			logger.info("---- BEGIN MAS CYCLE ----");
 		if (agents.isEmpty() || allAgentsAreDormant()) {
 			var newDataPoint = environment.pollPendingDataPoint();
-			newDataPoint.ifPresent(dataPoint -> new ClusterAgent(this, dataPoint));
+			newDataPoint.ifPresent(dataPoint -> new ClusterAgent<T>(this, dataPoint));
 		}
 	}
 
diff --git a/src/main/java/fr/irit/smac/amas/ClusterAgent.java b/src/main/java/fr/irit/smac/clumate/amas/ClusterAgent.java
similarity index 83%
rename from src/main/java/fr/irit/smac/amas/ClusterAgent.java
rename to src/main/java/fr/irit/smac/clumate/amas/ClusterAgent.java
index 94d7b7f3b93eecd635c6141e7a2dbbb9532c567b..63eb488a59f7173f87ebd316525e1d21598b44e3 100644
--- a/src/main/java/fr/irit/smac/amas/ClusterAgent.java
+++ b/src/main/java/fr/irit/smac/clumate/amas/ClusterAgent.java
@@ -1,9 +1,11 @@
-package fr.irit.smac.amas;
+package fr.irit.smac.clumate.amas;
 
 import fr.irit.smac.amak.Agent;
 import fr.irit.smac.amak.messaging.Mailbox;
-import fr.irit.smac.amas.messages.EvaluatedScoreMessage;
-import fr.irit.smac.amas.messages.RequestSimilarityMessage;
+import fr.irit.smac.clumate.amas.messages.EvaluatedScoreMessage;
+import fr.irit.smac.clumate.amas.messages.RequestSimilarityMessage;
+import fr.irit.smac.clumate.cluster.Cluster;
+import fr.irit.smac.clumate.cluster.DataPoint;
 import lombok.Getter;
 
 import java.text.MessageFormat;
@@ -11,10 +13,10 @@ import java.util.*;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-public class ClusterAgent extends Agent<ClusterAMAS, ClusterEnvironment> {
+public class ClusterAgent<T extends DataPoint> extends Agent<ClusterAMAS<T>, ClusterEnvironment<T>> {
 	private static final float FLOAT_COMPARISON_EPSILON = 0.000001f;
 	@Getter
-	private Cluster cluster;
+	private Cluster<T> cluster;
 	private State state = State.INIT;
 	private State nextState = state;
 
@@ -24,9 +26,9 @@ public class ClusterAgent extends Agent<ClusterAMAS, ClusterEnvironment> {
 	//endregion
 
 	//region Perception variables
-	private Cluster receivedClusterForRequestSimilarity;
-	private ClusterAgent receivedRequestSimilarityRequester;
-	private final Map<ClusterAgent, Float> similarityScoresReceived = new HashMap<>();
+	private Cluster<T> receivedClusterForRequestSimilarity;
+	private ClusterAgent<T> receivedRequestSimilarityRequester;
+	private final Map<ClusterAgent<T>, Float> similarityScoresReceived = new HashMap<>();
 	//endregion
 
 	//region Decision variables
@@ -36,17 +38,17 @@ public class ClusterAgent extends Agent<ClusterAMAS, ClusterEnvironment> {
 	//endregion
 
 	//region Action variables
-	private final List<ClusterAgent> requestSimilarityMessageSentAgents = new ArrayList<>();
+	private final List<ClusterAgent<T>> requestSimilarityMessageSentAgents = new ArrayList<>();
 	//endregion
 
-	private final List<ClusterAgent> clusterAgentsRoughlySimilarOnReady = new ArrayList<>();
+	private final List<ClusterAgent<T>> clusterAgentsRoughlySimilarOnReady = new ArrayList<>();
 	private final Random random = new Random();
 	private static final Logger logger = Logger.getLogger(ClusterAgent.class.getName());
 	private int amountOfCyclesWithTheSameState;
 
-	protected ClusterAgent(ClusterAMAS amas, DataPoint dataPoint) {
+	protected ClusterAgent(ClusterAMAS amas, T dataPoint) {
 		super(amas);
-		this.cluster = new Cluster(dataPoint);
+		this.cluster = new Cluster<T>(dataPoint);
 	}
 
 	public enum State {DORMANT, WAITING_FOR_REPLY, DECIDING, INIT, DEAD, ACTIVE}
@@ -67,7 +69,7 @@ public class ClusterAgent extends Agent<ClusterAMAS, ClusterEnvironment> {
 		}
 	}
 
-	private boolean isRoughlySimilarTo(ClusterAgent otherClusterAgent) {
+	private boolean isRoughlySimilarTo(ClusterAgent<T> otherClusterAgent) {
 		return getAmas().getMasSettings().similarityScoreMethod().areRoughlySimilar(cluster.getRepresentative(), otherClusterAgent.cluster.getRepresentative());
 	}
 
@@ -240,15 +242,21 @@ public class ClusterAgent extends Agent<ClusterAMAS, ClusterEnvironment> {
 		state = nextState;
 	}
 
-	private void fuse(ClusterAgent other) {
+	private void fuse(ClusterAgent<T> other) {
 		if (logger.isLoggable(Level.INFO))
 			logger.info(this + " fuses with " + other);
-		cluster = cluster.fuse(other.cluster);
+
+		cluster = fuse(other.cluster);
+	}
+
+	private Cluster<T> fuse(Cluster<T> other) {
+		T newRepresentative = (T) amas.getMasSettings().dataPointFuser().apply(cluster.getRepresentative(), other.getRepresentative());
+		return new Cluster<T>(newRepresentative, cluster.getSize()+other.getSize());
 	}
 
-	private List<ClusterAgent> getRequestedAgentsWithSimilarityScoreAboveFusionThreshold(Map<ClusterAgent, Float> similarityScoresReceived) {
+	private List<ClusterAgent<T>> getRequestedAgentsWithSimilarityScoreAboveFusionThreshold(Map<ClusterAgent<T>, Float> similarityScoresReceived) {
 		float highestScore = Float.MIN_VALUE;
-		List<ClusterAgent> agentsWithHighestScore = new ArrayList<>();
+		List<ClusterAgent<T>> agentsWithHighestScore = new ArrayList<>();
 		for (var e : similarityScoresReceived.entrySet()) {
 			if (e.getValue() >= getAmas().getMasSettings().fusionThreshold()) {
 				if (Math.abs(e.getValue() - highestScore) < FLOAT_COMPARISON_EPSILON) {
@@ -264,7 +272,7 @@ public class ClusterAgent extends Agent<ClusterAMAS, ClusterEnvironment> {
 	}
 
 
-	private float computeSimilarityScore(ClusterAgent other) {
+	private float computeSimilarityScore(ClusterAgent<T> other) {
 		return getAmas().getMasSettings().similarityScoreMethod().apply(this.cluster.getRepresentative(), other.cluster.getRepresentative());
 	}
 }
diff --git a/src/main/java/fr/irit/smac/amas/ClusterEnvironment.java b/src/main/java/fr/irit/smac/clumate/amas/ClusterEnvironment.java
similarity index 66%
rename from src/main/java/fr/irit/smac/amas/ClusterEnvironment.java
rename to src/main/java/fr/irit/smac/clumate/amas/ClusterEnvironment.java
index 8b98b8154d7fb9e1d1294fdbd5e1e86207f70c31..914f8245d2b482fd8394662e3411a71a680628c0 100644
--- a/src/main/java/fr/irit/smac/amas/ClusterEnvironment.java
+++ b/src/main/java/fr/irit/smac/clumate/amas/ClusterEnvironment.java
@@ -1,6 +1,7 @@
-package fr.irit.smac.amas;
+package fr.irit.smac.clumate.amas;
 
 import fr.irit.smac.amak.Environment;
+import fr.irit.smac.clumate.cluster.DataPoint;
 import lombok.Getter;
 
 import java.util.List;
@@ -9,12 +10,12 @@ import java.util.Queue;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.logging.Logger;
 
-public class ClusterEnvironment extends Environment {
+public class ClusterEnvironment<T extends DataPoint> extends Environment {
 
-	private final Queue<DataPoint> pendingAdditionDataPoints = new ConcurrentLinkedQueue<>();
+	private final Queue<T> pendingAdditionDataPoints = new ConcurrentLinkedQueue<>();
 	private static final Logger logger = Logger.getLogger(ClusterEnvironment.class.getName());
 	@Getter
-	private DataPoint lastPolledPendingDataPoint = null;
+	private T lastPolledPendingDataPoint = null;
 
 	@Override
 	public void onInitialEntitiesCreation() {
@@ -24,13 +25,13 @@ public class ClusterEnvironment extends Environment {
 	public boolean hasRemainingPendingAdditionDataPoints() {
 		return !pendingAdditionDataPoints.isEmpty();
 	}
-	public Optional<DataPoint> pollPendingDataPoint() {
+	public Optional<T> pollPendingDataPoint() {
 		var dataPoint = pendingAdditionDataPoints.poll();
 		if (dataPoint != null)
 			lastPolledPendingDataPoint = dataPoint;
 		return Optional.ofNullable(dataPoint);
 	}
-	public void addDataPoints(List<DataPoint> newDataPoints) {
+	public void addDataPoints(List<T> newDataPoints) {
 		pendingAdditionDataPoints.addAll(newDataPoints);
 	}
 }
diff --git a/src/main/java/fr/irit/smac/clumate/amas/MASSettings.java b/src/main/java/fr/irit/smac/clumate/amas/MASSettings.java
new file mode 100644
index 0000000000000000000000000000000000000000..c8721cf3be17cbb63788f2136c85524a63141084
--- /dev/null
+++ b/src/main/java/fr/irit/smac/clumate/amas/MASSettings.java
@@ -0,0 +1,11 @@
+package fr.irit.smac.clumate.amas;
+
+import fr.irit.smac.clumate.cluster.DataPoint;
+import fr.irit.smac.clumate.cluster.DataPointFuser;
+import fr.irit.smac.clumate.cluster.SimilarityScoreMethod;
+
+import java.util.EnumSet;
+
+public record MASSettings<T extends DataPoint>(SimilarityScoreMethod<T> similarityScoreMethod, float fusionThreshold, EnumSet<AMASOption> amasOptions, DataPointFuser<T> dataPointFuser) {
+
+}
diff --git a/src/main/java/fr/irit/smac/clumate/amas/controller/CluMATEController.java b/src/main/java/fr/irit/smac/clumate/amas/controller/CluMATEController.java
new file mode 100644
index 0000000000000000000000000000000000000000..64bcb2bd0e9295e012fa54c0986778ff9bc919a7
--- /dev/null
+++ b/src/main/java/fr/irit/smac/clumate/amas/controller/CluMATEController.java
@@ -0,0 +1,51 @@
+package fr.irit.smac.clumate.amas.controller;
+
+import fr.irit.smac.amak.scheduling.Scheduler;
+import fr.irit.smac.clumate.amas.controller.command.NewDataPointCommand;
+import fr.irit.smac.clumate.amas.controller.command.ShutdownCommand;
+import fr.irit.smac.clumate.amas.controller.command.SolveCommand;
+import fr.irit.smac.clumate.amas.controller.query.Result;
+import fr.irit.smac.clumate.amas.controller.query.RetrieveClustersResultQuery;
+import fr.irit.smac.clumate.amas.ClusterAMAS;
+import fr.irit.smac.clumate.amas.ClusterAgent;
+import fr.irit.smac.clumate.amas.ClusterEnvironment;
+import fr.irit.smac.clumate.amas.MASSettings;
+import fr.irit.smac.clumate.cluster.DataPoint;
+import lombok.Getter;
+
+import java.util.concurrent.Executors;
+
+public class CluMATEController<T extends DataPoint> {
+	@Getter
+	private final ClusterEnvironment<T> environment;
+	@Getter
+	private final MASSettings<T> masSettings;
+	@Getter
+	private final ClusterAMAS<T> amas;
+	@Getter
+	private final Scheduler scheduler;
+
+	public CluMATEController(MASSettings<T> masSettings) {
+		this.masSettings = masSettings;
+		environment = new ClusterEnvironment<T>();
+		amas = new ClusterAMAS<T>(environment, masSettings);
+		scheduler = new Scheduler(Executors.newSingleThreadExecutor(), amas, environment);
+	}
+
+	public void handle(NewDataPointCommand<T> newDataPointCommand) {
+		environment.addDataPoints(newDataPointCommand.newDataPoints());
+	}
+
+	public void handle(SolveCommand solveCommand) {
+		scheduler.startWithSleepSync(0);
+	}
+
+	public void handle(ShutdownCommand shutdownCommand) {
+		scheduler.stop();
+	}
+
+	public Result<T> handle(RetrieveClustersResultQuery retrieveClustersResultQuery) {
+		var clusters = amas.getAgents(ClusterAgent.class).stream().map(agent -> ((ClusterAgent<T>) agent).getCluster()).toList();
+		return new Result<T>(clusters, masSettings);
+	}
+}
diff --git a/src/main/java/fr/irit/smac/clumate/amas/controller/command/Command.java b/src/main/java/fr/irit/smac/clumate/amas/controller/command/Command.java
new file mode 100644
index 0000000000000000000000000000000000000000..ed8e0ada6c718f87aa9efaeae6b3eb97813030f1
--- /dev/null
+++ b/src/main/java/fr/irit/smac/clumate/amas/controller/command/Command.java
@@ -0,0 +1,4 @@
+package fr.irit.smac.clumate.amas.controller.command;
+
+public interface Command {
+}
diff --git a/src/main/java/fr/irit/smac/clumate/amas/controller/command/NewDataPointCommand.java b/src/main/java/fr/irit/smac/clumate/amas/controller/command/NewDataPointCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..1ec497ca9e5527c694631590d89f2a6c0af8f5e8
--- /dev/null
+++ b/src/main/java/fr/irit/smac/clumate/amas/controller/command/NewDataPointCommand.java
@@ -0,0 +1,8 @@
+package fr.irit.smac.clumate.amas.controller.command;
+
+import fr.irit.smac.clumate.cluster.DataPoint;
+
+import java.util.List;
+
+public record NewDataPointCommand<T extends DataPoint>(List<T> newDataPoints) implements Command {
+}
diff --git a/src/main/java/fr/irit/smac/amas/controller/command/ShutdownCommand.java b/src/main/java/fr/irit/smac/clumate/amas/controller/command/ShutdownCommand.java
similarity index 50%
rename from src/main/java/fr/irit/smac/amas/controller/command/ShutdownCommand.java
rename to src/main/java/fr/irit/smac/clumate/amas/controller/command/ShutdownCommand.java
index cbb39fa88ec746b1c11bb3fece5de4408edac95d..2345a49d312a4812b7a8eff62dc6aeb463ad337a 100644
--- a/src/main/java/fr/irit/smac/amas/controller/command/ShutdownCommand.java
+++ b/src/main/java/fr/irit/smac/clumate/amas/controller/command/ShutdownCommand.java
@@ -1,4 +1,4 @@
-package fr.irit.smac.amas.controller.command;
+package fr.irit.smac.clumate.amas.controller.command;
 
 public record ShutdownCommand() implements Command {
 }
diff --git a/src/main/java/fr/irit/smac/clumate/amas/controller/command/SolveCommand.java b/src/main/java/fr/irit/smac/clumate/amas/controller/command/SolveCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..7af6bdf5d371c834d9988bd75d6172c814142eb2
--- /dev/null
+++ b/src/main/java/fr/irit/smac/clumate/amas/controller/command/SolveCommand.java
@@ -0,0 +1,4 @@
+package fr.irit.smac.clumate.amas.controller.command;
+
+public record SolveCommand() implements Command {
+}
diff --git a/src/main/java/fr/irit/smac/clumate/amas/controller/query/Query.java b/src/main/java/fr/irit/smac/clumate/amas/controller/query/Query.java
new file mode 100644
index 0000000000000000000000000000000000000000..c6ae431415beefca372910ed824007eb0aebb330
--- /dev/null
+++ b/src/main/java/fr/irit/smac/clumate/amas/controller/query/Query.java
@@ -0,0 +1,4 @@
+package fr.irit.smac.clumate.amas.controller.query;
+
+public interface Query {
+}
diff --git a/src/main/java/fr/irit/smac/clumate/amas/controller/query/Result.java b/src/main/java/fr/irit/smac/clumate/amas/controller/query/Result.java
new file mode 100644
index 0000000000000000000000000000000000000000..aa5fba7217d5fb7496cdb9024b37c924ba6d11ce
--- /dev/null
+++ b/src/main/java/fr/irit/smac/clumate/amas/controller/query/Result.java
@@ -0,0 +1,20 @@
+package fr.irit.smac.clumate.amas.controller.query;
+
+import fr.irit.smac.clumate.amas.MASSettings;
+import fr.irit.smac.clumate.cluster.Cluster;
+import fr.irit.smac.clumate.cluster.DataPoint;
+
+import java.util.List;
+
+
+public record Result<T extends DataPoint>(List<Cluster<T>> clusters,
+                                          int amountOfClustersWithFrequencyEqualToOne,
+                                          int amountOfClustersWithFrequencyHigherThan5,
+                                          MASSettings<T> masSettings) {
+	public Result(List<Cluster<T>> clusters, MASSettings<T> masSettings) {
+		this(clusters,
+		     (int) clusters.stream().filter(l -> l.getSize() == 1).count(),
+		     (int) clusters.stream().filter(l -> l.getSize() > 5).count(),
+		     masSettings);
+	}
+}
diff --git a/src/main/java/fr/irit/smac/amas/controller/query/RetrieveClustersResultQuery.java b/src/main/java/fr/irit/smac/clumate/amas/controller/query/RetrieveClustersResultQuery.java
similarity index 55%
rename from src/main/java/fr/irit/smac/amas/controller/query/RetrieveClustersResultQuery.java
rename to src/main/java/fr/irit/smac/clumate/amas/controller/query/RetrieveClustersResultQuery.java
index e11e99df749b89c87693340752e474a736b91d74..efe797e20912a6edf9102769ca7e134919673b53 100644
--- a/src/main/java/fr/irit/smac/amas/controller/query/RetrieveClustersResultQuery.java
+++ b/src/main/java/fr/irit/smac/clumate/amas/controller/query/RetrieveClustersResultQuery.java
@@ -1,4 +1,4 @@
-package fr.irit.smac.amas.controller.query;
+package fr.irit.smac.clumate.amas.controller.query;
 
 public record RetrieveClustersResultQuery() implements Query {
 }
diff --git a/src/main/java/fr/irit/smac/amas/messages/EvaluatedScoreMessage.java b/src/main/java/fr/irit/smac/clumate/amas/messages/EvaluatedScoreMessage.java
similarity index 89%
rename from src/main/java/fr/irit/smac/amas/messages/EvaluatedScoreMessage.java
rename to src/main/java/fr/irit/smac/clumate/amas/messages/EvaluatedScoreMessage.java
index 8ba5c25b70fa4701a365473ce9092e949683edc3..67a311e75912267a01b8441a607a06f3ba92ad35 100644
--- a/src/main/java/fr/irit/smac/amas/messages/EvaluatedScoreMessage.java
+++ b/src/main/java/fr/irit/smac/clumate/amas/messages/EvaluatedScoreMessage.java
@@ -1,4 +1,4 @@
-package fr.irit.smac.amas.messages;
+package fr.irit.smac.clumate.amas.messages;
 
 import fr.irit.smac.amak.Agent;
 import fr.irit.smac.amak.messaging.Message;
diff --git a/src/main/java/fr/irit/smac/amas/messages/RequestSimilarityMessage.java b/src/main/java/fr/irit/smac/clumate/amas/messages/RequestSimilarityMessage.java
similarity index 77%
rename from src/main/java/fr/irit/smac/amas/messages/RequestSimilarityMessage.java
rename to src/main/java/fr/irit/smac/clumate/amas/messages/RequestSimilarityMessage.java
index a5b02c6920bb3ca9d8536a30082e4e3f8b9df89d..4cae77f7d33a52dd534cddb4151f93bdc64c904b 100644
--- a/src/main/java/fr/irit/smac/amas/messages/RequestSimilarityMessage.java
+++ b/src/main/java/fr/irit/smac/clumate/amas/messages/RequestSimilarityMessage.java
@@ -1,6 +1,6 @@
-package fr.irit.smac.amas.messages;
+package fr.irit.smac.clumate.amas.messages;
 
-import fr.irit.smac.amas.Cluster;
+import fr.irit.smac.clumate.cluster.Cluster;
 import fr.irit.smac.amak.Agent;
 import fr.irit.smac.amak.messaging.Message;
 import lombok.Getter;
diff --git a/src/main/java/fr/irit/smac/clumate/cluster/Cluster.java b/src/main/java/fr/irit/smac/clumate/cluster/Cluster.java
new file mode 100644
index 0000000000000000000000000000000000000000..3c3576aabf35c73483732c18a4ccb5c90b28a3e6
--- /dev/null
+++ b/src/main/java/fr/irit/smac/clumate/cluster/Cluster.java
@@ -0,0 +1,21 @@
+package fr.irit.smac.clumate.cluster;
+
+import lombok.Getter;
+import lombok.ToString;
+
+@ToString
+public class Cluster<T extends DataPoint> {
+	@Getter
+	private final T representative;
+	@Getter
+	private final int size;
+
+	public Cluster(T representative) {
+		this(representative, 1);
+	}
+
+	public Cluster(T representative, int newSize) {
+		this.representative = representative;
+		this.size = newSize;
+	}
+}
diff --git a/src/main/java/fr/irit/smac/clumate/cluster/DataPoint.java b/src/main/java/fr/irit/smac/clumate/cluster/DataPoint.java
new file mode 100644
index 0000000000000000000000000000000000000000..6189901916c975d4849f41a59578d04add126ac0
--- /dev/null
+++ b/src/main/java/fr/irit/smac/clumate/cluster/DataPoint.java
@@ -0,0 +1,4 @@
+package fr.irit.smac.clumate.cluster;
+
+public interface DataPoint {
+}
diff --git a/src/main/java/fr/irit/smac/clumate/cluster/DataPointFuser.java b/src/main/java/fr/irit/smac/clumate/cluster/DataPointFuser.java
new file mode 100644
index 0000000000000000000000000000000000000000..2a64227d3c42e3ff5d65d2f955db261807500b4f
--- /dev/null
+++ b/src/main/java/fr/irit/smac/clumate/cluster/DataPointFuser.java
@@ -0,0 +1,6 @@
+package fr.irit.smac.clumate.cluster;
+
+import java.util.function.BiFunction;
+
+public interface DataPointFuser<T extends DataPoint> extends BiFunction<T, T, T> {
+}
diff --git a/src/main/java/fr/irit/smac/clumate/cluster/SimilarityScoreMethod.java b/src/main/java/fr/irit/smac/clumate/cluster/SimilarityScoreMethod.java
new file mode 100644
index 0000000000000000000000000000000000000000..59a6410f0e0e2e76838845f83806924e6bb8e00e
--- /dev/null
+++ b/src/main/java/fr/irit/smac/clumate/cluster/SimilarityScoreMethod.java
@@ -0,0 +1,10 @@
+package fr.irit.smac.clumate.cluster;
+
+
+public interface SimilarityScoreMethod<T extends DataPoint> {
+	float MAX_SIMILARITY_VALUE = 1.0f;
+
+	float apply(T dp1, T dp2);
+
+	boolean areRoughlySimilar(T dp1, T dp2);
+}
diff --git a/src/test/groovy/SimpleClusteringIT.groovy b/src/test/groovy/SimpleClusteringIT.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..f77a4ba36e63b1d016f9e8ad6b6abc9a8ad36b92
--- /dev/null
+++ b/src/test/groovy/SimpleClusteringIT.groovy
@@ -0,0 +1,44 @@
+import fr.irit.smac.clumate.CluMATE
+import fr.irit.smac.clumate.amas.AMASOption
+import fr.irit.smac.clumate.amas.MASSettings
+import fr.irit.smac.clumate.cluster.DataPoint
+import fr.irit.smac.clumate.cluster.SimilarityScoreMethod
+import spock.lang.Specification
+
+class SimpleClusteringIT extends Specification {
+	def "Clustering of two similar 1D objects"() {
+		given:
+		def clumate = new CluMATE<OneDDataPoint>(new MASSettings(new OneDSimilarityScoreMethod(), 0.5f, EnumSet.noneOf(AMASOption), (OneDDataPoint dp1, OneDDataPoint dp2) -> {
+			return new OneDDataPoint(dp1.value + (dp2.value - dp1.value) * 0.1f)
+		}))
+
+		when:
+		clumate.fit([new OneDDataPoint(1), new OneDDataPoint(2)])
+		var results = clumate.retrieveClusters()
+
+		then:
+		results.clusters().size() == 1
+		results.clusters().get(0).getSize() == 2
+	}
+
+	class OneDDataPoint implements DataPoint {
+		public final double value
+
+		OneDDataPoint(double value) {
+			this.value = value;
+		}
+	}
+
+	class OneDSimilarityScoreMethod implements SimilarityScoreMethod<OneDDataPoint> {
+
+		@Override
+		float apply(OneDDataPoint dp1, OneDDataPoint dp2) {
+			return Math.abs(dp1.value - dp2.value) <= 1f ? 1f : 0f;
+		}
+
+		@Override
+		boolean areRoughlySimilar(OneDDataPoint dp1, OneDDataPoint dp2) {
+			return true
+		}
+	}
+}
diff --git a/src/test/groovy/SimpleClusteringTest.groovy b/src/test/groovy/SimpleClusteringTest.groovy
deleted file mode 100644
index 64b48c77ca86a3798f9f91e4ba3ec7fd5bd54828..0000000000000000000000000000000000000000
--- a/src/test/groovy/SimpleClusteringTest.groovy
+++ /dev/null
@@ -1,54 +0,0 @@
-import fr.irit.smac.amas.AMASOption
-import fr.irit.smac.amas.DataPoint
-import fr.irit.smac.amas.MASSettings
-import fr.irit.smac.amas.SimilarityScoreMethod
-import fr.irit.smac.amas.controller.ClusterAMASController
-import fr.irit.smac.amas.controller.command.NewDataPointCommand
-import fr.irit.smac.amas.controller.command.SolveCommand
-import fr.irit.smac.amas.controller.query.RetrieveClustersResultQuery
-import spock.lang.Specification
-
-class SimpleClusteringTest extends Specification {
-	def "Clustering of two similar 1D objects"() {
-		given:
-		def controller = new ClusterAMASController(new MASSettings(new OneDSimilarityScoreMethod(), 0.5f, EnumSet.noneOf(AMASOption)))
-
-		when:
-		controller.handle(new NewDataPointCommand([new DataPoint1D(1), new DataPoint1D(2)]))
-		controller.handle(new SolveCommand())
-		var results = controller.handle(new RetrieveClustersResultQuery())
-
-		then:
-		results.size() == 1
-		results.get(0).getSize() == 2
-	}
-}
-
-class DataPoint1D implements DataPoint {
-	public final double value
-
-	DataPoint1D(double value) {
-		this.value = value;
-	}
-
-	@Override
-	DataPoint fuse(DataPoint representative) {
-		if (representative instanceof DataPoint1D) {
-			def other = (DataPoint1D) representative;
-			return new DataPoint1D(value + (other.value - value) * 0.1f)
-		}
-	}
-}
-
-class OneDSimilarityScoreMethod implements SimilarityScoreMethod {
-
-	@Override
-	float apply(DataPoint dp1, DataPoint dp2) {
-		return Math.abs(((DataPoint1D) dp1).value - ((DataPoint1D) dp2).value) <= 1f ? 1f : 0f;
-	}
-
-	@Override
-	boolean areRoughlySimilar(DataPoint dp1, DataPoint dp2) {
-		return true
-	}
-}
\ No newline at end of file