diff --git a/Cargo.toml b/Cargo.toml
index 404f080bf421ce0213e693d17cf42e3b8280519d..103b5b7c06fb5c85961643b82d36a5aa6a4e757e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -23,7 +23,6 @@ rand = "0.8.5"
 [workspace]
 members = [
     "benchmarks",
-    "bins/inbreeding",
     "bins/rank",
 ]
 
diff --git a/bins/inbreeding/Cargo.toml b/bins/inbreeding/Cargo.toml
deleted file mode 100644
index 5fbcc6973bda64d8ceafa4d6d7aa506a46208afc..0000000000000000000000000000000000000000
--- a/bins/inbreeding/Cargo.toml
+++ /dev/null
@@ -1,14 +0,0 @@
-[package]
-name = "inbreeding"
-version = "0.1.0"
-edition = "2021"
-description = "Study the 'inbreeding' phenomenon in a system supporting 'recoding'."
-
-[dependencies]
-komodo = { version = "0.2.0", path = "../.." }
-ark-pallas = "0.4.0"
-clap = { version = "4.5.4", features = ["derive"] }
-rand = "0.8.5"
-indicatif = "0.17.8"
-ark-ff = "0.4.2"
-hex = "0.4.3"
diff --git a/bins/inbreeding/README.md b/bins/inbreeding/README.md
deleted file mode 100644
index cb18b6b1b09f537741247fd1b46b7b6592197adb..0000000000000000000000000000000000000000
--- a/bins/inbreeding/README.md
+++ /dev/null
@@ -1,7 +0,0 @@
-- build the example for best performance with [`inbreeding build`](./src/.nushell/build.nu)
-- run the experiment with [`inbreeding run`](./src/.nushell/run.nu)
-- plot the results with [`inbreeding plot`](./src/.nushell/plot.nu)
-
-# Example
-see [run](scripts/run.nu)
-see [plot](scripts/plot.nu)
diff --git a/bins/inbreeding/mod.nu b/bins/inbreeding/mod.nu
deleted file mode 100644
index bb0b45a4ea81ee30605a4db3c30e6978d6d3bb52..0000000000000000000000000000000000000000
--- a/bins/inbreeding/mod.nu
+++ /dev/null
@@ -1,7 +0,0 @@
-export use src/.nushell/build.nu
-export use src/.nushell/watch.nu
-export use src/.nushell/run.nu
-export use src/.nushell/inspect.nu
-export use src/.nushell/list.nu
-export use src/.nushell/load.nu
-export use src/.nushell/plot.nu
diff --git a/bins/inbreeding/scripts/plot.nu b/bins/inbreeding/scripts/plot.nu
deleted file mode 100644
index 335746c281ba4c60243a13134b6faed7d6d8c5c9..0000000000000000000000000000000000000000
--- a/bins/inbreeding/scripts/plot.nu
+++ /dev/null
@@ -1,11 +0,0 @@
-use ../src/.nushell/consts.nu CACHE
-use ../
-
-const FIGURES_DIR = ($CACHE | path join figures)
-
-mkdir $FIGURES_DIR
-
-for exp in (inbreeding list) {
-    let img = $FIGURES_DIR | path join $exp | path parse --extension '' | update extension "png" | path join
-    inbreeding load $exp | inbreeding plot --save $img
-}
diff --git a/bins/inbreeding/scripts/run.nu b/bins/inbreeding/scripts/run.nu
deleted file mode 100644
index aac3c5865b79c015aa43fc30b100b7e81d2a10e8..0000000000000000000000000000000000000000
--- a/bins/inbreeding/scripts/run.nu
+++ /dev/null
@@ -1,57 +0,0 @@
-use ../
-
-const OPTS = {
-    nb_bytes: (10 * 1_024),
-    k: 10,
-    n: null,
-    nb_measurements: 100,
-    nb_scenarii: 100,
-    measurement_schedule: 1,
-    measurement_schedule_start: 0,
-    max_t: 150,
-    strategies: [
-        "single:1",
-        "single:2",
-        "single:3"
-        "single:4"
-        "single:5",
-        "single:6",
-        "single:7",
-        "single:8",
-        "single:9",
-        "single:10",
-    ],
-    environment: null,
-}
-
-const RHOS = [ (1 / 2), (1 / 3), (1 / 5), (1 / 10) ]
-
-const ENVS = [
-    "fixed:0",
-    "random-fixed:0.1:1",
-    "random-fixed:0.2:1",
-    "random-fixed:0.3:1",
-    "random-fixed:0.4:1",
-    "random-fixed:0.5:1",
-    "random-fixed:0.6:1",
-    "random-fixed:0.7:1",
-    "random-fixed:0.8:1",
-    "random-fixed:0.9:1",
-    "fixed:1",
-]
-
-def prod [a: list, b: list]: [ nothing -> list<list> ] {
-    $a | each { |ia| $b | each { |ib| [$ia, $ib] }} | flatten
-}
-
-def main [
-    --nb-threads: int = 8,
-    --prng-seed: string = "0000000000000000000000000000000000000000000000000000000000000000",
-] {
-    inbreeding build
-
-    prod $RHOS $ENVS | par-each --threads $nb_threads { |exp|
-        let opts = $OPTS | update n ($OPTS.k / $exp.0 | math round --precision 0) | update environment $exp.1
-        inbreeding run --options $opts --prng-seed $prng_seed
-    }
-}
diff --git a/bins/inbreeding/scripts/show.nu b/bins/inbreeding/scripts/show.nu
deleted file mode 100644
index 15e22d991131f575ed05611f6a5651a9d2ae9f9b..0000000000000000000000000000000000000000
--- a/bins/inbreeding/scripts/show.nu
+++ /dev/null
@@ -1,55 +0,0 @@
-use ../src/.nushell/consts.nu CACHE
-use ../src/.nushell/path.nu [ "remove-cache-prefix" ]
-use ../src/.nushell/parse.nu [ "parse full-experiment" ]
-
-const SEED = "b239e48345ac457b492cc164f58c010d07292c88e4791e607d91796baec7f334"
-let k = 10
-
-# # Example
-# ``nushell
-# # sort files based on their file extensions
-# ls | where type == file | sort-by-closure { get name | path parse | get extension }
-# ``
-# ---
-# ``
-# ─┬───────────────────
-# 0│LICENSE
-# 1│Cargo.lock
-# 2│CODE_OF_CONDUCT.md
-# 3│CONTRIBUTING.md
-# 4│README.md
-# 5│toolkit.nu
-# 6│Cargo.toml
-# 7│Cross.toml
-# 8│rust-toolchain.toml
-# 9│typos.toml
-# ─┴───────────────────
-# ``
-def sort-by-closure [key: closure]: list -> list {
-    insert __key__ { do $key $in } | sort-by __key__ | reject __key__
-}
-
-def show [x: string] {
-    $CACHE
-        | path join figures $"($SEED)-($x).png"
-        | into glob
-        | ls $in
-        | sort-by-closure {
-            get name
-                | remove-cache-prefix
-                | path parse --extension 'png'
-                | reject extension
-                | path join
-                | parse full-experiment
-                | get n
-        }
-        | feh ...$in.name
-}
-
-show $"fixed:0-($k)-*-*"
-show $"random-fixed:0.5:1-($k)-*-*"
-show $"fixed:1-($k)-*-*"
-show $"*-($k)-20-*"
-show $"*-($k)-30-*"
-show $"*-($k)-50-*"
-show $"*-($k)-100-*"
diff --git a/bins/inbreeding/src/.nushell/build.nu b/bins/inbreeding/src/.nushell/build.nu
deleted file mode 100644
index 75de47de734055fae94aeaa185e9861cdcddcde6..0000000000000000000000000000000000000000
--- a/bins/inbreeding/src/.nushell/build.nu
+++ /dev/null
@@ -1,4 +0,0 @@
-export def main [] {
-    cd bins/inbreeding
-    cargo build --release
-}
diff --git a/bins/inbreeding/src/.nushell/consts.nu b/bins/inbreeding/src/.nushell/consts.nu
deleted file mode 100644
index f7357d9df011374c6a7ae6a6acc593dd64c3c592..0000000000000000000000000000000000000000
--- a/bins/inbreeding/src/.nushell/consts.nu
+++ /dev/null
@@ -1,4 +0,0 @@
-export const BIN = "./target/release/inbreeding"
-export const CACHE = ($nu.home-path | path join .cache komodo inbreeding)
-
-export const NUSHELL = "../../../../.nushell"
diff --git a/bins/inbreeding/src/.nushell/inspect.nu b/bins/inbreeding/src/.nushell/inspect.nu
deleted file mode 100644
index cb02c629618ff77d696dd724f456aa9572350f0c..0000000000000000000000000000000000000000
--- a/bins/inbreeding/src/.nushell/inspect.nu
+++ /dev/null
@@ -1,32 +0,0 @@
-use consts.nu
-use parse.nu [ "parse full-experiment" ]
-use path.nu [ "remove-cache-prefix" ]
-
-def get-seeds [] [ nothing -> list<string> ] {
-    $consts.CACHE | path join '*' | into glob | ls $in | get name | each { path split | last }
-}
-
-export def main [seed: string@get-seeds]: [
-    nothing -> table<
-        seed: string,
-        env: string,
-        strategy: string,
-        k: int,
-        n: int,
-        nb_bytes: int,
-        m: int,
-    >
-] {
-    $consts.CACHE
-        | path join ($seed | into string) '*'
-        | into glob
-        | ls $in
-        | insert m { ls $in.name | length }
-        | select name m
-        | update name { remove-cache-prefix | parse full-experiment | reject seed }
-        | flatten --all name
-        | into int k
-        | into int n
-        | into int nb_bytes
-}
-
diff --git a/bins/inbreeding/src/.nushell/list.nu b/bins/inbreeding/src/.nushell/list.nu
deleted file mode 100644
index 38a386ba51a9ea09d012e8f182a01fb78e85abc6..0000000000000000000000000000000000000000
--- a/bins/inbreeding/src/.nushell/list.nu
+++ /dev/null
@@ -1,16 +0,0 @@
-use consts.nu
-use parse.nu [ "parse full-experiment" ]
-use path.nu [ "remove-cache-prefix" ]
-
-# return experiment names following `$ARG_EXPERIMENT_FORMAT`
-export def main []: nothing -> list<string> {
-    $consts.CACHE
-        | path join '*' '*'
-        | into glob
-        | ls $in
-        | get name
-        | each { remove-cache-prefix }
-        | where ($it | path split | first | str length) == 64
-        | each { parse full-experiment | reject strategy | values | str join '-' }
-        | uniq
-}
diff --git a/bins/inbreeding/src/.nushell/load.nu b/bins/inbreeding/src/.nushell/load.nu
deleted file mode 100644
index d410a90ab6eb787a4ccfd7d96a2982920b01e533..0000000000000000000000000000000000000000
--- a/bins/inbreeding/src/.nushell/load.nu
+++ /dev/null
@@ -1,71 +0,0 @@
-use consts.nu
-use parse.nu [ "parse arg-experiment", "parse experiment" ]
-use path.nu [ "remove-cache-prefix" ]
-use $consts.NUSHELL error "error throw"
-
-use list.nu
-
-export def main [
-    experiment: string@list,
-]: [
-    nothing -> record<
-        experiment: record<k: int, n: int, nb_bytes: int, env: string>,
-        measurements: table<strategy: string, diversity: table<x: int, y: float, e: float>>,
-    >
-] {
-    let exp = $experiment | parse arg-experiment --span (metadata $experiment).span
-
-    let experiment_path = [
-        $consts.CACHE,
-        $exp.seed,
-        ([$exp.env, '*', $exp.k, $exp.n, $exp.nb_bytes] | str join '-')
-    ]
-        | path join
-        | into glob
-    let experiment_files = try {
-        ls $experiment_path
-    } catch {
-        error throw {
-            err: "invalid experiment",
-            label: $"experiment '($experiment)' does not appear to have data files",
-            span: (metadata $experiment).span,
-        }
-    }
-
-    let measurements = $experiment_files
-        | select name
-        | insert . { get name | remove-cache-prefix | parse experiment }
-        | flatten --all
-        | insert diversity {
-            ls $in.name
-                | each { get name | open | lines }
-                | flatten
-                | parse "{x}, {y}"
-                | into float y
-                | group-by x --to-table
-                | insert y { get items.y | math avg }
-                | insert e { get items.y | math stddev }
-                | rename --column { group: "x" }
-                | reject items
-                | into int x # NOTE: $.x needs to be converted to int here because
-                             # `group-by --to-table` converts the grouping key to
-                             # string
-        }
-        | reject name
-        | group-by strategy --to-table
-        | update items {|it|
-            let d = $it.items.diversity
-            $d | skip 1 | reduce --fold $d.0 {|it, acc| $acc | append $it}
-        }
-        | rename --column { group: "strategy", items: "diversity" }
-
-    {
-        experiment: {
-            env: $exp.env,
-            k: $exp.k,
-            n: $exp.n,
-            nb_bytes: $exp.nb_bytes,
-        },
-        measurements: $measurements,
-    }
-}
diff --git a/bins/inbreeding/src/.nushell/parse.nu b/bins/inbreeding/src/.nushell/parse.nu
deleted file mode 100644
index 6f251e74af4d4f1214d5454b2526f94f80be7496..0000000000000000000000000000000000000000
--- a/bins/inbreeding/src/.nushell/parse.nu
+++ /dev/null
@@ -1,52 +0,0 @@
-const FMT = {
-    env: "(?<env>.*)",
-    seed: "(?<seed>[a-zA-Z0-9]*)",
-    params: '(?<k>\d+)-(?<n>\d+)-(?<nb_bytes>\d+)',
-    strat: "(?<strategy>.*)" ,
-}
-
-const ARG_EXPERIMENT_FORMAT = $FMT.seed + '-' + $FMT.env + '-' + $FMT.params
-const EXPERIMENT_FORMAT = $FMT.env + '-' + $FMT.strat + '-' + $FMT.params
-const FULL_EXPERIMENT_FORMAT = $FMT.seed + (char path_sep) + $EXPERIMENT_FORMAT
-
-export def "parse full-experiment" []: [
-    string -> record<
-        seed: string, env: string, strategy: string, k: int, n: int, nb_bytes: int
-    >
-] {
-    parse --regex $FULL_EXPERIMENT_FORMAT
-        | into record
-        | into int k
-        | into int n
-        | into int nb_bytes
-}
-
-export def "parse experiment" []: [
-    string -> record<env: string, strategy: string, k: int, n: int, nb_bytes: int>
-] {
-    parse --regex $EXPERIMENT_FORMAT
-        | into record
-        | into int k
-        | into int n
-        | into int nb_bytes
-}
-
-export def "parse arg-experiment" [--span: record<start: int, end: int>]: [
-    string -> record<seed: string, env: string, k: int, n: int, nb_bytes: int>
-] {
-    let exp = $in
-        | parse --regex $ARG_EXPERIMENT_FORMAT
-        | into record
-        | into int k
-        | into int n
-        | into int nb_bytes
-    if $exp == {} {
-        error throw {
-            err: "invalid experiment",
-            label: $"should have format '($ARG_EXPERIMENT_FORMAT)', found ($experiment)",
-            span: $span,
-        }
-    }
-
-    $exp
-}
diff --git a/bins/inbreeding/src/.nushell/path.nu b/bins/inbreeding/src/.nushell/path.nu
deleted file mode 100644
index 4614db12ff3e211215ad1d5cebc21ba0a4ac2ae4..0000000000000000000000000000000000000000
--- a/bins/inbreeding/src/.nushell/path.nu
+++ /dev/null
@@ -1,5 +0,0 @@
-use consts.nu
-
-export def remove-cache-prefix []: path -> string {
-    str replace $"($consts.CACHE)(char path_sep)" ''
-}
diff --git a/bins/inbreeding/src/.nushell/plot.nu b/bins/inbreeding/src/.nushell/plot.nu
deleted file mode 100644
index 4405a12b22f49e649593777a953c4e4761ca4575..0000000000000000000000000000000000000000
--- a/bins/inbreeding/src/.nushell/plot.nu
+++ /dev/null
@@ -1,124 +0,0 @@
-use std repeat
-
-use consts.nu
-use $consts.NUSHELL plot gplt
-use $consts.NUSHELL color *
-use $consts.NUSHELL error "error throw"
-
-def "parse strategy" []: string -> record<type: string> {
-    let s = $in
-
-    if ($s | str starts-with "single") {
-        let res = $s
-            | parse "single:{n}"
-            | into record
-            | into int n
-        { type: "single", n: $res.n }
-    } else {
-        let res = $s
-            | parse "double:{p}:{n}:{m}"
-            | into record
-            | into float p
-            | into int n
-            | into int m
-        { type: "double", p: $res.p, n: $res.n, m: $res.m }
-    }
-}
-
-def get-color []: int -> string {
-    match $in {
-        10 => "#d62728",
-        9 => "#ff7f0e",
-        8 => "#bcbd22",
-        7 => "#1f77b4",
-        6 => "#9467bd",
-        5 => "#2ca02c",
-        4 => "#17becf",
-        3 => "#8c564b",
-        2 => "#e377c2",
-        _ => "#7f7f7f",
-    }
-}
-
-export def main [ --save: path ]: [
-    record<
-        experiment: record<k: int, n: int, nb_bytes: int, env: string>,
-        measurements: table<strategy: string, diversity: table<x: int, y: float, e: float>>,
-    > -> nothing
-] {
-    let experiment = $in
-    let l = $experiment.measurements.diversity.0 | length
-
-    $experiment.measurements
-        | update strategy { parse strategy }
-        | insert sort {|it|
-            match $it.strategy.type {
-                "single" => [$it.strategy.n, 1.0]
-                "double" => [$it.strategy.n, $it.strategy.p]
-            }
-        }
-        | sort-by sort
-        | reverse
-        | reject sort
-        | insert name {|it|
-            match $it.strategy.type {
-                "single" => {
-                    let sigma = if $it.strategy.n == $experiment.experiment.k {
-                        "k"
-                    } else {
-                        $"k - ($experiment.experiment.k - $it.strategy.n)"
-                    }
-                    $"$\\sigma = ($sigma) = ($it.strategy.n)$"
-                }
-                "double" => $"($it.strategy.p)? ($it.strategy.n) ; ($it.strategy.m)"
-            }
-        }
-        | rename --column { diversity: "points" }
-        | insert style {|it|
-            let color = match $it.strategy.type {
-                "single" => { $it.strategy.n | get-color },
-                "double" => {
-                    let c1 = $it.strategy.n | get-color | color from-string $in
-                    let c2 = $it.strategy.m | get-color | color from-string $in
-                    let c = $it.strategy.p
-
-                    color mix $c1 $c2 $c | color to-hex
-                },
-            }
-
-            let alpha = match $it.strategy.type {
-                "single" => 1.0,
-                "double" => 0.3,
-            }
-            let type = match $it.strategy.type {
-                "single" => "solid",
-                "double" => "dashed",
-            }
-
-            { color: $color, line: { alpha: $alpha, type: $type } }
-        }
-        | reject strategy
-        | save --force /tmp/graphs.json
-
-    let x_min = open /tmp/graphs.json | get points | flatten | get x | math min
-    let x_max = open /tmp/graphs.json | get points | flatten | get x | math max
-
-    let args = [
-        --json-data-file /tmp/graphs.json
-        --x-lim ($x_min - 1) ($x_max + 1)
-        --y-lim -0.01 1.01
-        --fullscreen
-        # --title "diversity over time when recoding shards $r$ shards"
-        --x-label "time (in nb of steps)"
-        --y-label "diversity $\\delta$"
-        --dpi 150
-        --fig-size ...[16, 5]
-        --font ({ size: 15, family: serif, sans-serif: Helvetica } | to json)
-        --use-tex
-        --legend-loc "upper right"
-        (if $save != null { [ --save $save ] })
-    ]
-
-    gplt plot ...($args | flatten | compact)
-
-}
diff --git a/bins/inbreeding/src/.nushell/run.nu b/bins/inbreeding/src/.nushell/run.nu
deleted file mode 100644
index 54e5c9335ad52169ff702c0f8bb2a67223a05393..0000000000000000000000000000000000000000
--- a/bins/inbreeding/src/.nushell/run.nu
+++ /dev/null
@@ -1,103 +0,0 @@
-use consts.nu
-use $consts.NUSHELL error "error throw"
-
-const VALID_HEX_CHARS = "abcdefABCDEF0123456789"
-
-def check-hex [-n: int]: [
-    string -> record<
-        ok: bool,
-        err: record<msg: string, label: string, help: string>,
-    >
-] {
-    let s = $in
-
-    if ($s | str length) != $n {
-        return {
-            ok: false,
-            err: {
-                msg: "invalid HEX length"
-                label : $"length is ($s | str length)",
-                help: "length should be 64",
-            },
-        }
-    }
-
-    for c in ($s | split chars | enumerate) {
-        if not ($VALID_HEX_CHARS | str contains $c.item) {
-            return {
-                ok: false,
-                err: {
-                    msg: "bad HEX character",
-                    label: $"found '($c.item)' at ($c.index)",
-                    help: $"expected one of '($VALID_HEX_CHARS)'",
-                },
-            }
-        }
-    }
-
-    { ok: true, err: {} }
-}
-
-export def main [
-    --options: record<
-        nb_bytes: int,
-        k: int,
-        n: int,
-        nb_measurements: int,
-        nb_scenarii: int,
-        measurement_schedule: int,
-        measurement_schedule_start: int,
-        max_t: int,
-        strategies: list<string>,
-        environment: string,
-    >,
-    --prng-seed: string = "0000000000000000000000000000000000000000000000000000000000000000",
-] {
-    if $options.measurement_schedule_start > $options.max_t {
-        error make --unspanned {
-            msg: $"measurement schedule will start after the max t, ($options.measurement_schedule_start) > ($options.max_t)"
-        }
-    }
-
-    let res = $prng_seed | check-hex -n 64
-    if not $res.ok {
-        error throw {
-            err: $res.err.msg,
-            label: $res.err.label,
-            span: (metadata $prng_seed).span,
-            help: $res.err.help,
-        }
-    }
-
-    let exp_hash = $options | select nb_bytes k n environment | sort | to nuon | hash sha256
-
-    for s in $options.strategies {
-        let output_dir = [
-            $consts.CACHE,
-            $"($prng_seed)",
-            ([$options.environment, $s, $options.k, $options.n, $options.nb_bytes] | str join '-')
-        ] | path join
-        mkdir $output_dir
-        print $"data will be dumped to `($output_dir)`"
-
-        for i in 1..$options.nb_scenarii {
-            let seed = [ $prng_seed, $exp_hash, $s, $i ] | str join | hash sha256
-            let output = [ $output_dir, $seed ] | path join
-
-            ^$consts.BIN ...[
-                $options.nb_bytes,
-                -k $options.k
-                -n $options.n
-                --nb-measurements $options.nb_measurements
-                --measurement-schedule $options.measurement_schedule
-                --measurement-schedule-start $options.measurement_schedule_start
-                -t $options.max_t
-                --strategy $s
-                --environment $options.environment
-                --prng-seed $seed
-            ] out> $output
-        }
-
-        print $"data has been dumped to `($output_dir)`"
-    }
-}
diff --git a/bins/inbreeding/src/.nushell/watch.nu b/bins/inbreeding/src/.nushell/watch.nu
deleted file mode 100644
index 6115192b75f2f3a31588c180639f7cf39ab214f3..0000000000000000000000000000000000000000
--- a/bins/inbreeding/src/.nushell/watch.nu
+++ /dev/null
@@ -1,30 +0,0 @@
-use consts.nu CACHE
-use path.nu [ "remove-cache-prefix" ]
-
-def pretty-hash []: [ string -> string ] {
-    str substring 0..7
-}
-
-export def main [] {
-    watch $CACHE { |op, path, new_path|
-        if $op != "Create" {
-            return
-        }
-
-        let path = $path | remove-cache-prefix
-
-        let now = date now | format date "%Y-%m-%d %H:%M:%S"
-        let p = $path | parse "{seed}/{exp}/{id}" | into record
-
-        if $p == {} {
-            let p = $path | parse "{seed}/{exp}" | into record
-            if $p == {} {
-                return $path
-            }
-
-            return $"($p.seed | pretty-hash)  ($p.exp)            at ($now)"
-        }
-
-        $"($p.seed | pretty-hash)  ($p.exp)  ($p.id | pretty-hash)   at ($now)"
-    }
-}
diff --git a/bins/inbreeding/src/environment.rs b/bins/inbreeding/src/environment.rs
deleted file mode 100644
index 11357823e32f5f68886ac738c1efdfa79a175368..0000000000000000000000000000000000000000
--- a/bins/inbreeding/src/environment.rs
+++ /dev/null
@@ -1,181 +0,0 @@
-use rand::{Rng, RngCore};
-
-use crate::random::draw_unique_elements;
-
-#[derive(Debug, PartialEq)]
-pub(super) enum Environment {
-    /// at each time step, remove `n` elements with probability `p`
-    RandomFixed { p: f64, n: usize },
-    /// at each time step, remove a fraction `q` of the elements with probability `p`
-    RandomDynamic { p: f64, q: f64 },
-    /// at each time step, remove `n` elements
-    Fixed { n: usize },
-}
-
-impl Environment {
-    /// `update(things, rng)` is `things` with some elements potentially removed according to the
-    /// [`Environment`] type
-    pub(super) fn update<T: Clone>(&self, things: &[T], rng: &mut impl RngCore) -> Vec<T> {
-        let nb_to_take = match self {
-            Environment::Fixed { n } => things.len() - n,
-            Environment::RandomFixed { p, n } => {
-                if rng.gen::<f64>() > *p {
-                    return things.to_vec();
-                }
-                things.len() - n
-            }
-            Environment::RandomDynamic { p, q } => {
-                if rng.gen::<f64>() > *p {
-                    return things.to_vec();
-                }
-                (things.len() as f64 * (1.0 - q)) as usize
-            }
-        };
-
-        draw_unique_elements(things, nb_to_take, rng)
-    }
-
-    pub(super) fn from_str(s: &str) -> Result<Self, String> {
-        let tokens: Vec<&str> = s.split(':').collect();
-        if tokens.is_empty() {
-            return Err(format!(
-                "expected at least one :-separated token in '{}', found 0",
-                s,
-            ));
-        }
-
-        match tokens[0] {
-            "fixed" => {
-                let n = match tokens[1].parse::<usize>() {
-                    Ok(u) => u,
-                    Err(_) => {
-                        return Err(format!(
-                            "could not parse positive integer from '{}'",
-                            tokens[1]
-                        ))
-                    }
-                };
-                Ok(Environment::Fixed { n })
-            }
-            "random-fixed" => {
-                if tokens.len() != 3 {
-                    return Err(format!(
-                        "expected 3 :-separated tokens in '{}', found {}",
-                        s,
-                        tokens.len(),
-                    ));
-                }
-
-                let p = match tokens[1].parse::<f64>() {
-                    Ok(f) => f,
-                    Err(_) => return Err(format!("could not parse float from '{}'", tokens[1])),
-                };
-                if !(0.0..=1.0).contains(&p) {
-                    return Err(format!("p should be a probability, found {}", p));
-                }
-                let n = match tokens[2].parse::<usize>() {
-                    Ok(u) => u,
-                    Err(_) => {
-                        return Err(format!(
-                            "could not parse positive integer from '{}'",
-                            tokens[2]
-                        ))
-                    }
-                };
-                Ok(Environment::RandomFixed { p, n })
-            }
-            "random-dynamic" => {
-                if tokens.len() != 3 {
-                    return Err(format!(
-                        "expected 3 :-separated tokens in '{}', found {}",
-                        s,
-                        tokens.len(),
-                    ));
-                }
-
-                let p = match tokens[1].parse::<f64>() {
-                    Ok(f) => f,
-                    Err(_) => return Err(format!("could not parse float from '{}'", tokens[1])),
-                };
-                if !(0.0..=1.0).contains(&p) {
-                    return Err(format!("p should be a probability, found {}", p));
-                }
-
-                let q = match tokens[2].parse::<f64>() {
-                    Ok(f) => f,
-                    Err(_) => return Err(format!("could not parse float from '{}'", tokens[2])),
-                };
-                if !(0.0..=1.0).contains(&q) {
-                    return Err(format!("q should be between 0 and 1, found {}", q));
-                }
-
-                Ok(Environment::RandomDynamic { p, q })
-            }
-            ty => Err(format!("unknow env type '{}'", ty)),
-        }
-    }
-}
-
-#[cfg(test)]
-mod test {
-    #[test]
-    fn environment() {
-        assert_eq!(
-            super::Environment::from_str("fixed:1"),
-            Ok(super::Environment::Fixed { n: 1 })
-        );
-        assert_eq!(
-            super::Environment::from_str("random-fixed:0.2:1"),
-            Ok(super::Environment::RandomFixed { p: 0.2, n: 1 })
-        );
-        assert_eq!(
-            super::Environment::from_str("random-dynamic:0.2:0.3"),
-            Ok(super::Environment::RandomDynamic { p: 0.2, q: 0.3 })
-        );
-
-        let cases = vec![
-            ("foo", "unknow env type 'foo'"),
-            ("foo:", "unknow env type 'foo'"),
-            ("fixed:", "could not parse positive integer from ''"),
-            ("fixed:foo", "could not parse positive integer from 'foo'"),
-            (
-                "random-fixed:",
-                "expected 3 :-separated tokens in 'random-fixed:', found 2",
-            ),
-            ("random-fixed:foo:", "could not parse float from 'foo'"),
-            ("random-fixed:1.2:", "p should be a probability, found 1.2"),
-            (
-                "random-fixed:0.2:",
-                "could not parse positive integer from ''",
-            ),
-            (
-                "random-fixed:0.2:foo",
-                "could not parse positive integer from 'foo'",
-            ),
-            (
-                "random-dynamic:",
-                "expected 3 :-separated tokens in 'random-dynamic:', found 2",
-            ),
-            ("random-dynamic:foo:", "could not parse float from 'foo'"),
-            (
-                "random-dynamic:1.2:",
-                "p should be a probability, found 1.2",
-            ),
-            ("random-dynamic:0.2:", "could not parse float from ''"),
-            ("random-dynamic:0.2:foo", "could not parse float from 'foo'"),
-            (
-                "random-dynamic:0.2:1.2",
-                "q should be between 0 and 1, found 1.2",
-            ),
-        ];
-
-        for (input, expected_error) in cases {
-            assert_eq!(
-                super::Environment::from_str(input),
-                Err(expected_error.to_string()),
-                "input: {}",
-                input
-            );
-        }
-    }
-}
diff --git a/bins/inbreeding/src/main.rs b/bins/inbreeding/src/main.rs
deleted file mode 100644
index 9ab72c6ea09468c276c53e5360153ae1ebae7b77..0000000000000000000000000000000000000000
--- a/bins/inbreeding/src/main.rs
+++ /dev/null
@@ -1,201 +0,0 @@
-use std::process::exit;
-
-use ark_ff::PrimeField;
-
-use clap::Parser;
-use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
-use komodo::{
-    error::KomodoError,
-    fec::{self, Shard},
-    linalg::Matrix,
-};
-use rand::{rngs::StdRng, Rng, RngCore, SeedableRng};
-use random::draw_unique_elements;
-
-mod environment;
-mod random;
-mod strategy;
-
-use crate::{environment::Environment, strategy::Strategy};
-
-fn random_bytes(n: usize, rng: &mut impl RngCore) -> Vec<u8> {
-    (0..n).map(|_| rng.gen::<u8>()).collect()
-}
-
-fn setup<F: PrimeField>(bytes: &[u8], k: usize, n: usize) -> Result<Vec<Shard<F>>, KomodoError> {
-    let points: Vec<F> = (0..n)
-        .map(|i| F::from_le_bytes_mod_order(&i.to_le_bytes()))
-        .collect();
-    let encoding_mat = Matrix::vandermonde_unchecked(&points, k);
-    let shards = fec::encode(bytes, &encoding_mat)?;
-
-    Ok(shards)
-}
-
-fn measure_inbreeding<F: PrimeField>(
-    shards: &[Shard<F>],
-    k: usize,
-    nb_measurements: usize,
-    mp: &MultiProgress,
-    sty: &ProgressStyle,
-    rng: &(impl RngCore + Clone),
-) -> f64 {
-    let mut rng = rng.clone();
-
-    let mut count = 0;
-
-    let pb = mp.add(ProgressBar::new(nb_measurements as u64));
-    pb.set_style(sty.clone());
-    pb.set_message("measure");
-    for _ in 0..nb_measurements {
-        if fec::decode(draw_unique_elements(shards, k, &mut rng)).is_ok() {
-            count += 1;
-        }
-        pb.inc(1);
-    }
-
-    count as f64 / nb_measurements as f64
-}
-
-#[allow(clippy::too_many_arguments)]
-fn run<F, Fun>(
-    bytes: &[u8],
-    k: usize,
-    n: usize,
-    max_t: usize,
-    strategy: Strategy,
-    env: Environment,
-    nb_measurements: usize,
-    measurement_schedule: Fun,
-    rng: &mut (impl RngCore + Clone),
-) -> Result<(), KomodoError>
-where
-    F: PrimeField,
-    Fun: Fn(usize) -> bool,
-{
-    let mut shards = setup::<F>(bytes, k, n)?;
-
-    let mp = MultiProgress::new();
-    let sty = ProgressStyle::with_template("{msg}: {bar:40.cyan/blue} {pos:>7}/{len:7}")
-        .unwrap()
-        .progress_chars("##-");
-    let pb = mp.add(ProgressBar::new(max_t as u64));
-    pb.set_style(sty.clone());
-    pb.set_message("main");
-    for t in 0..=max_t {
-        if shards.len() < k {
-            break;
-        }
-
-        if measurement_schedule(t) {
-            let inbreeding = measure_inbreeding(&shards, k, nb_measurements, &mp, &sty, rng);
-            println!("{}, {}", t, inbreeding);
-        }
-
-        // recode a new random shard
-        let new_shard = fec::recode_random(&strategy.draw(&shards, rng), rng)
-            .unwrap()
-            .unwrap();
-        shards.push(new_shard);
-
-        shards = env.update(&shards, rng);
-
-        pb.inc(1);
-    }
-    pb.finish_with_message("done");
-
-    Ok(())
-}
-
-fn parse_hex_string(s: &str) -> Result<[u8; 32], String> {
-    if s.len() != 64 {
-        return Err("Input string must be exactly 64 characters long".to_string());
-    }
-
-    match hex::decode(s) {
-        // `bytes` will be a `Vec<u8>` of size `32`, so it's safe to `unwrap`
-        // the conversion to `[u8: 32]`
-        Ok(bytes) => Ok(bytes.try_into().unwrap()),
-        Err(e) => Err(format!("Failed to decode hex string: {}", e)),
-    }
-}
-
-#[derive(Parser)]
-#[command(version, about, long_about = None)]
-struct Cli {
-    #[arg()]
-    nb_bytes: usize,
-
-    #[arg(short)]
-    k: usize,
-    #[arg(short)]
-    n: usize,
-    #[arg(short)]
-    t: usize,
-    /// something of the form `<p>:<i>,<j>`
-    /// at each time step, shard $i$ will be used for recoding with probability $p$, otherwise, $j$
-    /// will be used with probability $1 - p$
-    #[arg(long)]
-    strategy: String,
-    /// something of the form `random-dynamic:<p>:<q>` where a proportion $q$ of the shards will be removed at
-    /// each step with probability $p$
-    #[arg(long)]
-    environment: String,
-
-    /// the number of measurements to repeat each case, larger values will reduce the variance of
-    /// the measurements
-    #[arg(long)]
-    nb_measurements: usize,
-
-    #[arg(long)]
-    measurement_schedule: usize,
-    #[arg(long)]
-    measurement_schedule_start: usize,
-
-    #[arg(long, value_parser = parse_hex_string)]
-    prng_seed: [u8; 32],
-}
-
-fn main() {
-    let cli = Cli::parse();
-
-    if cli.nb_measurements == 0 {
-        eprintln!(
-            "`--nb-measurements` should be a strictly positive integer, found {}",
-            cli.nb_measurements
-        );
-        exit(1);
-    }
-
-    let mut rng = StdRng::from_seed(cli.prng_seed);
-
-    let bytes = random_bytes(cli.nb_bytes, &mut rng);
-
-    eprintln!(
-        "diversity will be measured every {} steps",
-        cli.measurement_schedule
-    );
-    let measurement_schedule = |t| {
-        t >= cli.measurement_schedule_start
-            && (t - cli.measurement_schedule_start) % cli.measurement_schedule == 0
-    };
-
-    let environment = Environment::from_str(&cli.environment).unwrap();
-    let strategy = Strategy::from_str(&cli.strategy).unwrap();
-
-    eprintln!(
-        "k-recoding: k = {}, n = {}, strategy = {:?}, environment = {:?}",
-        cli.k, cli.n, strategy, environment,
-    );
-    let _ = run::<ark_pallas::Fr, _>(
-        &bytes,
-        cli.k,
-        cli.n,
-        cli.t,
-        strategy,
-        environment,
-        cli.nb_measurements,
-        measurement_schedule,
-        &mut rng,
-    );
-}
diff --git a/bins/inbreeding/src/random.rs b/bins/inbreeding/src/random.rs
deleted file mode 100644
index d5cb9276b7efd99979a95e20d11e5a4c5cab6a7c..0000000000000000000000000000000000000000
--- a/bins/inbreeding/src/random.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-use rand::{seq::SliceRandom, Rng, RngCore};
-use std::collections::HashSet;
-
-#[allow(dead_code)]
-fn draw_unique_indices(n: usize, vec_len: usize, rng: &mut impl RngCore) -> HashSet<usize> {
-    let mut indices = HashSet::new();
-
-    while indices.len() < n {
-        let idx = rng.gen_range(0..vec_len);
-        indices.insert(idx);
-    }
-
-    indices
-}
-
-pub(super) fn draw_unique_elements<T: Clone>(
-    things: &[T],
-    n: usize,
-    rng: &mut impl RngCore,
-) -> Vec<T> {
-    let mut things = things.to_vec();
-    things.shuffle(rng);
-
-    things.iter().take(n).cloned().collect()
-}
diff --git a/bins/inbreeding/src/strategy.rs b/bins/inbreeding/src/strategy.rs
deleted file mode 100644
index ffe573606c721a452802bd4f95ec1d0905255593..0000000000000000000000000000000000000000
--- a/bins/inbreeding/src/strategy.rs
+++ /dev/null
@@ -1,148 +0,0 @@
-use rand::{Rng, RngCore};
-
-use crate::random::draw_unique_elements;
-
-#[derive(Debug, PartialEq)]
-pub(super) enum Strategy {
-    Single { n: usize },
-    Double { p: f64, n: usize, m: usize },
-}
-
-impl Strategy {
-    pub(super) fn draw<T: Clone>(&self, things: &[T], rng: &mut impl RngCore) -> Vec<T> {
-        let nb_to_take = match self {
-            Self::Single { n } => *n,
-            Self::Double { p, n, m } => {
-                if rng.gen::<f64>() < *p {
-                    *n
-                } else {
-                    *m
-                }
-            }
-        };
-
-        draw_unique_elements(things, nb_to_take, rng)
-    }
-
-    pub(super) fn from_str(s: &str) -> Result<Self, String> {
-        if !s.contains(':') {
-            return Err(format!(
-                "expected at least one :-separated token in '{}', found 0",
-                s,
-            ));
-        }
-
-        let tokens: Vec<&str> = s.split(':').collect();
-
-        match tokens[0] {
-            "single" => {
-                if tokens.len() != 2 {
-                    return Err(format!(
-                        "expected 2 :-separated tokens in '{}', found {}",
-                        s,
-                        tokens.len(),
-                    ));
-                }
-
-                let n = match tokens[1].parse::<usize>() {
-                    Ok(u) => u,
-                    Err(_) => {
-                        return Err(format!(
-                            "could not parse positive integer from '{}'",
-                            tokens[1]
-                        ))
-                    }
-                };
-                Ok(Self::Single { n })
-            }
-            "double" => {
-                if tokens.len() != 4 {
-                    return Err(format!(
-                        "expected 4 :-separated tokens in '{}', found {}",
-                        s,
-                        tokens.len(),
-                    ));
-                }
-
-                let p = match tokens[1].parse::<f64>() {
-                    Ok(f) => f,
-                    Err(_) => return Err(format!("could not parse float from '{}'", tokens[1])),
-                };
-                if !(0.0..=1.0).contains(&p) {
-                    return Err(format!("p should be a probability, found {}", p));
-                }
-
-                let n = match tokens[2].parse::<usize>() {
-                    Ok(u) => u,
-                    Err(_) => {
-                        return Err(format!(
-                            "could not parse positive integer from '{}'",
-                            tokens[2]
-                        ))
-                    }
-                };
-                let m = match tokens[3].parse::<usize>() {
-                    Ok(u) => u,
-                    Err(_) => {
-                        return Err(format!(
-                            "could not parse positive integer from '{}'",
-                            tokens[3]
-                        ))
-                    }
-                };
-
-                Ok(Self::Double { p, n, m })
-            }
-            ty => Err(format!("unknown strat type '{}'", ty)),
-        }
-    }
-}
-
-#[cfg(test)]
-mod test {
-    #[test]
-    fn strategy() {
-        assert_eq!(
-            super::Strategy::from_str("single:3"),
-            Ok(super::Strategy::Single { n: 3 })
-        );
-        assert_eq!(
-            super::Strategy::from_str("double:0.1:1:2"),
-            Ok(super::Strategy::Double { p: 0.1, n: 1, m: 2 })
-        );
-
-        let cases = vec![
-            (
-                "foo",
-                "expected at least one :-separated token in 'foo', found 0",
-            ),
-            ("foo:bar:baz", "unknown strat type 'foo'"),
-            ("single:", "could not parse positive integer from ''"),
-            (
-                "single::",
-                "expected 2 :-separated tokens in 'single::', found 3",
-            ),
-            (
-                "double:bar:baz",
-                "expected 4 :-separated tokens in 'double:bar:baz', found 3",
-            ),
-            (
-                "double:bar:baz:spam:eggs",
-                "expected 4 :-separated tokens in 'double:bar:baz:spam:eggs', found 5",
-            ),
-            ("double:bar::", "could not parse float from 'bar'"),
-            ("double:1.2::", "p should be a probability, found 1.2"),
-            ("double:0.2::", "could not parse positive integer from ''"),
-            ("double:0.2:1:", "could not parse positive integer from ''"),
-        ];
-
-        for (input, expected_error) in cases {
-            assert_eq!(
-                super::Strategy::from_str(input),
-                Err(expected_error.to_string()),
-                "input: {}",
-                input,
-            );
-        }
-    }
-}