diff --git a/pq-tls-benchmark-framework/emulation-exp/code/kex/docs/Makefile b/pq-tls-benchmark-framework/emulation-exp/code/kex/docs/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..fefa43230760f95275c39462144369d9350bd6f8
--- /dev/null
+++ b/pq-tls-benchmark-framework/emulation-exp/code/kex/docs/Makefile
@@ -0,0 +1,3 @@
+
+sequence_diagram.pdf: sequence_diagram.puml
+	plantuml -tpdf $<
diff --git a/pq-tls-benchmark-framework/emulation-exp/code/kex/docs/sequence_diagram.pdf b/pq-tls-benchmark-framework/emulation-exp/code/kex/docs/sequence_diagram.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..50f677da2ba13b84ddbd7111e8f27ea4a396b5f2
Binary files /dev/null and b/pq-tls-benchmark-framework/emulation-exp/code/kex/docs/sequence_diagram.pdf differ
diff --git a/pq-tls-benchmark-framework/emulation-exp/code/kex/docs/sequence_diagram.puml b/pq-tls-benchmark-framework/emulation-exp/code/kex/docs/sequence_diagram.puml
new file mode 100644
index 0000000000000000000000000000000000000000..c30ee26c657d5c2c0ec531e06184e5701513296b
--- /dev/null
+++ b/pq-tls-benchmark-framework/emulation-exp/code/kex/docs/sequence_diagram.puml
@@ -0,0 +1,43 @@
+@startuml sequence_diagram
+activate Client
+Client -> Client: do_tls_handshake()
+activate Client
+
+' activate NetEm
+' ' ||1||
+' deactivate NetEm
+
+Client -> Client: KeyGen()
+activate Client
+deactivate Client
+Client -> NetEm: { Client Hello }
+
+' Client -> Client: waiting for response
+' deactivate Client
+' ... Client waiting ...
+' deactivate Client
+
+activate NetEm
+NetEm -> Server: { Client Hello }
+' ... Server waiting ... 
+deactivate NetEm
+activate Server
+Server -> Server: generate_response()
+activate Server
+Server -> Server: Encaps()
+activate Server
+deactivate Server
+deactivate Server
+' Server -> NetEm: Server Hello, Encrypted Extensions, Certificate, Certificate Verify,Finished
+Server -> NetEm: { Server Hello, Certificate, Finished }
+deactivate Server
+
+activate NetEm
+NetEm -> Client: { Server Hello, Certificate, Finished }
+deactivate NetEm
+
+Client -> Client: Decaps()
+activate Client
+deactivate Client
+deactivate Client
+@enduml
diff --git a/pq-tls-benchmark-framework/emulation-exp/code/kex/scripts/generate_graphs.py b/pq-tls-benchmark-framework/emulation-exp/code/kex/scripts/generate_graphs.py
index 086e5f1934b01e8952bbd3480880b446e4799b99..6adcbba692502f4b8a4c3b0a4399ff4a0ccaab3c 100755
--- a/pq-tls-benchmark-framework/emulation-exp/code/kex/scripts/generate_graphs.py
+++ b/pq-tls-benchmark-framework/emulation-exp/code/kex/scripts/generate_graphs.py
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 
 from functools import cmp_to_key
+import colorsys
 import os
 import sys
 
@@ -18,17 +19,17 @@ PLOTS_DIR = "plots"
 FEATHERS_DIR = "feathers"
 
 cmap = plt.cm.hsv
-
-# TODO use the riqr to make some graphs
+# cmap = plt.colormaps.get_cmap("nipy_spectral")
 
 
 def main():
     data = load_data()
 
     # generally they both only seconds for graphs when not generating for single algorithms
+    # plot_general_plots()  # takes about 4 seconds
     plot_lines(data)  # takes about 1:50 min
     # plot_static_data(data)  # takes about 4 min
-    # plot_general_plots()  # takes about 4 seconds
+    # plot_distributions(data)
 
 
 def load_data():
@@ -215,6 +216,7 @@ def filter_data(
     protocol: str | None = None,
     sec_level: str | list[str] | None = None,
     kem_alg: str | None = None,
+    drop_zero_columns: bool = True,
 ):
     filtered_data = data
     # print(filtered_data["kem_alg"] == "x25519") # is a boolean series
@@ -241,7 +243,11 @@ def filter_data(
         ]
         return data.drop(columns=zero_columns_to_drop)
 
-    filtered_data = drop_columns_with_only_zero_values(filtered_data)
+    if drop_zero_columns and "measurements" in data.columns:
+        filtered_data = drop_columns_with_only_zero_values(filtered_data)
+
+        #     if drop_zero_columns:
+        # filtered_data = drop_columns_with_only_zero_values(filtered_data)
 
     # print(filtered_data["measurements"].head())
     # print(filtered_data)
@@ -269,7 +275,7 @@ def get_x_axis(scenario, data, length):
         case "rate_server":
             return data["srv_rate"]
         case "static":
-            return list(range(length))
+            return pd.Series(range(length))
         case _:
             print(f"NO MATCH FOUND FOR {scenario}", file=sys.stderr)
             sys.exit(1)
@@ -289,6 +295,327 @@ def map_security_level_hybrid_together(sec_level: str):
             return None
 
 
+def get_color_and_mode(kem_alg: str, combined_with_hybrids: bool = False):
+    # NOTE maybe just use hle colors directly from the start
+    primary_mode = "-"
+    secondary_mode = "--" if combined_with_hybrids else "-"
+    tertiary_mode = ":" if combined_with_hybrids else "--"
+
+    secondary_lightness_factor = 0.9 if combined_with_hybrids else 1
+    tertiary_lightness_factor = 0.8 if combined_with_hybrids else 0.9
+
+    a = 0.8 if combined_with_hybrids else 1
+
+    no_algos = 8
+    match kem_alg:
+        case "secp256r1":
+            return cmap(0 / no_algos), primary_mode
+        case "secp384r1":
+            return cmap(0.3 / no_algos), primary_mode
+        case "secp521r1":
+            return cmap(0.6 / no_algos), primary_mode
+        case "x25519":
+            return cmap(0 / no_algos), secondary_mode
+        case "x448":
+            return cmap(0.3 / no_algos), secondary_mode
+        case "mlkem512":
+            return cmap(1 / no_algos), primary_mode
+        case "p256_mlkem512":
+            return (
+                transform_cmap_color(
+                    cmap(1 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=secondary_lightness_factor,
+                ),
+                secondary_mode,
+            )
+        case "x25519_mlkem512":
+            return (
+                transform_cmap_color(
+                    cmap(1 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=tertiary_lightness_factor,
+                ),
+                tertiary_mode,
+            )
+        case "mlkem768":
+            return cmap(1.3 / no_algos), primary_mode
+        case "p384_mlkem768":
+            return (
+                transform_cmap_color(
+                    cmap(1.3 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=secondary_lightness_factor,
+                ),
+                secondary_mode,
+            )
+        case "x448_mlkem768":
+            return (
+                transform_cmap_color(
+                    cmap(1.3 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=tertiary_lightness_factor,
+                ),
+                tertiary_mode,
+            )
+        case "mlkem1024":
+            return cmap(1.6 / no_algos), primary_mode
+        case "p521_mlkem1024":
+            return (
+                transform_cmap_color(
+                    cmap(1.6 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=secondary_lightness_factor,
+                ),
+                secondary_mode,
+            )
+        case "bikel1":
+            return cmap(2 / no_algos), primary_mode
+        case "p256_bikel1":
+            return (
+                transform_cmap_color(
+                    cmap(2 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=secondary_lightness_factor,
+                ),
+                secondary_mode,
+            )
+        case "x25519_bikel1":
+            return (
+                transform_cmap_color(
+                    cmap(2 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=tertiary_lightness_factor,
+                ),
+                tertiary_mode,
+            )
+        case "bikel3":
+            return cmap(2.3 / no_algos), primary_mode
+        case "p384_bikel3":
+            return (
+                transform_cmap_color(
+                    cmap(2.3 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=secondary_lightness_factor,
+                ),
+                secondary_mode,
+            )
+        case "x448_bikel3":
+            return (
+                transform_cmap_color(
+                    cmap(2.3 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=tertiary_lightness_factor,
+                ),
+                tertiary_mode,
+            )
+        case "bikel5":
+            return cmap(2.6 / no_algos), primary_mode
+        case "p521_bikel5":
+            return (
+                transform_cmap_color(
+                    cmap(2.6 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=secondary_lightness_factor,
+                ),
+                secondary_mode,
+            )
+        case "hqc128":
+            return cmap(4 / no_algos), primary_mode
+        case "p256_hqc128":
+            return (
+                transform_cmap_color(
+                    cmap(4 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=secondary_lightness_factor,
+                ),
+                secondary_mode,
+            )
+        case "x25519_hqc128":
+            return (
+                transform_cmap_color(
+                    cmap(4 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=tertiary_lightness_factor,
+                ),
+                tertiary_mode,
+            )
+        case "hqc192":
+            return cmap(4.3 / no_algos), primary_mode
+        case "p384_hqc192":
+            return (
+                transform_cmap_color(
+                    cmap(4.3 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=secondary_lightness_factor,
+                ),
+                secondary_mode,
+            )
+        case "x448_hqc192":
+            return (
+                transform_cmap_color(
+                    cmap(4.3 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=tertiary_lightness_factor,
+                ),
+                tertiary_mode,
+            )
+        case "hqc256":
+            return cmap(4.6 / no_algos), primary_mode
+        case "p521_hqc256":
+            return (
+                transform_cmap_color(
+                    cmap(4.6 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=secondary_lightness_factor,
+                ),
+                secondary_mode,
+            )
+        case "frodo640aes":
+            return cmap(5 / no_algos), primary_mode
+        case "p256_frodo640aes":
+            return (
+                transform_cmap_color(
+                    cmap(5 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=secondary_lightness_factor,
+                ),
+                secondary_mode,
+            )
+        case "x25519_frodo640aes":
+            return (
+                transform_cmap_color(
+                    cmap(5 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=tertiary_lightness_factor,
+                ),
+                tertiary_mode,
+            )
+        case "frodo640shake":
+            return cmap(6 / no_algos), primary_mode
+        case "p256_frodo640shake":
+            return (
+                transform_cmap_color(
+                    cmap(6 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=secondary_lightness_factor,
+                ),
+                secondary_mode,
+            )
+        case "x25519_frodo640shake":
+            return (
+                transform_cmap_color(
+                    cmap(6 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=tertiary_lightness_factor,
+                ),
+                tertiary_mode,
+            )
+        case "frodo976aes":
+            return cmap(5.3 / no_algos), primary_mode
+        case "p384_frodo976aes":
+            return (
+                transform_cmap_color(
+                    cmap(5.3 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=secondary_lightness_factor,
+                ),
+                secondary_mode,
+            )
+        case "x448_frodo976aes":
+            return (
+                transform_cmap_color(
+                    cmap(5.3 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=tertiary_lightness_factor,
+                ),
+                tertiary_mode,
+            )
+        case "frodo976shake":
+            return cmap(6.3 / no_algos), primary_mode
+        case "p384_frodo976shake":
+            return (
+                transform_cmap_color(
+                    cmap(6.3 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=secondary_lightness_factor,
+                ),
+                secondary_mode,
+            )
+        case "x448_frodo976shake":
+            return (
+                transform_cmap_color(
+                    cmap(6.3 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=tertiary_lightness_factor,
+                ),
+                tertiary_mode,
+            )
+        case "frodo1344aes":
+            return cmap(5.6 / no_algos), primary_mode
+        case "p521_frodo1344aes":
+            return (
+                transform_cmap_color(
+                    cmap(5.6 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=secondary_lightness_factor,
+                ),
+                secondary_mode,
+            )
+        case "frodo1344shake":
+            return cmap(6.6 / no_algos), primary_mode
+        case "p521_frodo1344shake":
+            return (
+                transform_cmap_color(
+                    cmap(6.6 / no_algos),
+                    alpha_factor=a,
+                    lightness_factor=secondary_lightness_factor,
+                ),
+                secondary_mode,
+            )
+        case "x25519_mlkem768":
+            return (
+                transform_cmap_color(cmap(7 / no_algos), alpha_factor=a),
+                primary_mode,
+            )
+        case "p256_mlkem768":
+            return (
+                transform_cmap_color(
+                    cmap(7.5 / no_algos), lightness_factor=secondary_lightness_factor
+                ),
+                primary_mode,
+            )
+        case "p384_mlkem1024":
+            return (
+                transform_cmap_color(
+                    cmap(7.99 / no_algos), lightness_factor=tertiary_lightness_factor
+                ),
+                primary_mode,
+            )
+        case _:
+            print(f"NO COLOR MATCH FOUND FOR {kem_alg}", file=sys.stderr)
+            sys.exit(1)
+
+
+def transform_cmap_color(
+    color, hue_shift=0, saturation_factor=1, lightness_factor=1, alpha_factor=1
+):
+    def value_between(minimum, value, maximum):
+        return max(minimum, min(value, maximum))
+
+    r, g, b, a = color
+    h, l, s = colorsys.rgb_to_hls(r, g, b)
+    h += hue_shift
+    l *= lightness_factor
+    l = value_between(0, l, 1)
+    s *= saturation_factor
+    s = value_between(0, s, 1)
+    r, g, b = colorsys.hls_to_rgb(h, l, s)
+    a *= alpha_factor
+    return r, g, b, a
+
+
+# plots lines of different statistical values
 def plot_lines(data):
     def plot_lines_for_sec_level(
         data, line_type="median", combined_with_hybrids: bool = False
@@ -323,7 +650,11 @@ def plot_lines(data):
             for idx, kem_alg in enumerate(
                 filtered_data["kem_alg"].unique().sort_values()
             ):
-                color = cmap(idx / len(filtered_data["kem_alg"].unique()))
+                # color = cmap(idx / len(filtered_data["kem_alg"].unique()))
+                color, mode = get_color_and_mode(
+                    kem_alg, combined_with_hybrids=combined_with_hybrids
+                )
+
                 filtered_data_single_kem_alg = filter_data(
                     filtered_data, kem_alg=kem_alg
                 )
@@ -338,7 +669,7 @@ def plot_lines(data):
                 # print(f"y: {y}")
 
                 # plt.fill_between(x, filtered_data_single_kem_alg["qtl_25"], filtered_data_single_kem_alg["qtl_75"], alpha=0.2, color=color)
-                plt.plot(x, y, linestyle="-", marker=".", color=color, label=kem_alg)
+                plt.plot(x, y, linestyle=mode, marker=".", color=color, label=kem_alg)
 
             plt.ylim(bottom=0)
             plt.xlim(left=0)
@@ -358,7 +689,7 @@ def plot_lines(data):
                 subdir = "combined-with-hybrids/"
                 appendix = "-combined-with-hybrids"
             plt.savefig(
-                f"{PLOTS_DIR}/{line_type}s-of-sec-level/{subdir}{line_type}-{row['scenario']}-{row['protocol']}-{row['sec_level']}{appendix}.png"
+                f"{PLOTS_DIR}/{line_type}s-of-sec-level/{subdir}{line_type}-{row['scenario']}-{row['protocol']}-{row['sec_level']}{appendix}.pdf"
             )
             plt.close()
 
@@ -397,25 +728,10 @@ def plot_lines(data):
             )
 
             plt.savefig(
-                f"{PLOTS_DIR}/median-of-single-algorithm/median-{row['scenario']}-{row['protocol']}-{row['sec_level']}-{row['kem_alg']}.png"
+                f"{PLOTS_DIR}/median-of-single-algorithm/median-{row['scenario']}-{row['protocol']}-{row['sec_level']}-{row['kem_alg']}.pdf"
             )
             plt.close()
 
-    # This does not yet seem like a good idea
-    def plot_median_against_iqr(data):
-        plt.figure()
-        plt.hexbin(data["median"], data["iqr"], gridsize=50)
-        print(data["iqr"].describe())
-        print(data["median"].describe())
-        # get the line with the maximum median
-        max_median = data["median"].idxmax()
-        print(data.iloc[max_median])
-        plt.savefig(f"{PLOTS_DIR}/median_against_iqr_hexbin.png")
-
-        plt.figure()
-        plt.hist2d(data["median"], data["iqr"], bins=50)
-        plt.savefig(f"{PLOTS_DIR}/median_against_iqr_hist2d.png")
-
     do_graphs_for = [
         "median",
         "qtl_25",
@@ -428,6 +744,7 @@ def plot_lines(data):
         "kurtosis",
     ]
     for thing in do_graphs_for:
+        print(f"Generating graphs for {thing}")
         plot_lines_for_sec_level(data, line_type=thing, combined_with_hybrids=False)
         plot_lines_for_sec_level(data, line_type=thing, combined_with_hybrids=True)
 
@@ -435,6 +752,113 @@ def plot_lines(data):
     # plot_median_against_iqr(data)
 
 
+# plots distributions of the individual data points
+def plot_distributions(data):
+    os.makedirs(
+        f"{PLOTS_DIR}/distributions/single",
+        mode=0o777,
+        exist_ok=True,
+    )
+
+    def plot_multiple_violin_plots(data, filtered: bool = False):
+        os.makedirs(
+            f"{PLOTS_DIR}/distributions/filtered",
+            mode=0o777,
+            exist_ok=True,
+        )
+
+        unique_combinations = data[
+            ["scenario", "protocol", "sec_level", "kem_alg"]
+        ].drop_duplicates()
+        # print(unique_combinations)
+        for _, row in unique_combinations.iterrows():
+            filtered_data = filter_data(
+                data,
+                scenario=row["scenario"],
+                protocol=row["protocol"],
+                sec_level=row["sec_level"],
+                kem_alg=row["kem_alg"],
+            )
+            print(
+                f"scenario: {row['scenario']}, protocol: {row['protocol']}, sec_level: {row['sec_level']}, kem_alg: {row['kem_alg']}, len: {len(filtered_data)}"
+            )
+
+            if filtered:
+                filtered_data = pd.concat(
+                    [
+                        filtered_data.iloc[[0]],
+                        filtered_data.iloc[3:-4:4],
+                        filtered_data.iloc[[-1]],
+                    ]
+                )
+                print(filtered_data)
+
+            plt.figure()
+            x = get_x_axis(row["scenario"], filtered_data, len(filtered_data))
+            x = x.to_list()
+            # print(x)
+            width = 0.5 if not filtered else 2
+            plt.violinplot(
+                filtered_data["measurements"],
+                positions=x,
+                showmedians=True,
+                widths=width,
+            )
+            # make the median line transparent
+            # for pc in plt.gca().collections:
+            #     pc.set_alpha(0.5)
+
+            plt.ylim(bottom=0)
+            plt.xlim(left=0)
+            plt.xlabel(row["scenario"])
+            plt.ylabel(f"Time-to-first-byte (ms)")
+
+            subdir = "filtered/" if filtered else ""
+            appendix = "-filtered" if filtered else ""
+            plt.savefig(
+                f"{PLOTS_DIR}/distributions/{subdir}multiple-violin-plots-for-{row['scenario']}-{row['protocol']}-{row['sec_level']}-{row['kem_alg']}{appendix}.pdf"
+            )
+            plt.close()
+
+    def plot_single_violin_plot(data):
+        unique_combinations = data[
+            ["scenario", "protocol", "sec_level", "kem_alg"]
+        ].drop_duplicates()
+        for _, row in unique_combinations.iterrows():
+            filtered_data = filter_data(
+                data,
+                scenario=row["scenario"],
+                protocol=row["protocol"],
+                sec_level=row["sec_level"],
+                kem_alg=row["kem_alg"],
+            )
+            print(
+                f"scenario: {row['scenario']}, protocol: {row['protocol']}, sec_level: {row['sec_level']}, kem_alg: {row['kem_alg']}"
+            )
+            for _, row in filtered_data.iterrows():
+                if row["scenario"] == "static":
+                    continue
+                value = get_x_axis(row["scenario"], row, 1)
+                print(value)
+
+                plt.figure()
+                plt.violinplot(row["measurements"], showmedians=True)
+                # plt.ylim(bottom=0)
+                # plt.xlim(left=0)
+                plt.xlabel("Dichte")
+                plt.ylabel(f"Time-to-first-byte (ms)")
+
+                plt.savefig(
+                    f"{PLOTS_DIR}/distributions/single/single-violin-plot-for-{row['scenario']}-{row['protocol']}-{row['sec_level']}-{row['kem_alg']}-{value}.pdf"
+                )
+                plt.close()
+                # return
+
+    plot_multiple_violin_plots(data, filtered=False)
+    plot_multiple_violin_plots(data, filtered=True)
+    # plot_single_violin_plot(data)  # takes an age
+
+
 # TODO make a violinplot/eventplot for many algos in static scenario
 def plot_static_data(data):
     os.makedirs(f"{PLOTS_DIR}/static/single", mode=0o777, exist_ok=True)
@@ -492,9 +916,12 @@ def plot_static_data(data):
                 # )
 
             # Get the median irqs for all algorithms
+            # print(row["protocol"], row["sec_level"])
             # print(iqrs)
             # print(iqrs["iqr"].describe())
-            # print("Median: ", iqrs["iqr"].median())
+            # print("Median:", iqrs["iqr"].median())
+            # print("IQR:", scipy.stats.iqr(iqrs["iqr"]))
+            # print()
 
             plt.xticks(
                 range(len(filtered_data["kem_alg"].unique())),
@@ -513,7 +940,7 @@ def plot_static_data(data):
                 os.path.join(
                     PLOTS_DIR,
                     "static",
-                    f"boxplots-of-medians-for-static-{row['protocol']}-{sec_level_string}.png",
+                    f"boxplots-of-medians-for-static-{row['protocol']}-{sec_level_string}.pdf",
                 )
             )
             plt.close()
@@ -522,6 +949,7 @@ def plot_static_data(data):
         unique_combinations = data[
             ["scenario", "protocol", "sec_level", "kem_alg"]
         ].drop_duplicates()
+        unique_combinations = filter_data(unique_combinations, scenario="static")
         for idx, row in unique_combinations.iterrows():
             filtered_data = filter_data(
                 data,
@@ -531,45 +959,96 @@ def plot_static_data(data):
                 kem_alg=row["kem_alg"],
             )
 
-            plt.figure()
-            plt.boxplot(filtered_data["median"])
-            plt.savefig(
-                os.path.join(
-                    PLOTS_DIR,
-                    "static",
-                    "single",
-                    f"boxplot-of-medians-for-{row['scenario']}-{row['protocol']}-{row['sec_level']}-{row['kem_alg']}.png",
+            def boxplot_of_medians_for_configuration(filtered_data, row):
+                plt.figure()
+                plt.boxplot(filtered_data["median"])
+                plt.savefig(
+                    os.path.join(
+                        PLOTS_DIR,
+                        "static",
+                        "single",
+                        f"boxplot-of-medians-for-{row['scenario']}-{row['protocol']}-{row['sec_level']}-{row['kem_alg']}.pdf",
+                    )
                 )
-            )
-            plt.close()
-
-            plt.figure()
-            plt.violinplot(filtered_data["measurements"], showmedians=True)
-            plt.savefig(
-                os.path.join(
-                    PLOTS_DIR,
-                    "static",
-                    "single",
-                    f"multiple-violin-plots-for-{row['scenario']}-{row['protocol']}-{row['sec_level']}-{row['kem_alg']}.png",
+                plt.close()
+
+            # why the density of violin plot and kde plot differ, while using the same scott kde just sideways:
+            # Dove deep into the implementation from matplotlib and scipy, and they seem to calculate scotts factor in the same way, so dunno
+            def condensed_violin_plot_for_configuration(filtered_data, row):
+                # for multiple runs of the same static scenario, data taken together
+                measurements_flattend = filtered_data["measurements"].explode().tolist()
+                # print(filtered_data["measurements"].explode())
+                # print(len(measurements_flattend))
+                plt.figure()
+                plt.violinplot(measurements_flattend, showmedians=True)
+                plt.ylabel("Time-to-first-byte (ms)")
+                plt.xlabel("Dichte")
+                plt.savefig(
+                    os.path.join(
+                        PLOTS_DIR,
+                        "static",
+                        "single",
+                        f"condensed-violin-plot-for-{len(measurements_flattend)}-measurements-of-{row['scenario']}-{row['protocol']}-{row['sec_level']}-{row['kem_alg']}.pdf",
+                    )
                 )
-            )
-            plt.close()
+                plt.close()
+
+            def condensed_histogram_plot_for_configuration(filtered_data, row):
+                measurements_flattend = filtered_data["measurements"].explode().tolist()
+                plt.figure()
+                plt.hist(measurements_flattend, bins=100, density=True)
+                plt.xlabel("Time-to-first-byte (ms)")
+                plt.ylabel("Dichte")
+
+                plt.savefig(
+                    os.path.join(
+                        PLOTS_DIR,
+                        "static",
+                        "single",
+                        f"condensed-histogram-plot-for-{len(measurements_flattend)}-measurements-of-{row['scenario']}-{row['protocol']}-{row['sec_level']}-{row['kem_alg']}.pdf",
+                    )
+                )
+                plt.close()
 
-            # for multiple runs of the same static scenario, data taken together
-            measurements_flattend = filtered_data["measurements"].explode().tolist()
-            # print(filtered_data["measurements"].explode())
-            # print(len(measurements_flattend))
-            plt.figure()
-            plt.violinplot(measurements_flattend, showmedians=True)
-            plt.savefig(
-                os.path.join(
-                    PLOTS_DIR,
-                    "static",
-                    "single",
-                    f"condensed-violin-plot-for-{len(measurements_flattend)}-measurements-of-{row['scenario']}-{row['protocol']}-{row['sec_level']}-{row['kem_alg']}.png",
+            def condensed_kernel_density_estimate_plot_for_configuration(
+                filtered_data, row
+            ):
+                measurements_flattend = filtered_data["measurements"].explode().tolist()
+                plt.figure()
+
+                kde = scipy.stats.gaussian_kde(measurements_flattend)
+                xmin = min(measurements_flattend) - 0.2
+                xmax = max(measurements_flattend) + 0.1
+                x = np.linspace(
+                    xmin,
+                    xmax,
+                    1000,
                 )
-            )
-            plt.close()
+                kde_values = kde(x)
+
+                plt.plot(x, kde_values)
+                plt.fill_between(x, kde_values, alpha=0.5)
+
+                plt.xlabel("Time-to-first-byte (ms)")
+                plt.ylabel("Dichte")
+                plt.xlim([xmin, xmax])
+                plt.ylim([0, max(kde_values) + 0.1])
+
+                plt.savefig(
+                    os.path.join(
+                        PLOTS_DIR,
+                        "static",
+                        "single",
+                        f"condensed-kde-plot-for-{len(measurements_flattend)}-measurements-of-{row['scenario']}-{row['protocol']}-{row['sec_level']}-{row['kem_alg']}.pdf",
+                    )
+                )
+                plt.close()
+
+            boxplot_of_medians_for_configuration(filtered_data, row)
+            condensed_violin_plot_for_configuration(filtered_data, row)
+            condensed_histogram_plot_for_configuration(filtered_data, row)
+            condensed_kernel_density_estimate_plot_for_configuration(filtered_data, row)
+            # return
 
     plot_static_data_for_multiple_algorithms(data)
     plot_static_data_for_single_algorithms(data)
@@ -643,9 +1122,9 @@ def plot_general_plots():
         plt.ylabel("Performance (µs)")
 
         name = (
-            "scatter-of-bytes-sent-against-kem-performance-with-hybrids.png"
+            "scatter-of-bytes-sent-against-kem-performance-with-hybrids.pdf"
             if with_hybrids
-            else "scatter-of-bytes-sent-against-kem-performance.png"
+            else "scatter-of-bytes-sent-against-kem-performance.pdf"
         )
         plt.savefig(
             os.path.join(PLOTS_DIR, "general", name),
@@ -718,13 +1197,31 @@ def plot_general_plots():
 
         with_hybrids_string = "-with-hybrids" if with_hybrids else ""
         with_lines_string = "-with-lines" if with_lines else ""
-        name = f"scatter-of-public-key-against-ciphertext-length{with_hybrids_string}{with_lines_string}.png"
+        name = f"scatter-of-public-key-against-ciphertext-length{with_hybrids_string}{with_lines_string}.pdf"
         plt.savefig(
             os.path.join(PLOTS_DIR, "general", name),
             dpi=300,
         )
         plt.close()
 
+    # This does not yet seem like a good idea
+    # TODO use the riqr to make some graphs
+    def plot_median_against_iqr(data):
+        plt.figure()
+        plt.hexbin(data["median"], data["iqr"], gridsize=50)
+        print(data["iqr"].describe())
+        print(data["median"].describe())
+        # get the line with the maximum median
+        max_median = data["median"].idxmax()
+        print(data.iloc[max_median])
+        plt.savefig(f"{PLOTS_DIR}/general/median_against_iqr_hexbin.pdf")
+        plt.close()
+
+        plt.figure()
+        plt.hist2d(data["median"], data["iqr"], bins=50)
+        plt.savefig(f"{PLOTS_DIR}/general/median_against_iqr_hist2d.pdf")
+        plt.close()
+
     plot_send_bytes_against_kem_performance(df, with_hybrids=False)
     plot_send_bytes_against_kem_performance(df, with_hybrids=True)