diff --git a/.gitignore b/.gitignore
index b63da4551b2e169d28b20728d5d53c6ca1779da4..f8e56bdf97ea660da3dd96d8c65d49b59e05146e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,4 +39,5 @@ bin/
 .vscode/
 
 ### Mac OS ###
-.DS_Store
\ No newline at end of file
+.DS_Store
+cache
diff --git a/.idea/groovyc.xml b/.idea/groovyc.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c0981345324314a461959f5d06413f2824660ff2
--- /dev/null
+++ b/.idea/groovyc.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="GroovyCompilerProjectConfiguration">
+    <option name="invokeDynamic" value="true" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index d3f92fa60afd4b8809a5951c37855ad2497d547b..bf1a5fd186c16a465a7ea492ba18510391f07b2e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -11,12 +11,31 @@ repositories {
     maven { url "https://jitpack.io" }
 }
 
+sourceSets {
+    experiments {
+        groovy.srcDir "$projectDir/src/experiments/groovy"
+        resources.srcDir "$projectDir/src/experiments/resources"
+        compileClasspath += main.output + test.output
+        runtimeClasspath += main.output + test.output
+    }
+}
+configurations {
+    experimentsImplementation.extendsFrom testImplementation
+    experimentsRuntime.extendsFrom testRuntime
+}
+tasks.register('experimentsTest', Test) {
+    group = "verification"
+    testClassesDirs = sourceSets.experiments.output.classesDirs
+    classpath = sourceSets.experiments.runtimeClasspath
+}
+
 dependencies {
     // AMAK (Only one of the next two lines should be uncommented. Also, settings.gradle file should also be modified)
      implementation 'com.github.alexandreprl:amak:3.1.0' // Uncomment this line to get AMAK from git repository (The last part matches a tag, a commit hash or the last commit of a branch : branchName-SNAPSHOT
 //    implementation project(':amak') // Uncomment this line to get AMAK from local
 
-    
+     implementation 'com.github.alexandreprl:lxplot:main-SNAPSHOT'
+
     testImplementation platform('org.junit:junit-bom:5.9.1')
     testImplementation 'org.junit.jupiter:junit-jupiter'
 
diff --git a/src/experiments/groovy/TemperatureClustering.groovy b/src/experiments/groovy/TemperatureClustering.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..86b955cfaf1576cd163747e327b210f8bad91397
--- /dev/null
+++ b/src/experiments/groovy/TemperatureClustering.groovy
@@ -0,0 +1,186 @@
+import fr.irit.smac.amas4dc.AMAS4DC
+import fr.irit.smac.amas4dc.amas.AMASOption
+import fr.irit.smac.amas4dc.amas.MASSettings
+import fr.irit.smac.amas4dc.cluster.Cluster
+import fr.irit.smac.amas4dc.cluster.DataPoint
+import fr.irit.smac.amas4dc.cluster.DataPointFuser
+import fr.irit.smac.amas4dc.cluster.SimilarityScoreMethod
+import fr.irit.smac.lxplot.LxPlot
+import groovy.transform.ToString
+
+import javax.swing.*
+import java.awt.*
+import java.util.List
+
+class TemperatureClustering {
+	static void main(String[] args) {
+
+		def amas4dc = new AMAS4DC<TemperatureDataPoint>(new MASSettings<TemperatureDataPoint>(new TemperatureSimilarityMethod(),
+		                                                                                      0.5f,
+		                                                                                      EnumSet.noneOf(AMASOption),
+		                                                                                      new TemperatureDataPointFuser()))
+
+//		11 juin 2023
+//		https://www.infoclimat.fr/observations-meteo/archives/11/juin/2023/paris-montsouris/07156.html
+		def initialData = [
+				new TemperatureDataPoint(21.9, 0),
+				new TemperatureDataPoint(21.8, 1),
+				new TemperatureDataPoint(21.7, 2),
+				new TemperatureDataPoint(21.2, 3),
+				new TemperatureDataPoint(21.1, 4),
+				new TemperatureDataPoint(20.6, 5),
+				new TemperatureDataPoint(20.5, 6),
+				new TemperatureDataPoint(20.8, 7),
+				new TemperatureDataPoint(21.3, 8),
+				new TemperatureDataPoint(22.6, 9),
+				new TemperatureDataPoint(23.9, 10),
+				new TemperatureDataPoint(24.9, 11),
+				new TemperatureDataPoint(25.7, 12),
+				new TemperatureDataPoint(26.8, 13),
+				new TemperatureDataPoint(25.8, 14),
+				new TemperatureDataPoint(25.4, 15),
+				new TemperatureDataPoint(24.8, 16),
+				new TemperatureDataPoint(24.9, 17),
+				new TemperatureDataPoint(24.1, 18),
+				new TemperatureDataPoint(23.9, 19),
+				new TemperatureDataPoint(24.1, 20),
+				new TemperatureDataPoint(22.1, 21),
+				new TemperatureDataPoint(18.9, 22),
+				new TemperatureDataPoint(18.8, 23)
+		]
+
+
+		def frame = new JFrame()
+		def plotter = new Plotter()
+		frame.setContentPane(plotter)
+		frame.setSize(1400, 700)
+		frame.setVisible(true)
+
+		for (i in 0..<initialData.size()) {
+			def data = initialData.subList(i, i + 1)
+			plotter.renderInputData(initialData.subList(0, i + 1))
+			amas4dc.fit(data)
+			var results = amas4dc.retrieveClusters()
+			println(results)
+			plotter.renderClusters(results.clusters())
+			sleep(1000)
+		}
+	}
+
+	@ToString
+	static class TemperatureDataPoint implements DataPoint {
+		double degrees
+		double startTime, endTime
+		double averageTime
+
+		TemperatureDataPoint(double degrees, double startTime, double endTime) {
+			this.degrees = degrees
+			this.startTime = startTime
+			this.endTime = endTime
+			this.averageTime = (startTime + endTime) / 2
+		}
+
+		TemperatureDataPoint(double degrees, double time) {
+			this(degrees, time, time)
+		}
+	}
+
+	static class TemperatureDataPointFuser implements DataPointFuser<TemperatureDataPoint> {
+
+		@Override
+		TemperatureDataPoint apply(TemperatureDataPoint dp1, TemperatureDataPoint dp2) {
+			def newStartTime = dp1.startTime
+			def newEndTime = dp1.endTime
+			if (dp2.startTime < newStartTime)
+				newStartTime = dp2.startTime
+			if (dp2.endTime > newEndTime)
+				newEndTime = dp2.endTime
+			return new TemperatureDataPoint(dp1.degrees * 0.9 + dp2.degrees * 0.1, newStartTime, newEndTime)
+		}
+	}
+
+	static class TemperatureSimilarityMethod implements SimilarityScoreMethod<TemperatureDataPoint> {
+
+		@Override
+		float apply(TemperatureDataPoint dp1, TemperatureDataPoint dp2) {
+			var temporallySimilar = false
+			if (dp1.endTime < dp2.startTime) {
+				if (dp2.startTime - dp1.endTime < 2)
+					temporallySimilar = true
+				else
+					temporallySimilar = false
+			} else if (dp2.endTime < dp1.startTime) {
+				if (dp1.startTime - dp2.endTime < 2)
+					temporallySimilar = true
+				else
+					temporallySimilar = false
+			}
+			if (Math.abs(dp1.degrees - dp2.degrees) < 1 && temporallySimilar)
+				return 1;
+			return 0;
+		}
+
+		@Override
+		boolean areRoughlySimilar(TemperatureDataPoint dp1, TemperatureDataPoint dp2) {
+			return true
+		}
+	}
+
+	static class Plotter extends JPanel {
+		ArrayList<TemperatureDataPoint> inputData
+		List<Cluster<TemperatureDataPoint>> clusters
+		Map<String, Color> clustersColors = new HashMap<>()
+
+		@Override
+		protected void paintComponent(Graphics g) {
+			var g2d = (Graphics2D) g
+
+			if (inputData == null)
+				return
+			g2d.setColor(Color.white)
+			g2d.fillRect(0, 0, 1400, 700)
+			g2d.setColor(Color.BLACK)
+			g2d.drawLine(timeToX(0), degreeToY(0), timeToX(24), degreeToY(0))
+			g2d.drawLine(timeToX(0), degreeToY(0), timeToX(0), degreeToY(30))
+
+			for (def tdp : inputData) {
+				g2d.drawOval(timeToX(tdp.startTime) - 3, degreeToY(tdp.degrees) - 3, 6, 6)
+			}
+			def random = new Random()
+			for (def c : clusters) {
+				def color = clustersColors.computeIfAbsent(c.id, id -> new Color(random.nextFloat(), random.nextFloat(), random.nextFloat()))
+				g2d.setColor(Color.BLUE)
+				g2d.setStroke(new BasicStroke(2))
+				g2d.drawLine(timeToX(c.representative.startTime), degreeToY(c.representative.degrees), timeToX(c.representative.endTime), degreeToY(c.representative.degrees))
+			}
+			g2d.setColor(Color.BLACK)
+			for (i in 0..<24) {
+				g2d.drawString(i + "", timeToX(i), 665)
+			}
+			g2d.drawString("Hour of the day", timeToX(24), 665)
+			for (i in 0..<30) {
+				g2d.drawString(i + "", 10, degreeToY(i))
+			}
+			g2d.drawString("Temperature °C", 10, degreeToY(30))
+
+		}
+
+		int timeToX(double time) {
+			return (int) (time * 40) + 50
+		}
+
+		int degreeToY(double degree) {
+			return 650 - (int) (degree * 20)
+		}
+
+		void renderInputData(List<TemperatureDataPoint> inputDataList) {
+			this.inputData = inputDataList
+			repaint();
+		}
+
+		void renderClusters(List<Cluster<TemperatureDataPoint>> clusters) {
+			this.clusters = clusters
+			repaint()
+		}
+	}
+}
diff --git a/src/experiments/groovy/TwoDimensionsPointsClustering.groovy b/src/experiments/groovy/TwoDimensionsPointsClustering.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..31d9bbb526bcc3c142b6c47bb41e281d3e846ee6
--- /dev/null
+++ b/src/experiments/groovy/TwoDimensionsPointsClustering.groovy
@@ -0,0 +1,65 @@
+import fr.irit.smac.amas4dc.AMAS4DC
+import fr.irit.smac.amas4dc.amas.AMASOption
+import fr.irit.smac.amas4dc.amas.MASSettings
+import fr.irit.smac.amas4dc.cluster.Cluster
+import fr.irit.smac.amas4dc.cluster.DataPoint
+import fr.irit.smac.amas4dc.cluster.SimilarityScoreMethod
+import fr.irit.smac.lxplot.LxPlot
+import fr.irit.smac.lxplot.commons.ChartType
+import groovy.transform.ToString
+
+import java.awt.geom.Point2D
+
+class TwoDimensionsPointsClustering {
+	static void main(String[] args) {
+
+		def amas4dc = new AMAS4DC<TwoDimensionsDataPoint>(new MASSettings(new TwoDimensionsSimilarityMethod(), 0.5f, EnumSet.noneOf(AMASOption), (TwoDimensionsDataPoint dp1, TwoDimensionsDataPoint dp2) -> {
+			return new TwoDimensionsDataPoint(new Point2D.Double((dp1.getPoint2D().getX() + dp2.getPoint2D().getX()) / 2,
+			                                                     (dp1.getPoint2D().getY() + dp2.getPoint2D().getY()) / 2))
+		}))
+
+		LxPlot.getChart("Data", ChartType.PLOT);
+		def initialData = [new TwoDimensionsDataPoint(new Point2D.Double(1.0, 1.0)), new TwoDimensionsDataPoint(new Point2D.Double(2.0, 2.0))]
+		render(initialData)
+		amas4dc.fit(initialData)
+		var results = amas4dc.retrieveClusters()
+		println(results)
+		render(results.clusters())
+	}
+
+	static void render(ArrayList<TwoDimensionsDataPoint> twoDimensionsDataPoints) {
+		for (def tddp : twoDimensionsDataPoints) {
+			LxPlot.getChart("Data").add("Initial", tddp.point2D.getX(), tddp.getPoint2D().getY())
+		}
+	}
+
+	static void render(List<Cluster<TwoDimensionsDataPoint>> clusters) {
+		for (def c : clusters) {
+			LxPlot.getChart("Data").add("Cluster representative point", c.getRepresentative().point2D.getX(), c.getRepresentative().point2D.getY())
+		}
+	}
+
+	@ToString
+	static class TwoDimensionsDataPoint implements DataPoint {
+		Point2D.Double point2D
+
+		TwoDimensionsDataPoint(Point2D.Double point2D) {
+			this.point2D = point2D
+		}
+	}
+
+	static class TwoDimensionsSimilarityMethod implements SimilarityScoreMethod<TwoDimensionsDataPoint> {
+
+		@Override
+		float apply(TwoDimensionsDataPoint dp1, TwoDimensionsDataPoint dp2) {
+			if (dp1.point2D.distance(dp2.point2D) < 10)
+				return 1;
+			return 0
+		}
+
+		@Override
+		boolean areRoughlySimilar(TwoDimensionsDataPoint dp1, TwoDimensionsDataPoint dp2) {
+			return true
+		}
+	}
+}