diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000000000000000000000000000000000000..856f134cdf9cf134b270e3b58b32e8e50279cd39
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,74 @@
+BasedOnStyle: LLVM
+Language: Cpp
+Standard: c++20
+AccessModifierOffset: -4
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: None
+AlignOperands: Align
+AllowAllArgumentsOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: Empty
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: Always
+AllowShortLambdasOnASingleLine: All
+AllowShortLoopsOnASingleLine: true
+AlwaysBreakAfterReturnType: None
+AlwaysBreakTemplateDeclarations: Yes
+BinPackArguments: false
+BreakBeforeBraces: Custom
+BraceWrapping:
+  AfterCaseLabel: false
+  AfterClass: true
+  AfterStruct: true
+  AfterControlStatement: Always
+  AfterEnum: true
+  AfterFunction: true
+  AfterNamespace: true
+  AfterUnion: true
+  BeforeCatch: false
+  BeforeElse: true
+  IndentBraces: false
+  SplitEmptyFunction: false
+  SplitEmptyRecord: true
+BreakBeforeBinaryOperators: None
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: BeforeColon
+BreakInheritanceList: BeforeColon
+ColumnLimit: 120
+CompactNamespaces: false
+ContinuationIndentWidth: 8
+EmptyLineBeforeAccessModifier: LogicalBlock
+IncludeBlocks: Regroup
+IndentCaseLabels: true
+IndentPPDirectives: None
+IndentWidth: 4
+KeepEmptyLinesAtTheStartOfBlocks: false
+MaxEmptyLinesToKeep: 2
+NamespaceIndentation: All
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PackConstructorInitializers: BinPack
+PointerAlignment: Right
+ReferenceAlignment: Right
+QualifierAlignment: Right
+ReflowComments: false
+SeparateDefinitionBlocks: Always
+SpaceAfterCStyleCast: true
+SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeRangeBasedForLoopColon: false
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 0
+SpacesInAngles: false
+SpacesInCStyleCastParentheses: false
+SpacesInContainerLiterals: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+TabWidth: 4
+UseTab: Never
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9f668937ccd03e32265224592f491312cffa67d1..2edbfcd3398e760a527838bae587fbdf8a664670 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -15,10 +15,6 @@ find_package(CLI11 REQUIRED)
 find_package(nlohmann_json REQUIRED)
 find_package(spdlog REQUIRED)
 
-
-
-
-
 add_executable(pdptw src/mains/main.cpp
                 src/mains/main_interface.cpp
                 src/input/data.cpp
@@ -37,6 +33,10 @@ add_executable(pdptw src/mains/main.cpp
                 src/lns/modification/route/insert_route_with_pair.cpp
                 src/lns/modification/route/insert_route.cpp
                 src/lns/modification/route/remove_route.cpp
+                src/output/solution_checker.cpp
+                src/lns/operators/destruction/random_destroy.cpp
+                src/lns/operators/selector/operator_selection.cpp
+                src/utils.cpp
                 )
 
 target_link_libraries(pdptw PRIVATE CLI11::CLI11 nlohmann_json::nlohmann_json spdlog::spdlog)
diff --git a/src/config.h b/src/config.h
index bf368d85b4872e612aaa91af11e608fc79ab9e99..6bb3f2bf9fef9c3a67843f5e1c2d1f8093dd2a1a 100644
--- a/src/config.h
+++ b/src/config.h
@@ -1,3 +1,5 @@
 
 
-const int EXCLUSION_PENALTY = 100;
\ No newline at end of file
+const int EXCLUSION_PENALTY = 100;
+
+const int RANDOM_SEED = 10;
\ No newline at end of file
diff --git a/src/input/pdptw_data.cpp b/src/input/pdptw_data.cpp
index 142fbde6f7fa5740f4565f00b6e4ab7281fd09d8..706bd09777f3dabf1b2cc7b7513d738c2fc2d4a4 100644
--- a/src/input/pdptw_data.cpp
+++ b/src/input/pdptw_data.cpp
@@ -60,6 +60,18 @@ PDPTWData::PDPTWData(int size, int capacity, Location depot, std::vector<Locatio
     }
 }
 
+const Pair &PDPTWData::getPair(int id) const
+{
+    for (const Pair &pair : pairs)
+    {
+        if (id == pair.getID())
+        {
+            return pair;
+        }
+    }
+    spdlog::error("Pair not found for ID {}", id);
+    throw std::runtime_error("Pair not found");
+}
 
 void PDPTWData::print() const 
 {
diff --git a/src/input/pdptw_data.h b/src/input/pdptw_data.h
index ac0ea57d07eee3b0d22f12b4e31babf812521b04..3cfb82950e8cd08bc17615a670a2f3e7d8a385bc 100644
--- a/src/input/pdptw_data.h
+++ b/src/input/pdptw_data.h
@@ -54,6 +54,8 @@ public:
     std::vector<Location> const &getLocations() const;
     std::vector<Pair> const &getPairs() const;
 
+    const Pair &getPair(int id) const;
+
     /** 
     *   0 return the depot.
     *   Other numbers return the associated location.
diff --git a/src/lns/constraints/time_window/time_window_constraint.cpp b/src/lns/constraints/time_window/time_window_constraint.cpp
index b350692565d9355a55af991a789300494a4c3373..c0310483e1b7dd5b0b8ef0435b5b17a8f8875258 100644
--- a/src/lns/constraints/time_window/time_window_constraint.cpp
+++ b/src/lns/constraints/time_window/time_window_constraint.cpp
@@ -134,21 +134,6 @@ void TimeWindowConstraint::ApplyModif(const PDPTWData& data, const Pair & pair,
         allRouteReachTimes[routeIndex][i] = std::max(allRouteReachTimes[routeIndex][i - 1], startTW) + serviceTime + travelTime;
     }
 }
-/*
-    // Adjust the size of reachTimes vector
-    reachTimes.resize(routeIDs.size(), 0);
-    // Time to the first location
-    reachTimes[0] = data.getDepot().getTimeWindow().getStart() + data::TravelTime(data, 0, routeIDs.at(0));
-    // Compute other reachTimes (max between arrival and start of the time window + previous service time + travel time)
-    for (int i = 1; i < routeIDs.size(); ++i) {
-        TimeInteger travelTime = data::TravelTime(data, routeIDs[i - 1], routeIDs[i]);
-        // locations are indexed from 0 to n-1,
-        TimeInteger serviceTime = data.getLocation(routeIDs[i - 1]).getServiceDuration();
-        TimeInteger startTW = data.getLocation(routeIDs[i - 1]).getTimeWindow().getStart();
-
-        reachTimes[i] = std::max(reachTimes[i - 1], startTW) + serviceTime + travelTime;
-    }
-*/
 
 bool TimeWindowConstraint::check(InsertPair const &op) const
 {
diff --git a/src/lns/modification/atomic_destruction.h b/src/lns/modification/atomic_destruction.h
index f2ad02fdc434bacff27a0abde60e8878b9220421..1506348a69cc5bd183272e1e919f8d1f08db9375 100644
--- a/src/lns/modification/atomic_destruction.h
+++ b/src/lns/modification/atomic_destruction.h
@@ -15,5 +15,5 @@ public:
     /**
      * @return the location ID removed from the solution.
      */
-    virtual std::vector<int> const &getDeletedRequests() const = 0;
+    virtual std::vector<int> const &getDeletedPairs() const = 0;
 };
diff --git a/src/lns/modification/atomic_modification.h b/src/lns/modification/atomic_modification.h
index c1895949ea22ad2e269703a51af10d5d60dd5fe0..701be758a1553cd2d761dc6860612398539f09a2 100644
--- a/src/lns/modification/atomic_modification.h
+++ b/src/lns/modification/atomic_modification.h
@@ -1,8 +1,8 @@
 #pragma once
 
-#include <vector>
-
 #include "lns/constraints/constraint.h"
+
+#include <vector>
 class Solution;
 
 /**
@@ -21,11 +21,15 @@ public:
      */
     virtual double evaluate(Solution const &solution) const = 0;
 
-     /**
+    /**
      * Apply of the modification to the solution.
      * @note does not check the validity of the modification, does not update solution cost nor constraints
      */
     virtual void modifySolution(Solution &solution) = 0;
-};
-
 
+    /**
+     * Visitor pattern double dispatch.
+     * Only need to be implemented with `return *this;`, update ModificationApplyVariant typedef when adding new modification.
+     */
+    virtual ModificationApplyVariant asApplyVariant() const = 0;
+};
diff --git a/src/lns/modification/pair/insert_pair.cpp b/src/lns/modification/pair/insert_pair.cpp
index b3f9d4b9ecd151d76e45d1ae8979a4a510caae35..bf717d0d92c80742e187511107b8bd4febb3e61f 100644
--- a/src/lns/modification/pair/insert_pair.cpp
+++ b/src/lns/modification/pair/insert_pair.cpp
@@ -95,4 +95,9 @@ const Pair &InsertPair::getPair() const
 Index InsertPair::getIndex() const 
 {
     return std::make_tuple(routeIndex, pickupInsertion, deliveryInsertion);
+}
+
+ModificationApplyVariant InsertPair::asApplyVariant() const
+{
+    return (*this);
 }
\ No newline at end of file
diff --git a/src/lns/modification/pair/insert_pair.h b/src/lns/modification/pair/insert_pair.h
index d806765091adb2146185abd7b91ab573666b3ad0..84fe9186a262aa9888d299b60d633cc44f0fdec5 100644
--- a/src/lns/modification/pair/insert_pair.h
+++ b/src/lns/modification/pair/insert_pair.h
@@ -51,6 +51,8 @@ public:
 
     ~InsertPair() override = default;
 
+    ModificationApplyVariant asApplyVariant() const override;
+    
     void modifySolution(Solution &solution) override;
     double evaluate(Solution const &solution) const override;
     Location const *getAddedLocation() const override;
diff --git a/src/lns/modification/pair/remove_pair.cpp b/src/lns/modification/pair/remove_pair.cpp
index 1a1c808c1cc337e53b53e283c1a533eef569cc43..6cf567c3d5c24619ec48847701418b2357585c46 100644
--- a/src/lns/modification/pair/remove_pair.cpp
+++ b/src/lns/modification/pair/remove_pair.cpp
@@ -1,45 +1,35 @@
 #include "remove_pair.h"
-#include "input/data.h"
-
-
-RemovePair::RemovePair(int routeIndex, int pickupDeletion, int deliveryDeletion, Pair const &pair) :
-    routeIndex(routeIndex), 
-    pickupDeletion(pickupDeletion), 
-    deliveryDeletion(deliveryDeletion), 
-    pickupLocation(pair.getPickup()), 
-    deliveryLocation(pair.getDelivery()),
-    pair(pair) {}
 
+#include "input/data.h"
 
-RemovePair::RemovePair(Index position, Pair const &pair) :
-    routeIndex(std::get<0>(position)), 
-    pickupDeletion(std::get<1>(position)), 
-    deliveryDeletion(std::get<2>(position)), 
-    pickupLocation(pair.getPickup()), 
-    deliveryLocation(pair.getDelivery()),
-    pair(pair) {}
+RemovePair::RemovePair(int routeIndex, int pickupDeletion, int deliveryDeletion, Pair const &pair)
+    : routeIndex(routeIndex), pickupDeletion(pickupDeletion), deliveryDeletion(deliveryDeletion),
+      pickupLocation(pair.getPickup()), deliveryLocation(pair.getDelivery()), pair(pair)
+{}
 
+RemovePair::RemovePair(Index position, Pair const &pair)
+    : routeIndex(std::get<0>(position)), pickupDeletion(std::get<1>(position)), deliveryDeletion(std::get<2>(position)),
+      pickupLocation(pair.getPickup()), deliveryLocation(pair.getDelivery()), pair(pair)
+{}
 
 void RemovePair::modifySolution(Solution &solution)
 {
     Route &route = solution.getRoute(routeIndex);
 
-    // update removedLocationID
-    removedLocationID.push_back(route.getRoute()[pickupDeletion]);
-    removedLocationID.push_back(route.getRoute()[deliveryDeletion]);
+    // update removedPairID
+
+    removedPairID.push_back(route.getRoute()[pickupDeletion]);
 
     // remove the delivery before (to not have to update the index)
     route.deleteAt(deliveryDeletion);
     route.deleteAt(pickupDeletion);
-
-
 }
 
 double RemovePair::evaluate(Solution const &solution) const
 {
     Route const &route = solution.getRoute(routeIndex);
-    const std::vector<int> & routeIDs = route.getRoute();
-    const PDPTWData &data = solution.getData();
+    std::vector<int> const &routeIDs = route.getRoute();
+    PDPTWData const &data = solution.getData();
 
     int prevPickup = (pickupDeletion == 0) ? 0 : routeIDs[pickupDeletion - 1];
     // pickup location should not be at the end of a route anyway
@@ -47,9 +37,9 @@ double RemovePair::evaluate(Solution const &solution) const
 
     double pickupCost = data::removedCostForSuppression(data, prevPickup, pickupLocation.getId(), nextPickup);
 
-    int prevDelivery = (deliveryDeletion == 0) ? 0 : routeIDs[deliveryDeletion - 1]; 
+    int prevDelivery = (deliveryDeletion == 0) ? 0 : routeIDs[deliveryDeletion - 1];
     int nextDelivery = (deliveryDeletion >= routeIDs.size()) ? 0 : routeIDs[deliveryDeletion + 1];
-    if (deliveryDeletion == pickupDeletion+1)
+    if (deliveryDeletion == pickupDeletion + 1)
     {
         prevDelivery = prevPickup;
     }
@@ -62,7 +52,6 @@ double RemovePair::evaluate(Solution const &solution) const
 int RemovePair::getPickupDeletion() const
 {
     return pickupDeletion;
-
 }
 
 int RemovePair::getDeliveryDeletion() const
@@ -75,19 +64,19 @@ int RemovePair::getRouteIndex() const
     return routeIndex;
 }
 
-Location const & RemovePair::getPickupLocation() const
+Location const &RemovePair::getPickupLocation() const
 {
     return pickupLocation;
 }
 
-Location const & RemovePair::getDeliveryLocation() const
+Location const &RemovePair::getDeliveryLocation() const
 {
     return deliveryLocation;
 }
 
-std::vector<int> const & RemovePair::getDeletedRequests() const
+std::vector<int> const &RemovePair::getDeletedPairs() const
 {
-    return removedLocationID;
+    return removedPairID;
 }
 
 Pair const &RemovePair::getPair() const
@@ -95,7 +84,12 @@ Pair const &RemovePair::getPair() const
     return pair;
 }
 
-Index RemovePair::getIndex() const 
+Index RemovePair::getIndex() const
 {
     return std::make_tuple(routeIndex, pickupDeletion, deliveryDeletion);
+}
+
+ModificationApplyVariant RemovePair::asApplyVariant() const
+{
+    return (*this);
 }
\ No newline at end of file
diff --git a/src/lns/modification/pair/remove_pair.h b/src/lns/modification/pair/remove_pair.h
index ef9dbdb8dd255b8ad43b2158e2fe9172b348786d..db85dc26fffabbb2b0af2f74ac8b14f355280d26 100644
--- a/src/lns/modification/pair/remove_pair.h
+++ b/src/lns/modification/pair/remove_pair.h
@@ -41,9 +41,9 @@ class RemovePair : public AtomicDestruction
     Pair const & pair;
 
     /**
-     * Removed Location ID (empty before ModifySolution is called)
+     * Removed PairID (= pickupID) (empty before ModifySolution is called)
      */
-    std::vector<int> removedLocationID;
+    std::vector<int> removedPairID;
 
 public:
     
@@ -51,12 +51,14 @@ public:
     RemovePair(Index position, Pair const &pair);
     ~RemovePair() override = default;
 
+    ModificationApplyVariant asApplyVariant() const override;
+    
     void modifySolution(Solution &solution) override;
     double evaluate(Solution const &solution) const override;
     /**
      *  Return the location ID of location that has been deleted
      */
-    std::vector<int> const &getDeletedRequests() const override;
+    std::vector<int> const &getDeletedPairs() const override;
 
     int getPickupDeletion() const;
     int getDeliveryDeletion() const;
diff --git a/src/lns/modification/route/remove_route.cpp b/src/lns/modification/route/remove_route.cpp
index 006d5df6e7a16714105bfd6ef70bbb516d1cdcfc..aa08131bb2f14fdcb066a4b1366de8745e1da89b 100644
--- a/src/lns/modification/route/remove_route.cpp
+++ b/src/lns/modification/route/remove_route.cpp
@@ -1,23 +1,35 @@
 #include "remove_route.h"
 
+#include "input/location.h"
+
+#include <algorithm>
+#include <ranges>
 #include <utility>
+#include <vector>
+
+RemoveRoute::RemoveRoute() : routeIndex(-1), removedPairID({}) {}
 
+RemoveRoute::RemoveRoute(int routeIndex) : routeIndex(routeIndex), removedPairID({}) {}
 
-RemoveRoute::RemoveRoute() : routeIndex(-1), removedLocationID({}) {}
-RemoveRoute::RemoveRoute(int routeIndex) : routeIndex(routeIndex), removedLocationID({}) {}
-RemoveRoute::RemoveRoute(int routeIndex, std::vector<int> removedLocationID) : 
-    routeIndex(routeIndex), 
-    removedLocationID(std::move(removedLocationID)) {}
+RemoveRoute::RemoveRoute(int routeIndex, std::vector<int> &&removedPairID)
+    : routeIndex(routeIndex), removedPairID(std::move(removedPairID))
+{}
 
 void RemoveRoute::modifySolution(Solution &solution)
 {
     std::vector<Route> &routes = solution.getRoutes();
-
-    // update removedLocationID
-    removedLocationID.insert(removedLocationID.end(), 
-    std::make_move_iterator(routes[routeIndex].getRoute().begin()), 
-    std::make_move_iterator(routes[routeIndex].getRoute().end()));
-
+    std::vector<int> const &locationIDs = routes.at(routeIndex).getRoute();
+
+    // update removedPairID
+    removedPairID.reserve(routes.at(routeIndex).getSize());
+
+    for (int id : locationIDs)
+    {
+        if (solution.getData().getLocation(id).getLocType() == LocType::PICKUP)
+        {
+            removedPairID.push_back(id);
+        }
+    }
     routes.erase(routes.begin() + routeIndex);
 }
 
@@ -31,7 +43,7 @@ int RemoveRoute::getRouteIndex() const
     return routeIndex;
 }
 
-std::vector<int> const &RemoveRoute::getDeletedRequests() const
+std::vector<int> const &RemoveRoute::getDeletedPairs() const
 {
-    return removedLocationID;
+    return removedPairID;
 }
\ No newline at end of file
diff --git a/src/lns/modification/route/remove_route.h b/src/lns/modification/route/remove_route.h
index be981e8171ebede51d12a9cf7674644045908a1d..08fb830ac0fd5ebf51b9f6efcbcfb22dbdb019c6 100644
--- a/src/lns/modification/route/remove_route.h
+++ b/src/lns/modification/route/remove_route.h
@@ -21,20 +21,20 @@ class RemoveRoute : public AtomicDestruction
     /**
      * Removed Location ID (empty before ModifySolution is called)
      */
-    std::vector<int> removedLocationID;
+    std::vector<int> removedPairID;
 
 public:
 
     RemoveRoute();
     explicit RemoveRoute(int routeIndex);
-    RemoveRoute(int routeIndex, std::vector<int> removedLocationID);
+    RemoveRoute(int routeIndex, std::vector<int> &&removedPairID);
 
     void modifySolution(Solution &solution) override;
     double evaluate(Solution const &solution) const override;
     /**
     *  Return the location ID of location that has been deleted
     */
-    std::vector<int> const &getDeletedRequests() const override;
+    std::vector<int> const &getDeletedPairs() const override;
 
     int getRouteIndex() const;
 };
\ No newline at end of file
diff --git a/src/lns/operators/abstract_operator.h b/src/lns/operators/abstract_operator.h
new file mode 100644
index 0000000000000000000000000000000000000000..2223e22585de3fbe6cd0f7271daa2f91816b8104
--- /dev/null
+++ b/src/lns/operators/abstract_operator.h
@@ -0,0 +1,17 @@
+#pragma once
+
+class Solution;
+
+class DestructionOperator
+{
+public:
+    virtual void destroySolution(Solution &solution) const = 0;
+    virtual ~DestructionOperator() = default;
+};
+
+class ReconstructionOperator
+{
+public:
+    virtual void reconstructSolution(Solution &solution, double blinkRate) const = 0;
+    virtual ~ReconstructionOperator() = default;
+};
diff --git a/src/lns/operators/destruction/random_destroy.cpp b/src/lns/operators/destruction/random_destroy.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..975e1a469e4d5465957c0b0c728cde248e3f8396
--- /dev/null
+++ b/src/lns/operators/destruction/random_destroy.cpp
@@ -0,0 +1,64 @@
+#include "random_destroy.h"
+#include "lns/solution/solution.h"
+#include "utils.h"
+#include "types.h"
+
+#include "lns/modification/pair/remove_pair.h"
+
+#include <algorithm> 
+
+void RandomDestroy::destroySolution(Solution &solution) const
+{
+
+    int nbRequests = solution.requestsFulFilledCount();
+    int actualNumberOfPairsToDestroy = std::min(nbRequests, numberOfPairsToDestroy);
+    int remainingPairToDelete = actualNumberOfPairsToDestroy;
+
+    while (remainingPairToDelete < 0)
+    {
+        // too complicated
+        // Other (simpler) option -> choose random route then choose random pair ?
+     
+        // choose a random pair
+        int pairNumber = util::getRandomInt(0, remainingPairToDelete * 2 - 1);
+        
+        int pairID = 0;
+        int routeID = 0;
+        int count = 0;
+        Index index = std::make_tuple(0,0,0);
+        
+        // retrieve index (route and position)
+        for (const Route &route : solution.getRoutes())
+        {
+            count +=  route.getSize();
+            if (pairNumber < count)
+            {
+                count = count - pairNumber;
+
+                std::get<0>(index) = routeID;
+                std::get<1>(index) = count;
+                std::get<2>(index) = route.getPairLocationPosition(count, solution.getData());
+
+                // retrieve pickupID (= PairID)
+                if (solution.getData().getLocation(route.getRoute().at(count)).getLocType() == LocType::PICKUP )
+                {
+                    pairID = route.getRoute().at(count);
+                }
+                else
+                {
+                    pairID = route.getRoute().at(std::get<2>(index));
+                }
+                break;
+            }
+            ++routeID;
+        }
+
+        RemovePair remPair = RemovePair(index, solution.getData().getPair(pairID));
+
+        // appliquer la modif (via solution method -> update correctement la solution)
+        solution.applyDestructSolution(remPair);
+
+        // update les compteurs
+        --remainingPairToDelete;
+    }
+}
\ No newline at end of file
diff --git a/src/lns/operators/destruction/random_destroy.h b/src/lns/operators/destruction/random_destroy.h
new file mode 100644
index 0000000000000000000000000000000000000000..c7e6185adf29fe165641dd7f804335173d9c6cfc
--- /dev/null
+++ b/src/lns/operators/destruction/random_destroy.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "lns/operators/abstract_operator.h"
+
+class RandomDestroy : public DestructionOperator
+{
+public:
+    explicit RandomDestroy(int numberOfPairsToDestroy)
+        : numberOfPairsToDestroy(numberOfPairsToDestroy)
+    {}
+
+    /**
+     * This operator removes numberOfPairsToDestroy pairs randomly in the solution.
+     */
+    void destroySolution(Solution &solution) const override;
+
+private:
+    int numberOfPairsToDestroy;
+};
diff --git a/src/lns/operators/selector/operator_selection.cpp b/src/lns/operators/selector/operator_selection.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/lns/operators/selector/operator_selector.h b/src/lns/operators/selector/operator_selector.h
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/lns/solution/route.cpp b/src/lns/solution/route.cpp
index 6ebee237600353e77282a210cdc68d3f4b50337d..eb06aadd6ea7662ada2157f8a651fbfe1e708920 100644
--- a/src/lns/solution/route.cpp
+++ b/src/lns/solution/route.cpp
@@ -59,4 +59,22 @@ int Route::getLocation(int index) const
 int Route::getSize() const 
 {
     return route.size();
+}
+
+
+int Route::getPairLocationPosition(int position, const PDPTWData &data) const
+{
+    int firstID = getRoute().at(position);
+    int secondID = data.getLocation(firstID).getPair();
+
+    for (int i=0; i < getSize(); i++)
+    {
+        if (getRoute().at(i) == secondID)
+        {
+            return i;
+        }
+    }
+    
+    spdlog::error("Paired location not found for ID {}", firstID);
+    throw std::runtime_error("Paired location not found in the route");
 }
\ No newline at end of file
diff --git a/src/lns/solution/route.h b/src/lns/solution/route.h
index aac79920ea2b0e1bbf74534fbb87efe96f89dc0c..32010af46aefd7c2cab0c8737aeb19fb33c392a2 100644
--- a/src/lns/solution/route.h
+++ b/src/lns/solution/route.h
@@ -2,6 +2,7 @@
 
 #include <vector>
 
+#include "input/pdptw_data.h"
 #include "input/time_window.h"
 
 /**
@@ -15,13 +16,6 @@ private:
     std::vector<int> route;
     int cost;
     
-
-
-    /* Stocké dans les contraintes
-    std::vector<TimeInteger> reach_time; // debut d'arrivee
-    std::vector<double> FTS; // forward time slack
-    std::vector<std::vector<double>> acc_cap; // inscreasing capacity allowed between two positions
-    */
    
 public:
 
@@ -33,11 +27,20 @@ public:
     // get Location
     int getLocation(int index) const;    
 
+    
+    /**
+     *  Given the position of a location in a route, return the paired location position.
+     *  Example, a pair of location ID 3 and 4 are in a route at position 7 and 18, 
+     *  getPairLocationPosition(7) must return 18 and vice versa.
+     */
+    int getPairLocationPosition(int position, const PDPTWData &data) const;
+
+
     void print() const;
 
-    /*
-    * Add a location index in the route (does not update the route cost)
-    */
+    /**
+     *  Add a location index in the route (does not update the route cost)
+     */
     void insertAt(int locationIndex, int position);
 
     /*
diff --git a/src/lns/solution/solution.cpp b/src/lns/solution/solution.cpp
index 8c3bf532650fef0c72face3af56bab4bdc11feed..f996203ae4d26b110a5fa32f97f89e6678ef313f 100644
--- a/src/lns/solution/solution.cpp
+++ b/src/lns/solution/solution.cpp
@@ -1,19 +1,18 @@
 #include "solution.h"
 
-#include <utility>
-
+#include "config.h"
 #include "input/data.h"
 #include "input/time_window.h"
 #include "lns/constraints/capacity/capacity_constraint.h"
 #include "lns/constraints/time_window/time_window_constraint.h"
-
-#include "config.h"
 #include "lns/solution/route.h"
 
+#include <utility>
+
 void Solution::initPairBank()
-{   
+{
     pairBank.clear();
-    for (const Pair & pair : getData().getPairs())
+    for (Pair const &pair: getData().getPairs())
     {
         pairBank.push_back(pair.getID());
     }
@@ -35,14 +34,14 @@ void Solution::computeAndStoreSolutionCost()
 {
     routeCost = computeSolutionCost();
 
-    // add penalty for solution in the pairBank ? 
+    // add penalty for solution in the pairBank ?
     totalCost = routeCost + computePenalization();
 }
 
 double Solution::computeSolutionCost() const
 {
     double cost = 0;
-    for (const Route & route : getRoutes())
+    for (Route const &route: getRoutes())
     {
         cost += data::routeCost(data, route);
     }
@@ -55,7 +54,6 @@ double Solution::computePenalization() const
     return getBank().size() * EXCLUSION_PENALTY;
 }
 
-
 void Solution::init()
 {
     initPairBank();
@@ -64,43 +62,40 @@ void Solution::init()
     computeAndStoreSolutionCost();
 }
 
+Solution::Solution(PDPTWData const &data, Solution::PairBank pairbank, std::vector<Route> routes, double routeCost,
+                   double totalCost)
+    : data(data), pairBank(std::move(pairbank)), routes(std::move(routes)), routeCost(routeCost), totalCost(totalCost)
+{}
 
-Solution::Solution(const PDPTWData &data, Solution::PairBank pairbank, std::vector<Route> routes, double routeCost, double totalCost) :
-    data(data), 
-    pairBank(std::move(pairbank)), 
-    routes(std::move(routes)), 
-    routeCost(routeCost),
-    totalCost(totalCost) {}
-
-Solution::Solution(const PDPTWData &data) : data(data)
+Solution::Solution(PDPTWData const &data) : data(data)
 {
     init();
 }
 
-Solution Solution::emptySolution(const PDPTWData &data)
+Solution Solution::emptySolution(PDPTWData const &data)
 {
     Solution s = Solution(data);
     return s;
 }
 
-const Solution::PairBank & Solution::getBank() const
+Solution::PairBank const &Solution::getBank() const
 {
     return pairBank;
 }
 
-const std::vector<Route> & Solution::getRoutes() const
+std::vector<Route> const &Solution::getRoutes() const
 {
     return routes;
 }
 
-std::vector<Route> & Solution::getRoutes()
+std::vector<Route> &Solution::getRoutes()
 {
     return routes;
 }
 
-Route & Solution::getRoute(int routeIndex) 
+Route &Solution::getRoute(int routeIndex)
 {
-    if (routeIndex < 0 || routeIndex >= routes.size()) 
+    if (routeIndex < 0 || routeIndex >= routes.size())
     {
         spdlog::error("Invalid route index: {}", routeIndex);
         throw std::out_of_range("Invalid route index.");
@@ -108,9 +103,9 @@ Route & Solution::getRoute(int routeIndex)
     return routes[routeIndex];
 }
 
-const Route & Solution::getRoute(int routeIndex) const
+Route const &Solution::getRoute(int routeIndex) const
 {
-    if (routeIndex < 0 || routeIndex >= routes.size()) 
+    if (routeIndex < 0 || routeIndex >= routes.size())
     {
         spdlog::error("Invalid route index: {}", routeIndex);
         throw std::out_of_range("Invalid route index.");
@@ -123,23 +118,68 @@ double Solution::getCost() const
     return totalCost;
 }
 
-const PDPTWData & Solution::getData() const
+PDPTWData const &Solution::getData() const
 {
     return data;
 }
 
+int Solution::requestsFulFilledCount() const
+{
+    int count = 0;
+    for (Route const &route: getRoutes())
+    {
+        count += route.getRoute().size() / 2;
+    }
+    return count;
+}
+
+void Solution::beforeApplyModification(AtomicModification &modification)
+{
+    // pre check to do ?
+}
+
+void Solution::afterApplyModification(AtomicModification &modification)
+{
+    // constraint status update
+    for (std::unique_ptr<Constraint> &constraint: constraints)
+    {
+        constraint->applyVariant(modification.asApplyVariant());
+    }
+}
+
+void Solution::applyRecreateSolution(AtomicRecreation &modification)
+{
+    // apply the modification to the solution
+    
+}
+
+void Solution::applyDestructSolution(AtomicDestruction &modification)
+{
+    beforeApplyModification(modification);
+
+    modification.modifySolution(*this);
+    // updating request bank
+    std::vector<int> const &deletedPair = modification.getDeletedPairs();
+    
+    //pairBank.reserve(pairBank.size() + deletedPair.size()); 
+    pairBank.insert(pairBank.end(), deletedPair.begin(), deletedPair.end());
+
+    afterApplyModification(modification);
+}
+
 void Solution::print() const
 {
-    std::cout << "Cost :" << totalCost << "\n" << "Routes : \n";
+    std::cout << "Cost : " << totalCost << "\n"
+              << "Routes : \n";
 
-    for (const Route& id : getRoutes())
+    for (Route const &id: getRoutes())
     {
         id.print();
     }
 
-    std::cout << "Banques : \n";
+    std::cout << "Pair Bank : \n";
 
-    for (const int id : getBank())
+    for (int const id: getBank())
     {
         std::cout << id << ", ";
     }
diff --git a/src/lns/solution/solution.h b/src/lns/solution/solution.h
index 48bffdadfc167b0bf4fd427f6e511b1eada8d72a..31affb5ed7557ac59ca156a72232c74bcfbef4f7 100644
--- a/src/lns/solution/solution.h
+++ b/src/lns/solution/solution.h
@@ -1,13 +1,12 @@
 #pragma once
 
-#include <vector>
-#include <iostream>
-#include <spdlog/spdlog.h>
-
-
+#include "input/pair.h"
 #include "lns/constraints/constraint.h"
 #include "route.h"
-#include "input/pair.h"
+
+#include <iostream>
+#include <spdlog/spdlog.h>
+#include <vector>
 
 class AtomicModification;
 class AtomicRecreation;
@@ -24,68 +23,101 @@ public:
     using PairBank = std::vector<int>;
 
 private:
-    PDPTWData const & data;
+    // ref_wrapper
+    PDPTWData const &data;
     /*
-    *   Store IDs of a pairs (Pickup & Delivery) that are not assigned yet to a route.
-    */
+     *  Store IDs of a pairs (Pickup & Delivery) that are not assigned yet to a route.
+     */
     PairBank pairBank;
     /*
-    *   Vector of routes representing the solution
-    */
+     *  Vector of routes representing the solution
+     */
     std::vector<Route> routes;
     double routeCost;
     double totalCost;
     std::vector<std::unique_ptr<Constraint>> constraints;
 
 public:
-
     /**
-    *   Expected way to construct a solution.
-    */
-    static Solution emptySolution(const PDPTWData &data);
+     *  Expected way to construct a solution.
+     */
+    static Solution emptySolution(PDPTWData const &data);
 
-    explicit Solution(const PDPTWData &data);
+    explicit Solution(PDPTWData const &data);
 
     /**
-    *   For testing/debugging.
-    *   Use emptySolution(const PDPTWData &data) to create an initial empty solution.
-    */
-    Solution(const PDPTWData &data, Solution::PairBank pairbank, std::vector<Route> routes, double routeCost, double totalCost);
+     *  For testing/debugging.
+     *  Use emptySolution(const PDPTWData &data) to create an initial empty solution.
+     */
+    Solution(PDPTWData const &data, Solution::PairBank pairbank, std::vector<Route> routes, double routeCost,
+             double totalCost);
 
 
-    PairBank const & getBank() const;
-    std::vector<Route> const & getRoutes() const;
-    Route const & getRoute(int routeIndex) const; 
-    const PDPTWData & getData() const;
+    PairBank const &getBank() const;
+    std::vector<Route> const &getRoutes() const;
+    Route const &getRoute(int routeIndex) const;
+    PDPTWData const &getData() const;
     double getCost() const;
 
+    /**
+     *  Return the number of fullfilled requests.
+     *  The solution must be consistent !
+     *  (compute this number by looking at the size of each routes)
+     */
+    int requestsFulFilledCount() const;
+
+
+    /**
+     *  Pre modification check.
+     *  @param modification Must be a valid modification.
+     */
+    void beforeApplyModification(AtomicModification &modification);
+
+    /**
+     *  Update constraints, it is expected that the solution has already been modified.
+     *  @param modification Must be a valid modification.
+     */
+    void afterApplyModification(AtomicModification &modification);
+
+    /**
+     *  Apply the given the modification on the solution. Does not check validity.
+     *  @param modification Must be a valid recreation.
+     */
+    void applyRecreateSolution(AtomicRecreation &modification);
+
+    /**
+     *  Apply the given the modification on the solution. Does not check validity.
+     *  @param modification Must be a destruction.
+     */
+    void applyDestructSolution(AtomicDestruction &modification);
+
+
     // For route modification
-    std::vector<Route> & getRoutes();
-    Route & getRoute(int routeIndex);
-    
-    void print() const;
+    std::vector<Route> &getRoutes();
+    Route &getRoute(int routeIndex);
 
+    void print() const;
 
-private :
 
+private:
     /**
-    *   Does the initialisation of the object, called by the constructor
-    */
+     *  Does the initialisation of the object, called by the constructor
+     */
     void init();
 
     /**
-    *   Init/reset the constraints of the problem
-    */
+     *  Init/reset the constraints of the problem
+     */
     void initConstraints();
     void initRoutes();
     void initPairBank();
- 
+
     void computeAndStoreSolutionCost();
-    
+
     /**
-    *   Compute the cost of the solution (routes cost)
-    */
+     *  Compute the cost of the solution (routes cost)
+     */
     double computeSolutionCost() const;
-    
+
     double computePenalization() const;
 };
\ No newline at end of file
diff --git a/src/mains/main.cpp b/src/mains/main.cpp
index 67c7b747917efcb3396eb00db2aa060486787b40..d6fb7022565ea0374483464e0c7f0b974221834f 100644
--- a/src/mains/main.cpp
+++ b/src/mains/main.cpp
@@ -42,82 +42,5 @@ int main(int argc, char const *argv[])
     Solution emptySol = Solution::emptySolution(data);
     emptySol.print();
 
-    
-
-    // // SOLUTION 1-6-2-7 (time ; 104)
-
-    // Solution::PairBank bank {3,4,5,8,9,10};
-
-    // std::vector<int> path {1,6,2,7};
-    // Route route = Route(path, 0);
-    // std::vector<Route> routes;
-    // routes.push_back(route);
-
-    // int totalCost = 0;
-    
-    // Solution sol = Solution(data, bank, routes, totalCost);
-
-    // sol.print();
-
-
-    // // Couple pickup/delivery
-    // Location r2P = data.getLocations()[1];
-    // Location r2D = data.getLocations()[6];
-    // Location r3P = data.getLocations()[2];
-    // Location r3D = data.getLocations()[7];
-
-    // Pair pair2 = Pair(r2P, r2D);
-    // Pair pair3 = Pair(r3P, r3D);
-
-
-
-    // /*
-
-    // CapacityConstraint cap_const = CapacityConstraint(sol);
-    
-
-    // cap_const.initCapacities();
-    // cap_const.print();
-
-    // std::cout << "Is Valid ? : " << cap_const.checkVariant(opInsert) << "\n";
-
-    // cap_const.applyVariant(opInsert);
-
-    // cap_const.print();
-
-    // RemovePair opRemove = RemovePair(0, 2, 3, pair3);
-
-    // cap_const.applyVariant(opRemove);
-    
-    // cap_const.print();
-    // */
-
-
-    // TimeWindowConstraint twconst = TimeWindowConstraint(sol);
-
-    // std::cout << "\n --- : \n";
-    
-    // twconst.print();
-
-    // twconst.initReachTimes();
-
-    // twconst.print();
-    
-    // // Modification 
-    // InsertPair opInsert = InsertPair(0, 0, 0, pair3);
-
-    // std::cout << "is valid ? : " << twconst.checkVariant(opInsert) << "\n";
-
-    // twconst.applyVariant(opInsert);
-
-    // twconst.print();
-    // opInsert.modifySolution(sol);
-
-    // RemovePair opRemove = RemovePair(0, 0, 1, pair3);
-
-    // twconst.applyVariant(opRemove);
-    
-    // twconst.print();
-
     return 0;
 }
diff --git a/src/output/solution_checker.cpp b/src/output/solution_checker.cpp
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9918138fad80eecbe706c3214bde01e4293ff9f2 100644
--- a/src/output/solution_checker.cpp
+++ b/src/output/solution_checker.cpp
@@ -0,0 +1,178 @@
+#include "solution_checker.h"
+#include "input/location.h"
+#include "input/pdptw_data.h"
+#include "lns/solution/solution.h"
+#include "types.h"
+#include "input/data.h"
+
+#include <spdlog/spdlog.h>
+
+void checker::checkSolutionCoherence(Solution const &sol, PDPTWData const &data)
+{
+    bool errorFlag = false;
+
+    // Vector that will store the route ID serving the location, (-1 if no routes)
+    std::vector<int> check(data.getLocations().size(), -1);
+
+    // checking routes coherence
+    int routeID = 0;
+    for (const Route &route : sol.getRoutes())
+    {
+        for (int id : route.getRoute())
+        {
+            if (check.at(id) != -1)
+            {
+                // Error the location is already attributed (doublon)
+                spdlog::error("Location {} has already been visited.", id);
+                errorFlag = true;
+            }
+
+            check.at(id) = routeID;
+
+            // if the location is a delivery, check if the pickup location has already been visited in the same route
+            if ((data.getLocation(id).getLocType() == LocType::DELIVERY) 
+            && (check[data.getLocation(id).getPair()] != routeID) )
+            {
+                // Error Pickup and Delivery are not in the same route (wrong route)
+                // OR Delivery before Pickup (wrong order)
+                spdlog::error("Pair associated to {} is not consistent (route or order problem).", id);
+                errorFlag = true;
+            }
+        }
+        ++routeID;
+    }
+
+    // checking PairBank coherence (given the routes)
+    for (int pairID : sol.getBank())
+    {
+        if ((check.at(pairID) != -1) || (data.getLocation(pairID).getPair() != -1))
+        {
+            // Error Pair in the bank but one location of the pair seems to be visited by a route (doublon)
+            spdlog::error("Pair associated to {} is in a route and in the bank at the same time.", pairID);
+            errorFlag = true;
+        }
+    }
+
+    if (errorFlag)
+    {
+        throw SolutionConstraintError("Error in the consistency of the solution.", sol);
+    }
+
+}
+
+
+void checker::checkCapacity(Solution const &sol, PDPTWData const &data)
+{   
+    bool errorFlag = false;
+    int capa = 0;
+    int routeID = 0;
+
+    for (const Route &route : sol.getRoutes() )
+    {
+        for (int id : route.getRoute() )
+        {
+            capa += data.getLocation(id).getDemand();
+            if (capa > data.getCapacity())
+            {
+                // Error, max capacity is exceeded
+                spdlog::error("Maximum Capacity is exceeded at {} in the route {}.", id, routeID);
+                errorFlag = true;
+            }
+        }
+        if (capa != 0) 
+        {
+            // Error, all the capacity is supposed to be free at the end of a route
+            spdlog::error("Some capacity still used at the end of the route {}.", routeID);
+            errorFlag = true; 
+        }
+        ++ routeID;
+    }
+
+    if (errorFlag)
+    {
+        throw SolutionConstraintError("Error in the capacity constraint of the solution.", sol);
+    }
+}
+
+void checker::checkTimeWindows(Solution const &sol, PDPTWData const &data)
+{
+    bool errorFlag = false;
+    TimeInteger reachTime = 0;
+    int routeID = 0;
+    int locID = 0;
+
+    for (const Route &route : sol.getRoutes() )
+    {
+        // travel to the first location
+        reachTime = data.getDepot().getTimeWindow().getStart() + data::TravelTime(data, 0, route.getRoute().at(0));
+
+        for (int i=0; i < route.getRoute().size() - 1; i++)
+        {
+            locID = route.getRoute().at(i);
+            // check TimeWindow
+            if (!(data.getLocation(locID).getTimeWindow().isValid(reachTime)) )
+            {
+                // Error, reach time not valid for the location time window
+                spdlog::error("Reach time not valid for the location {} time window in route {}.", locID, routeID);
+                errorFlag = true; 
+            }
+            
+            TimeInteger travelTime = data::TravelTime(data, locID, route.getRoute().at(i+1));
+            TimeInteger serviceTime = data.getLocation(locID).getServiceDuration();
+            TimeInteger startTW = data.getLocation(locID).getTimeWindow().getStart();
+
+            reachTime = std::max(reachTime, startTW) + serviceTime + travelTime;
+        } 
+
+        // check last timeWindow
+        locID = route.getRoute().back();
+        if (!(data.getLocation(locID).getTimeWindow().isValid(reachTime)) )
+        {
+            // Error, reach time not valid for the location time window
+            spdlog::error("Reach time not valid for the location {} time window in route {}.", locID, routeID);
+            errorFlag = true; 
+        }
+
+        ++ routeID;
+    }
+
+    if (errorFlag)
+    {
+        throw SolutionConstraintError("Error in the time windows constraint of the solution.", sol);
+    }
+}
+
+void checker::checkAll(Solution const &sol , PDPTWData const &data, bool checkRequests)
+{
+
+    // TO DO, check in the solution if all locations are visited ! (in checkSolutionCoherence ?)
+    if (checkRequests)
+    {
+        if (!sol.getBank().empty())
+        {
+            // Error, all locations are supposed to be visited, the pairBank is not empty
+            spdlog::error("All locations are supposed to be visited, the pairBank is not empty.");
+        }
+    }
+
+    checker::checkSolutionCoherence(sol, data);
+    checker::checkCapacity(sol, data);
+    checker::checkTimeWindows(sol,data);
+
+}
+
+
+// No logging yet
+
+checker::SolutionInternalError::SolutionInternalError(std::string reason, Solution const &sol)
+    : std::logic_error(reason /*+ " More details in logs"*/)
+{
+    // logging::errorDumpLogger().error("{}\n{}", reason, sol);
+    // sol.exportDotFile();
+}
+
+checker::SolutionConstraintError::SolutionConstraintError(std::string reason, Solution const &sol)
+    : std::logic_error(reason /*+ " More details in logs"*/)
+{
+    // logging::errorDumpLogger().error("{}\n{}", reason, sol);
+}
diff --git a/src/output/solution_checker.h b/src/output/solution_checker.h
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4f84139a3bc6f9bab745e7abace2997d54097cdf 100644
--- a/src/output/solution_checker.h
+++ b/src/output/solution_checker.h
@@ -0,0 +1,58 @@
+#pragma once
+
+#include <stdexcept>
+
+#include "input/pdptw_data.h"
+#include "input/time_window.h"
+#include "lns/solution/solution.h"
+
+
+namespace checker
+{
+
+    /**
+     *  Does a check full check on the solution.
+     *  @param checkRequests, if false will not throw on missing requests
+     */
+    void checkAll(Solution const &, PDPTWData const &data, bool checkRequests = true);
+
+    /**
+     *  Check that the solution represent a PDPTW solution.
+     *  No doublon, Ordered pickup and delivery, pickup and delivery in the same route.
+     *  (don't check the constraints)
+     */
+    void checkSolutionCoherence(Solution const &sol, PDPTWData const &data);
+
+    /**
+     *  Check the capacity constraint.
+     */
+    void checkCapacity(Solution const &sol, PDPTWData const &data);
+
+    /**
+     *  Check the time windows constraint.
+     *  (Maybe a better way to do it than reconstructing the ordo ?)
+     */
+    void checkTimeWindows(Solution const &sol, PDPTWData const &data);
+
+
+    /**
+     * This error means that the internal Solutions values are broken.
+     * Errors like this are not linked to the model constraint.
+     */
+    class SolutionInternalError : public std::logic_error
+    {
+    public:
+        SolutionInternalError(std::string reason, Solution const &);
+    };
+ 
+    /**
+    * This exception means that the solution is not valid regarding the PDPTW problem
+    */
+    class SolutionConstraintError : public std::logic_error
+    {
+    public:
+        SolutionConstraintError(std::string reason, Solution const &);
+    };
+
+
+}// namespace checker
diff --git a/src/utils.cpp b/src/utils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6ab381b9b788f2b9551f92f5e70d27581a79ee7a
--- /dev/null
+++ b/src/utils.cpp
@@ -0,0 +1,32 @@
+#include "utils.h"
+#include "config.h"
+
+#include <random>
+
+namespace// anonymous namespace
+{
+    std::mt19937_64 randomGenerator;// NOLINT(*-msc51-cpp) => deterministic random!
+    bool seedSet = false;
+    std::uniform_real_distribution<> distribution(0, 1);
+}// namespace
+
+
+double util::getRandom()
+{
+    if (!seedSet) [[unlikely]]
+    {
+        randomGenerator.seed(RANDOM_SEED);
+        seedSet = true;
+    }
+    return distribution(randomGenerator);
+}
+
+unsigned int util::getRandomInt(unsigned int min, unsigned int max)
+{
+    if (!seedSet) [[unlikely]]
+    {
+        randomGenerator.seed(RANDOM_SEED);
+        seedSet = true;
+    }
+    return std::uniform_int_distribution<>(min, max)(randomGenerator);
+}
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000000000000000000000000000000000000..323fe4f2b22c3fa8941a7e2b9f851a9b13871a23
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <random>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+namespace util
+{
+    /**
+     * Get a random number
+     * @return a number between 0 (inclusive) and 1 (exclusive)
+     */
+    double getRandom();
+    /**
+     * @return a random integer number between min (included) and max (included)
+     */
+    unsigned int getRandomInt(unsigned int min, unsigned int max);
+
+
+}// namespace util