From 54e16ee5a1a09dd028a364430cc1eafe789b54d2 Mon Sep 17 00:00:00 2001
From: awenjb <126257927+awenjb@users.noreply.github.com>
Date: Tue, 4 Mar 2025 15:43:42 +0100
Subject: [PATCH] add Solution Components

- add basic solution Checker
- add random destroy Operator
- revised some parts of the code
---
 .clang-format                                 |  74 ++++++++
 CMakeLists.txt                                |   8 +-
 src/config.h                                  |   4 +-
 src/input/pdptw_data.cpp                      |  12 ++
 src/input/pdptw_data.h                        |   2 +
 .../time_window/time_window_constraint.cpp    |  15 --
 src/lns/modification/atomic_destruction.h     |   2 +-
 src/lns/modification/atomic_modification.h    |  14 +-
 src/lns/modification/pair/insert_pair.cpp     |   5 +
 src/lns/modification/pair/insert_pair.h       |   2 +
 src/lns/modification/pair/remove_pair.cpp     |  58 +++---
 src/lns/modification/pair/remove_pair.h       |   8 +-
 src/lns/modification/route/remove_route.cpp   |  38 ++--
 src/lns/modification/route/remove_route.h     |   6 +-
 src/lns/operators/abstract_operator.h         |  17 ++
 .../operators/destruction/random_destroy.cpp  |  64 +++++++
 .../operators/destruction/random_destroy.h    |  19 ++
 .../operators/selector/operator_selection.cpp |   0
 .../operators/selector/operator_selector.h    |   0
 src/lns/solution/route.cpp                    |  18 ++
 src/lns/solution/route.h                      |  23 ++-
 src/lns/solution/solution.cpp                 | 100 +++++++---
 src/lns/solution/solution.h                   | 108 +++++++----
 src/mains/main.cpp                            |  77 --------
 src/output/solution_checker.cpp               | 178 ++++++++++++++++++
 src/output/solution_checker.h                 |  58 ++++++
 src/utils.cpp                                 |  32 ++++
 src/utils.h                                   |  24 +++
 28 files changed, 734 insertions(+), 232 deletions(-)
 create mode 100644 .clang-format
 create mode 100644 src/lns/operators/abstract_operator.h
 create mode 100644 src/lns/operators/destruction/random_destroy.cpp
 create mode 100644 src/lns/operators/destruction/random_destroy.h
 create mode 100644 src/lns/operators/selector/operator_selection.cpp
 create mode 100644 src/lns/operators/selector/operator_selector.h
 create mode 100644 src/utils.cpp
 create mode 100644 src/utils.h

diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..856f134
--- /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 9f66893..2edbfcd 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 bf368d8..6bb3f2b 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 142fbde..706bd09 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 ac0ea57..3cfb829 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 b350692..c031048 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 f2ad02f..1506348 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 c189594..701be75 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 b3f9d4b..bf717d0 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 d806765..84fe918 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 1a1c808..6cf567c 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 ef9dbdb..db85dc2 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 006d5df..aa08131 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 be981e8..08fb830 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 0000000..2223e22
--- /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 0000000..975e1a4
--- /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 0000000..c7e6185
--- /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 0000000..e69de29
diff --git a/src/lns/operators/selector/operator_selector.h b/src/lns/operators/selector/operator_selector.h
new file mode 100644
index 0000000..e69de29
diff --git a/src/lns/solution/route.cpp b/src/lns/solution/route.cpp
index 6ebee23..eb06aad 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 aac7992..32010af 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 8c3bf53..f996203 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 48bffda..31affb5 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 67c7b74..d6fb702 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 e69de29..9918138 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 e69de29..4f84139 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 0000000..6ab381b
--- /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 0000000..323fe4f
--- /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
-- 
GitLab