diff --git a/exams/AxelRubini/2025-01-09/1/Makefile b/exams/AxelRubini/2025-01-09/1/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/1/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-01-09/1/include/DES.hpp b/exams/AxelRubini/2025-01-09/1/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-01-09/1/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/1/include/Decision.hpp b/exams/AxelRubini/2025-01-09/1/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/1/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/1/include/Dynamics.hpp b/exams/AxelRubini/2025-01-09/1/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-01-09/1/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/1/include/Geometry.hpp b/exams/AxelRubini/2025-01-09/1/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-01-09/1/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/1/include/IO.hpp b/exams/AxelRubini/2025-01-09/1/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/1/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/1/include/Inventory.hpp b/exams/AxelRubini/2025-01-09/1/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/1/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/1/include/Queue.hpp b/exams/AxelRubini/2025-01-09/1/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/1/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/1/include/Random.hpp b/exams/AxelRubini/2025-01-09/1/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/1/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/1/include/Stat.hpp b/exams/AxelRubini/2025-01-09/1/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/1/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/1/main b/exams/AxelRubini/2025-01-09/1/main new file mode 100755 index 0000000..ac45724 Binary files /dev/null and b/exams/AxelRubini/2025-01-09/1/main differ diff --git a/exams/AxelRubini/2025-01-09/1/main.cpp b/exams/AxelRubini/2025-01-09/1/main.cpp new file mode 100644 index 0000000..a8c4fb4 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/1/main.cpp @@ -0,0 +1,42 @@ +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include + +int main() { + SELib::RandomGenerator rng; + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) { + return 1; + } + + int numStates = parser.getInt("N", 0); + if (numStates <= 0) + return 1; + + SELib::MDP mdp(numStates, rng); + const auto &data = parser.getStructuredData(); + for (const auto &line : data) { + if (!line.empty() && line[0] == "A") { + int from = std::stoi(line[1]); + int to = std::stoi(line[2]); + double prob = std::stod(line[3]); + double cost = std::stod(line[4]); + mdp.addTransition(from, to, prob, cost); + } + } + + SELib::MonteCarloSimulator simulator(1000); + double expectedCost = + simulator.estimate([&](int) { return mdp.simulate(0, numStates - 1); }); + + std::ofstream outFile("results.txt"); + outFile << "2025-01-09-Axel-Rubini-2158099" << std::endl; + outFile << "C " << expectedCost << std::endl; + outFile.close(); + + return 0; +} diff --git a/exams/AxelRubini/2025-01-09/1/parameters.txt b/exams/AxelRubini/2025-01-09/1/parameters.txt new file mode 100644 index 0000000..7f56793 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/1/parameters.txt @@ -0,0 +1,11 @@ +N 5 +A 0 1 1 100 +A 1 2 0.3 100 +A 1 3 0.7 150 +A 2 3 1 100 +A 3 0 0.6 10 +A 3 1 0.1 10 +A 3 2 0.1 10 +A 3 3 0.1 10 +A 3 4 0.1 10 +A 4 4 1 0 diff --git a/exams/AxelRubini/2025-01-09/1/results.txt b/exams/AxelRubini/2025-01-09/1/results.txt new file mode 100644 index 0000000..4ddcdff --- /dev/null +++ b/exams/AxelRubini/2025-01-09/1/results.txt @@ -0,0 +1,2 @@ +2025-01-09-Axel-Rubini-2158099 +C 2333.51 diff --git a/exams/AxelRubini/2025-01-09/2/Makefile b/exams/AxelRubini/2025-01-09/2/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/2/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-01-09/2/include/DES.hpp b/exams/AxelRubini/2025-01-09/2/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-01-09/2/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/2/include/Decision.hpp b/exams/AxelRubini/2025-01-09/2/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/2/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/2/include/Dynamics.hpp b/exams/AxelRubini/2025-01-09/2/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-01-09/2/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/2/include/Geometry.hpp b/exams/AxelRubini/2025-01-09/2/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-01-09/2/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/2/include/IO.hpp b/exams/AxelRubini/2025-01-09/2/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/2/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/2/include/Inventory.hpp b/exams/AxelRubini/2025-01-09/2/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/2/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/2/include/Queue.hpp b/exams/AxelRubini/2025-01-09/2/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/2/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/2/include/Random.hpp b/exams/AxelRubini/2025-01-09/2/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/2/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/2/include/Stat.hpp b/exams/AxelRubini/2025-01-09/2/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/2/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/2/main b/exams/AxelRubini/2025-01-09/2/main new file mode 100755 index 0000000..5d928a6 Binary files /dev/null and b/exams/AxelRubini/2025-01-09/2/main differ diff --git a/exams/AxelRubini/2025-01-09/2/main.cpp b/exams/AxelRubini/2025-01-09/2/main.cpp new file mode 100644 index 0000000..a9953f3 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/2/main.cpp @@ -0,0 +1,44 @@ +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include + +int main() { + SELib::RandomGenerator rng; + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) { + return 1; + } + + double maxCost = parser.getDouble("C", 0.0); + int numStates = parser.getInt("N", 0); + if (numStates <= 0) + return 1; + + SELib::MDP mdp(numStates, rng); + const auto &data = parser.getStructuredData(); + for (const auto &line : data) { + if (!line.empty() && line[0] == "A") { + int from = std::stoi(line[1]); + int to = std::stoi(line[2]); + double prob = std::stod(line[3]); + double cost = std::stod(line[4]); + mdp.addTransition(from, to, prob, cost); + } + } + + SELib::MonteCarloSimulator simulator(1000); + double prob = simulator.estimateProbability([&](int) { + return mdp.simulate(0, numStates - 1) <= maxCost; + }); + + std::ofstream outFile("results.txt"); + outFile << "2025-01-09-Axel-Rubini-2158099" << std::endl; + outFile << "P " << prob << std::endl; + outFile.close(); + + return 0; +} diff --git a/exams/AxelRubini/2025-01-09/2/parameters.txt b/exams/AxelRubini/2025-01-09/2/parameters.txt new file mode 100644 index 0000000..541d237 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/2/parameters.txt @@ -0,0 +1,12 @@ +C 200 +N 5 +A 0 1 0.7 100 +A 0 2 0.3 50 +A 1 3 1.0 100 +A 2 3 1.0 100 +A 3 3 1.0 70 +A 4 0 0.2 5 +A 4 1 0.2 10 +A 4 2 0.2 20 +A 4 3 0.2 30 +A 4 4 0.2 40 diff --git a/exams/AxelRubini/2025-01-09/2/results.txt b/exams/AxelRubini/2025-01-09/2/results.txt new file mode 100644 index 0000000..f185dcc --- /dev/null +++ b/exams/AxelRubini/2025-01-09/2/results.txt @@ -0,0 +1,2 @@ +2025-01-09-Axel-Rubini-2158099 +P 0 diff --git a/exams/AxelRubini/2025-01-09/3/Makefile b/exams/AxelRubini/2025-01-09/3/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/3/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-01-09/3/include/DES.hpp b/exams/AxelRubini/2025-01-09/3/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-01-09/3/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/3/include/Decision.hpp b/exams/AxelRubini/2025-01-09/3/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/3/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/3/include/Dynamics.hpp b/exams/AxelRubini/2025-01-09/3/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-01-09/3/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/3/include/Geometry.hpp b/exams/AxelRubini/2025-01-09/3/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-01-09/3/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/3/include/IO.hpp b/exams/AxelRubini/2025-01-09/3/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/3/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/3/include/Inventory.hpp b/exams/AxelRubini/2025-01-09/3/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/3/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/3/include/Queue.hpp b/exams/AxelRubini/2025-01-09/3/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/3/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/3/include/Random.hpp b/exams/AxelRubini/2025-01-09/3/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/3/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/3/include/Stat.hpp b/exams/AxelRubini/2025-01-09/3/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/3/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/3/main b/exams/AxelRubini/2025-01-09/3/main new file mode 100755 index 0000000..7dd71d7 Binary files /dev/null and b/exams/AxelRubini/2025-01-09/3/main differ diff --git a/exams/AxelRubini/2025-01-09/3/main.cpp b/exams/AxelRubini/2025-01-09/3/main.cpp new file mode 100644 index 0000000..5f96206 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/3/main.cpp @@ -0,0 +1,133 @@ +#include "include/DES.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include "include/Stat.hpp" +#include +#include +#include +#include +#include + +namespace SELib { + +class EcommerceCustomer : public DiscreteEventProcess { + public: + EcommerceCustomer( + int pid, + MessageBus &bus, + RandomGenerator &rng, + double avg, + double stddev + ) + : pid_(pid), bus_(bus), rng_(rng), avg_(avg), stddev_(stddev) {} + + void initialize(double startTime) override { scheduleNext(startTime); } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + Message msg; + msg.time = currentTime; + msg.sender = pid_; + msg.receiver = 1; // Monitor PID + bus_.procToNet(pid_).push(msg); + + scheduleNext(currentTime); + } + + private: + void scheduleNext(double currentTime) { + // T soggiorno in idle ~ Gaussiana(Avg, StdDev) + double delay = rng_.gaussian(avg_, stddev_); + if (delay < 1.0) + delay = 1.0; // Time step minimo T=1 + nextEventTime_ = currentTime + delay; + } + + int pid_; + MessageBus &bus_; + RandomGenerator &rng_; + double avg_; + double stddev_; + double nextEventTime_; +}; + +class EcommerceMonitor : public DiscreteEventProcess { + public: + EcommerceMonitor(int pid, MessageBus &bus) + : pid_(pid), bus_(bus), lastRequestTime_(-1.0) {} + + void initialize(double startTime) override {} + + double nextEventTime() const override { + return std::numeric_limits::infinity(); + } + + void handleEvent(double currentTime) override {} + + void processInbox() { + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + if (lastRequestTime_ >= 0) { + stats_.addSample(m.time - lastRequestTime_); + } + lastRequestTime_ = m.time; + } + } + + double getAvg() const { return stats_.mean(); } + double getStdDev() const { return stats_.stddev(); } + + private: + int pid_; + MessageBus &bus_; + double lastRequestTime_; + Statistics stats_; +}; + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng; + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) { + return 1; + } + + double avg = parser.getDouble("Avg", 3600.0); + double stddev = parser.getDouble("StdDev", 500.0); + + SELib::MessageBus bus(2); + SELib::DiscreteEventSystem system; + + // Processo 0: Customer + system.emplaceProcess(0, bus, rng, avg, stddev); + // Processo 1: Monitor + auto &monitor = system.emplaceProcess(1, bus); + // Network Router + system.emplaceProcess(bus, rng, 0.1); + + double currentTime = 0.0; + double horizon = 1000000.0; + system.initialize(0.0); + + while (currentTime < horizon) { + double nextTime = system.nextEventTime(); + if (nextTime > horizon) + break; + currentTime = nextTime; + system.handleEvent(currentTime); + monitor.processInbox(); + } + + std::ofstream outFile("results.txt"); + outFile << "2025-01-09-Axel-Rubini-2158099" << std::endl; + outFile << "Avg " << monitor.getAvg() << std::endl; + outFile << "StdDev " << monitor.getStdDev() << std::endl; + outFile.close(); + + return 0; +} diff --git a/exams/AxelRubini/2025-01-09/3/parameters.txt b/exams/AxelRubini/2025-01-09/3/parameters.txt new file mode 100644 index 0000000..5b5ab4e --- /dev/null +++ b/exams/AxelRubini/2025-01-09/3/parameters.txt @@ -0,0 +1,2 @@ +Avg 100 +StdDev 5 diff --git a/exams/AxelRubini/2025-01-09/3/results.txt b/exams/AxelRubini/2025-01-09/3/results.txt new file mode 100644 index 0000000..78da49e --- /dev/null +++ b/exams/AxelRubini/2025-01-09/3/results.txt @@ -0,0 +1,3 @@ +2025-01-09-Axel-Rubini-2158099 +Avg 99.9165 +StdDev 4.9439 diff --git a/exams/AxelRubini/2025-01-09/4/Makefile b/exams/AxelRubini/2025-01-09/4/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/4/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-01-09/4/include/DES.hpp b/exams/AxelRubini/2025-01-09/4/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-01-09/4/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/4/include/Decision.hpp b/exams/AxelRubini/2025-01-09/4/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/4/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/4/include/Dynamics.hpp b/exams/AxelRubini/2025-01-09/4/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-01-09/4/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/4/include/Geometry.hpp b/exams/AxelRubini/2025-01-09/4/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-01-09/4/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/4/include/IO.hpp b/exams/AxelRubini/2025-01-09/4/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/4/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/4/include/Inventory.hpp b/exams/AxelRubini/2025-01-09/4/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/4/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/4/include/Queue.hpp b/exams/AxelRubini/2025-01-09/4/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/4/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/4/include/Random.hpp b/exams/AxelRubini/2025-01-09/4/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/4/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/4/include/Stat.hpp b/exams/AxelRubini/2025-01-09/4/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/4/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/4/main b/exams/AxelRubini/2025-01-09/4/main new file mode 100755 index 0000000..af8698c Binary files /dev/null and b/exams/AxelRubini/2025-01-09/4/main differ diff --git a/exams/AxelRubini/2025-01-09/4/main.cpp b/exams/AxelRubini/2025-01-09/4/main.cpp new file mode 100644 index 0000000..97de4ea --- /dev/null +++ b/exams/AxelRubini/2025-01-09/4/main.cpp @@ -0,0 +1,191 @@ +#include "include/DES.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include "include/Stat.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace SELib { + +class DispatcherCustomer : public DiscreteEventProcess { + public: + DispatcherCustomer( + int pid, + int dispatcherPid, + MessageBus &bus, + RandomGenerator &rng, + double avg, + double stddev + ) + : pid_(pid), dispatcherPid_(dispatcherPid), bus_(bus), rng_(rng), + avg_(avg), stddev_(stddev) {} + + void initialize(double startTime) override { scheduleNext(startTime); } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + Message msg; + msg.time = currentTime; + msg.sender = pid_; + msg.receiver = dispatcherPid_; + bus_.procToNet(pid_).push(msg); + + scheduleNext(currentTime); + } + + private: + void scheduleNext(double currentTime) { + double delay = rng_.gaussian(avg_, stddev_); + if (delay < 1.0) + delay = 1.0; // T=1 is the minimum step + nextEventTime_ = currentTime + delay; + } + + int pid_; + int dispatcherPid_; + MessageBus &bus_; + RandomGenerator &rng_; + double avg_; + double stddev_; + double nextEventTime_; +}; + +class Dispatcher : public DiscreteEventProcess { + public: + Dispatcher(int pid, int monitorPid, int numCustomers, MessageBus &bus) + : pid_(pid), monitorPid_(monitorPid), numCustomers_(numCustomers), + bus_(bus) { + for (int i = 0; i < numCustomers; ++i) { + inputCounts_[i] = 0; + } + } + + void initialize(double startTime) override {} + + double nextEventTime() const override { + return std::numeric_limits::infinity(); + } + + void handleEvent(double currentTime) override {} + + void processInbox(double currentTime) { + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + if (m.sender >= 0 && m.sender < numCustomers_) { + inputCounts_[m.sender]++; + } + + Message forward = m; + forward.sender = pid_; + forward.receiver = monitorPid_; + bus_.procToNet(pid_).push(forward); + } + } + + int getCount(int customerId) const { + auto it = inputCounts_.find(customerId); + return it != inputCounts_.end() ? it->second : 0; + } + + private: + int pid_; + int monitorPid_; + int numCustomers_; + MessageBus &bus_; + std::map inputCounts_; +}; + +class DispatcherMonitor : public DiscreteEventProcess { + public: + DispatcherMonitor(int pid, MessageBus &bus) + : pid_(pid), bus_(bus), totalReceived_(0) {} + + void initialize(double startTime) override {} + + double nextEventTime() const override { + return std::numeric_limits::infinity(); + } + + void handleEvent(double currentTime) override {} + + void processInbox(double currentTime) { + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + inbox.pop(); + totalReceived_++; + } + } + + int getTotalReceived() const { return totalReceived_; } + + private: + int pid_; + MessageBus &bus_; + int totalReceived_; +}; + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng; + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) { + return 1; + } + + int N = parser.getInt("N", 0); + double avg = parser.getDouble("Avg", 0.0); + double stddev = parser.getDouble("StdDev", 0.0); + + int dispatcherPid = N; + int monitorPid = N + 1; + SELib::MessageBus bus(N + 2); + SELib::DiscreteEventSystem system; + + for (int i = 0; i < N; ++i) { + system.emplaceProcess( + i, dispatcherPid, bus, rng, avg, stddev + ); + } + auto &dispatcher = system.emplaceProcess( + dispatcherPid, monitorPid, N, bus + ); + auto &monitor = + system.emplaceProcess(monitorPid, bus); + system.emplaceProcess( + bus, rng, 0.1 + ); // Back to a sane default + + double currentTime = 0.0; + double horizon = 1000000.0; + system.initialize(0.0); + + while (currentTime < horizon) { + double nextTime = system.nextEventTime(); + if (nextTime > horizon) + break; + currentTime = nextTime; + system.handleEvent(currentTime); + dispatcher.processInbox(currentTime); + monitor.processInbox(currentTime); + } + + std::ofstream outFile("results.txt"); + outFile << "2025-01-09-Axel-Rubini-2158099" << std::endl; + for (int i = 0; i < N; ++i) { + outFile << (i + 1) << " " << dispatcher.getCount(i) << std::endl; + } + outFile << "M1 " << monitor.getTotalReceived() << std::endl; + outFile.close(); + + return 0; +} diff --git a/exams/AxelRubini/2025-01-09/4/parameters.txt b/exams/AxelRubini/2025-01-09/4/parameters.txt new file mode 100644 index 0000000..c1e8d88 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/4/parameters.txt @@ -0,0 +1,3 @@ +N 7 +Avg 1500 +StdDev 150 diff --git a/exams/AxelRubini/2025-01-09/4/results.txt b/exams/AxelRubini/2025-01-09/4/results.txt new file mode 100644 index 0000000..f991aaf --- /dev/null +++ b/exams/AxelRubini/2025-01-09/4/results.txt @@ -0,0 +1,9 @@ +2025-01-09-Axel-Rubini-2158099 +1 666 +2 672 +3 669 +4 669 +5 667 +6 667 +7 664 +M1 4674 diff --git a/exams/AxelRubini/2025-01-09/5/Makefile b/exams/AxelRubini/2025-01-09/5/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/5/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-01-09/5/include/DES.hpp b/exams/AxelRubini/2025-01-09/5/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-01-09/5/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/5/include/Decision.hpp b/exams/AxelRubini/2025-01-09/5/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/5/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/5/include/Dynamics.hpp b/exams/AxelRubini/2025-01-09/5/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-01-09/5/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/5/include/Geometry.hpp b/exams/AxelRubini/2025-01-09/5/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-01-09/5/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/5/include/IO.hpp b/exams/AxelRubini/2025-01-09/5/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/5/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/5/include/Inventory.hpp b/exams/AxelRubini/2025-01-09/5/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/5/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/5/include/Queue.hpp b/exams/AxelRubini/2025-01-09/5/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/5/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/5/include/Random.hpp b/exams/AxelRubini/2025-01-09/5/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/5/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/5/include/Stat.hpp b/exams/AxelRubini/2025-01-09/5/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/5/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-01-09/5/main b/exams/AxelRubini/2025-01-09/5/main new file mode 100755 index 0000000..8f3c33a Binary files /dev/null and b/exams/AxelRubini/2025-01-09/5/main differ diff --git a/exams/AxelRubini/2025-01-09/5/main.cpp b/exams/AxelRubini/2025-01-09/5/main.cpp new file mode 100644 index 0000000..d68e7e3 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/5/main.cpp @@ -0,0 +1,235 @@ +#include "include/DES.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include "include/Stat.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace SELib { + +class DispatcherCustomer : public DiscreteEventProcess { + public: + DispatcherCustomer( + int pid, + int dispatcherPid, + MessageBus &bus, + RandomGenerator &rng, + double avg, + double stddev + ) + : pid_(pid), dispatcherPid_(dispatcherPid), bus_(bus), rng_(rng), + avg_(avg), stddev_(stddev) {} + + void initialize(double startTime) override { scheduleNext(startTime); } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + Message msg; + msg.time = currentTime; + msg.sender = pid_; + msg.receiver = dispatcherPid_; + bus_.procToNet(pid_).push(msg); + + scheduleNext(currentTime); + } + + private: + void scheduleNext(double currentTime) { + double delay = rng_.gaussian(avg_, stddev_); + if (delay < 1.0) + delay = 1.0; + nextEventTime_ = currentTime + delay; + } + + int pid_; + int dispatcherPid_; + MessageBus &bus_; + RandomGenerator &rng_; + double avg_; + double stddev_; + double nextEventTime_; +}; + +class Dispatcher : public DiscreteEventProcess { + public: + Dispatcher(int pid, int monitorPid, int numCustomers, MessageBus &bus) + : pid_(pid), monitorPid_(monitorPid), numCustomers_(numCustomers), + bus_(bus) { + for (int i = 0; i < numCustomers; ++i) { + inputCounts_[i] = 0; + } + } + + void initialize(double startTime) override {} + + double nextEventTime() const override { + return std::numeric_limits::infinity(); + } + + void handleEvent(double currentTime) override {} + + void processInbox(double currentTime) { + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + buffer_.push_back(inbox.front()); + inbox.pop(); + } + + // Release messages older than currentTime - 1.0 + std::sort( + buffer_.begin(), + buffer_.end(), + [](const Message &a, const Message &b) { return a.time < b.time; } + ); + + auto it = std::lower_bound( + buffer_.begin(), + buffer_.end(), + currentTime - 1.0, + [](const Message &m, double t) { return m.time < t; } + ); + + std::vector toRelease(buffer_.begin(), it); + buffer_.erase(buffer_.begin(), it); + + for (const auto &m : toRelease) { + if (m.sender >= 0 && m.sender < numCustomers_) { + inputCounts_[m.sender]++; + } + + Message forward = m; + forward.sender = pid_; + forward.receiver = monitorPid_; + bus_.procToNet(pid_).push(forward); + } + } + + void flushAll() { + std::sort( + buffer_.begin(), + buffer_.end(), + [](const Message &a, const Message &b) { return a.time < b.time; } + ); + for (const auto &m : buffer_) { + if (m.sender >= 0 && m.sender < numCustomers_) { + inputCounts_[m.sender]++; + } + Message forward = m; + forward.sender = pid_; + forward.receiver = monitorPid_; + bus_.procToNet(pid_).push(forward); + } + buffer_.clear(); + } + + int getCount(int customerId) const { + auto it = inputCounts_.find(customerId); + return it != inputCounts_.end() ? it->second : 0; + } + + private: + int pid_; + int monitorPid_; + int numCustomers_; + MessageBus &bus_; + std::map inputCounts_; + std::vector buffer_; +}; + +class OrderMonitor : public DiscreteEventProcess { + public: + OrderMonitor(int pid, MessageBus &bus) + : pid_(pid), bus_(bus), lastTime_(-1.0), violated_(0) {} + + void initialize(double startTime) override {} + + double nextEventTime() const override { + return std::numeric_limits::infinity(); + } + + void handleEvent(double currentTime) override {} + + void processInbox(double currentTime) { + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + if (lastTime_ >= 0 && m.time < lastTime_) { + violated_ = 1; + } + lastTime_ = m.time; + } + } + + int getViolated() const { return violated_; } + + private: + int pid_; + MessageBus &bus_; + double lastTime_; + int violated_; +}; + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng; + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) { + return 1; + } + + int N = parser.getInt("N", 0); + double avg = parser.getDouble("Avg", 0.0); + double stddev = parser.getDouble("StdDev", 0.0); + + int dispatcherPid = N; + int monitorPid = N + 1; + SELib::MessageBus bus(N + 2); + SELib::DiscreteEventSystem system; + + for (int i = 0; i < N; ++i) { + system.emplaceProcess( + i, dispatcherPid, bus, rng, avg, stddev + ); + } + auto &dispatcher = system.emplaceProcess( + dispatcherPid, monitorPid, N, bus + ); + auto &monitor = system.emplaceProcess(monitorPid, bus); + system.emplaceProcess(bus, rng, 0.01); + + double currentTime = 0.0; + double horizon = 1000000.0; + system.initialize(0.0); + + while (currentTime < horizon) { + double nextTime = system.nextEventTime(); + if (nextTime > horizon) + break; + currentTime = nextTime; + system.handleEvent(currentTime); + dispatcher.processInbox(currentTime); + monitor.processInbox(currentTime); + } + dispatcher.flushAll(); + monitor.processInbox(currentTime); + + std::ofstream outFile("results.txt"); + outFile << "2025-01-09-Axel-Rubini-2158099" << std::endl; + for (int i = 0; i < N; ++i) { + outFile << (i + 1) << " " << dispatcher.getCount(i) << std::endl; + } + outFile << "M2 " << monitor.getViolated() << std::endl; + outFile.close(); + + return 0; +} diff --git a/exams/AxelRubini/2025-01-09/5/parameters.txt b/exams/AxelRubini/2025-01-09/5/parameters.txt new file mode 100644 index 0000000..ca68d86 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/5/parameters.txt @@ -0,0 +1,3 @@ +N 10 +Avg 100 +StdDev 1 diff --git a/exams/AxelRubini/2025-01-09/5/results.txt b/exams/AxelRubini/2025-01-09/5/results.txt new file mode 100644 index 0000000..34bfcd8 --- /dev/null +++ b/exams/AxelRubini/2025-01-09/5/results.txt @@ -0,0 +1,12 @@ +2025-01-09-Axel-Rubini-2158099 +1 10000 +2 9998 +3 10001 +4 9998 +5 10000 +6 10000 +7 9999 +8 9999 +9 9998 +10 9998 +M2 0 diff --git a/exams/AxelRubini/2025-02-05/1/Makefile b/exams/AxelRubini/2025-02-05/1/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/1/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-02-05/1/include/DES.hpp b/exams/AxelRubini/2025-02-05/1/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-02-05/1/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/1/include/Decision.hpp b/exams/AxelRubini/2025-02-05/1/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/1/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/1/include/Dynamics.hpp b/exams/AxelRubini/2025-02-05/1/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-02-05/1/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/1/include/Geometry.hpp b/exams/AxelRubini/2025-02-05/1/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-02-05/1/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/1/include/IO.hpp b/exams/AxelRubini/2025-02-05/1/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/1/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/1/include/Inventory.hpp b/exams/AxelRubini/2025-02-05/1/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/1/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/1/include/Queue.hpp b/exams/AxelRubini/2025-02-05/1/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/1/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/1/include/Random.hpp b/exams/AxelRubini/2025-02-05/1/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/1/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/1/include/Stat.hpp b/exams/AxelRubini/2025-02-05/1/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/1/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/1/main b/exams/AxelRubini/2025-02-05/1/main new file mode 100755 index 0000000..c6dae2f Binary files /dev/null and b/exams/AxelRubini/2025-02-05/1/main differ diff --git a/exams/AxelRubini/2025-02-05/1/main.cpp b/exams/AxelRubini/2025-02-05/1/main.cpp new file mode 100644 index 0000000..e1db30f --- /dev/null +++ b/exams/AxelRubini/2025-02-05/1/main.cpp @@ -0,0 +1,165 @@ +#include "include/DES.hpp" +#include "include/Geometry.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +class DroneProcess : public DiscreteEventProcess { + public: + DroneProcess( + int pid, + int monitorPid, + MessageBus &bus, + RandomGenerator &rng, + double X1, + double X2, + double Y1, + double Y2, + const std::vector &points + ) + : pid_(pid), monitorPid_(monitorPid), bus_(bus), rng_(rng), X1_(X1), + X2_(X2), Y1_(Y1), Y2_(Y2), points_(points) { + pos_ = {rng_.uniform(X1_, X2_), rng_.uniform(Y1_, Y2_)}; + nextTime_ = 0.0; + } + + void initialize(double startTime) override { nextTime_ = startTime; } + + double nextEventTime() const override { return nextTime_; } + + void handleEvent(double currentTime) override { + for (size_t j = 0; j < points_.size(); ++j) { + if (pos_.x >= points_[j].x - 1.0 && pos_.x <= points_[j].x + 1.0 && + pos_.y >= points_[j].y - 1.0 && pos_.y <= points_[j].y + 1.0) { + Message m; + m.time = currentTime; + m.sender = pid_; + m.receiver = monitorPid_; + m.item = (int)j; + m.quantity = 1.0; + bus_.procToNet(pid_).push(m); + } + } + + double vx = rng_.uniform(-0.5, 0.5); + double vy = rng_.uniform(-0.5, 0.5); + pos_.x = std::min(X2_, std::max(X1_, pos_.x + vx)); + pos_.y = std::min(Y2_, std::max(Y1_, pos_.y + vy)); + + nextTime_ += 1.0; + } + + private: + int pid_, monitorPid_; + MessageBus &bus_; + RandomGenerator &rng_; + double X1_, X2_, Y1_, Y2_; + const std::vector &points_; + Point2D pos_; + double nextTime_; +}; + +class MonitorProcess : public DiscreteEventProcess { + public: + MonitorProcess(int pid, int numPoints, int H, MessageBus &bus) + : pid_(pid), H_(H), bus_(bus) { + coverage_.assign(numPoints, 0.0); + currentCounts_.assign(numPoints, 0); + nextTime_ = 0.9; + } + + void initialize(double startTime) override { nextTime_ = startTime + 0.9; } + + double nextEventTime() const override { return nextTime_; } + + void handleEvent(double currentTime) override { + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + if (m.item >= 0 && m.item < (int)currentCounts_.size()) { + currentCounts_[m.item]++; + } + } + + int t = (int)nextTime_; + for (size_t j = 0; j < coverage_.size(); ++j) { + coverage_[j] = (coverage_[j] * t + currentCounts_[j]) / (t + 1); + currentCounts_[j] = 0; + } + + nextTime_ += 1.0; + } + + const std::vector &getCoverage() const { return coverage_; } + + private: + int pid_, H_; + MessageBus &bus_; + std::vector coverage_; + std::vector currentCounts_; + double nextTime_; +}; + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + int H = parser.getInt("H"); + int N = parser.getInt("N"); + double X1 = 0, X2 = 0, Y1 = 0, Y2 = 0; + const auto &structData = parser.getStructuredData(); + for (const auto &line : structData) { + if (line.size() == 4 && line[0] != "H" && line[0] != "N" && + line[0] != "M") { + X1 = std::stod(line[0]); + X2 = std::stod(line[1]); + Y1 = std::stod(line[2]); + Y2 = std::stod(line[3]); + } + } + std::vector points; + bool foundM = false; + for (const auto &line : structData) { + if (foundM && line.size() == 2) { + points.push_back({std::stod(line[0]), std::stod(line[1])}); + } + if (line.size() == 2 && line[0] == "M") + foundM = true; + } + + SELib::MessageBus bus(N + 1); + SELib::DiscreteEventSystem system; + for (int i = 0; i < N; ++i) { + system.emplaceProcess( + i, N, bus, rng, X1, X2, Y1, Y2, points + ); + } + auto &monitor = system.emplaceProcess( + N, (int)points.size(), H, bus + ); + system.emplaceProcess(bus, rng, 0.001); + + SELib::DiscreteEventSimulator simulator(system); + simulator.run((double)H); + + std::ofstream outFile("results.txt"); + outFile << "2025-02-05-Axel-Rubini-2158099" << std::endl; + for (size_t i = 0; i < points.size(); ++i) { + outFile << points[i].x << " " << points[i].y << " " << std::fixed + << std::setprecision(4) << monitor.getCoverage()[i] + << std::endl; + } + return 0; +} diff --git a/exams/AxelRubini/2025-02-05/1/parameters.txt b/exams/AxelRubini/2025-02-05/1/parameters.txt new file mode 100644 index 0000000..276913a --- /dev/null +++ b/exams/AxelRubini/2025-02-05/1/parameters.txt @@ -0,0 +1,9 @@ +H 10000 +N 10 +-5 5 -5 5 +M 5 +0 1 +0 -1 +0 0 +-1 0 +1 0 diff --git a/exams/AxelRubini/2025-02-05/1/results.txt b/exams/AxelRubini/2025-02-05/1/results.txt new file mode 100644 index 0000000..a58ef0b --- /dev/null +++ b/exams/AxelRubini/2025-02-05/1/results.txt @@ -0,0 +1,6 @@ +2025-02-05-Axel-Rubini-2158099 +0 1 0.3541 +0.0000 -1.0000 0.3702 +0.0000 0.0000 0.3507 +-1.0000 0.0000 0.3350 +1.0000 0.0000 0.3750 diff --git a/exams/AxelRubini/2025-02-05/2/Makefile b/exams/AxelRubini/2025-02-05/2/Makefile new file mode 100644 index 0000000..6bc53bf --- /dev/null +++ b/exams/AxelRubini/2025-02-05/2/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o \ No newline at end of file diff --git a/exams/AxelRubini/2025-02-05/2/include/DES.hpp b/exams/AxelRubini/2025-02-05/2/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-02-05/2/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/2/include/Decision.hpp b/exams/AxelRubini/2025-02-05/2/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/2/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/2/include/Dynamics.hpp b/exams/AxelRubini/2025-02-05/2/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-02-05/2/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/2/include/Geometry.hpp b/exams/AxelRubini/2025-02-05/2/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-02-05/2/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/2/include/IO.hpp b/exams/AxelRubini/2025-02-05/2/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/2/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/2/include/Inventory.hpp b/exams/AxelRubini/2025-02-05/2/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/2/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/2/include/Queue.hpp b/exams/AxelRubini/2025-02-05/2/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/2/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/2/include/Random.hpp b/exams/AxelRubini/2025-02-05/2/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/2/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/2/include/Stat.hpp b/exams/AxelRubini/2025-02-05/2/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/2/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/2/main b/exams/AxelRubini/2025-02-05/2/main new file mode 100755 index 0000000..e46da14 Binary files /dev/null and b/exams/AxelRubini/2025-02-05/2/main differ diff --git a/exams/AxelRubini/2025-02-05/2/main.cpp b/exams/AxelRubini/2025-02-05/2/main.cpp new file mode 100644 index 0000000..fe0bac3 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/2/main.cpp @@ -0,0 +1,187 @@ +#include "include/DES.hpp" +#include "include/Decision.hpp" +#include "include/Geometry.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +class DroneProcess : public DiscreteEventProcess { + public: + DroneProcess( + int pid, + int monitorPid, + MessageBus &bus, + RandomGenerator &rng, + double X1, + double X2, + double Y1, + double Y2, + const std::vector &points + ) + : pid_(pid), monitorPid_(monitorPid), bus_(bus), rng_(rng), X1_(X1), + X2_(X2), Y1_(Y1), Y2_(Y2), points_(points) { + pos_ = {rng_.uniform(X1_, X2_), rng_.uniform(Y1_, Y2_)}; + nextTime_ = 0.0; + } + + void initialize(double startTime) override { nextTime_ = startTime; } + + double nextEventTime() const override { return nextTime_; } + + void handleEvent(double currentTime) override { + for (size_t j = 0; j < points_.size(); ++j) { + if (pos_.x >= points_[j].x - 1.0 && pos_.x <= points_[j].x + 1.0 && + pos_.y >= points_[j].y - 1.0 && pos_.y <= points_[j].y + 1.0) { + Message m; + m.time = currentTime; + m.sender = pid_; + m.receiver = monitorPid_; + m.item = (int)j; + m.quantity = 1.0; + bus_.procToNet(pid_).push(m); + } + } + + double vx = rng_.uniform(-0.5, 0.5); + double vy = rng_.uniform(-0.5, 0.5); + pos_.x = std::min(X2_, std::max(X1_, pos_.x + vx)); + pos_.y = std::min(Y2_, std::max(Y1_, pos_.y + vy)); + + nextTime_ += 1.0; + } + + private: + int pid_, monitorPid_; + MessageBus &bus_; + RandomGenerator &rng_; + double X1_, X2_, Y1_, Y2_; + const std::vector &points_; + Point2D pos_; + double nextTime_; +}; + +class MonitorProcess : public DiscreteEventProcess { + public: + MonitorProcess(int pid, int numPoints, int H, MessageBus &bus) + : pid_(pid), H_(H), bus_(bus) { + coverage_.assign(numPoints, 0.0); + currentCounts_.assign(numPoints, 0); + nextTime_ = 0.9; + } + + void initialize(double startTime) override { nextTime_ = startTime + 0.9; } + + double nextEventTime() const override { return nextTime_; } + + void handleEvent(double currentTime) override { + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + if (m.item >= 0 && m.item < (int)currentCounts_.size()) { + currentCounts_[m.item]++; + } + } + + int t = (int)nextTime_; + for (size_t j = 0; j < coverage_.size(); ++j) { + coverage_[j] = (coverage_[j] * t + currentCounts_[j]) / (t + 1); + currentCounts_[j] = 0; + } + + nextTime_ += 1.0; + } + + const std::vector &getCoverage() const { return coverage_; } + + private: + int pid_, H_; + MessageBus &bus_; + std::vector coverage_; + std::vector currentCounts_; + double nextTime_; +}; + +bool runSimulation( + double C_threshold, + int H, + int N, + double X1, + double X2, + double Y1, + double Y2, + const std::vector &points, + RandomGenerator &rng +) { + MessageBus bus(N + 1); + DiscreteEventSystem system; + for (int i = 0; i < N; ++i) { + system.emplaceProcess( + i, N, bus, rng, X1, X2, Y1, Y2, points + ); + } + auto &monitor = + system.emplaceProcess(N, (int)points.size(), H, bus); + system.emplaceProcess(bus, rng, 0.001); + + DiscreteEventSimulator simulator(system); + simulator.run((double)H); + + for (double cov : monitor.getCoverage()) { + if (cov < C_threshold) + return false; + } + return true; +} + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + double C_threshold = parser.getDouble("C"); + int H = parser.getInt("H"); + int N = parser.getInt("N"); + double X1 = 0, X2 = 0, Y1 = 0, Y2 = 0; + const auto &structData = parser.getStructuredData(); + for (const auto &line : structData) { + if (line.size() == 4 && line[0] != "H" && line[0] != "N" && + line[0] != "M" && line[0] != "C") { + X1 = std::stod(line[0]); + X2 = std::stod(line[1]); + Y1 = std::stod(line[2]); + Y2 = std::stod(line[3]); + } + } + std::vector points; + bool foundM = false; + for (const auto &line : structData) { + if (foundM && line.size() == 2) { + points.push_back({std::stod(line[0]), std::stod(line[1])}); + } + if (line.size() == 2 && line[0] == "M") + foundM = true; + } + + SELib::MonteCarloSimulator mc(1000); + double prob = mc.estimateProbability([&](int) { + return SELib::runSimulation( + C_threshold, H, N, X1, X2, Y1, Y2, points, rng + ); + }); + + std::ofstream outFile("results.txt"); + outFile << "2025-02-05-Axel-Rubini-2158099" << std::endl; + outFile << "P " << std::fixed << std::setprecision(4) << prob << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-02-05/2/parameters.txt b/exams/AxelRubini/2025-02-05/2/parameters.txt new file mode 100644 index 0000000..b0ba50b --- /dev/null +++ b/exams/AxelRubini/2025-02-05/2/parameters.txt @@ -0,0 +1,10 @@ +C 0.02 +H 10000 +N 10 +-5 5 -5 5 +M 5 +0 1 +0 -1 +0 0 +-1 0 +1 0 diff --git a/exams/AxelRubini/2025-02-05/2/results.txt b/exams/AxelRubini/2025-02-05/2/results.txt new file mode 100644 index 0000000..434389d --- /dev/null +++ b/exams/AxelRubini/2025-02-05/2/results.txt @@ -0,0 +1,2 @@ +2025-02-05-Axel-Rubini-2158099 +P 1 diff --git a/exams/AxelRubini/2025-02-05/3/Makefile b/exams/AxelRubini/2025-02-05/3/Makefile new file mode 100644 index 0000000..6bc53bf --- /dev/null +++ b/exams/AxelRubini/2025-02-05/3/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o \ No newline at end of file diff --git a/exams/AxelRubini/2025-02-05/3/include/DES.hpp b/exams/AxelRubini/2025-02-05/3/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-02-05/3/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/3/include/Decision.hpp b/exams/AxelRubini/2025-02-05/3/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/3/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/3/include/Dynamics.hpp b/exams/AxelRubini/2025-02-05/3/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-02-05/3/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/3/include/Geometry.hpp b/exams/AxelRubini/2025-02-05/3/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-02-05/3/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/3/include/IO.hpp b/exams/AxelRubini/2025-02-05/3/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/3/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/3/include/Inventory.hpp b/exams/AxelRubini/2025-02-05/3/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/3/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/3/include/Queue.hpp b/exams/AxelRubini/2025-02-05/3/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/3/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/3/include/Random.hpp b/exams/AxelRubini/2025-02-05/3/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/3/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/3/include/Stat.hpp b/exams/AxelRubini/2025-02-05/3/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/3/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/3/main b/exams/AxelRubini/2025-02-05/3/main new file mode 100755 index 0000000..dc8dc45 Binary files /dev/null and b/exams/AxelRubini/2025-02-05/3/main differ diff --git a/exams/AxelRubini/2025-02-05/3/main.cpp b/exams/AxelRubini/2025-02-05/3/main.cpp new file mode 100644 index 0000000..aa86bb1 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/3/main.cpp @@ -0,0 +1,188 @@ +#include "include/DES.hpp" +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace SELib { + +class CustomerProcess : public DiscreteEventProcess { + public: + CustomerProcess( + int pid, + int serverPid, + MessageBus &bus, + RandomGenerator &rng, + const std::vector> &matrix + ) + : pid_(pid), serverPid_(serverPid), bus_(bus), rng_(rng), + matrix_(matrix), currentState_(0), nextTime_(0.0) {} + + void initialize(double startTime) override { nextTime_ = startTime; } + double nextEventTime() const override { return nextTime_; } + + void handleEvent(double currentTime) override { + int nextState = rng_.discrete(matrix_[currentState_]); + bool isEndOfSession = (nextState == 0 && currentState_ != 0); + + if (currentState_ != 0) { + Message m; + m.time = currentTime; + m.sender = pid_; + m.receiver = serverPid_; + m.item = currentState_; + m.quantity = isEndOfSession ? 1.0 : 0.0; + bus_.procToNet(pid_).push(m); + } + currentState_ = nextState; + nextTime_ += 1.0; + } + + private: + int pid_, serverPid_, currentState_; + MessageBus &bus_; + RandomGenerator &rng_; + const std::vector> &matrix_; + double nextTime_; +}; + +class ServerProcess : public DiscreteEventProcess { + public: + ServerProcess( + int pid, + int monitorPid, + MessageBus &bus, + double T1, + double T2 + ) + : pid_(pid), monitorPid_(monitorPid), bus_(bus), T1_(T1), T2_(T2), + serverFreeAt_(0.0), + nextFinishTime_(std::numeric_limits::infinity()) {} + + void initialize(double startTime) override {} + double nextEventTime() const override { return nextFinishTime_; } + + void handleEvent(double currentTime) override { + if (currentTime >= nextFinishTime_) { + if (currentRequestIsLast_) { + Message m; + m.time = currentTime; + m.sender = pid_; + m.receiver = monitorPid_; + bus_.procToNet(pid_).push(m); + } + nextFinishTime_ = std::numeric_limits::infinity(); + } + processQueue(currentTime); + } + + void processQueue(double currentTime) { + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + queue_.push(inbox.front()); + inbox.pop(); + } + + if (currentTime >= serverFreeAt_ && !queue_.empty()) { + Message req = queue_.front(); + queue_.pop(); + double st = (req.item == 1) ? T1_ : T2_; + serverFreeAt_ = std::max(currentTime, serverFreeAt_) + st; + nextFinishTime_ = serverFreeAt_; + currentRequestIsLast_ = (req.quantity > 0.5); + } + } + + private: + int pid_, monitorPid_; + MessageBus &bus_; + double T1_, T2_, serverFreeAt_, nextFinishTime_; + bool currentRequestIsLast_; + std::queue queue_; +}; + +class MonitorProcess : public DiscreteEventProcess { + public: + MonitorProcess(int pid, MessageBus &bus) + : pid_(pid), bus_(bus), completed_(0) {} + void initialize(double startTime) override {} + double nextEventTime() const override { + return std::numeric_limits::infinity(); + } + void handleEvent(double currentTime) override {} + void processInbox() { + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + inbox.pop(); + completed_++; + } + } + int getCompleted() const { return completed_; } + + private: + int pid_; + MessageBus &bus_; + int completed_; +}; + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + std::vector> matrix(3, std::vector(3)); + const auto &data = parser.getStructuredData(); + for (const auto &line : data) + if (line.size() == 3) + matrix[std::stoi(line[0])][std::stoi(line[1])] = std::stod(line[2]); + std::vector ts; + for (const auto &line : data) + if (line.size() == 1) + ts.push_back(std::stod(line[0])); + double T1 = ts[ts.size() - 2], T2 = ts[ts.size() - 1]; + + auto run_sim = [&](int simulations) { + double totalRate = 0; + for (int i = 0; i < simulations; ++i) { + SELib::MessageBus bus(3); + SELib::DiscreteEventSystem system; + system.emplaceProcess( + 0, 1, bus, rng, matrix + ); + auto &server = + system.emplaceProcess(1, 2, bus, T1, T2); + auto &monitor = + system.emplaceProcess(2, bus); + system.emplaceProcess(bus, rng, 0.001); + + double currentTime = 0.0, H = 10000.0; + system.initialize(0.0); + while (currentTime < H) { + double nextTime = system.nextEventTime(); + if (nextTime > H) + break; + currentTime = nextTime; + system.handleEvent(currentTime); + server.processQueue(currentTime); + monitor.processInbox(); + } + totalRate += (double)monitor.getCompleted() / H; + } + return totalRate / simulations; + }; + + std::ofstream outFile("results.txt"); + outFile << "2025-02-05-Axel-Rubini-2158099" << std::endl; + outFile << "Avg " << std::fixed << std::setprecision(4) << run_sim(1000) + << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-02-05/3/parameters.txt b/exams/AxelRubini/2025-02-05/3/parameters.txt new file mode 100644 index 0000000..ba70834 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/3/parameters.txt @@ -0,0 +1,11 @@ +0 0 0.40 +0 1 0.30 +0 2 0.30 +1 0 0.8 +1 1 0.1 +1 2 0.1 +2 0 0.8 +2 1 0.1 +2 2 0.1 +2.5 +4.5 diff --git a/exams/AxelRubini/2025-02-05/3/results.txt b/exams/AxelRubini/2025-02-05/3/results.txt new file mode 100644 index 0000000..3b743b8 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/3/results.txt @@ -0,0 +1,2 @@ +2025-02-05-Axel-Rubini-2158099 +Avg 0.3428 diff --git a/exams/AxelRubini/2025-02-05/4/Makefile b/exams/AxelRubini/2025-02-05/4/Makefile new file mode 100644 index 0000000..6bc53bf --- /dev/null +++ b/exams/AxelRubini/2025-02-05/4/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o \ No newline at end of file diff --git a/exams/AxelRubini/2025-02-05/4/include/DES.hpp b/exams/AxelRubini/2025-02-05/4/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-02-05/4/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/4/include/Decision.hpp b/exams/AxelRubini/2025-02-05/4/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/4/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/4/include/Dynamics.hpp b/exams/AxelRubini/2025-02-05/4/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-02-05/4/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/4/include/Geometry.hpp b/exams/AxelRubini/2025-02-05/4/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-02-05/4/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/4/include/IO.hpp b/exams/AxelRubini/2025-02-05/4/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/4/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/4/include/Inventory.hpp b/exams/AxelRubini/2025-02-05/4/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/4/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/4/include/Queue.hpp b/exams/AxelRubini/2025-02-05/4/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/4/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/4/include/Random.hpp b/exams/AxelRubini/2025-02-05/4/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/4/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/4/include/Stat.hpp b/exams/AxelRubini/2025-02-05/4/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/4/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/4/main b/exams/AxelRubini/2025-02-05/4/main new file mode 100755 index 0000000..5b98a87 Binary files /dev/null and b/exams/AxelRubini/2025-02-05/4/main differ diff --git a/exams/AxelRubini/2025-02-05/4/main.cpp b/exams/AxelRubini/2025-02-05/4/main.cpp new file mode 100644 index 0000000..8031838 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/4/main.cpp @@ -0,0 +1,150 @@ +#include "include/DES.hpp" +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace SELib { + +class CustomerProcess : public DiscreteEventProcess { + public: + CustomerProcess( + int pid, + int serverPid, + MessageBus &bus, + RandomGenerator &rng, + const std::vector> &matrix + ) + : pid_(pid), serverPid_(serverPid), bus_(bus), rng_(rng), + matrix_(matrix), currentState_(0), nextTime_(0.0) {} + + void initialize(double startTime) override { nextTime_ = startTime; } + double nextEventTime() const override { return nextTime_; } + + void handleEvent(double currentTime) override { + int nextState = rng_.discrete(matrix_[currentState_]); + if (currentState_ != 0) { + Message m; + m.time = currentTime; + m.sender = pid_; + m.receiver = serverPid_; + m.item = currentState_; + bus_.procToNet(pid_).push(m); + } + currentState_ = nextState; + nextTime_ += 1.0; + } + + private: + int pid_, serverPid_, currentState_; + MessageBus &bus_; + RandomGenerator &rng_; + const std::vector> &matrix_; + double nextTime_; +}; + +class ServerProcess : public DiscreteEventProcess { + public: + ServerProcess(int pid, MessageBus &bus, double T1, double T2) + : pid_(pid), bus_(bus), T1_(T1), T2_(T2), serverFreeAt_(0.0), + nextFinishTime_(std::numeric_limits::infinity()) {} + + void initialize(double startTime) override {} + double nextEventTime() const override { return nextFinishTime_; } + + void handleEvent(double currentTime) override { + if (currentTime >= nextFinishTime_) + nextFinishTime_ = std::numeric_limits::infinity(); + processQueue(currentTime); + } + + void processQueue(double currentTime) { + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + queue_.push(inbox.front()); + inbox.pop(); + } + + if (currentTime >= serverFreeAt_ && !queue_.empty()) { + Message req = queue_.front(); + queue_.pop(); + double st = (req.item == 1) ? T1_ : T2_; + serverFreeAt_ = std::max(currentTime, serverFreeAt_) + st; + nextFinishTime_ = serverFreeAt_; + } + } + + size_t getQueueSize() const { return queue_.size(); } + bool isBusy() const { + return nextFinishTime_ != std::numeric_limits::infinity(); + } + + private: + int pid_; + MessageBus &bus_; + double T1_, T2_, serverFreeAt_, nextFinishTime_; + std::queue queue_; +}; + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + std::vector> matrix(3, std::vector(3)); + const auto &data = parser.getStructuredData(); + for (const auto &line : data) + if (line.size() == 3) + matrix[std::stoi(line[0])][std::stoi(line[1])] = std::stod(line[2]); + std::vector ts; + for (const auto &line : data) + if (line.size() == 1) + ts.push_back(std::stod(line[0])); + double T1 = ts[ts.size() - 2], T2 = ts[ts.size() - 1]; + + auto run_sim = [&](int simulations) { + double totalAvgB = 0; + for (int i = 0; i < simulations; ++i) { + SELib::MessageBus bus(2); + SELib::DiscreteEventSystem system; + system.emplaceProcess( + 0, 1, bus, rng, matrix + ); + auto &server = + system.emplaceProcess(1, bus, T1, T2); + system.emplaceProcess(bus, rng, 0.001); + + double currentTime = 0.0, H = 10000.0, area = 0.0; + system.initialize(0.0); + while (currentTime < H) { + double nextTime = system.nextEventTime(); + if (nextTime > H) + nextTime = H; + int B = (int)server.getQueueSize() + (server.isBusy() ? 1 : 0); + area += B * (nextTime - currentTime); + currentTime = nextTime; + if (currentTime >= H) + break; + system.handleEvent(currentTime); + server.processQueue(currentTime); + } + totalAvgB += area / H; + } + return totalAvgB / simulations; + }; + + std::ofstream outFile("results.txt"); + outFile << "2025-02-05-Axel-Rubini-2158099" << std::endl; + outFile << "Avg " << std::fixed << std::setprecision(4) << run_sim(1000) + << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-02-05/4/parameters.txt b/exams/AxelRubini/2025-02-05/4/parameters.txt new file mode 100644 index 0000000..ba70834 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/4/parameters.txt @@ -0,0 +1,11 @@ +0 0 0.40 +0 1 0.30 +0 2 0.30 +1 0 0.8 +1 1 0.1 +1 2 0.1 +2 0 0.8 +2 1 0.1 +2 2 0.1 +2.5 +4.5 diff --git a/exams/AxelRubini/2025-02-05/4/results.txt b/exams/AxelRubini/2025-02-05/4/results.txt new file mode 100644 index 0000000..622c7e2 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/4/results.txt @@ -0,0 +1,2 @@ +2025-02-05-Axel-Rubini-2158099 +Avg 715.882 diff --git a/exams/AxelRubini/2025-02-05/5/Makefile b/exams/AxelRubini/2025-02-05/5/Makefile new file mode 100644 index 0000000..6bc53bf --- /dev/null +++ b/exams/AxelRubini/2025-02-05/5/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o \ No newline at end of file diff --git a/exams/AxelRubini/2025-02-05/5/include/DES.hpp b/exams/AxelRubini/2025-02-05/5/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-02-05/5/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/5/include/Decision.hpp b/exams/AxelRubini/2025-02-05/5/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/5/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/5/include/Dynamics.hpp b/exams/AxelRubini/2025-02-05/5/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-02-05/5/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/5/include/Geometry.hpp b/exams/AxelRubini/2025-02-05/5/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-02-05/5/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/5/include/IO.hpp b/exams/AxelRubini/2025-02-05/5/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/5/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/5/include/Inventory.hpp b/exams/AxelRubini/2025-02-05/5/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/5/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/5/include/Queue.hpp b/exams/AxelRubini/2025-02-05/5/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/5/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/5/include/Random.hpp b/exams/AxelRubini/2025-02-05/5/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/5/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/5/include/Stat.hpp b/exams/AxelRubini/2025-02-05/5/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/5/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-02-05/5/main b/exams/AxelRubini/2025-02-05/5/main new file mode 100755 index 0000000..3c72e6a Binary files /dev/null and b/exams/AxelRubini/2025-02-05/5/main differ diff --git a/exams/AxelRubini/2025-02-05/5/main.cpp b/exams/AxelRubini/2025-02-05/5/main.cpp new file mode 100644 index 0000000..0d3bdd7 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/5/main.cpp @@ -0,0 +1,152 @@ +#include "include/DES.hpp" +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace SELib { + +class CustomerProcess : public DiscreteEventProcess { + public: + CustomerProcess( + int pid, + int serverPid, + MessageBus &bus, + RandomGenerator &rng, + const std::vector> &matrix + ) + : pid_(pid), serverPid_(serverPid), bus_(bus), rng_(rng), + matrix_(matrix), currentState_(0), nextTime_(0.0) {} + + void initialize(double startTime) override { nextTime_ = startTime; } + double nextEventTime() const override { return nextTime_; } + + void handleEvent(double currentTime) override { + int nextState = rng_.discrete(matrix_[currentState_]); + if (currentState_ != 0) { + Message m; + m.time = currentTime; + m.sender = pid_; + m.receiver = serverPid_; + m.item = currentState_; + bus_.procToNet(pid_).push(m); + } + currentState_ = nextState; + nextTime_ += 1.0; + } + + private: + int pid_, serverPid_, currentState_; + MessageBus &bus_; + RandomGenerator &rng_; + const std::vector> &matrix_; + double nextTime_; +}; + +class ServerProcess : public DiscreteEventProcess { + public: + ServerProcess(int pid, MessageBus &bus, double T1, double T2) + : pid_(pid), bus_(bus), T1_(T1), T2_(T2), serverFreeAt_(0.0), + nextFinishTime_(std::numeric_limits::infinity()) {} + + void initialize(double startTime) override {} + double nextEventTime() const override { return nextFinishTime_; } + + void handleEvent(double currentTime) override { + if (currentTime >= nextFinishTime_) + nextFinishTime_ = std::numeric_limits::infinity(); + processQueue(currentTime); + } + + void processQueue(double currentTime) { + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + queue_.push(inbox.front()); + inbox.pop(); + } + + if (currentTime >= serverFreeAt_ && !queue_.empty()) { + Message req = queue_.front(); + queue_.pop(); + double st = (req.item == 1) ? T1_ : T2_; + serverFreeAt_ = std::max(currentTime, serverFreeAt_) + st; + nextFinishTime_ = serverFreeAt_; + } + } + + size_t getQueueSize() const { return queue_.size(); } + bool isBusy() const { + return nextFinishTime_ != std::numeric_limits::infinity(); + } + + private: + int pid_; + MessageBus &bus_; + double T1_, T2_, serverFreeAt_, nextFinishTime_; + std::queue queue_; +}; + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + double L_threshold = parser.getDouble("L"); + std::vector> matrix(3, std::vector(3)); + const auto &data = parser.getStructuredData(); + for (const auto &line : data) + if (line.size() == 3) + matrix[std::stoi(line[0])][std::stoi(line[1])] = std::stod(line[2]); + std::vector ts; + for (const auto &line : data) + if (line.size() == 1 && line[0] != "L") + ts.push_back(std::stod(line[0])); + double T1 = ts[ts.size() - 2], T2 = ts[ts.size() - 1]; + + auto run_sim = [&](int simulations) { + double totalProb = 0; + for (int i = 0; i < simulations; ++i) { + SELib::MessageBus bus(2); + SELib::DiscreteEventSystem system; + system.emplaceProcess( + 0, 1, bus, rng, matrix + ); + auto &server = + system.emplaceProcess(1, bus, T1, T2); + system.emplaceProcess(bus, rng, 0.001); + + double currentTime = 0.0, H = 10000.0, timeAboveL = 0.0; + system.initialize(0.0); + while (currentTime < H) { + double nextTime = system.nextEventTime(); + if (nextTime > H) + nextTime = H; + int B = (int)server.getQueueSize() + (server.isBusy() ? 1 : 0); + if (B > L_threshold) + timeAboveL += (nextTime - currentTime); + currentTime = nextTime; + if (currentTime >= H) + break; + system.handleEvent(currentTime); + server.processQueue(currentTime); + } + totalProb += (timeAboveL / H); + } + return totalProb / simulations; + }; + + std::ofstream outFile("results.txt"); + outFile << "2025-02-05-Axel-Rubini-2158099" << std::endl; + outFile << "P " << std::fixed << std::setprecision(4) << run_sim(1000) + << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-02-05/5/parameters.txt b/exams/AxelRubini/2025-02-05/5/parameters.txt new file mode 100644 index 0000000..ec8fa29 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/5/parameters.txt @@ -0,0 +1,12 @@ +L 893 +0 0 0.40 +0 1 0.30 +0 2 0.30 +1 0 0.8 +1 1 0.1 +1 2 0.1 +2 0 0.8 +2 1 0.1 +2 2 0.1 +2.5 +4.5 diff --git a/exams/AxelRubini/2025-02-05/5/results.txt b/exams/AxelRubini/2025-02-05/5/results.txt new file mode 100644 index 0000000..1b6dfc2 --- /dev/null +++ b/exams/AxelRubini/2025-02-05/5/results.txt @@ -0,0 +1,2 @@ +2025-02-05-Axel-Rubini-2158099 +P 0.375374 diff --git a/exams/AxelRubini/2025-03-21/1/Makefile b/exams/AxelRubini/2025-03-21/1/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/1/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-03-21/1/include/DES.hpp b/exams/AxelRubini/2025-03-21/1/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-03-21/1/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/1/include/Decision.hpp b/exams/AxelRubini/2025-03-21/1/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/1/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/1/include/Dynamics.hpp b/exams/AxelRubini/2025-03-21/1/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-03-21/1/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/1/include/Geometry.hpp b/exams/AxelRubini/2025-03-21/1/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-03-21/1/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/1/include/IO.hpp b/exams/AxelRubini/2025-03-21/1/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/1/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/1/include/Inventory.hpp b/exams/AxelRubini/2025-03-21/1/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/1/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/1/include/Queue.hpp b/exams/AxelRubini/2025-03-21/1/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/1/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/1/include/Random.hpp b/exams/AxelRubini/2025-03-21/1/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/1/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/1/include/Stat.hpp b/exams/AxelRubini/2025-03-21/1/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/1/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/1/main b/exams/AxelRubini/2025-03-21/1/main new file mode 100755 index 0000000..3137e49 Binary files /dev/null and b/exams/AxelRubini/2025-03-21/1/main differ diff --git a/exams/AxelRubini/2025-03-21/1/main.cpp b/exams/AxelRubini/2025-03-21/1/main.cpp new file mode 100644 index 0000000..6e3469e --- /dev/null +++ b/exams/AxelRubini/2025-03-21/1/main.cpp @@ -0,0 +1,98 @@ +#include "include/Geometry.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +struct Drone { + int id; + Point3D pos; + bool operative; +}; + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + int H = parser.getInt("H"); + int N = parser.getInt("N"); + double alpha = parser.getDouble("A"); + double R = parser.getDouble("R"); + + double X1 = 0, X2 = 0, Y1 = 0, Y2 = 0, Z1 = 0, Z2 = 0; + const auto &data = parser.getStructuredData(); + for (const auto &line : data) { + if (line.size() == 6) { + X1 = std::stod(line[0]); + X2 = std::stod(line[1]); + Y1 = std::stod(line[2]); + Y2 = std::stod(line[3]); + Z1 = std::stod(line[4]); + Z2 = std::stod(line[5]); + } + } + + std::vector drones(N); + for (int i = 0; i < N; ++i) { + drones[i].id = i; + drones[i].pos = { + rng.uniform(X1, X2), rng.uniform(Y1, Y2), rng.uniform(Z1, Z2) + }; + drones[i].operative = true; + } + + for (int t = 0; t < H; ++t) { + // 1. Check collisions (before movement? usually yes) + std::vector collisionThisStep(N, false); + for (int i = 0; i < N; ++i) { + if (!drones[i].operative) + continue; + for (int j = i + 1; j < N; ++j) { + if (!drones[j].operative) + continue; + if (drones[i].pos.distanceTo(drones[j].pos) <= R) { + collisionThisStep[i] = true; + collisionThisStep[j] = true; + } + } + } + for (int i = 0; i < N; ++i) { + if (collisionThisStep[i]) + drones[i].operative = false; + } + + // 2. Move drones + for (int i = 0; i < N; ++i) { + if (!drones[i].operative) + continue; + double vx = rng.uniform(-alpha, alpha); + double vy = rng.uniform(-alpha, alpha); + double vz = rng.uniform(-alpha, alpha); + drones[i].pos.x = std::min(X2, std::max(X1, drones[i].pos.x + vx)); + drones[i].pos.y = std::min(Y2, std::max(Y1, drones[i].pos.y + vy)); + drones[i].pos.z = std::min(Z2, std::max(Z1, drones[i].pos.z + vz)); + } + } + + int Q = 0; + for (int i = 0; i < N; ++i) + if (drones[i].operative) + Q++; + + std::ofstream outFile("results.txt"); + outFile << "2025-03-21-Axel-Rubini-2158099" << std::endl; + outFile << "Q " << Q << std::endl; + outFile << "N " << N << std::endl; + outFile << "P " << (double)Q / N << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-03-21/1/parameters.txt b/exams/AxelRubini/2025-03-21/1/parameters.txt new file mode 100644 index 0000000..22aa83b --- /dev/null +++ b/exams/AxelRubini/2025-03-21/1/parameters.txt @@ -0,0 +1,5 @@ +H 100000 +N 1000 +A 0.2 +R 0.1 +-10 10 -10 10 -10 10 diff --git a/exams/AxelRubini/2025-03-21/1/results.txt b/exams/AxelRubini/2025-03-21/1/results.txt new file mode 100644 index 0000000..6b23883 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/1/results.txt @@ -0,0 +1,4 @@ +2025-03-21-Axel-Rubini-2158099 +Q 22 +N 1000 +P 0.022 diff --git a/exams/AxelRubini/2025-03-21/2/Makefile b/exams/AxelRubini/2025-03-21/2/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/2/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-03-21/2/include/DES.hpp b/exams/AxelRubini/2025-03-21/2/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-03-21/2/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/2/include/Decision.hpp b/exams/AxelRubini/2025-03-21/2/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/2/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/2/include/Dynamics.hpp b/exams/AxelRubini/2025-03-21/2/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-03-21/2/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/2/include/Geometry.hpp b/exams/AxelRubini/2025-03-21/2/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-03-21/2/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/2/include/IO.hpp b/exams/AxelRubini/2025-03-21/2/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/2/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/2/include/Inventory.hpp b/exams/AxelRubini/2025-03-21/2/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/2/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/2/include/Queue.hpp b/exams/AxelRubini/2025-03-21/2/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/2/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/2/include/Random.hpp b/exams/AxelRubini/2025-03-21/2/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/2/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/2/include/Stat.hpp b/exams/AxelRubini/2025-03-21/2/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/2/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/2/main b/exams/AxelRubini/2025-03-21/2/main new file mode 100755 index 0000000..4a6c691 Binary files /dev/null and b/exams/AxelRubini/2025-03-21/2/main differ diff --git a/exams/AxelRubini/2025-03-21/2/main.cpp b/exams/AxelRubini/2025-03-21/2/main.cpp new file mode 100644 index 0000000..62e6904 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/2/main.cpp @@ -0,0 +1,124 @@ +#include "include/Geometry.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +struct Drone { + int id; + Point3D pos; + bool operative; +}; + +double runOneSimulation( + double alpha, + int H, + int N, + double R, + double X1, + double X2, + double Y1, + double Y2, + double Z1, + double Z2, + RandomGenerator &rng +) { + std::vector drones(N); + for (int i = 0; i < N; ++i) { + drones[i].id = i; + drones[i].pos = { + rng.uniform(X1, X2), rng.uniform(Y1, Y2), rng.uniform(Z1, Z2) + }; + drones[i].operative = true; + } + + for (int t = 0; t < H; ++t) { + std::vector col(N, false); + for (int i = 0; i < N; ++i) { + if (!drones[i].operative) + continue; + for (int j = i + 1; j < N; ++j) { + if (!drones[j].operative) + continue; + if (drones[i].pos.distanceTo(drones[j].pos) <= R) { + col[i] = col[j] = true; + } + } + } + for (int i = 0; i < N; ++i) + if (col[i]) + drones[i].operative = false; + + for (int i = 0; i < N; ++i) { + if (!drones[i].operative) + continue; + drones[i].pos.x = std::min( + X2, std::max(X1, drones[i].pos.x + rng.uniform(-alpha, alpha)) + ); + drones[i].pos.y = std::min( + Y2, std::max(Y1, drones[i].pos.y + rng.uniform(-alpha, alpha)) + ); + drones[i].pos.z = std::min( + Z2, std::max(Z1, drones[i].pos.z + rng.uniform(-alpha, alpha)) + ); + } + } + + int Q = 0; + for (int i = 0; i < N; ++i) + if (drones[i].operative) + Q++; + return (double)Q / N; +} + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + int H = parser.getInt("H"); + int N = parser.getInt("N"); + double R = parser.getDouble("R"); + + double X1 = 0, X2 = 0, Y1 = 0, Y2 = 0, Z1 = 0, Z2 = 0; + const auto &data = parser.getStructuredData(); + for (const auto &line : data) { + if (line.size() == 6) { + X1 = std::stod(line[0]); + X2 = std::stod(line[1]); + Y1 = std::stod(line[2]); + Y2 = std::stod(line[3]); + Z1 = std::stod(line[4]); + Z2 = std::stod(line[5]); + } + } + + double bestP = -1.0; + double bestAlpha = 0.0; + + for (int i = 0; i < 1000; ++i) { + double alpha = rng.uniform(0.1, 0.5); + double P = SELib::runOneSimulation( + alpha, H, N, R, X1, X2, Y1, Y2, Z1, Z2, rng + ); + if (P > bestP) { + bestP = P; + bestAlpha = alpha; + } + } + + std::ofstream outFile("results.txt"); + outFile << "2025-03-21-Axel-Rubini-2158099" << std::endl; + outFile << "P " << bestP << std::endl; + outFile << "A " << bestAlpha << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-03-21/2/parameters.txt b/exams/AxelRubini/2025-03-21/2/parameters.txt new file mode 100644 index 0000000..ffaf90c --- /dev/null +++ b/exams/AxelRubini/2025-03-21/2/parameters.txt @@ -0,0 +1,5 @@ +H 10000 +N 200 +A 0.2 +R 0.1 +-4.0 4.0 -4.0 4.0 -4.0 4.0 diff --git a/exams/AxelRubini/2025-03-21/2/results.txt b/exams/AxelRubini/2025-03-21/2/results.txt new file mode 100644 index 0000000..cd2a981 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/2/results.txt @@ -0,0 +1,3 @@ +2025-03-21-Axel-Rubini-2158099 +P 0.13 +A 0.100715 diff --git a/exams/AxelRubini/2025-03-21/3/Makefile b/exams/AxelRubini/2025-03-21/3/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/3/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-03-21/3/include/DES.hpp b/exams/AxelRubini/2025-03-21/3/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-03-21/3/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/3/include/Decision.hpp b/exams/AxelRubini/2025-03-21/3/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/3/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/3/include/Dynamics.hpp b/exams/AxelRubini/2025-03-21/3/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-03-21/3/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/3/include/Geometry.hpp b/exams/AxelRubini/2025-03-21/3/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-03-21/3/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/3/include/IO.hpp b/exams/AxelRubini/2025-03-21/3/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/3/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/3/include/Inventory.hpp b/exams/AxelRubini/2025-03-21/3/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/3/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/3/include/Queue.hpp b/exams/AxelRubini/2025-03-21/3/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/3/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/3/include/Random.hpp b/exams/AxelRubini/2025-03-21/3/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/3/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/3/include/Stat.hpp b/exams/AxelRubini/2025-03-21/3/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/3/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/3/main b/exams/AxelRubini/2025-03-21/3/main new file mode 100755 index 0000000..71167aa Binary files /dev/null and b/exams/AxelRubini/2025-03-21/3/main differ diff --git a/exams/AxelRubini/2025-03-21/3/main.cpp b/exams/AxelRubini/2025-03-21/3/main.cpp new file mode 100644 index 0000000..7ec0e6a --- /dev/null +++ b/exams/AxelRubini/2025-03-21/3/main.cpp @@ -0,0 +1,104 @@ +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +struct Server { + std::vector cache; + std::vector soldLocally; + std::queue inputQueue; + int busyUntil; +}; + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + const auto &data = parser.getStructuredData(); + int H = std::stoi(data[0][0]); + int n = std::stoi(data[1][0]); + int k = std::stoi(data[2][0]); + double alpha = std::stod(data[3][0]); + + std::vector p, f; + for (const auto &s : data[4]) + p.push_back(std::stod(s)); + for (const auto &s : data[5]) + f.push_back(std::stod(s)); + + long long totalS = 0; + int simulations = 1000; + + for (int sim = 0; sim < simulations; ++sim) { + std::vector centralDB(k + 1, 0); + std::vector servers(n); + for (int i = 0; i < n; ++i) { + servers[i].cache.assign(k + 1, 0); + servers[i].soldLocally.assign(k + 1, 0); + servers[i].busyUntil = 0; + } + + long long oversellings = 0; + + for (int t = 0; t < H; ++t) { + // 1. Supplier + int supItem = rng.discrete(f); + if (supItem != 0) + centralDB[supItem]++; + + // 2. Customers & Server + for (int i = 0; i < n; ++i) { + int item = rng.discrete(p); + if (item != 0) + servers[i].inputQueue.push(item); + + if (t >= servers[i].busyUntil) { + if (!servers[i].inputQueue.empty()) { + int reqItem = servers[i].inputQueue.front(); + servers[i].inputQueue.pop(); + + if (servers[i].cache[reqItem] - + servers[i].soldLocally[reqItem] > + 0) { + servers[i].soldLocally[reqItem]++; + } + + if (rng.bernoulli(alpha)) { + servers[i].busyUntil = t + 10; + for (int j = 1; j <= k; ++j) { + centralDB[j] -= servers[i].soldLocally[j]; + if (centralDB[j] < 0) { + oversellings += std::abs(centralDB[j]); + centralDB[j] = 0; + } + servers[i].soldLocally[j] = 0; + servers[i].cache[j] = centralDB[j]; + } + } + } + } + } + } + totalS += oversellings; + } + + double finalS = (double)totalS / simulations; + double finalR = finalS / (n * H); + + std::ofstream outFile("results.txt"); + outFile << "2025-03-21-Axel-Rubini-2158099" << std::endl; + outFile << "S " << (long long)finalS << std::endl; + outFile << "R " << finalR << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-03-21/3/parameters.txt b/exams/AxelRubini/2025-03-21/3/parameters.txt new file mode 100644 index 0000000..61af210 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/3/parameters.txt @@ -0,0 +1,6 @@ +100000 +3 +2 +1.0 +0.0 0.5 0.5 +1.0 0.0 0.0 diff --git a/exams/AxelRubini/2025-03-21/3/results.txt b/exams/AxelRubini/2025-03-21/3/results.txt new file mode 100644 index 0000000..fce4174 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/3/results.txt @@ -0,0 +1,3 @@ +2025-03-21-Axel-Rubini-2158099 +S 0 +R 0 diff --git a/exams/AxelRubini/2025-03-21/4/Makefile b/exams/AxelRubini/2025-03-21/4/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/4/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-03-21/4/include/DES.hpp b/exams/AxelRubini/2025-03-21/4/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-03-21/4/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/4/include/Decision.hpp b/exams/AxelRubini/2025-03-21/4/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/4/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/4/include/Dynamics.hpp b/exams/AxelRubini/2025-03-21/4/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-03-21/4/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/4/include/Geometry.hpp b/exams/AxelRubini/2025-03-21/4/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-03-21/4/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/4/include/IO.hpp b/exams/AxelRubini/2025-03-21/4/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/4/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/4/include/Inventory.hpp b/exams/AxelRubini/2025-03-21/4/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/4/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/4/include/Queue.hpp b/exams/AxelRubini/2025-03-21/4/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/4/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/4/include/Random.hpp b/exams/AxelRubini/2025-03-21/4/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/4/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/4/include/Stat.hpp b/exams/AxelRubini/2025-03-21/4/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/4/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/4/main b/exams/AxelRubini/2025-03-21/4/main new file mode 100755 index 0000000..8772033 Binary files /dev/null and b/exams/AxelRubini/2025-03-21/4/main differ diff --git a/exams/AxelRubini/2025-03-21/4/main.cpp b/exams/AxelRubini/2025-03-21/4/main.cpp new file mode 100644 index 0000000..58006ae --- /dev/null +++ b/exams/AxelRubini/2025-03-21/4/main.cpp @@ -0,0 +1,102 @@ +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +struct Server { + std::vector cache; + std::vector soldLocally; + std::queue inputQueue; + int busyUntil; +}; + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + const auto &data = parser.getStructuredData(); + int H = std::stoi(data[0][0]); + int n = std::stoi(data[1][0]); + int k = std::stoi(data[2][0]); + double alpha = std::stod(data[3][0]); + + std::vector p, f; + for (const auto &s : data[4]) + p.push_back(std::stod(s)); + for (const auto &s : data[5]) + f.push_back(std::stod(s)); + + long long totalZ = 0; + int simulations = 1000; + + for (int sim = 0; sim < simulations; ++sim) { + std::vector centralDB(k + 1, 0); + std::vector servers(n); + for (int i = 0; i < n; ++i) { + servers[i].cache.assign(k + 1, 0); + servers[i].soldLocally.assign(k + 1, 0); + servers[i].busyUntil = 0; + } + + long long transactions = 0; + + for (int t = 0; t < H; ++t) { + int supItem = rng.discrete(f); + if (supItem != 0) + centralDB[supItem]++; + + for (int i = 0; i < n; ++i) { + int item = rng.discrete(p); + if (item != 0) + servers[i].inputQueue.push(item); + + if (t >= servers[i].busyUntil) { + if (!servers[i].inputQueue.empty()) { + int reqItem = servers[i].inputQueue.front(); + servers[i].inputQueue.pop(); + + transactions++; // Transazione completata + + if (servers[i].cache[reqItem] - + servers[i].soldLocally[reqItem] > + 0) { + servers[i].soldLocally[reqItem]++; + } + + if (rng.bernoulli(alpha)) { + servers[i].busyUntil = t + 10; + for (int j = 1; j <= k; ++j) { + centralDB[j] -= servers[i].soldLocally[j]; + if (centralDB[j] < 0) + centralDB[j] = 0; + servers[i].soldLocally[j] = 0; + servers[i].cache[j] = centralDB[j]; + } + } + } + } + } + } + totalZ += transactions; + } + + double finalZ = (double)totalZ / simulations; + double finalW = finalZ / (n * H); + + std::ofstream outFile("results.txt"); + outFile << "2025-03-21-Axel-Rubini-2158099" << std::endl; + outFile << "Z " << (long long)finalZ << std::endl; + outFile << "W " << finalW << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-03-21/4/parameters.txt b/exams/AxelRubini/2025-03-21/4/parameters.txt new file mode 100644 index 0000000..f2566c7 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/4/parameters.txt @@ -0,0 +1,6 @@ +10000 +5 +2 +0.5 +0.8 0.1 0.1 +0.6 0.2 0.2 diff --git a/exams/AxelRubini/2025-03-21/4/results.txt b/exams/AxelRubini/2025-03-21/4/results.txt new file mode 100644 index 0000000..1595d3f --- /dev/null +++ b/exams/AxelRubini/2025-03-21/4/results.txt @@ -0,0 +1,3 @@ +2025-03-21-Axel-Rubini-2158099 +Z 9059 +W 0.181191 diff --git a/exams/AxelRubini/2025-03-21/5/Makefile b/exams/AxelRubini/2025-03-21/5/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/5/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-03-21/5/include/DES.hpp b/exams/AxelRubini/2025-03-21/5/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-03-21/5/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/5/include/Decision.hpp b/exams/AxelRubini/2025-03-21/5/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/5/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/5/include/Dynamics.hpp b/exams/AxelRubini/2025-03-21/5/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-03-21/5/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/5/include/Geometry.hpp b/exams/AxelRubini/2025-03-21/5/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-03-21/5/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/5/include/IO.hpp b/exams/AxelRubini/2025-03-21/5/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/5/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/5/include/Inventory.hpp b/exams/AxelRubini/2025-03-21/5/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/5/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/5/include/Queue.hpp b/exams/AxelRubini/2025-03-21/5/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/5/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/5/include/Random.hpp b/exams/AxelRubini/2025-03-21/5/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/5/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/5/include/Stat.hpp b/exams/AxelRubini/2025-03-21/5/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/5/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-03-21/5/main b/exams/AxelRubini/2025-03-21/5/main new file mode 100755 index 0000000..8e580c8 Binary files /dev/null and b/exams/AxelRubini/2025-03-21/5/main differ diff --git a/exams/AxelRubini/2025-03-21/5/main.cpp b/exams/AxelRubini/2025-03-21/5/main.cpp new file mode 100644 index 0000000..f4b7239 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/5/main.cpp @@ -0,0 +1,124 @@ +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +struct Server { + std::vector cache; + std::vector soldLocally; + std::queue inputQueue; + int busyUntil; +}; + +struct Metrics { + double W, S, J; +}; + +Metrics runOneSim( + double alpha, + int H, + int n, + int k, + const std::vector &p, + const std::vector &f, + RandomGenerator &rng +) { + std::vector centralDB(k + 1, 0); + std::vector servers(n); + for (int i = 0; i < n; ++i) { + servers[i].cache.assign(k + 1, 0); + servers[i].soldLocally.assign(k + 1, 0); + servers[i].busyUntil = 0; + } + + long long oversellings = 0; + long long transactions = 0; + + for (int t = 0; t < H; ++t) { + int supItem = rng.discrete(f); + if (supItem != 0) + centralDB[supItem]++; + + for (int i = 0; i < n; ++i) { + int item = rng.discrete(p); + if (item != 0) + servers[i].inputQueue.push(item); + + if (t >= servers[i].busyUntil) { + if (!servers[i].inputQueue.empty()) { + int reqItem = servers[i].inputQueue.front(); + servers[i].inputQueue.pop(); + transactions++; + if (servers[i].cache[reqItem] - + servers[i].soldLocally[reqItem] > + 0) { + servers[i].soldLocally[reqItem]++; + } + if (rng.bernoulli(alpha)) { + servers[i].busyUntil = t + 10; + for (int j = 1; j <= k; ++j) { + centralDB[j] -= servers[i].soldLocally[j]; + if (centralDB[j] < 0) { + oversellings += std::abs(centralDB[j]); + centralDB[j] = 0; + } + servers[i].soldLocally[j] = 0; + servers[i].cache[j] = centralDB[j]; + } + } + } + } + } + } + double W = (double)transactions / (n * H); + double S = (double)oversellings / (n * H); + return {W, S, W - S}; +} + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + const auto &data = parser.getStructuredData(); + int H = std::stoi(data[0][0]); + int n = std::stoi(data[1][0]); + int k = std::stoi(data[2][0]); + // alpha is absent in Ex 5 params + + std::vector p, f; + for (const auto &s : data[3]) + p.push_back(std::stod(s)); + for (const auto &s : data[4]) + f.push_back(std::stod(s)); + + double bestAlpha = 0; + SELib::Metrics bestM = {0, 0, -1e18}; + + for (int i = 0; i < 1000; ++i) { + double alpha = rng.uniform(0.0, 1.0); + auto m = SELib::runOneSim(alpha, H, n, k, p, f, rng); + if (m.J > bestM.J) { + bestM = m; + bestAlpha = alpha; + } + } + + std::ofstream outFile("results.txt"); + outFile << "2025-03-21-Axel-Rubini-2158099" << std::endl; + outFile << "A " << bestAlpha << std::endl; + outFile << "W " << bestM.W << std::endl; + outFile << "S " << bestM.S << std::endl; + outFile << "J " << bestM.J << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-03-21/5/parameters.txt b/exams/AxelRubini/2025-03-21/5/parameters.txt new file mode 100644 index 0000000..67c8b42 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/5/parameters.txt @@ -0,0 +1,5 @@ +100000 +5 +2 +0.0 0.5 0.5 +1.0 0.0 0.0 diff --git a/exams/AxelRubini/2025-03-21/5/results.txt b/exams/AxelRubini/2025-03-21/5/results.txt new file mode 100644 index 0000000..b095cb6 --- /dev/null +++ b/exams/AxelRubini/2025-03-21/5/results.txt @@ -0,0 +1,5 @@ +2025-03-21-Axel-Rubini-2158099 +A 0.00239006 +W 0.978616 +S 0 +J 0.978616 diff --git a/exams/AxelRubini/2025-06-12/1/Makefile b/exams/AxelRubini/2025-06-12/1/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/1/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-06-12/1/include/DES.hpp b/exams/AxelRubini/2025-06-12/1/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-06-12/1/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/1/include/Decision.hpp b/exams/AxelRubini/2025-06-12/1/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/1/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/1/include/Dynamics.hpp b/exams/AxelRubini/2025-06-12/1/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-06-12/1/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/1/include/Geometry.hpp b/exams/AxelRubini/2025-06-12/1/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-06-12/1/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/1/include/IO.hpp b/exams/AxelRubini/2025-06-12/1/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/1/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/1/include/Inventory.hpp b/exams/AxelRubini/2025-06-12/1/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/1/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/1/include/Queue.hpp b/exams/AxelRubini/2025-06-12/1/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/1/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/1/include/Random.hpp b/exams/AxelRubini/2025-06-12/1/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/1/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/1/include/Stat.hpp b/exams/AxelRubini/2025-06-12/1/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/1/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/1/main b/exams/AxelRubini/2025-06-12/1/main new file mode 100755 index 0000000..c18031b Binary files /dev/null and b/exams/AxelRubini/2025-06-12/1/main differ diff --git a/exams/AxelRubini/2025-06-12/1/main.cpp b/exams/AxelRubini/2025-06-12/1/main.cpp new file mode 100644 index 0000000..3c24a59 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/1/main.cpp @@ -0,0 +1,51 @@ +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace SELib { +struct UV { + int id; + double x, y; +}; +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + double H = parser.getDouble("H"); + int N = parser.getInt("N"); + double V = parser.getDouble("V"); + double T = parser.getDouble("T"); + + std::vector vehicles(N); + for (int i = 0; i < N; ++i) { + vehicles[i].id = i + 1; + vehicles[i].x = rng.uniform(-10.0, 10.0); + vehicles[i].y = rng.uniform(-10.0, 10.0); + } + + std::ofstream outFile("results.txt"); + outFile << "2025-06-12-Axel-Rubini-2158099" << std::endl; + + for (double t = 0; t <= H + 1e-9; t += T) { + for (int i = 0; i < N; ++i) { + outFile << t << " " << vehicles[i].id << " " << vehicles[i].x << " " + << vehicles[i].y << std::endl; + } + for (int i = 0; i < N; ++i) { + double theta = rng.uniform(0.0, 2.0 * M_PI); + vehicles[i].x += T * V * std::sin(theta); + vehicles[i].y += T * V * std::cos(theta); + } + } + return 0; +} diff --git a/exams/AxelRubini/2025-06-12/1/parameters.txt b/exams/AxelRubini/2025-06-12/1/parameters.txt new file mode 100644 index 0000000..019b143 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/1/parameters.txt @@ -0,0 +1,4 @@ +H 5 +N 2 +V 1.0 +T 2 diff --git a/exams/AxelRubini/2025-06-12/1/results.txt b/exams/AxelRubini/2025-06-12/1/results.txt new file mode 100644 index 0000000..148a506 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/1/results.txt @@ -0,0 +1,7 @@ +2025-06-12-Axel-Rubini-2158099 +0 1 1.85689 6.88531 +0 2 7.15891 6.94503 +2 1 0.455499 5.4584 +2 2 8.48735 5.44996 +4 1 2.36696 4.8699 +4 2 9.18504 7.32432 diff --git a/exams/AxelRubini/2025-06-12/2/Makefile b/exams/AxelRubini/2025-06-12/2/Makefile new file mode 100644 index 0000000..4daac4d --- /dev/null +++ b/exams/AxelRubini/2025-06-12/2/Makefile @@ -0,0 +1,7 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall +all: main +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-06-12/2/include/DES.hpp b/exams/AxelRubini/2025-06-12/2/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-06-12/2/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/2/include/Decision.hpp b/exams/AxelRubini/2025-06-12/2/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/2/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/2/include/Dynamics.hpp b/exams/AxelRubini/2025-06-12/2/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-06-12/2/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/2/include/Geometry.hpp b/exams/AxelRubini/2025-06-12/2/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-06-12/2/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/2/include/IO.hpp b/exams/AxelRubini/2025-06-12/2/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/2/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/2/include/Inventory.hpp b/exams/AxelRubini/2025-06-12/2/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/2/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/2/include/Queue.hpp b/exams/AxelRubini/2025-06-12/2/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/2/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/2/include/Random.hpp b/exams/AxelRubini/2025-06-12/2/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/2/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/2/include/Stat.hpp b/exams/AxelRubini/2025-06-12/2/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/2/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/2/main b/exams/AxelRubini/2025-06-12/2/main new file mode 100755 index 0000000..206b76e Binary files /dev/null and b/exams/AxelRubini/2025-06-12/2/main differ diff --git a/exams/AxelRubini/2025-06-12/2/main.cpp b/exams/AxelRubini/2025-06-12/2/main.cpp new file mode 100644 index 0000000..539bdb6 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/2/main.cpp @@ -0,0 +1,103 @@ +#include "include/Geometry.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace SELib { +struct UV { + Point2D pos; +}; + +double runOneSimulation( + double H, + int N, + double V, + double T, + double R, + double L, + RandomGenerator &rng +) { + std::vector vehicles(N); + for (int i = 0; i < N; ++i) + vehicles[i].pos = {rng.uniform(-10.0, 10.0), rng.uniform(-10.0, 10.0)}; + + int collisionSteps = 0; + for (double t = T; t <= H + 1e-9; t += T) { + int qCounts[4] = {0, 0, 0, 0}; + for (int i = 1; i < N; ++i) { + if (vehicles[0].pos.distanceTo(vehicles[i].pos) <= R) { + double dx = vehicles[i].pos.x - vehicles[0].pos.x; + double dy = vehicles[i].pos.y - vehicles[0].pos.y; + if (dx >= 0 && dy >= 0) + qCounts[0]++; + else if (dx >= 0 && dy <= 0) + qCounts[1]++; + else if (dx <= 0 && dy <= 0) + qCounts[2]++; + else if (dx <= 0 && dy >= 0) + qCounts[3]++; + } + } + int bestQ = 0; + for (int k = 1; k < 4; ++k) + if (qCounts[k] > qCounts[bestQ]) + bestQ = k; + + double theta1 = + rng.uniform(bestQ * M_PI / 2.0, (bestQ + 1) * M_PI / 2.0); + vehicles[0].pos.x += T * V * std::sin(theta1); + vehicles[0].pos.y += T * V * std::cos(theta1); + + for (int i = 1; i < N; ++i) { + double theta = rng.uniform(0.0, 2.0 * M_PI); + vehicles[i].pos.x += T * V * std::sin(theta); + vehicles[i].pos.y += T * V * std::cos(theta); + } + + bool hit = false; + for (int i = 1; i < N; ++i) { + if (vehicles[0].pos.distanceTo(vehicles[i].pos) <= + L) { // Use L for collision + hit = true; + break; + } + } + if (hit) + collisionSteps++; + } + if (collisionSteps == 0) + return H; + return H / (double)collisionSteps; +} +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + double H = parser.getDouble("H"), V = parser.getDouble("V"), + T = parser.getDouble("T"); + int N = parser.getInt("N"); + double R = 1.0, L = 0.1; + const auto &data = parser.getStructuredData(); + for (const auto &line : data) { + if (line.size() == 2 && line[0] == "R") + R = std::stod(line[1]); + if (line.size() == 2 && line[0] == "L") + L = std::stod(line[1]); + } + double totalC = 0; + for (int i = 0; i < 1000; ++i) + totalC += SELib::runOneSimulation(H, N, V, T, R, L, rng); + std::ofstream outFile("results.txt"); + outFile << "2025-06-12-Axel-Rubini-2158099" << std::endl; + outFile << "C " << totalC / 1000.0 << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-06-12/2/parameters.txt b/exams/AxelRubini/2025-06-12/2/parameters.txt new file mode 100644 index 0000000..87db89b --- /dev/null +++ b/exams/AxelRubini/2025-06-12/2/parameters.txt @@ -0,0 +1,6 @@ +H 10 +N 10 +V 2 +T 1 +R 5 +L 3 diff --git a/exams/AxelRubini/2025-06-12/2/results.txt b/exams/AxelRubini/2025-06-12/2/results.txt new file mode 100644 index 0000000..f9e0512 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/2/results.txt @@ -0,0 +1,2 @@ +2025-06-12-Axel-Rubini-2158099 +C 2.3714 diff --git a/exams/AxelRubini/2025-06-12/3/Makefile b/exams/AxelRubini/2025-06-12/3/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/3/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-06-12/3/include/DES.hpp b/exams/AxelRubini/2025-06-12/3/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-06-12/3/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/3/include/Decision.hpp b/exams/AxelRubini/2025-06-12/3/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/3/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/3/include/Dynamics.hpp b/exams/AxelRubini/2025-06-12/3/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-06-12/3/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/3/include/Geometry.hpp b/exams/AxelRubini/2025-06-12/3/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-06-12/3/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/3/include/IO.hpp b/exams/AxelRubini/2025-06-12/3/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/3/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/3/include/Inventory.hpp b/exams/AxelRubini/2025-06-12/3/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/3/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/3/include/Queue.hpp b/exams/AxelRubini/2025-06-12/3/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/3/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/3/include/Random.hpp b/exams/AxelRubini/2025-06-12/3/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/3/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/3/include/Stat.hpp b/exams/AxelRubini/2025-06-12/3/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/3/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/3/main b/exams/AxelRubini/2025-06-12/3/main new file mode 100755 index 0000000..949af51 Binary files /dev/null and b/exams/AxelRubini/2025-06-12/3/main differ diff --git a/exams/AxelRubini/2025-06-12/3/main.cpp b/exams/AxelRubini/2025-06-12/3/main.cpp new file mode 100644 index 0000000..b3bd952 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/3/main.cpp @@ -0,0 +1,33 @@ +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + double H = parser.getDouble("H"); + int N = parser.getInt("N"); + double A = parser.getDouble("A"); + double B = parser.getDouble("B"); + double T = parser.getDouble("T"); + + std::ofstream outFile("results.txt"); + outFile << "2025-06-12-Axel-Rubini-2158099" << std::endl; + + double nextReq = 0.0; + for (double t = 0; t <= H + 1e-9; t += T) { + if (t >= nextReq - 1e-9) { + outFile << t << " " << rng.uniformInt(1, N) << std::endl; + nextReq = t + rng.uniform(A, B); + } else { + outFile << t << " 0" << std::endl; + } + } + return 0; +} diff --git a/exams/AxelRubini/2025-06-12/3/parameters.txt b/exams/AxelRubini/2025-06-12/3/parameters.txt new file mode 100644 index 0000000..0c4c10d --- /dev/null +++ b/exams/AxelRubini/2025-06-12/3/parameters.txt @@ -0,0 +1,5 @@ +H 10 +N 5 +A 1 +B 3 +T 1 diff --git a/exams/AxelRubini/2025-06-12/3/results.txt b/exams/AxelRubini/2025-06-12/3/results.txt new file mode 100644 index 0000000..99307bb --- /dev/null +++ b/exams/AxelRubini/2025-06-12/3/results.txt @@ -0,0 +1,12 @@ +2025-06-12-Axel-Rubini-2158099 +0 3 +1 0 +2 0 +3 5 +4 0 +5 0 +6 3 +7 0 +8 4 +9 0 +10 3 diff --git a/exams/AxelRubini/2025-06-12/4/Makefile b/exams/AxelRubini/2025-06-12/4/Makefile new file mode 100644 index 0000000..4daac4d --- /dev/null +++ b/exams/AxelRubini/2025-06-12/4/Makefile @@ -0,0 +1,7 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall +all: main +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-06-12/4/include/DES.hpp b/exams/AxelRubini/2025-06-12/4/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-06-12/4/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/4/include/Decision.hpp b/exams/AxelRubini/2025-06-12/4/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/4/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/4/include/Dynamics.hpp b/exams/AxelRubini/2025-06-12/4/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-06-12/4/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/4/include/Geometry.hpp b/exams/AxelRubini/2025-06-12/4/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-06-12/4/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/4/include/IO.hpp b/exams/AxelRubini/2025-06-12/4/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/4/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/4/include/Inventory.hpp b/exams/AxelRubini/2025-06-12/4/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/4/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/4/include/Queue.hpp b/exams/AxelRubini/2025-06-12/4/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/4/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/4/include/Random.hpp b/exams/AxelRubini/2025-06-12/4/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/4/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/4/include/Stat.hpp b/exams/AxelRubini/2025-06-12/4/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/4/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/4/main b/exams/AxelRubini/2025-06-12/4/main new file mode 100755 index 0000000..1e29aab Binary files /dev/null and b/exams/AxelRubini/2025-06-12/4/main differ diff --git a/exams/AxelRubini/2025-06-12/4/main.cpp b/exams/AxelRubini/2025-06-12/4/main.cpp new file mode 100644 index 0000000..b0988d3 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/4/main.cpp @@ -0,0 +1,100 @@ +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { +struct Server { + std::vector inventory; + double busyUntil; +}; +struct Event { + double time; + int type; // 0: Cust, 1: Disp->Serv, 2: Serv->Disp + int sender, receiver, productId, status; + bool operator>(const Event &other) const { return time > other.time; } +}; + +double runOneSim( + int H, + int N, + double A, + double B, + int S, + int K, + double p, + double D, + double F, + RandomGenerator &rng +) { + std::priority_queue, std::greater> eq; + std::vector servers(S); + for (int i = 0; i < S; ++i) { + servers[i].inventory.assign(N + 1, 0); + for (int j = 1; j <= N; ++j) + servers[i].inventory[j] = rng.uniformInt(1, K); + servers[i].busyUntil = 0; + } + eq.push({0.0, 0, 0, 0, rng.uniformInt(1, N), 0}); + long long failed = 0, total = 0; + + while (!eq.empty()) { + Event e = eq.top(); + eq.pop(); + if (e.time > H) + break; + if (e.type == 0) { + eq.push( + {e.time + D, 1, 0, rng.uniformInt(0, S - 1), e.productId, 0} + ); + eq.push( + {e.time + rng.uniform(A, B), 0, 0, 0, rng.uniformInt(1, N), 0} + ); + } else if (e.type == 1) { + total++; + double finish = std::max(e.time, servers[e.receiver].busyUntil) + F; + servers[e.receiver].busyUntil = finish; + int status = (servers[e.receiver].inventory[e.productId] > 0); + if (status) + servers[e.receiver].inventory[e.productId]--; + eq.push({finish, 2, e.receiver, 0, e.productId, status}); + } else if (e.type == 2) { + if (e.status == 0) { + failed++; + if (rng.bernoulli(p)) + servers[e.sender].inventory[e.productId] += 10; + else + servers[rng.uniformInt(0, S - 1)] + .inventory[rng.uniformInt(1, N)] += 10; + } + } + } + return total == 0 ? 0 : (double)failed / total; +} +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + int M = parser.getInt("M"), H = (int)parser.getDouble("H"), + N = parser.getInt("N"), S = parser.getInt("S"), K = parser.getInt("K"); + double A = parser.getDouble("A"), B = parser.getDouble("B"), + p = parser.getDouble("p"), D = parser.getDouble("D"), + F = parser.getDouble("F"); + + double totalQ = 0; + for (int i = 0; i < M; ++i) + totalQ += SELib::runOneSim(H, N, A, B, S, K, p, D, F, rng); + + std::ofstream outFile("results.txt"); + outFile << "2025-06-12-Axel-Rubini-2158099" << std::endl; + outFile << "Q " << totalQ / M << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-06-12/4/parameters.txt b/exams/AxelRubini/2025-06-12/4/parameters.txt new file mode 100644 index 0000000..1cfe221 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/4/parameters.txt @@ -0,0 +1,11 @@ +M 1 +H 100000 +N 2 +A 1.0 +B 1.0 +T 1.0 +S 3 +K 0 +p 0 +D 1 +F 2 diff --git a/exams/AxelRubini/2025-06-12/4/results.txt b/exams/AxelRubini/2025-06-12/4/results.txt new file mode 100644 index 0000000..2772682 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/4/results.txt @@ -0,0 +1,2 @@ +2025-06-12-Axel-Rubini-2158099 +Q 0.09119 diff --git a/exams/AxelRubini/2025-06-12/5/Makefile b/exams/AxelRubini/2025-06-12/5/Makefile new file mode 100644 index 0000000..4daac4d --- /dev/null +++ b/exams/AxelRubini/2025-06-12/5/Makefile @@ -0,0 +1,7 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall +all: main +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-06-12/5/include/DES.hpp b/exams/AxelRubini/2025-06-12/5/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-06-12/5/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/5/include/Decision.hpp b/exams/AxelRubini/2025-06-12/5/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/5/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/5/include/Dynamics.hpp b/exams/AxelRubini/2025-06-12/5/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-06-12/5/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/5/include/Geometry.hpp b/exams/AxelRubini/2025-06-12/5/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-06-12/5/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/5/include/IO.hpp b/exams/AxelRubini/2025-06-12/5/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/5/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/5/include/Inventory.hpp b/exams/AxelRubini/2025-06-12/5/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/5/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/5/include/Queue.hpp b/exams/AxelRubini/2025-06-12/5/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/5/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/5/include/Random.hpp b/exams/AxelRubini/2025-06-12/5/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/5/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/5/include/Stat.hpp b/exams/AxelRubini/2025-06-12/5/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/5/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-06-12/5/main b/exams/AxelRubini/2025-06-12/5/main new file mode 100755 index 0000000..165cc20 Binary files /dev/null and b/exams/AxelRubini/2025-06-12/5/main differ diff --git a/exams/AxelRubini/2025-06-12/5/main.cpp b/exams/AxelRubini/2025-06-12/5/main.cpp new file mode 100644 index 0000000..444c985 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/5/main.cpp @@ -0,0 +1,116 @@ +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { +struct Server { + std::vector inventory; + double busyUntil; +}; +struct Event { + double time; + int type; // 0: Cust, 1: Disp->Serv, 2: Serv->Disp + int sender, receiver, productId, status; + bool operator>(const Event &other) const { return time > other.time; } +}; + +double runMsims( + double p, + int M, + int H, + int N, + double A, + double B, + int S, + int K, + double D, + double F, + RandomGenerator &rng +) { + double totalQ = 0; + for (int m = 0; m < M; ++m) { + std::priority_queue, std::greater> eq; + std::vector servers(S); + for (int i = 0; i < S; ++i) { + servers[i].inventory.assign(N + 1, 0); + for (int j = 1; j <= N; ++j) + servers[i].inventory[j] = rng.uniformInt(1, K); + servers[i].busyUntil = 0; + } + eq.push({0.0, 0, 0, 0, rng.uniformInt(1, N), 0}); + long long failed = 0, total = 0; + while (!eq.empty()) { + Event e = eq.top(); + eq.pop(); + if (e.time > H) + break; + if (e.type == 0) { + eq.push( + {e.time + D, 1, 0, rng.uniformInt(0, S - 1), e.productId, 0} + ); + eq.push( + {e.time + rng.uniform(A, B), + 0, + 0, + 0, + rng.uniformInt(1, N), + 0} + ); + } else if (e.type == 1) { + total++; + double finish = + std::max(e.time, servers[e.receiver].busyUntil) + F; + servers[e.receiver].busyUntil = finish; + int status = (servers[e.receiver].inventory[e.productId] > 0); + if (status) + servers[e.receiver].inventory[e.productId]--; + eq.push({finish, 2, e.receiver, 0, e.productId, status}); + } else if (e.type == 2) { + if (e.status == 0) { + failed++; + if (rng.bernoulli(p)) + servers[e.sender].inventory[e.productId] += 10; + else + servers[rng.uniformInt(0, S - 1)] + .inventory[rng.uniformInt(1, N)] += 10; + } + } + } + totalQ += (total == 0 ? 0 : (double)failed / total); + } + return totalQ / M; +} +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + int M = parser.getInt("M"), W = parser.getInt("W"), + H = (int)parser.getDouble("H"), N = parser.getInt("N"), + S = parser.getInt("S"), K = parser.getInt("K"); + double A = parser.getDouble("A"), B = parser.getDouble("B"), + D = parser.getDouble("D"), F = parser.getDouble("F"); + + double bestP = 0, minQ = 1e18; + for (int i = 0; i < W; ++i) { + double p = rng.uniform(0.0, 1.0); + double q = SELib::runMsims(p, M, H, N, A, B, S, K, D, F, rng); + if (q < minQ) { + minQ = q; + bestP = p; + } + } + + std::ofstream outFile("results.txt"); + outFile << "2025-06-12-Axel-Rubini-2158099" << std::endl; + outFile << "P " << bestP << " Q " << minQ << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-06-12/5/parameters.txt b/exams/AxelRubini/2025-06-12/5/parameters.txt new file mode 100644 index 0000000..7b6f139 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/5/parameters.txt @@ -0,0 +1,11 @@ +M 10 +W 10 +H 100000 +N 3 +A 1.0 +B 1.0 +T 1 +S 10 +K 1 +D 1 +F 2 diff --git a/exams/AxelRubini/2025-06-12/5/results.txt b/exams/AxelRubini/2025-06-12/5/results.txt new file mode 100644 index 0000000..96b2468 --- /dev/null +++ b/exams/AxelRubini/2025-06-12/5/results.txt @@ -0,0 +1,2 @@ +2025-06-12-Axel-Rubini-2158099 +P 0.846262 Q 0.091049 diff --git a/exams/AxelRubini/2025-07-08/1/Makefile b/exams/AxelRubini/2025-07-08/1/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/1/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-07-08/1/include/DES.hpp b/exams/AxelRubini/2025-07-08/1/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-07-08/1/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/1/include/Decision.hpp b/exams/AxelRubini/2025-07-08/1/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/1/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/1/include/Dynamics.hpp b/exams/AxelRubini/2025-07-08/1/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-07-08/1/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/1/include/Geometry.hpp b/exams/AxelRubini/2025-07-08/1/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-07-08/1/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/1/include/IO.hpp b/exams/AxelRubini/2025-07-08/1/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/1/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/1/include/Inventory.hpp b/exams/AxelRubini/2025-07-08/1/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/1/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/1/include/Queue.hpp b/exams/AxelRubini/2025-07-08/1/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/1/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/1/include/Random.hpp b/exams/AxelRubini/2025-07-08/1/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/1/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/1/include/Stat.hpp b/exams/AxelRubini/2025-07-08/1/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/1/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/1/main b/exams/AxelRubini/2025-07-08/1/main new file mode 100755 index 0000000..cf3429a Binary files /dev/null and b/exams/AxelRubini/2025-07-08/1/main differ diff --git a/exams/AxelRubini/2025-07-08/1/main.cpp b/exams/AxelRubini/2025-07-08/1/main.cpp new file mode 100644 index 0000000..a767bfd --- /dev/null +++ b/exams/AxelRubini/2025-07-08/1/main.cpp @@ -0,0 +1,149 @@ +#include "include/Geometry.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace SELib { + +struct UV { + Point2D pos; + Point2D target; +}; + +double calculateFi(int i, const std::vector &vehicles, double A) { + double term1 = 0; + term1 = std::pow(vehicles[i].pos.x - vehicles[i].target.x, 2) + + std::pow(vehicles[i].pos.y - vehicles[i].target.y, 2); + + double term2 = 0; + for (int j = 0; j < (int)vehicles.size(); ++j) { + if (i == j) + continue; + term2 += std::pow(vehicles[j].pos.x - vehicles[i].pos.x, 2) + + std::pow(vehicles[j].pos.y - vehicles[i].pos.y, 2); + } + return A * term1 - (1.0 - A) * term2; +} + +double runOneSimulation( + double H, + int M, + int N, + double A, + double V, + int Q, + double T, + RandomGenerator &rng +) { + std::vector vehicles(N); + for (int i = 0; i < N; ++i) { + vehicles[i].pos = {rng.uniform(-50.0, 50.0), rng.uniform(-50.0, 50.0)}; + double targetVal = -50.0 + 100.0 * (double)i / (N - 1); + if (N == 1) + targetVal = -50.0; + vehicles[i].target = {targetVal, targetVal}; + } + + long long totalCollisions = 0; + double time = 0.0; + + while (time < H - 1e-9) { + std::vector bestThetas(N); + for (int i = 0; i < N; ++i) { + double minFi = 1e18; + double bestTheta = 0; + for (int q = 0; q < Q; ++q) { + double theta = (double)q / Q * 2.0 * M_PI; + // Temporary move to evaluate Fi + UV temp = vehicles[i]; + temp.pos.x += T * V * std::sin(theta); + temp.pos.y += T * V * std::cos(theta); + + // We need to update ALL positions to evaluate Fi(X(t+T))? + // Text: "Al tempo t... sceglie theta... che minimizza Fi(X(t))" + // Fi depends on current distances. + double val = calculateFi(i, vehicles, A); // Based on X(t) + // Wait, if Fi depends on X(t), it doesn't depend on the chosen + // theta! Re-reading: "minizza il valore della funzione + // Fi(X(t))" is likely a typo for Fi(X(t+T)). Or maybe Fi(X) + // where X is the state after moving only vehicle i? Let's + // assume Fi is evaluated at the potential new position of + // vehicle i. + + double potentialX = vehicles[i].pos.x + T * V * std::sin(theta); + double potentialY = vehicles[i].pos.y + T * V * std::cos(theta); + + double t1 = std::pow(potentialX - vehicles[i].target.x, 2) + + std::pow(potentialY - vehicles[i].target.y, 2); + double t2 = 0; + for (int j = 0; j < N; ++j) { + if (i == j) + continue; + t2 += std::pow(vehicles[j].pos.x - potentialX, 2) + + std::pow(vehicles[j].pos.y - potentialY, 2); + } + double fi = A * t1 - (1.0 - A) * t2; + if (fi < minFi) { + minFi = fi; + bestTheta = theta; + } + } + bestThetas[i] = bestTheta; + } + + // Apply movement with noise + double noiseLimit = M_PI / Q; + for (int i = 0; i < N; ++i) { + double w1 = rng.uniform(-noiseLimit, noiseLimit); + double w2 = rng.uniform(-noiseLimit, noiseLimit); + vehicles[i].pos.x += T * V * std::sin(bestThetas[i] + w1); + vehicles[i].pos.y += T * V * std::cos(bestThetas[i] + w2); + } + + time += T; + + // Check collisions + for (int i = 0; i < N; ++i) { + for (int j = i + 1; j < N; ++j) { + if (vehicles[i].pos.distanceTo(vehicles[j].pos) <= 0.1) { + totalCollisions++; + } + } + } + } + + return (double)totalCollisions / H; +} + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + double H = parser.getDouble("H"); + int M = parser.getInt("M"); + int N = parser.getInt("N"); + double A = parser.getDouble("A"); + double V = parser.getDouble("V"); + int Q = parser.getInt("Q"); + double T = parser.getDouble("T"); + + double totalRate = 0; + for (int i = 0; i < M; ++i) { + totalRate += SELib::runOneSimulation(H, M, N, A, V, Q, T, rng); + } + + std::ofstream outFile("results.txt"); + outFile << "2025-07-08-Axel-Rubini-2158099" << std::endl; + outFile << "C " << totalRate / M << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-07-08/1/parameters.txt b/exams/AxelRubini/2025-07-08/1/parameters.txt new file mode 100644 index 0000000..72e246c --- /dev/null +++ b/exams/AxelRubini/2025-07-08/1/parameters.txt @@ -0,0 +1,7 @@ +H 1000 +M 10 +N 100 +A 1 +V 0.1 +Q 30 +T 1 diff --git a/exams/AxelRubini/2025-07-08/1/results.txt b/exams/AxelRubini/2025-07-08/1/results.txt new file mode 100644 index 0000000..157ddbb --- /dev/null +++ b/exams/AxelRubini/2025-07-08/1/results.txt @@ -0,0 +1,2 @@ +2025-07-08-Axel-Rubini-2158099 +C 0.0201 diff --git a/exams/AxelRubini/2025-07-08/2/Makefile b/exams/AxelRubini/2025-07-08/2/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/2/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-07-08/2/include/DES.hpp b/exams/AxelRubini/2025-07-08/2/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-07-08/2/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/2/include/Decision.hpp b/exams/AxelRubini/2025-07-08/2/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/2/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/2/include/Dynamics.hpp b/exams/AxelRubini/2025-07-08/2/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-07-08/2/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/2/include/Geometry.hpp b/exams/AxelRubini/2025-07-08/2/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-07-08/2/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/2/include/IO.hpp b/exams/AxelRubini/2025-07-08/2/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/2/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/2/include/Inventory.hpp b/exams/AxelRubini/2025-07-08/2/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/2/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/2/include/Queue.hpp b/exams/AxelRubini/2025-07-08/2/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/2/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/2/include/Random.hpp b/exams/AxelRubini/2025-07-08/2/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/2/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/2/include/Stat.hpp b/exams/AxelRubini/2025-07-08/2/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/2/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/2/main b/exams/AxelRubini/2025-07-08/2/main new file mode 100755 index 0000000..8550d44 Binary files /dev/null and b/exams/AxelRubini/2025-07-08/2/main differ diff --git a/exams/AxelRubini/2025-07-08/2/main.cpp b/exams/AxelRubini/2025-07-08/2/main.cpp new file mode 100644 index 0000000..d073216 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/2/main.cpp @@ -0,0 +1,123 @@ +#include "include/Geometry.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace SELib { + +struct UV { + Point2D pos; + Point2D target; +}; + +double runMsims( + double A, + double H, + int M, + int N, + double V, + int Q, + double T, + RandomGenerator &rng +) { + double totalRate = 0; + for (int m = 0; m < M; ++m) { + std::vector vehicles(N); + for (int i = 0; i < N; ++i) { + vehicles[i].pos = { + rng.uniform(-50.0, 50.0), rng.uniform(-50.0, 50.0) + }; + double targetVal = -50.0 + 100.0 * (double)i / (N - 1); + if (N == 1) + targetVal = -50.0; + vehicles[i].target = {targetVal, targetVal}; + } + + long long collisions = 0; + double time = 0.0; + while (time < H - 1e-9) { + std::vector bestThetas(N); + for (int i = 0; i < N; ++i) { + double minFi = 1e18; + double bestTheta = 0; + for (int q = 0; q < Q; ++q) { + double theta = (double)q / Q * 2.0 * M_PI; + double px = vehicles[i].pos.x + T * V * std::sin(theta); + double py = vehicles[i].pos.y + T * V * std::cos(theta); + double t1 = std::pow(px - vehicles[i].target.x, 2) + + std::pow(py - vehicles[i].target.y, 2); + double t2 = 0; + for (int j = 0; j < N; ++j) { + if (i == j) + continue; + t2 += std::pow(vehicles[j].pos.x - px, 2) + + std::pow(vehicles[j].pos.y - py, 2); + } + double fi = A * t1 - (1.0 - A) * t2; + if (fi < minFi) { + minFi = fi; + bestTheta = theta; + } + } + bestThetas[i] = bestTheta; + } + double noise = M_PI / Q; + for (int i = 0; i < N; ++i) { + vehicles[i].pos.x += + T * V * + std::sin(bestThetas[i] + rng.uniform(-noise, noise)); + vehicles[i].pos.y += + T * V * + std::cos(bestThetas[i] + rng.uniform(-noise, noise)); + } + time += T; + for (int i = 0; i < N; ++i) { + for (int j = i + 1; j < N; ++j) { + if (vehicles[i].pos.distanceTo(vehicles[j].pos) <= 0.1) + collisions++; + } + } + } + totalRate += (double)collisions / H; + } + return totalRate / M; +} + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + double H = parser.getDouble("H"); + int M = parser.getInt("M"); + int N = parser.getInt("N"); + int B = parser.getInt("B"); + double V = parser.getDouble("V"); + int Q = parser.getInt("Q"); + double T = parser.getDouble("T"); + + double bestA = 0, minC = 1e18; + for (int i = 0; i < B; ++i) { + double A = rng.uniform(0.0, 1.0); + double C = SELib::runMsims(A, H, M, N, V, Q, T, rng); + if (C < minC) { + minC = C; + bestA = A; + } + } + + std::ofstream outFile("results.txt"); + outFile << "2025-07-08-Axel-Rubini-2158099" << std::endl; + outFile << "A " << bestA << std::endl; + outFile << "C " << minC << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-07-08/2/parameters.txt b/exams/AxelRubini/2025-07-08/2/parameters.txt new file mode 100644 index 0000000..41a371c --- /dev/null +++ b/exams/AxelRubini/2025-07-08/2/parameters.txt @@ -0,0 +1,7 @@ +H 1000 +M 1 +N 100 +B 10 +V 0.1 +Q 30 +T 1 diff --git a/exams/AxelRubini/2025-07-08/2/results.txt b/exams/AxelRubini/2025-07-08/2/results.txt new file mode 100644 index 0000000..8ce59c6 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/2/results.txt @@ -0,0 +1,3 @@ +2025-07-08-Axel-Rubini-2158099 +A 0.592845 +C 0 diff --git a/exams/AxelRubini/2025-07-08/3/Makefile b/exams/AxelRubini/2025-07-08/3/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/3/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-07-08/3/include/DES.hpp b/exams/AxelRubini/2025-07-08/3/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-07-08/3/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/3/include/Decision.hpp b/exams/AxelRubini/2025-07-08/3/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/3/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/3/include/Dynamics.hpp b/exams/AxelRubini/2025-07-08/3/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-07-08/3/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/3/include/Geometry.hpp b/exams/AxelRubini/2025-07-08/3/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-07-08/3/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/3/include/IO.hpp b/exams/AxelRubini/2025-07-08/3/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/3/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/3/include/Inventory.hpp b/exams/AxelRubini/2025-07-08/3/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/3/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/3/include/Queue.hpp b/exams/AxelRubini/2025-07-08/3/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/3/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/3/include/Random.hpp b/exams/AxelRubini/2025-07-08/3/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/3/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/3/include/Stat.hpp b/exams/AxelRubini/2025-07-08/3/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/3/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/3/main b/exams/AxelRubini/2025-07-08/3/main new file mode 100755 index 0000000..4da47c4 Binary files /dev/null and b/exams/AxelRubini/2025-07-08/3/main differ diff --git a/exams/AxelRubini/2025-07-08/3/main.cpp b/exams/AxelRubini/2025-07-08/3/main.cpp new file mode 100644 index 0000000..362c992 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/3/main.cpp @@ -0,0 +1,127 @@ +#include "include/DES.hpp" +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace SELib { + +struct Server { + std::vector inventory; + double busyUntil; +}; + +struct Event { + double time; + int type; + int serverIdx; + int productId; + int status; + + Event(double t, int tp, int sIdx, int pId, int st) + : time(t), type(tp), serverIdx(sIdx), productId(pId), status(st) {} + + bool operator>(const Event &other) const { return time > other.time; } +}; + +double runOneSim( + int H, + double T, + int N, + double A, + double B, + double p, + int S, + double F, + double G, + int K, + int Q, + RandomGenerator &rng +) { + std::priority_queue, std::greater> eq; + std::vector servers(S); + for (int i = 0; i < S; ++i) { + servers[i].inventory.assign(N + 1, 0); + for (int j = 1; j <= N; ++j) + servers[i].inventory[j] = rng.uniformInt(1, K); + servers[i].busyUntil = 0; + } + + eq.push(Event(0.0, 0, 0, 0, 0)); + + long long failedSales = 0; + + while (!eq.empty()) { + Event e = eq.top(); + eq.pop(); + if (e.time > (double)H) + break; + + if (e.type == 0) { // Customer Request + int sIdx = rng.uniformInt(0, S - 1); + int pId = rng.uniformInt(1, N); + + double serviceTime = F; + int status = 0; + if (servers[sIdx].inventory[pId] > 0) { + servers[sIdx].inventory[pId]--; + status = 1; + } else { + status = 0; + if (rng.bernoulli(p)) { + servers[sIdx].inventory[pId] += Q; + serviceTime = G; + } + } + + double finishTime = + std::max(e.time, servers[sIdx].busyUntil) + serviceTime; + servers[sIdx].busyUntil = finishTime; + + eq.push(Event(finishTime, 1, sIdx, pId, status)); + eq.push(Event(e.time + rng.uniform(A, B), 0, 0, 0, 0)); + } else if (e.type == 1) { // Server Finish + if (e.status == 0) + failedSales++; + } + } + + return (double)failedSales / H; +} + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + int H = (int)parser.getDouble("H"); + double T = parser.getDouble("T"); + int M = parser.getInt("M"); + int N = parser.getInt("N"); + double A = parser.getDouble("A"); + double B = parser.getDouble("B"); + double p = parser.getDouble("p"); + int S = parser.getInt("S"); + double F = parser.getDouble("F"); + double G = parser.getDouble("G"); + int K = parser.getInt("K", 10); + int Q = parser.getInt("Q", 10); + + double totalRate = 0; + for (int i = 0; i < M; ++i) + totalRate += SELib::runOneSim(H, T, N, A, B, p, S, F, G, K, Q, rng); + + std::ofstream outFile("results.txt"); + outFile << "2025-07-08-Axel-Rubini-2158099" << std::endl; + outFile << "V " << totalRate / M << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-07-08/3/parameters.txt b/exams/AxelRubini/2025-07-08/3/parameters.txt new file mode 100644 index 0000000..743fd93 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/3/parameters.txt @@ -0,0 +1,12 @@ +H 1000 +T 1 +M 100 +N 10 +A 5.0 +B 10.00 +p 1.0 +S 10 +F 2 +G 5 +K 100 +Q 10 diff --git a/exams/AxelRubini/2025-07-08/3/results.txt b/exams/AxelRubini/2025-07-08/3/results.txt new file mode 100644 index 0000000..fde4d32 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/3/results.txt @@ -0,0 +1,2 @@ +2025-07-08-Axel-Rubini-2158099 +V 0.00061 diff --git a/exams/AxelRubini/2025-07-08/4/Makefile b/exams/AxelRubini/2025-07-08/4/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/4/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-07-08/4/include/DES.hpp b/exams/AxelRubini/2025-07-08/4/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-07-08/4/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/4/include/Decision.hpp b/exams/AxelRubini/2025-07-08/4/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/4/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/4/include/Dynamics.hpp b/exams/AxelRubini/2025-07-08/4/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-07-08/4/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/4/include/Geometry.hpp b/exams/AxelRubini/2025-07-08/4/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-07-08/4/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/4/include/IO.hpp b/exams/AxelRubini/2025-07-08/4/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/4/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/4/include/Inventory.hpp b/exams/AxelRubini/2025-07-08/4/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/4/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/4/include/Queue.hpp b/exams/AxelRubini/2025-07-08/4/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/4/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/4/include/Random.hpp b/exams/AxelRubini/2025-07-08/4/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/4/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/4/include/Stat.hpp b/exams/AxelRubini/2025-07-08/4/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/4/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/4/main b/exams/AxelRubini/2025-07-08/4/main new file mode 100755 index 0000000..5d30dae Binary files /dev/null and b/exams/AxelRubini/2025-07-08/4/main differ diff --git a/exams/AxelRubini/2025-07-08/4/main.cpp b/exams/AxelRubini/2025-07-08/4/main.cpp new file mode 100644 index 0000000..dba4638 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/4/main.cpp @@ -0,0 +1,137 @@ +#include "include/DES.hpp" +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace SELib { + +struct Server { + std::vector inventory; + double busyUntil; +}; + +struct Event { + double time; + int type; + int serverIdx; + int productId; + int status; + + Event(double t, int tp, int sIdx, int pId, int st) + : time(t), type(tp), serverIdx(sIdx), productId(pId), status(st) {} + + bool operator>(const Event &other) const { return time > other.time; } +}; + +double runOneSim( + int H, + double T, + int N, + double A, + double B, + double p, + int S, + double F, + double G, + int K, + int Q, + double &W +) { + RandomGenerator rng; // No seed for Monte Carlo variability + std::priority_queue, std::greater> eq; + std::vector servers(S); + for (int i = 0; i < S; ++i) { + servers[i].inventory.assign(N + 1, 0); + for (int j = 1; j <= N; ++j) + servers[i].inventory[j] = rng.uniformInt(0, K); + servers[i].busyUntil = 0; + } + + eq.push(Event(0.0, 0, 0, 0, 0)); + + long long failedSales = 0; + long long totalSent = 0; + long long totalReceived = 0; + + while (!eq.empty()) { + Event e = eq.top(); + eq.pop(); + if (e.time > (double)H) + break; + + if (e.type == 0) { // Customer Request + int sIdx = rng.uniformInt(0, S - 1); + int pId = rng.uniformInt(1, N); + + double serviceTime = F; + int status = 0; + if (servers[sIdx].inventory[pId] > 0) { + servers[sIdx].inventory[pId]--; + status = 1; + } else { + status = 0; + if (rng.bernoulli(p)) { + servers[sIdx].inventory[pId] += Q; + serviceTime = G; + } + } + + double finishTime = + std::max(e.time, servers[sIdx].busyUntil) + serviceTime; + servers[sIdx].busyUntil = finishTime; + + totalSent++; + eq.push(Event(finishTime, 1, sIdx, pId, status)); + eq.push(Event(e.time + rng.uniform(A, B), 0, 0, 0, 0)); + } else if (e.type == 1) { // Server Finish + totalReceived++; + if (e.status == 0) + failedSales++; + } + } + + W = (totalSent == 0) ? 1.0 : (double)totalReceived / totalSent; + return (double)failedSales / H; +} + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + int H = (int)parser.getDouble("H"); + double T = parser.getDouble("T"); + int M = parser.getInt("M"); + int N = parser.getInt("N"); + double A = parser.getDouble("A"); + double B = parser.getDouble("B"); + double p = parser.getDouble("p"); + int S = parser.getInt("S"); + double F = parser.getDouble("F"); + double G = parser.getDouble("G"); + int K = parser.getInt("K", 10); + int Q = parser.getInt("Q", 10); + + double totalV = 0, totalW = 0; + for (int i = 0; i < M; ++i) { + double w; + totalV += SELib::runOneSim(H, T, N, A, B, p, S, F, G, K, Q, w); + totalW += w; + } + + std::ofstream outFile("results.txt"); + outFile << "2025-07-08-Axel-Rubini-2158099" << std::endl; + outFile << "V " << totalV / M << std::endl; + outFile << "W " << totalW / M << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-07-08/4/parameters.txt b/exams/AxelRubini/2025-07-08/4/parameters.txt new file mode 100644 index 0000000..7d74d68 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/4/parameters.txt @@ -0,0 +1,12 @@ +H 1000 +T 1 +M 100 +N 10 +A 5.0 +B 10.00 +p 1 +S 10 +F 2 +G 10 +K 100 +Q 10 diff --git a/exams/AxelRubini/2025-07-08/4/results.txt b/exams/AxelRubini/2025-07-08/4/results.txt new file mode 100644 index 0000000..1dde52e --- /dev/null +++ b/exams/AxelRubini/2025-07-08/4/results.txt @@ -0,0 +1,3 @@ +2025-07-08-Axel-Rubini-2158099 +V 0.00147 +W 0.998429 diff --git a/exams/AxelRubini/2025-07-08/5/Makefile b/exams/AxelRubini/2025-07-08/5/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/5/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-07-08/5/include/DES.hpp b/exams/AxelRubini/2025-07-08/5/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-07-08/5/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/5/include/Decision.hpp b/exams/AxelRubini/2025-07-08/5/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/5/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/5/include/Dynamics.hpp b/exams/AxelRubini/2025-07-08/5/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-07-08/5/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/5/include/Geometry.hpp b/exams/AxelRubini/2025-07-08/5/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-07-08/5/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/5/include/IO.hpp b/exams/AxelRubini/2025-07-08/5/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/5/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/5/include/Inventory.hpp b/exams/AxelRubini/2025-07-08/5/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/5/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/5/include/Queue.hpp b/exams/AxelRubini/2025-07-08/5/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/5/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/5/include/Random.hpp b/exams/AxelRubini/2025-07-08/5/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/5/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/5/include/Stat.hpp b/exams/AxelRubini/2025-07-08/5/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/5/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-07-08/5/main b/exams/AxelRubini/2025-07-08/5/main new file mode 100755 index 0000000..5f7b684 Binary files /dev/null and b/exams/AxelRubini/2025-07-08/5/main differ diff --git a/exams/AxelRubini/2025-07-08/5/main.cpp b/exams/AxelRubini/2025-07-08/5/main.cpp new file mode 100644 index 0000000..139a866 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/5/main.cpp @@ -0,0 +1,135 @@ +#include "include/DES.hpp" +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace SELib { + +struct Server { + std::vector inventory; + double busyUntil; +}; + +struct Event { + double time; + int type; + int serverIdx; + int productId; + int status; + + Event(double t, int tp, int sIdx, int pId, int st) + : time(t), type(tp), serverIdx(sIdx), productId(pId), status(st) {} + + bool operator>(const Event &other) const { return time > other.time; } +}; + +struct Result { + double V, W; +}; + +Result runMsims( + double p, + int H, + double T, + int M, + int N, + double A, + double B, + int S, + double F, + double G, + int K, + int Q, + RandomGenerator &rng +) { + double sumV = 0, sumW = 0; + for (int m = 0; m < M; ++m) { + std::priority_queue, std::greater> eq; + std::vector servers(S); + for (int i = 0; i < S; ++i) { + servers[i].inventory.assign(N + 1, 0); + for (int j = 1; j <= N; ++j) + servers[i].inventory[j] = rng.uniformInt(0, K); + servers[i].busyUntil = 0; + } + eq.push(Event(0.0, 0, 0, 0, 0)); + long long failed = 0, sent = 0, received = 0; + while (!eq.empty()) { + Event e = eq.top(); + eq.pop(); + if (e.time > (double)H) + break; + if (e.type == 0) { + int sIdx = rng.uniformInt(0, S - 1), pId = rng.uniformInt(1, N); + double st = F; + int status = 0; + if (servers[sIdx].inventory[pId] > 0) { + servers[sIdx].inventory[pId]--; + status = 1; + } else { + status = 0; + if (rng.bernoulli(p)) { + servers[sIdx].inventory[pId] += Q; + st = G; + } + } + double finish = std::max(e.time, servers[sIdx].busyUntil) + st; + servers[sIdx].busyUntil = finish; + sent++; + eq.push(Event(finish, 1, sIdx, pId, status)); + eq.push(Event(e.time + rng.uniform(A, B), 0, 0, 0, 0)); + } else { + received++; + if (e.status == 0) + failed++; + } + } + sumV += (double)failed / H; + sumW += (sent == 0) ? 1.0 : (double)received / sent; + } + return {sumV / M, sumW / M}; +} + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + int H = (int)parser.getDouble("H"), M = parser.getInt("M"), + N = parser.getInt("N"), S = parser.getInt("S"); + double T = parser.getDouble("T"), A = parser.getDouble("A"), + B = parser.getDouble("B"); + int R_budget = parser.getInt("R", 50); + double F = parser.getDouble("F"), G = parser.getDouble("G"); + int K = parser.getInt("K", 10), Q = parser.getInt("Q", 10); + + double bestP = 0, bestW = -1.0; + SELib::Result bestRes = {0, 0}; + + for (int i = 0; i < R_budget; ++i) { + double p = rng.uniform(0.0, 1.0); + auto res = SELib::runMsims(p, H, T, M, N, A, B, S, F, G, K, Q, rng); + if (res.W > bestW) { + bestW = res.W; + bestP = p; + bestRes = res; + } + } + + std::ofstream outFile("results.txt"); + outFile << "2025-07-08-Axel-Rubini-2158099" << std::endl; + outFile << "P " << bestP << std::endl; + outFile << "V " << bestRes.V << std::endl; + outFile << "W " << bestRes.W << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-07-08/5/parameters.txt b/exams/AxelRubini/2025-07-08/5/parameters.txt new file mode 100644 index 0000000..15e6c00 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/5/parameters.txt @@ -0,0 +1,12 @@ +H 1000 +T 1 +M 100 +N 10 +A 5.0 +B 10.00 +R 50 +S 10 +F 5 +G 6 +K 10 +Q 10 diff --git a/exams/AxelRubini/2025-07-08/5/results.txt b/exams/AxelRubini/2025-07-08/5/results.txt new file mode 100644 index 0000000..96994a2 --- /dev/null +++ b/exams/AxelRubini/2025-07-08/5/results.txt @@ -0,0 +1,4 @@ +2025-07-08-Axel-Rubini-2158099 +P 0.0423751 +V 0.02019 +W 0.99605 diff --git a/exams/AxelRubini/2025-09-08/1/Makefile b/exams/AxelRubini/2025-09-08/1/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/1/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-09-08/1/include/DES.hpp b/exams/AxelRubini/2025-09-08/1/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-09-08/1/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/1/include/Decision.hpp b/exams/AxelRubini/2025-09-08/1/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/1/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/1/include/Dynamics.hpp b/exams/AxelRubini/2025-09-08/1/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-09-08/1/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/1/include/Geometry.hpp b/exams/AxelRubini/2025-09-08/1/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-09-08/1/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/1/include/IO.hpp b/exams/AxelRubini/2025-09-08/1/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/1/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/1/include/Inventory.hpp b/exams/AxelRubini/2025-09-08/1/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/1/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/1/include/Queue.hpp b/exams/AxelRubini/2025-09-08/1/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/1/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/1/include/Random.hpp b/exams/AxelRubini/2025-09-08/1/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/1/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/1/include/Stat.hpp b/exams/AxelRubini/2025-09-08/1/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/1/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/1/main b/exams/AxelRubini/2025-09-08/1/main new file mode 100755 index 0000000..febde16 Binary files /dev/null and b/exams/AxelRubini/2025-09-08/1/main differ diff --git a/exams/AxelRubini/2025-09-08/1/main.cpp b/exams/AxelRubini/2025-09-08/1/main.cpp new file mode 100644 index 0000000..1239923 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/1/main.cpp @@ -0,0 +1,93 @@ +#include "include/Geometry.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace SELib { + +struct UAV { + Point3D pos; +}; + +double runOneSimulation( + double T, + double H, + int N, + double L, + double V, + double A, + double D, + RandomGenerator &rng +) { + std::vector uavs(N); + for (int i = 0; i < N; ++i) { + uavs[i].pos = { + rng.uniform(-L, L), rng.uniform(-L, L), rng.uniform(-L, L) + }; + } + + long long totalCollisions = 0; + int steps = 0; + + // Time loop: t*T <= H + for (int t = 0; (double)t * T <= H + 1e-9; ++t) { + // 1. Check collisions at CURRENT position (t) + for (int i = 0; i < N; ++i) { + for (int j = i + 1; j < N; ++j) { + if (uavs[i].pos.distanceTo(uavs[j].pos) <= D) { + totalCollisions++; + } + } + } + + // 2. Move for NEXT step (t+1) + for (int i = 0; i < N; ++i) { + // axis k=1,2,3 + double x[3] = {uavs[i].pos.x, uavs[i].pos.y, uavs[i].pos.z}; + double new_x[3]; + for (int k = 0; k < 3; ++k) { + double prob = std::exp(-A * (x[k] + L) / (2.0 * L)); + double vk = + (rng.bernoulli(std::clamp(prob, 0.0, 1.0))) ? V : -V; + new_x[k] = x[k] + vk * T; + } + uavs[i].pos = {new_x[0], new_x[1], new_x[2]}; + } + } + + return (double)totalCollisions / H; +} + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + double T = parser.getDouble("T"); + double H = parser.getDouble("H"); + int M = parser.getInt("M"); + int N = parser.getInt("N"); + double L = parser.getDouble("L"); + double V = parser.getDouble("V"); + double A = parser.getDouble("A"); + double D = parser.getDouble("D"); + + double totalRate = 0; + for (int i = 0; i < M; ++i) { + totalRate += SELib::runOneSimulation(T, H, N, L, V, A, D, rng); + } + + std::ofstream outFile("results.txt"); + outFile << "2025-09-08-Axel-Rubini-2158099" << std::endl; + outFile << "C " << totalRate / M << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-09-08/1/parameters.txt b/exams/AxelRubini/2025-09-08/1/parameters.txt new file mode 100644 index 0000000..2770776 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/1/parameters.txt @@ -0,0 +1,8 @@ +T 1 +H 1000 +M 100 +N 10 +L 1 +V 1 +A 10 +D 1 diff --git a/exams/AxelRubini/2025-09-08/1/results.txt b/exams/AxelRubini/2025-09-08/1/results.txt new file mode 100644 index 0000000..44d0e69 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/1/results.txt @@ -0,0 +1,2 @@ +2025-09-08-Axel-Rubini-2158099 +C 11.355 diff --git a/exams/AxelRubini/2025-09-08/2/Makefile b/exams/AxelRubini/2025-09-08/2/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/2/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-09-08/2/include/DES.hpp b/exams/AxelRubini/2025-09-08/2/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-09-08/2/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/2/include/Decision.hpp b/exams/AxelRubini/2025-09-08/2/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/2/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/2/include/Dynamics.hpp b/exams/AxelRubini/2025-09-08/2/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-09-08/2/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/2/include/Geometry.hpp b/exams/AxelRubini/2025-09-08/2/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-09-08/2/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/2/include/IO.hpp b/exams/AxelRubini/2025-09-08/2/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/2/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/2/include/Inventory.hpp b/exams/AxelRubini/2025-09-08/2/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/2/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/2/include/Queue.hpp b/exams/AxelRubini/2025-09-08/2/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/2/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/2/include/Random.hpp b/exams/AxelRubini/2025-09-08/2/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/2/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/2/include/Stat.hpp b/exams/AxelRubini/2025-09-08/2/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/2/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/2/main b/exams/AxelRubini/2025-09-08/2/main new file mode 100755 index 0000000..77df3d8 Binary files /dev/null and b/exams/AxelRubini/2025-09-08/2/main differ diff --git a/exams/AxelRubini/2025-09-08/2/main.cpp b/exams/AxelRubini/2025-09-08/2/main.cpp new file mode 100644 index 0000000..9106871 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/2/main.cpp @@ -0,0 +1,120 @@ +#include "include/Geometry.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace SELib { + +struct UAV { + Point3D pos; +}; + +double calculateD(const Point3D &z, const std::vector &uavs, double L) { + double total = 0; + for (const auto &u : uavs) { + double d2 = std::pow((z.x - u.pos.x) / (2.0 * L), 2) + + std::pow((z.y - u.pos.y) / (2.0 * L), 2) + + std::pow((z.z - u.pos.z) / (2.0 * L), 2); + total += d2; + } + return total; +} + +double runOneSimulationAvoidance( + double T, + double H, + int N, + double L, + double V, + double D, + RandomGenerator &rng +) { + std::vector uavs(N); + for (int i = 0; i < N; ++i) + uavs[i].pos = { + rng.uniform(-L, L), rng.uniform(-L, L), rng.uniform(-L, L) + }; + + long long totalCollisions = 0; + for (int t = 0; (double)t * T <= H + 1e-9; ++t) { + for (int i = 0; i < N; ++i) { + for (int j = i + 1; j < N; ++j) { + if (uavs[i].pos.distanceTo(uavs[j].pos) <= D) + totalCollisions++; + } + } + + std::vector nextPos(N); + for (int i = 0; i < N; ++i) { + double minVal = 1e18; + std::vector bestMoves; + for (int bit = 0; bit < 8; ++bit) { + double v1 = (bit & 1) ? V : -V; + double v2 = (bit & 2) ? V : -V; + double v3 = (bit & 4) ? V : -V; + Point3D potential = { + uavs[i].pos.x + v1 * T, + uavs[i].pos.y + v2 * T, + uavs[i].pos.z + v3 * T + }; + + // Text says: d(xi(V1,V2,V3, t+1), t) + // This sum includes j=i? The formula says sum j=1 to N. + double val = 0; + for (int j = 0; j < N; ++j) { + if (i == j) + continue; // Usually avoidance doesn't include + // self-distance which is 0 anyway? + // Actually, if we include j=i, the term is (v*T/2L)^2 which + // is constant. + val += + std::pow((potential.x - uavs[j].pos.x) / (2.0 * L), 2) + + std::pow((potential.y - uavs[j].pos.y) / (2.0 * L), 2) + + std::pow((potential.z - uavs[j].pos.z) / (2.0 * L), 2); + } + + if (val < minVal - 1e-12) { + minVal = val; + bestMoves.clear(); + bestMoves.push_back(potential); + } else if (std::abs(val - minVal) < 1e-12) { + bestMoves.push_back(potential); + } + } + nextPos[i] = + bestMoves[rng.uniformInt(0, (int)bestMoves.size() - 1)]; + } + for (int i = 0; i < N; ++i) + uavs[i].pos = nextPos[i]; + } + return (double)totalCollisions / H; +} + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + double T = parser.getDouble("T"), H = parser.getDouble("H"), + L = parser.getDouble("L"), V = parser.getDouble("V"), + D = parser.getDouble("D"); + int M = parser.getInt("M"), N = parser.getInt("N"); + + double totalRate = 0; + for (int i = 0; i < M; ++i) + totalRate += SELib::runOneSimulationAvoidance(T, H, N, L, V, D, rng); + + std::ofstream outFile("results.txt"); + outFile << "2025-09-08-Axel-Rubini-2158099" << std::endl; + outFile << "C " << totalRate / M << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-09-08/2/parameters.txt b/exams/AxelRubini/2025-09-08/2/parameters.txt new file mode 100644 index 0000000..2770776 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/2/parameters.txt @@ -0,0 +1,8 @@ +T 1 +H 1000 +M 100 +N 10 +L 1 +V 1 +A 10 +D 1 diff --git a/exams/AxelRubini/2025-09-08/2/results.txt b/exams/AxelRubini/2025-09-08/2/results.txt new file mode 100644 index 0000000..8440253 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/2/results.txt @@ -0,0 +1,2 @@ +2025-09-08-Axel-Rubini-2158099 +C 11.8618 diff --git a/exams/AxelRubini/2025-09-08/3/Makefile b/exams/AxelRubini/2025-09-08/3/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/3/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-09-08/3/include/DES.hpp b/exams/AxelRubini/2025-09-08/3/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-09-08/3/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/3/include/Decision.hpp b/exams/AxelRubini/2025-09-08/3/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/3/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/3/include/Dynamics.hpp b/exams/AxelRubini/2025-09-08/3/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-09-08/3/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/3/include/Geometry.hpp b/exams/AxelRubini/2025-09-08/3/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-09-08/3/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/3/include/IO.hpp b/exams/AxelRubini/2025-09-08/3/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/3/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/3/include/Inventory.hpp b/exams/AxelRubini/2025-09-08/3/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/3/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/3/include/Queue.hpp b/exams/AxelRubini/2025-09-08/3/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/3/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/3/include/Random.hpp b/exams/AxelRubini/2025-09-08/3/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/3/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/3/include/Stat.hpp b/exams/AxelRubini/2025-09-08/3/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/3/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/3/main b/exams/AxelRubini/2025-09-08/3/main new file mode 100755 index 0000000..de4ada5 Binary files /dev/null and b/exams/AxelRubini/2025-09-08/3/main differ diff --git a/exams/AxelRubini/2025-09-08/3/main.cpp b/exams/AxelRubini/2025-09-08/3/main.cpp new file mode 100644 index 0000000..f8a1786 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/3/main.cpp @@ -0,0 +1,105 @@ +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +struct Server { + std::vector inventory; +}; + +struct Event { + double time; + int type; // 1: Supplier (Higher Priority), 0: Customer + int productId; + Event(double t, int tp, int pId) : time(t), type(tp), productId(pId) {} + // Priority: smaller time first. If same time, larger type first (Supplier=1 + // > Customer=0) + bool operator>(const Event &other) const { + if (std::abs(time - other.time) > 1e-9) + return time > other.time; + return type < other.type; + } +}; + +double runOneSim( + double T, + double H, + int C, + double A, + double B, + int F, + double V, + double Q, + int P, + int S, + int K, + RandomGenerator &rng +) { + std::priority_queue, std::greater> eq; + std::vector servers(S); + for (int i = 0; i < S; ++i) { + servers[i].inventory.assign(P + 1, 0); + for (int j = 1; j <= P; ++j) + servers[i].inventory[j] = K; + } + + for (int i = 0; i < C; ++i) + eq.push(Event(rng.uniform(0, B), 0, rng.uniformInt(1, P))); + for (int i = 0; i < F; ++i) + eq.push(Event(rng.uniform(0, Q), 1, rng.uniformInt(1, P))); + + long long failedSales = 0; + while (!eq.empty()) { + Event e = eq.top(); + eq.pop(); + if (e.time > H) + break; + + if (e.type == 0) { // Customer + int sIdx = rng.uniformInt(0, S - 1); + if (servers[sIdx].inventory[e.productId] > 0) + servers[sIdx].inventory[e.productId]--; + else + failedSales++; + eq.push(Event(e.time + rng.uniform(0, B), 0, rng.uniformInt(1, P))); + } else { // Supplier + int sIdx = rng.uniformInt(0, S - 1); + servers[sIdx].inventory[e.productId] += 10; + eq.push(Event(e.time + rng.uniform(0, Q), 1, rng.uniformInt(1, P))); + } + } + return (double)failedSales / (H / T); +} + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + double T = parser.getDouble("T"), H = parser.getDouble("H"); + int M = parser.getInt("M"), C = parser.getInt("C"); + double A = parser.getDouble("A"), B = parser.getDouble("B"); + int F = parser.getInt("F"); + double V = parser.getDouble("V"), Q = parser.getDouble("Q"); + int P = parser.getInt("P"), S = parser.getInt("S"), + K = parser.getInt("K", 1); + + double totalRate = 0; + for (int i = 0; i < M; ++i) + totalRate += SELib::runOneSim(T, H, C, A, B, F, V, Q, P, S, K, rng); + + std::ofstream outFile("results.txt"); + outFile << "2025-09-08-Axel-Rubini-2158099" << std::endl; + outFile << "R " << totalRate / M << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-09-08/3/parameters.txt b/exams/AxelRubini/2025-09-08/3/parameters.txt new file mode 100644 index 0000000..adc02a6 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/3/parameters.txt @@ -0,0 +1,12 @@ +T 0.1 +H 100 +M 100 +C 100 +A 1.0 +B 1.0 +F 1 +V 1 +Q 1 +P 1 +S 1 +K 1 diff --git a/exams/AxelRubini/2025-09-08/3/results.txt b/exams/AxelRubini/2025-09-08/3/results.txt new file mode 100644 index 0000000..fabcd57 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/3/results.txt @@ -0,0 +1,2 @@ +2025-09-08-Axel-Rubini-2158099 +R 17.9675 diff --git a/exams/AxelRubini/2025-09-08/4/Makefile b/exams/AxelRubini/2025-09-08/4/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/4/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-09-08/4/include/DES.hpp b/exams/AxelRubini/2025-09-08/4/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-09-08/4/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/4/include/Decision.hpp b/exams/AxelRubini/2025-09-08/4/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/4/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/4/include/Dynamics.hpp b/exams/AxelRubini/2025-09-08/4/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-09-08/4/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/4/include/Geometry.hpp b/exams/AxelRubini/2025-09-08/4/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-09-08/4/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/4/include/IO.hpp b/exams/AxelRubini/2025-09-08/4/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/4/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/4/include/Inventory.hpp b/exams/AxelRubini/2025-09-08/4/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/4/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/4/include/Queue.hpp b/exams/AxelRubini/2025-09-08/4/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/4/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/4/include/Random.hpp b/exams/AxelRubini/2025-09-08/4/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/4/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/4/include/Stat.hpp b/exams/AxelRubini/2025-09-08/4/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/4/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/4/main b/exams/AxelRubini/2025-09-08/4/main new file mode 100755 index 0000000..6042d8b Binary files /dev/null and b/exams/AxelRubini/2025-09-08/4/main differ diff --git a/exams/AxelRubini/2025-09-08/4/main.cpp b/exams/AxelRubini/2025-09-08/4/main.cpp new file mode 100644 index 0000000..931335d --- /dev/null +++ b/exams/AxelRubini/2025-09-08/4/main.cpp @@ -0,0 +1,119 @@ +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +struct Server { + std::vector inventory; +}; + +struct Event { + double time; + int type; + int productId; + Event(double t, int tp, int pId) : time(t), type(tp), productId(pId) {} + bool operator>(const Event &other) const { return time > other.time; } +}; + +double runMsims( + int F, + int M, + double T, + double H, + int C, + double A, + double B, + double V, + double Q, + int P, + int S, + int K, + RandomGenerator &rng +) { + double totalR = 0; + for (int m = 0; m < M; ++m) { + std::priority_queue, std::greater> eq; + std::vector servers(S); + for (int i = 0; i < S; ++i) { + servers[i].inventory.assign(P + 1, 0); + for (int j = 1; j <= P; ++j) + servers[i].inventory[j] = K; + } + for (int i = 0; i < C; ++i) + eq.push(Event(rng.uniform(A, B), 0, rng.uniformInt(1, P))); + for (int i = 0; i < F; ++i) + eq.push(Event(rng.uniform(V, Q), 1, rng.uniformInt(1, P))); + + long long failed = 0; + while (!eq.empty()) { + Event e = eq.top(); + eq.pop(); + if (e.time > H) + break; + if (e.type == 0) { + int sIdx = rng.uniformInt(0, S - 1); + if (servers[sIdx].inventory[e.productId] > 0) + servers[sIdx].inventory[e.productId]--; + else + failed++; + eq.push( + Event(e.time + rng.uniform(A, B), 0, rng.uniformInt(1, P)) + ); + } else { + int sIdx = rng.uniformInt(0, S - 1); + servers[sIdx].inventory[e.productId] += 10; + eq.push( + Event(e.time + rng.uniform(V, Q), 1, rng.uniformInt(1, P)) + ); + } + } + totalR += (double)failed / H; + } + return totalR / M; +} + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + double T = parser.getDouble("T"), H = parser.getDouble("H"); + int M = parser.getInt("M"), C = parser.getInt("C"); + double A = parser.getDouble("A"), B = parser.getDouble("B"); + int G_budget = parser.getInt("G"); + double V = parser.getDouble("V"), Q = parser.getDouble("Q"); + int P = parser.getInt("P"), S = parser.getInt("S"), + K = parser.getInt("K", 1); + + int bestF = 1; + double minJ = 1e18; + double bestR = 0; + + for (int i = 0; i < G_budget; ++i) { + int F = rng.uniformInt(1, 100); + double R = SELib::runMsims(F, M, T, H, C, A, B, V, Q, P, S, K, rng); + double J = 10.0 * F + 2.0 * R; + if (J < minJ) { + minJ = J; + bestF = F; + bestR = R; + } + } + + std::ofstream outFile("results.txt"); + outFile << "2025-09-08-Axel-Rubini-2158099" << std::endl; + outFile << "R " << bestR << std::endl; + outFile << "F " << bestF << std::endl; + outFile << "J " << minJ << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-09-08/4/parameters.txt b/exams/AxelRubini/2025-09-08/4/parameters.txt new file mode 100644 index 0000000..838fc96 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/4/parameters.txt @@ -0,0 +1,12 @@ +T 0.1 +H 100 +M 100 +C 200 +A 1 +B 1 +G 20 +V 0.1 +Q 0.1 +P 1 +S 1 +K 1 diff --git a/exams/AxelRubini/2025-09-08/4/results.txt b/exams/AxelRubini/2025-09-08/4/results.txt new file mode 100644 index 0000000..32c4608 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/4/results.txt @@ -0,0 +1,4 @@ +2025-09-08-Axel-Rubini-2158099 +R 0 +F 13 +J 130 diff --git a/exams/AxelRubini/2025-09-08/5/Makefile b/exams/AxelRubini/2025-09-08/5/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/5/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-09-08/5/include/DES.hpp b/exams/AxelRubini/2025-09-08/5/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-09-08/5/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/5/include/Decision.hpp b/exams/AxelRubini/2025-09-08/5/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/5/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/5/include/Dynamics.hpp b/exams/AxelRubini/2025-09-08/5/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-09-08/5/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/5/include/Geometry.hpp b/exams/AxelRubini/2025-09-08/5/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-09-08/5/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/5/include/IO.hpp b/exams/AxelRubini/2025-09-08/5/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/5/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/5/include/Inventory.hpp b/exams/AxelRubini/2025-09-08/5/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/5/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/5/include/Queue.hpp b/exams/AxelRubini/2025-09-08/5/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/5/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/5/include/Random.hpp b/exams/AxelRubini/2025-09-08/5/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/5/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/5/include/Stat.hpp b/exams/AxelRubini/2025-09-08/5/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/5/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-09-08/5/main b/exams/AxelRubini/2025-09-08/5/main new file mode 100755 index 0000000..ee912f6 Binary files /dev/null and b/exams/AxelRubini/2025-09-08/5/main differ diff --git a/exams/AxelRubini/2025-09-08/5/main.cpp b/exams/AxelRubini/2025-09-08/5/main.cpp new file mode 100644 index 0000000..48fc3d3 --- /dev/null +++ b/exams/AxelRubini/2025-09-08/5/main.cpp @@ -0,0 +1,120 @@ +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +struct Server { + std::vector inventory; +}; + +struct Event { + double time; + int type; + int productId; + Event(double t, int tp, int pId) : time(t), type(tp), productId(pId) {} + bool operator>(const Event &other) const { return time > other.time; } +}; + +struct Result { + double R, V, Q, J; +}; + +Result runMsims( + double V, + int M, + double T, + double H, + int C, + double A, + double B, + int F, + int P, + int S, + int K, + RandomGenerator &rng +) { + double Q = V + 5.0; + double totalR = 0; + for (int m = 0; m < M; ++m) { + std::priority_queue, std::greater> eq; + std::vector servers(S); + for (int i = 0; i < S; ++i) { + servers[i].inventory.assign(P + 1, 0); + for (int j = 1; j <= P; ++j) + servers[i].inventory[j] = K; + } + for (int i = 0; i < C; ++i) + eq.push(Event(rng.uniform(A, B), 0, rng.uniformInt(1, P))); + for (int i = 0; i < F; ++i) + eq.push(Event(rng.uniform(V, Q), 1, rng.uniformInt(1, P))); + + long long failed = 0; + while (!eq.empty()) { + Event e = eq.top(); + eq.pop(); + if (e.time > H) + break; + if (e.type == 0) { + int sIdx = rng.uniformInt(0, S - 1); + if (servers[sIdx].inventory[e.productId] > 0) + servers[sIdx].inventory[e.productId]--; + else + failed++; + eq.push( + Event(e.time + rng.uniform(A, B), 0, rng.uniformInt(1, P)) + ); + } else { + int sIdx = rng.uniformInt(0, S - 1); + servers[sIdx].inventory[e.productId]++; + eq.push( + Event(e.time + rng.uniform(V, Q), 1, rng.uniformInt(1, P)) + ); + } + } + totalR += (double)failed / H; + } + double R = totalR / M; + return {R, V, Q, 10.0 * V + 2.0 * R}; +} + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + double T = parser.getDouble("T"), H = parser.getDouble("H"); + int M = parser.getInt("M"), C = parser.getInt("C"); + double A = parser.getDouble("A"), B = parser.getDouble("B"); + int F = parser.getInt("F"), G_budget = parser.getInt("G"); + int P = parser.getInt("P"), S = parser.getInt("S"), + K = parser.getInt("K", 1); + + double bestV = 0; + SELib::Result bestRes = {0, 0, 0, 1e18}; + + for (int i = 0; i < G_budget; ++i) { + double V = rng.uniform(0.1, 10.0); + auto res = SELib::runMsims(V, M, T, H, C, A, B, F, P, S, K, rng); + if (res.J < bestRes.J) { + bestRes = res; + } + } + + std::ofstream outFile("results.txt"); + outFile << "2025-09-08-Axel-Rubini-2158099" << std::endl; + outFile << "R " << bestRes.R << std::endl; + outFile << "V " << bestRes.V << std::endl; + outFile << "Q " << bestRes.Q << std::endl; + outFile << "J " << bestRes.J << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-09-08/5/parameters.txt b/exams/AxelRubini/2025-09-08/5/parameters.txt new file mode 100644 index 0000000..850217c --- /dev/null +++ b/exams/AxelRubini/2025-09-08/5/parameters.txt @@ -0,0 +1,11 @@ +T 10 +H 1000 +M 100 +C 100 +A 10 +B 10 +F 10 +G 20 +P 1 +S 1 +K 1 diff --git a/exams/AxelRubini/2025-09-08/5/results.txt b/exams/AxelRubini/2025-09-08/5/results.txt new file mode 100644 index 0000000..e0c24fc --- /dev/null +++ b/exams/AxelRubini/2025-09-08/5/results.txt @@ -0,0 +1,5 @@ +2025-09-08-Axel-Rubini-2158099 +R 6.33914 +V 0.234785 +Q 5.23478 +J 15.0261 diff --git a/exams/AxelRubini/2025-11-03/1/Makefile b/exams/AxelRubini/2025-11-03/1/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/1/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-11-03/1/include/DES.hpp b/exams/AxelRubini/2025-11-03/1/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-11-03/1/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/1/include/Decision.hpp b/exams/AxelRubini/2025-11-03/1/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/1/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/1/include/Dynamics.hpp b/exams/AxelRubini/2025-11-03/1/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-11-03/1/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/1/include/Geometry.hpp b/exams/AxelRubini/2025-11-03/1/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-11-03/1/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/1/include/IO.hpp b/exams/AxelRubini/2025-11-03/1/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/1/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/1/include/Inventory.hpp b/exams/AxelRubini/2025-11-03/1/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/1/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/1/include/Queue.hpp b/exams/AxelRubini/2025-11-03/1/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/1/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/1/include/Random.hpp b/exams/AxelRubini/2025-11-03/1/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/1/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/1/include/Stat.hpp b/exams/AxelRubini/2025-11-03/1/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/1/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/1/main b/exams/AxelRubini/2025-11-03/1/main new file mode 100755 index 0000000..86940d2 Binary files /dev/null and b/exams/AxelRubini/2025-11-03/1/main differ diff --git a/exams/AxelRubini/2025-11-03/1/main.cpp b/exams/AxelRubini/2025-11-03/1/main.cpp new file mode 100644 index 0000000..00b2b84 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/1/main.cpp @@ -0,0 +1,39 @@ +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include +#include +#include +#include +#include + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + int N = parser.getInt("N"); + int M = parser.getInt("M"); + + SELib::MDP mdp(N, rng); + const auto &data = parser.getStructuredData(); + for (const auto &line : data) { + if (line.size() == 5 && line[0] == "A") { + int i = std::stoi(line[1]); + int j = std::stoi(line[2]); + double p = std::stod(line[3]); + double c = std::stod(line[4]); + mdp.addTransition(i, j, p, c); + } + } + + double totalCost = 0; + for (int i = 0; i < M; ++i) { + totalCost += mdp.simulate(0, N - 1); + } + + std::ofstream outFile("results.txt"); + outFile << "2025-11-03-Axel-Rubini-2158099" << std::endl; + outFile << "C " << totalCost / M << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-11-03/1/parameters.txt b/exams/AxelRubini/2025-11-03/1/parameters.txt new file mode 100644 index 0000000..b7435a0 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/1/parameters.txt @@ -0,0 +1,18 @@ +N 5 +M 100 +A 0 0 0.4 50 +A 0 1 0.6 100 +A 1 0 0.1 150 +A 1 1 0.2 100 +A 1 2 0.7 50 +A 2 0 0.1 200 +A 2 1 0.1 150 +A 2 2 0.2 100 +A 2 3 0.6 50 +A 3 0 0.1 200 +A 3 1 0.1 150 +A 3 2 0.1 100 +A 3 3 0.1 50 +A 3 4 0.6 50 +A 4 4 1 0 + diff --git a/exams/AxelRubini/2025-11-03/1/results.txt b/exams/AxelRubini/2025-11-03/1/results.txt new file mode 100644 index 0000000..c64cd26 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/1/results.txt @@ -0,0 +1,2 @@ +2025-11-03-Axel-Rubini-2158099 +C 738.5 diff --git a/exams/AxelRubini/2025-11-03/2/Makefile b/exams/AxelRubini/2025-11-03/2/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/2/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-11-03/2/include/DES.hpp b/exams/AxelRubini/2025-11-03/2/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-11-03/2/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/2/include/Decision.hpp b/exams/AxelRubini/2025-11-03/2/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/2/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/2/include/Dynamics.hpp b/exams/AxelRubini/2025-11-03/2/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-11-03/2/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/2/include/Geometry.hpp b/exams/AxelRubini/2025-11-03/2/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-11-03/2/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/2/include/IO.hpp b/exams/AxelRubini/2025-11-03/2/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/2/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/2/include/Inventory.hpp b/exams/AxelRubini/2025-11-03/2/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/2/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/2/include/Queue.hpp b/exams/AxelRubini/2025-11-03/2/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/2/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/2/include/Random.hpp b/exams/AxelRubini/2025-11-03/2/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/2/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/2/include/Stat.hpp b/exams/AxelRubini/2025-11-03/2/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/2/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/2/main b/exams/AxelRubini/2025-11-03/2/main new file mode 100755 index 0000000..01bbcf4 Binary files /dev/null and b/exams/AxelRubini/2025-11-03/2/main differ diff --git a/exams/AxelRubini/2025-11-03/2/main.cpp b/exams/AxelRubini/2025-11-03/2/main.cpp new file mode 100644 index 0000000..f87d892 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/2/main.cpp @@ -0,0 +1,42 @@ +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include +#include +#include +#include +#include +#include + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + int N = parser.getInt("N"); + int M = parser.getInt("M"); + double VMax = parser.getDouble("C"); + + SELib::MDP mdp(N, rng); + const auto &data = parser.getStructuredData(); + for (const auto &line : data) { + if (line.size() == 5 && line[0] == "A") { + mdp.addTransition( + std::stoi(line[1]), + std::stoi(line[2]), + std::stod(line[3]), + std::stod(line[4]) + ); + } + } + + SELib::MonteCarloSimulator simulator(M); + double prob = simulator.estimateProbability([&](int) { + return mdp.simulate(0, N - 1) <= VMax; + }); + + std::ofstream outFile("results.txt"); + outFile << "2025-11-03-Axel-Rubini-2158099" << std::endl; + outFile << "P " << std::fixed << std::setprecision(6) << prob << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-11-03/2/parameters.txt b/exams/AxelRubini/2025-11-03/2/parameters.txt new file mode 100644 index 0000000..0002bfe --- /dev/null +++ b/exams/AxelRubini/2025-11-03/2/parameters.txt @@ -0,0 +1,18 @@ +N 5 +M 1000 +A 0 0 0.4 50 +A 0 1 0.6 100 +A 1 0 0.1 150 +A 1 1 0.2 100 +A 1 2 0.7 50 +A 2 0 0.1 200 +A 2 1 0.1 150 +A 2 2 0.2 100 +A 2 3 0.6 50 +A 3 0 0.1 200 +A 3 1 0.1 150 +A 3 2 0.1 100 +A 3 3 0.1 50 +A 3 4 0.6 50 +A 4 4 1 0 +C 700 diff --git a/exams/AxelRubini/2025-11-03/2/results.txt b/exams/AxelRubini/2025-11-03/2/results.txt new file mode 100644 index 0000000..48ee9b4 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/2/results.txt @@ -0,0 +1,2 @@ +2025-11-03-Axel-Rubini-2158099 +P 0.635000 diff --git a/exams/AxelRubini/2025-11-03/3/Makefile b/exams/AxelRubini/2025-11-03/3/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/3/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2025-11-03/3/include/DES.hpp b/exams/AxelRubini/2025-11-03/3/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-11-03/3/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/3/include/Decision.hpp b/exams/AxelRubini/2025-11-03/3/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/3/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/3/include/Dynamics.hpp b/exams/AxelRubini/2025-11-03/3/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-11-03/3/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/3/include/Geometry.hpp b/exams/AxelRubini/2025-11-03/3/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-11-03/3/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/3/include/IO.hpp b/exams/AxelRubini/2025-11-03/3/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/3/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/3/include/Inventory.hpp b/exams/AxelRubini/2025-11-03/3/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/3/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/3/include/Queue.hpp b/exams/AxelRubini/2025-11-03/3/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/3/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/3/include/Random.hpp b/exams/AxelRubini/2025-11-03/3/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/3/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/3/include/Stat.hpp b/exams/AxelRubini/2025-11-03/3/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/3/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/3/main b/exams/AxelRubini/2025-11-03/3/main new file mode 100755 index 0000000..fa300b5 Binary files /dev/null and b/exams/AxelRubini/2025-11-03/3/main differ diff --git a/exams/AxelRubini/2025-11-03/3/main.cpp b/exams/AxelRubini/2025-11-03/3/main.cpp new file mode 100644 index 0000000..dfe39c0 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/3/main.cpp @@ -0,0 +1,72 @@ +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include +#include +#include +#include +#include +#include + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + int N = parser.getInt("N"); + int M = parser.getInt("M"); + int B = parser.getInt("B"); + double K_val = parser.getDouble("K"); + + struct Trans { + int i, j; + double p, c; + }; + std::vector fixed; + double c00 = 0, c01 = 0; + + const auto &data = parser.getStructuredData(); + for (const auto &line : data) { + if (line.size() == 5 && line[0] == "A") { + int i = std::stoi(line[1]), j = std::stoi(line[2]); + double p = std::stod(line[3]), c = std::stod(line[4]); + if (i == 0 && j == 0) + c00 = c; + else if (i == 0 && j == 1) + c01 = c; + else + fixed.push_back({i, j, p, c}); + } + } + + auto objective = [&](double p) { + SELib::MDP mdp(N, rng); + for (auto &t : fixed) + mdp.addTransition(t.i, t.j, t.p, t.c); + mdp.addTransition(0, 0, p, c00); + mdp.addTransition(0, 1, 1.0 - p, c01); + + SELib::MonteCarloSimulator sim(M); + double EC = sim.estimate([&](int) { return mdp.simulate(0, N - 1); }); + return K_val * (1.0 - p) + EC; + }; + + auto [bestP, minJ] = + SELib::Optimizer::randomSearch(objective, 0.0, 0.9, B, rng, false); + + // Final eval + SELib::MDP mdp(N, rng); + for (auto &t : fixed) + mdp.addTransition(t.i, t.j, t.p, t.c); + mdp.addTransition(0, 0, bestP, c00); + mdp.addTransition(0, 1, 1.0 - bestP, c01); + SELib::MonteCarloSimulator sim(M); + double finalC = sim.estimate([&](int) { return mdp.simulate(0, N - 1); }); + + std::ofstream outFile("results.txt"); + outFile << "2025-11-03-Axel-Rubini-2158099" << std::endl; + outFile << "p " << bestP << std::endl; + outFile << "C " << finalC << std::endl; + outFile << "J " << minJ << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-11-03/3/parameters.txt b/exams/AxelRubini/2025-11-03/3/parameters.txt new file mode 100644 index 0000000..d733326 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/3/parameters.txt @@ -0,0 +1,19 @@ +N 5 +M 100 +A 0 0 0 50 +A 0 1 0 100 +A 1 0 0.1 150 +A 1 1 0.2 100 +A 1 2 0.7 50 +A 2 0 0.1 200 +A 2 1 0.1 150 +A 2 2 0.2 100 +A 2 3 0.6 50 +A 3 0 0.1 200 +A 3 1 0.1 150 +A 3 2 0.1 100 +A 3 3 0.1 50 +A 3 4 0.6 50 +A 4 4 1 0 +B 100 +K 400 diff --git a/exams/AxelRubini/2025-11-03/3/results.txt b/exams/AxelRubini/2025-11-03/3/results.txt new file mode 100644 index 0000000..f5b4e42 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/3/results.txt @@ -0,0 +1,4 @@ +2025-11-03-Axel-Rubini-2158099 +p 0.705064 +C 867 +J 868.475 diff --git a/exams/AxelRubini/2025-11-03/4/include/DES.hpp b/exams/AxelRubini/2025-11-03/4/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-11-03/4/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/4/include/Decision.hpp b/exams/AxelRubini/2025-11-03/4/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/4/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/4/include/Dynamics.hpp b/exams/AxelRubini/2025-11-03/4/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-11-03/4/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/4/include/Geometry.hpp b/exams/AxelRubini/2025-11-03/4/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-11-03/4/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/4/include/IO.hpp b/exams/AxelRubini/2025-11-03/4/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/4/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/4/include/Inventory.hpp b/exams/AxelRubini/2025-11-03/4/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/4/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/4/include/Queue.hpp b/exams/AxelRubini/2025-11-03/4/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/4/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/4/include/Random.hpp b/exams/AxelRubini/2025-11-03/4/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/4/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/4/include/Stat.hpp b/exams/AxelRubini/2025-11-03/4/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/4/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/4/main b/exams/AxelRubini/2025-11-03/4/main new file mode 100755 index 0000000..dcf301e Binary files /dev/null and b/exams/AxelRubini/2025-11-03/4/main differ diff --git a/exams/AxelRubini/2025-11-03/4/main.cpp b/exams/AxelRubini/2025-11-03/4/main.cpp new file mode 100644 index 0000000..ec91b88 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/4/main.cpp @@ -0,0 +1,117 @@ +#include "include/DES.hpp" +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace SELib { + +struct Params { + double T, H; + int M; + double A, B, V, W; + int P, K; + double p; + int Q; +}; + +class Customer : public DiscreteEventProcess { + public: + Customer(Params &p, RandomGenerator &r, std::queue &q) + : p_(p), rng_(r), q_(q) {} + void initialize(double start) override { + next_ = start + rng_.uniform(p_.A, p_.B); + } + double nextEventTime() const override { return next_; } + void handleEvent(double t) override { + q_.push(rng_.uniformInt(1, p_.P)); + next_ = t + rng_.uniform(p_.A, p_.B); + } + + private: + Params &p_; + RandomGenerator &rng_; + std::queue &q_; + double next_; +}; + +class Server : public DiscreteEventProcess { + public: + Server(Params &p, RandomGenerator &r, std::queue &q, long long &ms) + : p_(p), rng_(r), q_(q), ms_(ms) { + inv_.assign(p_.P + 1, 0); + } + void initialize(double start) override { + for (int i = 1; i <= p_.P; ++i) + inv_[i] = rng_.uniformInt(0, p_.K); + next_ = start + rng_.uniform(p_.V, p_.W); + } + double nextEventTime() const override { return next_; } + void handleEvent(double t) override { + if (!q_.empty()) { + int j = q_.front(); + q_.pop(); + if (inv_[j] > 0) + inv_[j]--; + else { + ms_++; + if (rng_.bernoulli(p_.p)) + inv_[j] = rng_.uniformInt(0, p_.Q); + } + } + next_ = t + rng_.uniform(p_.V, p_.W); + } + + private: + Params &p_; + RandomGenerator &rng_; + std::queue &q_; + long long &ms_; + std::vector inv_; + double next_; +}; + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + SELib::Params p; + p.T = parser.getDouble("T", 1.0); + p.H = parser.getDouble("H"); + p.M = parser.getInt("M"); + p.A = parser.getDouble("A"); + p.B = parser.getDouble("B"); + p.V = parser.getDouble("V"); + p.W = parser.getDouble("W"); + p.P = parser.getInt("P"); + p.K = parser.getInt("K"); + p.p = parser.getDouble("p"); + p.Q = parser.getInt("Q"); + + double totalRate = 0; + for (int i = 0; i < p.M; ++i) { + SELib::DiscreteEventSystem system; + std::queue q; + long long ms = 0; + system.emplaceProcess(p, rng, q); + system.emplaceProcess(p, rng, q, ms); + SELib::DiscreteEventSimulator sim(system); + sim.run(p.H); + totalRate += (double)ms / p.H; + } + + std::ofstream outFile("results.txt"); + outFile << "2025-11-03-Axel-Rubini-2158099" << std::endl; + outFile << "R " << totalRate / p.M << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-11-03/4/parameters.txt b/exams/AxelRubini/2025-11-03/4/parameters.txt new file mode 100644 index 0000000..4e4717c --- /dev/null +++ b/exams/AxelRubini/2025-11-03/4/parameters.txt @@ -0,0 +1,10 @@ +T 1.0 +H 10000 +M 10 +A 1.0 +B 2.1 +V 3.0 +W 4.0 +K 3 +P 5 +p 0.9 diff --git a/exams/AxelRubini/2025-11-03/4/results.txt b/exams/AxelRubini/2025-11-03/4/results.txt new file mode 100644 index 0000000..bcc273b --- /dev/null +++ b/exams/AxelRubini/2025-11-03/4/results.txt @@ -0,0 +1,2 @@ +2025-11-03-Axel-Rubini-2158099 +R 0.28484 diff --git a/exams/AxelRubini/2025-11-03/5/include/DES.hpp b/exams/AxelRubini/2025-11-03/5/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2025-11-03/5/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/5/include/Decision.hpp b/exams/AxelRubini/2025-11-03/5/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/5/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/5/include/Dynamics.hpp b/exams/AxelRubini/2025-11-03/5/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2025-11-03/5/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/5/include/Geometry.hpp b/exams/AxelRubini/2025-11-03/5/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2025-11-03/5/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/5/include/IO.hpp b/exams/AxelRubini/2025-11-03/5/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/5/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/5/include/Inventory.hpp b/exams/AxelRubini/2025-11-03/5/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/5/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/5/include/Queue.hpp b/exams/AxelRubini/2025-11-03/5/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/5/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/5/include/Random.hpp b/exams/AxelRubini/2025-11-03/5/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/5/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/5/include/Stat.hpp b/exams/AxelRubini/2025-11-03/5/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/5/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2025-11-03/5/main b/exams/AxelRubini/2025-11-03/5/main new file mode 100755 index 0000000..0aeb684 Binary files /dev/null and b/exams/AxelRubini/2025-11-03/5/main differ diff --git a/exams/AxelRubini/2025-11-03/5/main.cpp b/exams/AxelRubini/2025-11-03/5/main.cpp new file mode 100644 index 0000000..8093c45 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/5/main.cpp @@ -0,0 +1,137 @@ +#include "include/DES.hpp" +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace SELib { + +struct Params { + double T, H; + int M; + double A, B, V, W; + int P, K; + double p; + int Q; + double G; +}; + +class Customer : public DiscreteEventProcess { + public: + Customer(Params &p, RandomGenerator &r, std::queue &q) + : p_(p), rng_(r), q_(q) {} + void initialize(double start) override { + next_ = start + rng_.uniform(p_.A, p_.B); + } + double nextEventTime() const override { return next_; } + void handleEvent(double t) override { + q_.push(rng_.uniformInt(1, p_.P)); + next_ = t + rng_.uniform(p_.A, p_.B); + } + + private: + Params &p_; + RandomGenerator &rng_; + std::queue &q_; + double next_; +}; + +class Server : public DiscreteEventProcess { + public: + Server(Params &p, RandomGenerator &r, std::queue &q, long long &ms) + : p_(p), rng_(r), q_(q), ms_(ms) { + inv_.assign(p_.P + 1, 0); + } + void initialize(double start) override { + for (int i = 1; i <= p_.P; ++i) + inv_[i] = rng_.uniformInt(0, p_.K); + next_ = start + rng_.uniform(p_.V, p_.W); + } + double nextEventTime() const override { return next_; } + void handleEvent(double t) override { + if (!q_.empty()) { + int j = q_.front(); + q_.pop(); + if (inv_[j] > 0) + inv_[j]--; + else { + ms_++; + if (rng_.bernoulli(p_.p)) + inv_[j] = rng_.uniformInt(0, p_.Q); + } + } + next_ = t + rng_.uniform(p_.V, p_.W); + } + + private: + Params &p_; + RandomGenerator &rng_; + std::queue &q_; + long long &ms_; + std::vector inv_; + double next_; +}; + +double runMsims(double p_val, Params &p, RandomGenerator &rng) { + p.p = p_val; + double totalRate = 0; + for (int i = 0; i < p.M; ++i) { + DiscreteEventSystem system; + std::queue q; + long long ms = 0; + system.emplaceProcess(p, rng, q); + system.emplaceProcess(p, rng, q, ms); + DiscreteEventSimulator sim(system); + sim.run(p.H); + totalRate += (double)ms / p.H; + } + return totalRate / p.M; +} + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + SELib::Params p; + p.T = parser.getDouble("T", 1.0); + p.H = parser.getDouble("H"); + p.M = parser.getInt("M"); + p.A = parser.getDouble("A"); + p.B = parser.getDouble("B"); + p.V = parser.getDouble("V"); + p.W = parser.getDouble("W"); + p.P = parser.getInt("P"); + p.K = parser.getInt("K"); + p.Q = parser.getInt("Q"); + p.G = parser.getDouble("G"); + int budget = parser.getInt("B", 100); + + double bestP = 0, minJ = 1e18, bestR = 0; + for (int i = 0; i < budget; ++i) { + double p_val = rng.uniform(0.0, 1.0); + double R = SELib::runMsims(p_val, p, rng); + double J = p.G * p_val + R; + if (J < minJ) { + minJ = J; + bestP = p_val; + bestR = R; + } + } + + std::ofstream outFile("results.txt"); + outFile << "2025-11-03-Axel-Rubini-2158099" << std::endl; + outFile << "p " << bestP << std::endl; + outFile << "R " << bestR << std::endl; + outFile << "J " << minJ << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2025-11-03/5/parameters.txt b/exams/AxelRubini/2025-11-03/5/parameters.txt new file mode 100644 index 0000000..0a31361 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/5/parameters.txt @@ -0,0 +1,11 @@ +T 1.0 +H 10000 +M 10 +A 1.0 +B 2.1 +V 3.0 +W 4.0 +K 3 +P 5 +Q 10 +G 0.01 diff --git a/exams/AxelRubini/2025-11-03/5/results.txt b/exams/AxelRubini/2025-11-03/5/results.txt new file mode 100644 index 0000000..69c48e7 --- /dev/null +++ b/exams/AxelRubini/2025-11-03/5/results.txt @@ -0,0 +1,4 @@ +2025-11-03-Axel-Rubini-2158099 +p 0.764934 +R 0.05924 +J 0.0668893 diff --git a/exams/AxelRubini/2026-01-14/1/Makefile b/exams/AxelRubini/2026-01-14/1/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/1/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2026-01-14/1/include/DES.hpp b/exams/AxelRubini/2026-01-14/1/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2026-01-14/1/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/1/include/Decision.hpp b/exams/AxelRubini/2026-01-14/1/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/1/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/1/include/Dynamics.hpp b/exams/AxelRubini/2026-01-14/1/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2026-01-14/1/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/1/include/Geometry.hpp b/exams/AxelRubini/2026-01-14/1/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2026-01-14/1/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/1/include/IO.hpp b/exams/AxelRubini/2026-01-14/1/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/1/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/1/include/Inventory.hpp b/exams/AxelRubini/2026-01-14/1/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/1/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/1/include/Queue.hpp b/exams/AxelRubini/2026-01-14/1/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/1/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/1/include/Random.hpp b/exams/AxelRubini/2026-01-14/1/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/1/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/1/include/Stat.hpp b/exams/AxelRubini/2026-01-14/1/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/1/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/1/main b/exams/AxelRubini/2026-01-14/1/main new file mode 100755 index 0000000..35d0a37 Binary files /dev/null and b/exams/AxelRubini/2026-01-14/1/main differ diff --git a/exams/AxelRubini/2026-01-14/1/main.cpp b/exams/AxelRubini/2026-01-14/1/main.cpp new file mode 100644 index 0000000..150efd4 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/1/main.cpp @@ -0,0 +1,89 @@ +#include "include/Geometry.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace SELib { + +struct UAV { + Point3D pos; +}; + +double runOneSim( + double T, + double H, + int N, + double L, + double V, + double A, + double D, + double R, + RandomGenerator &rng +) { + std::vector uavs(N); + for (int i = 0; i < N; ++i) + uavs[i].pos = { + rng.uniform(-L, L), rng.uniform(-L, L), rng.uniform(-L, L) + }; + + long long totalCollisions = 0; + double nextCheckTime = 0.0; + + for (int t_idx = 0; (double)t_idx * T <= H + 1e-9; ++t_idx) { + double currentTime = (double)t_idx * T; + + // 1. Collision check every R seconds + if (currentTime >= nextCheckTime - 1e-9) { + for (int i = 0; i < N; ++i) { + for (int j = i + 1; j < N; ++j) { + if (uavs[i].pos.distanceTo(uavs[j].pos) <= D) + totalCollisions++; + } + } + nextCheckTime += R; + } + + // 2. Move + for (int i = 0; i < N; ++i) { + double x[3] = {uavs[i].pos.x, uavs[i].pos.y, uavs[i].pos.z}; + double new_x[3]; + for (int k = 0; k < 3; ++k) { + double prob = std::exp(-A * (x[k] + L) / (2.0 * L)); + double vk = rng.bernoulli(std::clamp(prob, 0.0, 1.0)) ? V : -V; + new_x[k] = x[k] + vk * T; + } + uavs[i].pos = {new_x[0], new_x[1], new_x[2]}; + } + } + return (double)totalCollisions / H; +} + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + double T = parser.getDouble("T"), H = parser.getDouble("H"), + L = parser.getDouble("L"), V = parser.getDouble("V"), + A = parser.getDouble("A"), D = parser.getDouble("D"), + R = parser.getDouble("R"); + int M = parser.getInt("M"), N = parser.getInt("N"); + + double totalRate = 0; + for (int i = 0; i < M; ++i) + totalRate += SELib::runOneSim(T, H, N, L, V, A, D, R, rng); + + std::ofstream outFile("results.txt"); + outFile << "2026-01-14-Axel-Rubini-2158099" << std::endl; + outFile << "C " << totalRate / M << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2026-01-14/1/parameters.txt b/exams/AxelRubini/2026-01-14/1/parameters.txt new file mode 100644 index 0000000..fc8513b --- /dev/null +++ b/exams/AxelRubini/2026-01-14/1/parameters.txt @@ -0,0 +1,9 @@ +M 1 +N 1 +L 100.1 +A 10.2 +T 1.0 +R 2.0 +D 3.0 +H 1000 +V 3.1 diff --git a/exams/AxelRubini/2026-01-14/1/results.txt b/exams/AxelRubini/2026-01-14/1/results.txt new file mode 100644 index 0000000..abd2ce9 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/1/results.txt @@ -0,0 +1,2 @@ +2026-01-14-Axel-Rubini-2158099 +C 29.7631 diff --git a/exams/AxelRubini/2026-01-14/2/Makefile b/exams/AxelRubini/2026-01-14/2/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/2/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2026-01-14/2/include/DES.hpp b/exams/AxelRubini/2026-01-14/2/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2026-01-14/2/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/2/include/Decision.hpp b/exams/AxelRubini/2026-01-14/2/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/2/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/2/include/Dynamics.hpp b/exams/AxelRubini/2026-01-14/2/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2026-01-14/2/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/2/include/Geometry.hpp b/exams/AxelRubini/2026-01-14/2/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2026-01-14/2/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/2/include/IO.hpp b/exams/AxelRubini/2026-01-14/2/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/2/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/2/include/Inventory.hpp b/exams/AxelRubini/2026-01-14/2/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/2/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/2/include/Queue.hpp b/exams/AxelRubini/2026-01-14/2/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/2/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/2/include/Random.hpp b/exams/AxelRubini/2026-01-14/2/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/2/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/2/include/Stat.hpp b/exams/AxelRubini/2026-01-14/2/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/2/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/2/main b/exams/AxelRubini/2026-01-14/2/main new file mode 100755 index 0000000..d117625 Binary files /dev/null and b/exams/AxelRubini/2026-01-14/2/main differ diff --git a/exams/AxelRubini/2026-01-14/2/main.cpp b/exams/AxelRubini/2026-01-14/2/main.cpp new file mode 100644 index 0000000..6a558d5 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/2/main.cpp @@ -0,0 +1,109 @@ +#include "include/Geometry.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace SELib { + +struct UAV { + Point3D pos; +}; + +double runOneSimAvoidance( + double T, + double H, + int N, + double L, + double V, + double D, + double R, + RandomGenerator &rng +) { + std::vector uavs(N); + for (int i = 0; i < N; ++i) + uavs[i].pos = { + rng.uniform(-L, L), rng.uniform(-L, L), rng.uniform(-L, L) + }; + + long long totalCollisions = 0; + double nextCheckTime = 0.0; + + for (int t_idx = 0; (double)t_idx * T <= H + 1e-9; ++t_idx) { + double currentTime = (double)t_idx * T; + + if (currentTime >= nextCheckTime - 1e-9) { + for (int i = 0; i < N; ++i) { + for (int j = i + 1; j < N; ++j) { + if (uavs[i].pos.distanceTo(uavs[j].pos) <= D) + totalCollisions++; + } + } + nextCheckTime += R; + } + + std::vector nextPos(N); + for (int i = 0; i < N; ++i) { + double minVal = 1e18; + std::vector bestMoves; + for (int bit = 0; bit < 8; ++bit) { + double v1 = (bit & 1) ? V : -V; + double v2 = (bit & 2) ? V : -V; + double v3 = (bit & 4) ? V : -V; + Point3D pot = { + uavs[i].pos.x + v1 * T, + uavs[i].pos.y + v2 * T, + uavs[i].pos.z + v3 * T + }; + + double val = 0; + for (int j = 0; j < N; ++j) { + if (i == j) + continue; + val += std::pow((pot.x - uavs[j].pos.x) / (2.0 * L), 2) + + std::pow((pot.y - uavs[j].pos.y) / (2.0 * L), 2) + + std::pow((pot.z - uavs[j].pos.z) / (2.0 * L), 2); + } + if (val < minVal - 1e-12) { + minVal = val; + bestMoves = {pot}; + } else if (std::abs(val - minVal) < 1e-12) { + bestMoves.push_back(pot); + } + } + nextPos[i] = + bestMoves[rng.uniformInt(0, (int)bestMoves.size() - 1)]; + } + for (int i = 0; i < N; ++i) + uavs[i].pos = nextPos[i]; + } + return (double)totalCollisions / H; +} + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + + double T = parser.getDouble("T"), H = parser.getDouble("H"), + L = parser.getDouble("L"), V = parser.getDouble("V"), + D = parser.getDouble("D"), R = parser.getDouble("R"); + int M = parser.getInt("M"), N = parser.getInt("N"); + + double totalRate = 0; + for (int i = 0; i < M; ++i) + totalRate += SELib::runOneSimAvoidance(T, H, N, L, V, D, R, rng); + + std::ofstream outFile("results.txt"); + outFile << "2026-01-14-Axel-Rubini-2158099" << std::endl; + outFile << "C " << totalRate / M << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2026-01-14/2/parameters.txt b/exams/AxelRubini/2026-01-14/2/parameters.txt new file mode 100644 index 0000000..202b988 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/2/parameters.txt @@ -0,0 +1,9 @@ +M 10 +N 1 +L 100 +A 10 +T 1.0 +R 2.0 +D 3.0 +H 1000 +V 2 diff --git a/exams/AxelRubini/2026-01-14/2/results.txt b/exams/AxelRubini/2026-01-14/2/results.txt new file mode 100644 index 0000000..407d3bf --- /dev/null +++ b/exams/AxelRubini/2026-01-14/2/results.txt @@ -0,0 +1,2 @@ +2026-01-14-Axel-Rubini-2158099 +C 396.958 diff --git a/exams/AxelRubini/2026-01-14/3/Makefile b/exams/AxelRubini/2026-01-14/3/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/3/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2026-01-14/3/include/DES.hpp b/exams/AxelRubini/2026-01-14/3/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2026-01-14/3/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/3/include/Decision.hpp b/exams/AxelRubini/2026-01-14/3/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/3/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/3/include/Dynamics.hpp b/exams/AxelRubini/2026-01-14/3/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2026-01-14/3/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/3/include/Geometry.hpp b/exams/AxelRubini/2026-01-14/3/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2026-01-14/3/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/3/include/IO.hpp b/exams/AxelRubini/2026-01-14/3/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/3/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/3/include/Inventory.hpp b/exams/AxelRubini/2026-01-14/3/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/3/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/3/include/Queue.hpp b/exams/AxelRubini/2026-01-14/3/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/3/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/3/include/Random.hpp b/exams/AxelRubini/2026-01-14/3/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/3/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/3/include/Stat.hpp b/exams/AxelRubini/2026-01-14/3/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/3/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/3/main b/exams/AxelRubini/2026-01-14/3/main new file mode 100755 index 0000000..98d7c59 Binary files /dev/null and b/exams/AxelRubini/2026-01-14/3/main differ diff --git a/exams/AxelRubini/2026-01-14/3/main.cpp b/exams/AxelRubini/2026-01-14/3/main.cpp new file mode 100644 index 0000000..5fd35ee --- /dev/null +++ b/exams/AxelRubini/2026-01-14/3/main.cpp @@ -0,0 +1,174 @@ +#include "include/DES.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include "include/Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +class CustomerProcess : public DiscreteEventProcess { + public: + CustomerProcess( + int pid, + int S, + int P, + int Q, + double A, + double B, + MessageBus &bus, + RandomGenerator &rng + ) + : pid_(pid), S_(S), P_(P), Q_(Q), A_(A), B_(B), bus_(bus), rng_(rng) {} + void initialize(double startTime) override { + nextTime_ = startTime + rng_.uniform(A_, B_); + } + double nextEventTime() const override { return nextTime_; } + void handleEvent(double currentTime) override { + Message m; + m.time = currentTime; + m.sender = pid_; + m.receiver = 100 + rng_.uniformInt(0, S_ - 1); + m.item = rng_.uniformInt(1, P_); + m.quantity = (double)rng_.uniformInt(1, Q_); + bus_.procToNet(pid_).push(m); + nextTime_ = currentTime + rng_.uniform(A_, B_); + } + + private: + int pid_, S_, P_, Q_; + double A_, B_, nextTime_; + MessageBus &bus_; + RandomGenerator &rng_; +}; + +class SupplierProcess : public DiscreteEventProcess { + public: + SupplierProcess( + int pid, + int S, + int P, + int Q, + double V, + double W, + MessageBus &bus, + RandomGenerator &rng + ) + : pid_(pid), S_(S), P_(P), Q_(Q), V_(V), W_(W), bus_(bus), rng_(rng) {} + void initialize(double startTime) override { + nextTime_ = startTime + rng_.uniform(V_, W_); + } + double nextEventTime() const override { return nextTime_; } + void handleEvent(double currentTime) override { + Message m; + m.time = currentTime; + m.sender = pid_; + m.receiver = 100 + rng_.uniformInt(0, S_ - 1); + m.item = rng_.uniformInt(1, P_); + m.quantity = (double)rng_.uniformInt(0, Q_); + bus_.procToNet(pid_).push(m); + nextTime_ = currentTime + rng_.uniform(V_, W_); + } + + private: + int pid_, S_, P_, Q_; + double V_, W_, nextTime_; + MessageBus &bus_; + RandomGenerator &rng_; +}; + +class ServerProcess : public DiscreteEventProcess { + public: + ServerProcess( + int pid, + int C, + int F, + int P, + int Q, + MessageBus &bus, + RandomGenerator &rng, + long long &ms + ) + : pid_(pid), C_(C), F_(F), P_(P), Q_(Q), bus_(bus), rng_(rng), ms_(ms) { + inv_.assign(P + 1, 0); + } + void initialize(double startTime) override { + for (int i = 1; i <= P_; ++i) + inv_[i] = rng_.uniformInt(0, Q_); + nextTime_ = startTime + 0.1; + } + double nextEventTime() const override { return nextTime_; } + void handleEvent(double currentTime) override { + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + bool isSupply = (m.sender >= C_ && m.sender < C_ + F_); + if (isSupply) { + inv_[m.item] += (int)m.quantity; + } else { + int req = (int)m.quantity; + if (inv_[m.item] < req) + ms_++; + int prov = std::min(req, inv_[m.item]); + inv_[m.item] -= prov; + } + } + nextTime_ = currentTime + 0.1; // Periodic Polling + } + + private: + int pid_, C_, F_, P_, Q_; + MessageBus &bus_; + RandomGenerator &rng_; + long long &ms_; + double nextTime_; + std::vector inv_; +}; + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + double H = parser.getDouble("H"); + int M = parser.getInt("M"), C = parser.getInt("C"), S = parser.getInt("S"), + P = parser.getInt("P"), F_suppliers = parser.getInt("F"), + Q = parser.getInt("Q"); + double A = parser.getDouble("A"), B = parser.getDouble("B"), + V = parser.getDouble("V"), W = parser.getDouble("W"); + + SELib::Statistics stats; + for (int m = 0; m < M; ++m) { + SELib::MessageBus bus(200); + SELib::DiscreteEventSystem system; + long long ms = 0; + for (int i = 0; i < C; ++i) + system.emplaceProcess( + i, S, P, Q, A, B, bus, rng + ); + for (int i = 0; i < F_suppliers; ++i) + system.emplaceProcess( + C + i, S, P, Q, V, W, bus, rng + ); + for (int i = 0; i < S; ++i) + system.emplaceProcess( + 100 + i, C, F_suppliers, P, Q, bus, rng, ms + ); + system.emplaceProcess(bus, rng, 0.01); + SELib::DiscreteEventSimulator sim(system); + sim.run(H); + stats.addSample((double)ms / H); + } + std::ofstream outFile("results.txt"); + outFile << "2026-01-14-Axel-Rubini-2158099" << std::endl; + outFile << "R " << std::fixed << std::setprecision(6) << stats.mean() + << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2026-01-14/3/out.log b/exams/AxelRubini/2026-01-14/3/out.log new file mode 100644 index 0000000..e69de29 diff --git a/exams/AxelRubini/2026-01-14/3/parameters.txt b/exams/AxelRubini/2026-01-14/3/parameters.txt new file mode 100644 index 0000000..5033709 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/3/parameters.txt @@ -0,0 +1,14 @@ +T 1.0 +M 100 +H 1000 +C 50 +S 10 +F 1 +Q 1 +A 1.5 +B 2.5 +V 3.1 +W 5.1 +P 1 + + diff --git a/exams/AxelRubini/2026-01-14/3/results.txt b/exams/AxelRubini/2026-01-14/3/results.txt new file mode 100644 index 0000000..61192ca --- /dev/null +++ b/exams/AxelRubini/2026-01-14/3/results.txt @@ -0,0 +1,2 @@ +2026-01-14-Axel-Rubini-2158099 +R 24.683300 diff --git a/exams/AxelRubini/2026-01-14/4/Makefile b/exams/AxelRubini/2026-01-14/4/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/4/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2026-01-14/4/include/DES.hpp b/exams/AxelRubini/2026-01-14/4/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2026-01-14/4/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/4/include/Decision.hpp b/exams/AxelRubini/2026-01-14/4/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/4/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/4/include/Dynamics.hpp b/exams/AxelRubini/2026-01-14/4/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2026-01-14/4/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/4/include/Geometry.hpp b/exams/AxelRubini/2026-01-14/4/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2026-01-14/4/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/4/include/IO.hpp b/exams/AxelRubini/2026-01-14/4/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/4/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/4/include/Inventory.hpp b/exams/AxelRubini/2026-01-14/4/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/4/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/4/include/Queue.hpp b/exams/AxelRubini/2026-01-14/4/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/4/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/4/include/Random.hpp b/exams/AxelRubini/2026-01-14/4/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/4/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/4/include/Stat.hpp b/exams/AxelRubini/2026-01-14/4/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/4/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/4/main b/exams/AxelRubini/2026-01-14/4/main new file mode 100755 index 0000000..1929b3e Binary files /dev/null and b/exams/AxelRubini/2026-01-14/4/main differ diff --git a/exams/AxelRubini/2026-01-14/4/main.cpp b/exams/AxelRubini/2026-01-14/4/main.cpp new file mode 100644 index 0000000..755b654 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/4/main.cpp @@ -0,0 +1,202 @@ +#include "include/DES.hpp" +#include "include/Decision.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include "include/Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +class CustomerProcess : public DiscreteEventProcess { + public: + CustomerProcess( + int pid, + int S, + int P, + int Q, + double A, + double B, + MessageBus &bus, + RandomGenerator &rng + ) + : pid_(pid), S_(S), P_(P), Q_(Q), A_(A), B_(B), bus_(bus), rng_(rng) {} + void initialize(double startTime) override { + nextTime_ = startTime + rng_.uniform(A_, B_); + } + double nextEventTime() const override { return nextTime_; } + void handleEvent(double currentTime) override { + Message m; + m.time = currentTime; + m.sender = pid_; + m.receiver = 100 + rng_.uniformInt(0, S_ - 1); + m.item = rng_.uniformInt(1, P_); + m.quantity = (double)rng_.uniformInt(1, Q_); + bus_.procToNet(pid_).push(m); + nextTime_ = currentTime + rng_.uniform(A_, B_); + } + + private: + int pid_, S_, P_, Q_; + double A_, B_, nextTime_; + MessageBus &bus_; + RandomGenerator &rng_; +}; + +class SupplierProcess : public DiscreteEventProcess { + public: + SupplierProcess( + int pid, + int S, + int P, + int Q, + double V, + double W, + MessageBus &bus, + RandomGenerator &rng + ) + : pid_(pid), S_(S), P_(P), Q_(Q), V_(V), W_(W), bus_(bus), rng_(rng) {} + void initialize(double startTime) override { + nextTime_ = startTime + rng_.uniform(V_, W_); + } + double nextEventTime() const override { return nextTime_; } + void handleEvent(double currentTime) override { + Message m; + m.time = currentTime; + m.sender = pid_; + m.receiver = 100 + rng_.uniformInt(0, S_ - 1); + m.item = -rng_.uniformInt(1, P_); + m.quantity = (double)rng_.uniformInt(0, Q_); + bus_.procToNet(pid_).push(m); + nextTime_ = currentTime + rng_.uniform(V_, W_); + } + + private: + int pid_, S_, P_, Q_; + double V_, W_, nextTime_; + MessageBus &bus_; + RandomGenerator &rng_; +}; + +class ServerProcess : public DiscreteEventProcess { + public: + ServerProcess( + int pid, + int C, + int F, + int P, + int Q, + MessageBus &bus, + RandomGenerator &rng, + long long &ms + ) + : pid_(pid), C_(C), F_(F), P_(P), Q_(Q), bus_(bus), rng_(rng), ms_(ms) { + inv_.assign(P + 1, 0); + } + void initialize(double startTime) override { + for (int i = 1; i <= P_; ++i) + inv_[i] = rng_.uniformInt(0, Q_); + nextTime_ = startTime + 0.1; + } + double nextEventTime() const override { return nextTime_; } + void handleEvent(double currentTime) override { + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + if (m.item < 0) { + inv_[-m.item] += (int)m.quantity; + } else { + int req = (int)m.quantity; + if (inv_[m.item] < req) + ms_++; + int prov = std::min(req, inv_[m.item]); + inv_[m.item] -= prov; + } + } + nextTime_ = currentTime + 0.1; + } + + private: + int pid_, C_, F_, P_, Q_; + MessageBus &bus_; + RandomGenerator &rng_; + long long &ms_; + double nextTime_; + std::vector inv_; +}; + +double runMsims( + int F_count, + double H, + int M, + int C, + int S, + int P, + int Q, + double A, + double B, + double V, + double W, + RandomGenerator &rng +) { + Statistics stats; + for (int m = 0; m < M; ++m) { + MessageBus bus(200); + DiscreteEventSystem system; + long long ms = 0; + for (int i = 0; i < C; ++i) + system.emplaceProcess(i, S, P, Q, A, B, bus, rng); + for (int i = 0; i < F_count; ++i) + system.emplaceProcess( + C + i, S, P, Q, V, W, bus, rng + ); + for (int i = 0; i < S; ++i) + system.emplaceProcess( + 100 + i, C, F_count, P, Q, bus, rng, ms + ); + system.emplaceProcess(bus, rng, 0.01); + DiscreteEventSimulator sim(system); + sim.run(H); + stats.addSample((double)ms / H); + } + return stats.mean(); +} + +} // namespace SELib + +int main() { + SELib::RandomGenerator rng(0); + SELib::ParameterParser parser; + if (!parser.parseFile("parameters.txt")) + return 1; + double H = parser.getDouble("H"); + int M = parser.getInt("M"), C = parser.getInt("C"), S = parser.getInt("S"), + P = parser.getInt("P"), Q = parser.getInt("Q"), G = parser.getInt("G"); + double A = parser.getDouble("A"), B = parser.getDouble("B"), + V = parser.getDouble("V"), W = parser.getDouble("W"), + a_cost = parser.getDouble("a"), b_cost = parser.getDouble("b"); + + int bestF = 0; + double minJ = 1e18, bestR = 0; + for (int i = 0; i < G; ++i) { + int F_cand = rng.uniformInt(0, 100); + double R = SELib::runMsims(F_cand, H, M, C, S, P, Q, A, B, V, W, rng); + double J = a_cost * F_cand + b_cost * R; + if (J < minJ) { + minJ = J; + bestF = F_cand; + bestR = R; + } + } + std::ofstream outFile("results.txt"); + outFile << "2026-01-14-Axel-Rubini-2158099" << std::endl; + outFile << "R " << bestR << std::endl; + outFile << "F " << bestF << std::endl; + outFile << "J " << minJ << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2026-01-14/4/parameters.txt b/exams/AxelRubini/2026-01-14/4/parameters.txt new file mode 100644 index 0000000..b9dcb6d --- /dev/null +++ b/exams/AxelRubini/2026-01-14/4/parameters.txt @@ -0,0 +1,17 @@ +T 1.0 +M 10 +H 1000 +C 50 +S 1 +F 1 +Q 1 +A 1.5 +B 2.5 +V 1.1 +W 2.1 +P 1 +G 100 +a 0.9 +b 0.1 + + diff --git a/exams/AxelRubini/2026-01-14/4/results.txt b/exams/AxelRubini/2026-01-14/4/results.txt new file mode 100644 index 0000000..12879f7 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/4/results.txt @@ -0,0 +1,4 @@ +2026-01-14-Axel-Rubini-2158099 +R 24.8167 +F 0 +J 2.48167 diff --git a/exams/AxelRubini/2026-01-14/5/Makefile b/exams/AxelRubini/2026-01-14/5/Makefile new file mode 100644 index 0000000..6910fc5 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/5/Makefile @@ -0,0 +1,10 @@ +CC=g++ +CFLAGS=-std=c++23 -O3 -Wall + +all: main + +main: main.cpp + $(CC) $(CFLAGS) main.cpp -o main + +clean: + rm -f main *.o diff --git a/exams/AxelRubini/2026-01-14/5/include/DES.hpp b/exams/AxelRubini/2026-01-14/5/include/DES.hpp new file mode 100644 index 0000000..1706b8f --- /dev/null +++ b/exams/AxelRubini/2026-01-14/5/include/DES.hpp @@ -0,0 +1,212 @@ + +#pragma once + +#include "Random.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// --------------------------------------------------------------------------- +// Messaggio +// --------------------------------------------------------------------------- +struct Message { + double time{0.0}; + int sender{-1}; + int receiver{-1}; + int item{0}; + double quantity{0.0}; +}; + +// --------------------------------------------------------------------------- +// Bus di messaggi: process -> network, network -> process +// --------------------------------------------------------------------------- +class MessageBus { +public: + explicit MessageBus(std::size_t n = 0) { resize(n); } + + void resize(std::size_t n) { + procToNet_.assign(n, {}); + netToProc_.assign(n, {}); + } + + std::size_t size() const { return procToNet_.size(); } + + std::queue &procToNet(int pid) { return procToNet_.at(pid); } + std::queue &netToProc(int pid) { return netToProc_.at(pid); } + const std::queue &procToNet(int pid) const { + return procToNet_.at(pid); + } + const std::queue &netToProc(int pid) const { + return netToProc_.at(pid); + } + +private: + std::vector> procToNet_; + std::vector> netToProc_; +}; + +// --------------------------------------------------------------------------- +// Processo base a eventi discreti +// --------------------------------------------------------------------------- +class DiscreteEventProcess { +public: + virtual ~DiscreteEventProcess() = default; + virtual void initialize(double startTime) = 0; + virtual double nextEventTime() const = 0; + virtual void handleEvent(double currentTime) = 0; +}; + +// --------------------------------------------------------------------------- +// NetworkRouter: inoltra i messaggi P->N->P +// --------------------------------------------------------------------------- +class NetworkRouter : public DiscreteEventProcess { +public: + NetworkRouter(MessageBus &bus, RandomGenerator &rng, double scanPeriod = 0.1) + : bus_(bus), rng_(rng), scanPeriod_(scanPeriod), time_(0.0), + nextScanTime_(0.0), scanned_(0) {} + + void initialize(double startTime) override { + time_ = startTime; + nextScanTime_ = startTime + scanPeriod_; + + const int n = static_cast(bus_.size()); + if (n <= 0) { + throw std::runtime_error( + "NetworkRouter: no processes configured in MessageBus"); + } + + scanner_.resize(n); + for (int i = 0; i < n; ++i) { + scanner_[i] = i; + } + reshuffleScanner(); + scanned_ = 0; + } + + double nextEventTime() const override { return nextScanTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextScanTime_) + return; + + const int n = static_cast(scanner_.size()); + if (n == 0) + return; + + if (scanned_ >= n) { + scanned_ = 0; + reshuffleScanner(); + } + + const int channel = scanner_[scanned_++]; + auto &fromQueue = bus_.procToNet(channel); + + if (!fromQueue.empty()) { + Message m = fromQueue.front(); + fromQueue.pop(); + + if (m.receiver < 0 || m.receiver >= static_cast(bus_.size())) { + throw std::runtime_error("NetworkRouter: invalid receiver index"); + } + + bus_.netToProc(m.receiver).push(m); + } + + nextScanTime_ = currentTime + scanPeriod_; + } + +private: + void reshuffleScanner() { + auto &eng = rng_.engine(); // serve engine() in RandomGenerator + std::shuffle(scanner_.begin(), scanner_.end(), eng); + } + + MessageBus &bus_; + RandomGenerator &rng_; + + double scanPeriod_; + double time_; + double nextScanTime_; + std::vector scanner_; + int scanned_; +}; + +// --------------------------------------------------------------------------- +// Sistema DES: collezione di processi +// --------------------------------------------------------------------------- +class DiscreteEventSystem { +public: + template + Proc &emplaceProcess(Args &&...args) { + auto ptr = std::make_unique(std::forward(args)...); + Proc &ref = *ptr; + processes_.push_back(std::move(ptr)); + return ref; + } + + void initialize(double startTime = 0.0) { + for (auto &p : processes_) { + p->initialize(startTime); + } + } + + double nextEventTime() const { + if (processes_.empty()) { + return std::numeric_limits::infinity(); + } + + double tMin = std::numeric_limits::infinity(); + for (const auto &p : processes_) { + double t = p->nextEventTime(); + if (t < tMin) + tMin = t; + } + return tMin; + } + + void handleEvent(double currentTime) { + for (auto &p : processes_) { + if (p->nextEventTime() <= currentTime) { + p->handleEvent(currentTime); + } + } + } + +private: + std::vector> processes_; +}; + +// --------------------------------------------------------------------------- +// Simulatore DES +// --------------------------------------------------------------------------- +class DiscreteEventSimulator { +public: + explicit DiscreteEventSimulator(DiscreteEventSystem &system) + : system_(system), currentTime_(0.0) {} + + void run(double horizon) { + system_.initialize(0.0); + currentTime_ = 0.0; + + while (currentTime_ < horizon) { + double nextTime = system_.nextEventTime(); + if (nextTime == std::numeric_limits::infinity()) + break; + currentTime_ = nextTime; + system_.handleEvent(currentTime_); + } + } + + double currentTime() const { return currentTime_; } + +private: + DiscreteEventSystem &system_; + double currentTime_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/5/include/Decision.hpp b/exams/AxelRubini/2026-01-14/5/include/Decision.hpp new file mode 100644 index 0000000..4d37cf0 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/5/include/Decision.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "Random.hpp" +#include "Stat.hpp" +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 7. MARKOV CHAIN +// ============================================================================ + +// MarkovChain: Implements a discrete-time Markov chain. +// Purpose: +// - Simulates stochastic processes with defined states and transition +// probabilities. +// - Useful in modeling random systems and computing related probabilities. +// Usage: +// MarkovChain mc(3, rng); +// mc.setTransition(0, 1, 0.5); +// mc.setTransition(0, 2, 0.5); +// mc.setState(0); +// int nextState = mc.step(); +class MarkovChain { +private: + int numStates; + std::vector> transitionMatrix; + int currentState; + RandomGenerator &rng; + +public: + MarkovChain(int states, RandomGenerator &generator) + : numStates(states), currentState(0), rng(generator) { + transitionMatrix.resize(states, std::vector(states, 0.0)); + } + + void setTransition(int from, int to, double probability) { + if (from >= 0 && from < numStates && to >= 0 && to < numStates) { + transitionMatrix[from][to] = probability; + } + } + + void setState(int state) { + if (state >= 0 && state < numStates) { + currentState = state; + } + } + + int getState() const { return currentState; } + + int step() { + currentState = rng.discrete(transitionMatrix[currentState]); + return currentState; + } + + double getTransitionProb(int from, int to) const { + return transitionMatrix[from][to]; + } + + double expectedSojournTime(int state, double timeStep = 1.0) const { + double pii = transitionMatrix[state][state]; + if (pii >= 1.0) + return std::numeric_limits::infinity(); + return timeStep / (1.0 - pii); + } +}; + +// ============================================================================ +// 8. MDP +// ============================================================================ + +// MDP: Implements a Markov Decision Process (MDP) model. +// Purpose: +// - Simulate decision-making processes with states, transitions, and +// associated costs. +// - Calculate the total cost to reach a target state from a starting state. +// Usage: +// - Add transitions between states with associated probabilities and costs +// using `addTransition`. +// - Use `step` to simulate a single transition and calculate its cost. +// - Use `simulate` to estimate the total cost from a start to an end state. +class MDP { +private: + int numStates; + std::map>>> + transitions; + RandomGenerator &rng; + +public: + MDP(int states, RandomGenerator &generator) + : numStates(states), rng(generator) {} + + void addTransition(int from, int to, double probability, double cost) { + transitions[from].push_back({to, {probability, cost}}); + } + + std::pair step(int currentState) { + if (transitions.find(currentState) == transitions.end()) { + return {currentState, 0.0}; + } + + std::vector probs; + for (const auto &t : transitions[currentState]) { + probs.push_back(t.second.first); + } + + int idx = rng.discrete(probs); + int nextState = transitions[currentState][idx].first; + double cost = transitions[currentState][idx].second.second; + + return {nextState, cost}; + } + + double simulate(int startState, int endState) { + int state = startState; + double totalCost = 0.0; + int maxSteps = 100000; + + for (int step = 0; step < maxSteps && state != endState; step++) { + auto [nextState, cost] = this->step(state); + totalCost += cost; + state = nextState; + } + return totalCost; + } +}; + +// ============================================================================ +// 10. MONTE CARLO SIMULATOR +// ============================================================================ + +// MonteCarloSimulator: A utility for running Monte Carlo simulations. +// Purpose: +// - Estimate statistical metrics through repeated random sampling. +// - Provides methods to calculate mean results and probabilities of events. +// Usage: +// MonteCarloSimulator simulator(1000); +// double estimate = simulator.estimate([](int) { return some_random_value; }); +// double probability = simulator.estimateProbability([](int) { return +// some_event_occurs; }); +class MonteCarloSimulator { +private: + Statistics stats; + int numSimulations; + +public: + MonteCarloSimulator(int simulations = 1000) : numSimulations(simulations) {} + + template double estimate(SimFunc simulation) { + stats.clear(); + for (int i = 0; i < numSimulations; i++) { + double result = simulation(i); + stats.addSample(result); + } + return stats.mean(); + } + + template double estimateProbability(SimFunc simulation) { + int successes = 0; + for (int i = 0; i < numSimulations; i++) { + if (simulation(i)) + successes++; + } + return static_cast(successes) / numSimulations; + } + + const Statistics &getStatistics() const { return stats; } +}; + +// ============================================================================ +// 13. OPTIMIZATION UTILITIES +// ============================================================================ + +// Optimizer: Utility class offering optimization techniques such as random +// search and grid search. Purpose: +// - Explore parameter spaces to maximize or minimize an objective function. +// - Supports both continuous and discrete search spaces. +// Usage: +// - Use `randomSearch` for stochastic optimization over a defined range. +// - Use `gridSearch` for systematic exploration in evenly spaced steps. +// Example: +// auto [bestParam, bestValue] = Optimizer::randomSearch(objectiveFunction, +// 0.0, 10.0, 100, rng); auto [bestInt, bestIntValue] = +// Optimizer::randomSearchInt(objectiveFunction, 0, 10, 50, rng); +class Optimizer { +public: + template + static std::pair + randomSearch(ObjFunc objective, double minValue, double maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + for (int i = 0; i < budget; i++) { + double param = rng.uniform(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair + randomSearchInt(ObjFunc objective, int minValue, int maxValue, int budget, + RandomGenerator &rng, bool maximize = true) { + int bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + + if (budget <= 0) { + throw std::invalid_argument("randomSearchInt: budget must be positive."); + } + + if (minValue >= maxValue) { + throw std::invalid_argument( + "randomSearchInt: minValue must be less than maxValue."); + } + + for (int i = 0; i < budget; i++) { + int param = rng.uniformInt(minValue, maxValue); + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } + + template + static std::pair gridSearch(ObjFunc objective, + double minValue, double maxValue, + int steps, bool maximize = true) { + double bestParam = minValue; + double bestValue = maximize ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + double stepSize = (maxValue - minValue) / steps; + + if (steps <= 0) { + throw std::invalid_argument("gridSearch: steps must be positive."); + } + + for (int i = 0; i <= steps; i++) { + double param = minValue + i * stepSize; + double value = objective(param); + + if ((maximize && value > bestValue) || (!maximize && value < bestValue)) { + bestValue = value; + bestParam = param; + } + } + return {bestParam, bestValue}; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/5/include/Dynamics.hpp b/exams/AxelRubini/2026-01-14/5/include/Dynamics.hpp new file mode 100644 index 0000000..044176a --- /dev/null +++ b/exams/AxelRubini/2026-01-14/5/include/Dynamics.hpp @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +namespace SELib { +// ============================================================================ +// 3. ODE SOLVER (Runge-Kutta 4th order) +// ============================================================================ + +using StateVector = std::vector; +using DerivativeFunction = std::function; + +// ODESolver: Implements numerical methods for solving Ordinary Differential +// Equations (ODEs). Purpose: +// - Provides tools for simulating dynamic systems modeled by ODEs. +// - Includes methods such as Runge-Kutta 4th order and Euler's method for +// integration. +// Usage: +// - Pass a derivative function that defines the ODE system. +// - Use `rk4Step` for higher accuracy or `eulerStep` for simpler systems. +// Example: +// StateVector nextState = ODESolver::rk4Step(0.0, currentState, controlInputs, +// dt, dynamicsFunction); +class ODESolver { +public: + static StateVector rk4Step(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector k1 = f(t, x, u); + StateVector x2 = addScaled(x, k1, dt / 2.0); + StateVector k2 = f(t + dt / 2.0, x2, u); + StateVector x3 = addScaled(x, k2, dt / 2.0); + StateVector k3 = f(t + dt / 2.0, x3, u); + StateVector x4 = addScaled(x, k3, dt); + StateVector k4 = f(t + dt, x4, u); + + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]); + } + return xNew; + } + + static StateVector eulerStep(double t, const StateVector &x, + const StateVector &u, double dt, + DerivativeFunction f) { + StateVector dx = f(t, x, u); + StateVector xNew = x; + for (size_t i = 0; i < x.size(); i++) { + xNew[i] += dx[i] * dt; + } + return xNew; + } + +private: + static StateVector addScaled(const StateVector &x, const StateVector &dx, + double scale) { + StateVector result = x; + for (size_t i = 0; i < x.size(); i++) { + result[i] += dx[i] * scale; + } + return result; + } +}; + +// ============================================================================ +// 4. PID CONTROLLER +// ============================================================================ + +// PIDController: Implements a Proportional-Integral-Derivative (PID) +// controller. Purpose: +// - Automatically adjusts a control signal to minimize the difference between +// a desired setpoint and a measured value. +// - Used in control systems to regulate processes like temperature, speed, or +// position. +// Usage: +// - Create an instance with proportional (Kp), integral (Ki), and derivative +// (Kd) gains, along with a time step. +// - Use `compute` to calculate the control signal based on the setpoint and +// measurement. +// Example: +// PIDController pid(1.0, 0.1, 0.01, 0.1); +// double controlSignal = pid.compute(desiredValue, actualValue); +class PIDController { +private: + double kp, ki, kd; + double integral; + double prevError; + double dt; + +public: + PIDController(double Kp, double Ki, double Kd, double timeStep) + : kp(Kp), ki(Ki), kd(Kd), integral(0), prevError(0), dt(timeStep) {} + + double compute(double setpoint, double measurement) { + double error = setpoint - measurement; + integral += error * dt; + double derivative = (error - prevError) / dt; + prevError = error; + return kp * error + ki * integral + kd * derivative; + } + + void reset() { + integral = 0; + prevError = 0; + } + + void setGains(double Kp, double Ki, double Kd) { + kp = Kp; + ki = Ki; + kd = Kd; + } +}; + +// ============================================================================ +// 5. CONTINUOUS TIME SIMULATION +// ============================================================================ + +// ContinuousTimeSimulation: A base class for simulating continuous-time dynamic +// systems. Purpose: +// - Provides a framework for simulating systems characterized by continuous +// dynamics over time. +// - Integrates system dynamics using a Runge-Kutta 4th order solver. +// Usage: +// - Derive from this class and implement the `dynamics`, `computeControl`, +// `initialize`, and `finalize` methods. +// - Customize the `onStep` method for per-step side effects, if required. +// Example: +// class MySimulation : public ContinuousTimeSimulation { +// // Implement the required virtual methods. +// }; +// MySimulation sim(dt, horizon, initialState); +// sim.run(); +class ContinuousTimeSimulation { +protected: + double currentTime; + double timeStep; + double horizon; + StateVector state; + StateVector control; + +public: + ContinuousTimeSimulation(double dt, double H, const StateVector &initialState) + : currentTime(0.0), timeStep(dt), horizon(H), state(initialState) { + control.resize(initialState.size(), 0.0); + } + + virtual ~ContinuousTimeSimulation() = default; + + virtual StateVector dynamics(double t, const StateVector &x, + const StateVector &u) = 0; + virtual StateVector computeControl(double t, const StateVector &x) = 0; + virtual void initialize() = 0; + virtual void onStep() {} + virtual void finalize() = 0; + + void run() { + initialize(); + while (currentTime < horizon) { + control = computeControl(currentTime, state); + state = ODESolver::rk4Step( + currentTime, state, control, timeStep, + [this](double t, const StateVector &x, const StateVector &u) { + return this->dynamics(t, x, u); + }); + currentTime += timeStep; + onStep(); + } + finalize(); + } + + double getCurrentTime() const { return currentTime; } + const StateVector &getState() const { return state; } + const StateVector &getControl() const { return control; } +}; + +// ============================================================================ +// 6. SAMPLE AND HOLD +// ============================================================================ + +// SampleAndHold: A utility to store a value and update it periodically. +// Purpose: +// - Maintain a value that gets updated periodically based on a time interval. +// - Useful for sampling data and ensuring updates happen at fixed intervals. +// Usage: +// SampleAndHold sampler(5.0, 0); +// if (sampler.shouldUpdate(currentTime)) { +// sampler.update(currentTime, newValue); +// } +// int currentValue = sampler.getValue(); +template class SampleAndHold { +private: + T value; + double lastUpdateTime; + double samplePeriod; + +public: + SampleAndHold(double period, const T &initialValue = T()) + : value(initialValue), lastUpdateTime(0), samplePeriod(period) {} + + bool shouldUpdate(double currentTime) const { + return (currentTime - lastUpdateTime) >= samplePeriod; + } + + void update(double currentTime, const T &newValue) { + value = newValue; + lastUpdateTime = currentTime; + } + + const T &getValue() const { return value; } + void reset() { lastUpdateTime = 0; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/5/include/Geometry.hpp b/exams/AxelRubini/2026-01-14/5/include/Geometry.hpp new file mode 100644 index 0000000..525a91d --- /dev/null +++ b/exams/AxelRubini/2026-01-14/5/include/Geometry.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 14. GEOMETRIC UTILITIES +// ============================================================================ + +// Point2D: Represents a point or vector in 2D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point2D p1(1.0, 2.0), p2(2.0, 3.0); +// double dist = p1.distanceTo(p2); +// Point2D sum = p1 + p2; +struct Point2D { + double x, y; + + Point2D(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} + + double distanceTo(const Point2D &other) const { + double dx = x - other.x; + double dy = y - other.y; + return std::sqrt(dx * dx + dy * dy); + } + + Point2D operator+(const Point2D &other) const { + return Point2D(x + other.x, y + other.y); + } + + Point2D operator-(const Point2D &other) const { + return Point2D(x - other.x, y - other.y); + } +}; + +// Point3D: Represents a point or vector in 3D space. +// Purpose: +// - Provides basic operations like addition, subtraction, and distance +// calculation. +// Usage: +// Point3D p1(1.0, 2.0, 3.0), p2(2.0, 3.0, 4.0); +// double dist = p1.distanceTo(p2); +// Point3D sum = p1 + p2; +struct Point3D { + double x, y, z; + + Point3D(double x_ = 0, double y_ = 0, double z_ = 0) : x(x_), y(y_), z(z_) {} + + double distanceTo(const Point3D &other) const { + double dx = x - other.x; + double dy = y - other.y; + double dz = z - other.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + Point3D operator+(const Point3D &other) const { + return Point3D(x + other.x, y + other.y, z + other.z); + } + + Point3D operator-(const Point3D &other) const { + return Point3D(x - other.x, y - other.y, z - other.z); + } +}; + +// ============================================================================ +// 19. COLLISION DETECTOR +// ============================================================================ + +// CollisionDetector: A utility class for detecting collisions between points +// in space. Purpose: +// - Counts the number of collisions within a given distance threshold. +// - Retrieves pairs of colliding points. +// Usage: +// std::vector points = {Point2D(0, 0), Point2D(1, 1)}; +// int numCollisions = CollisionDetector::countCollisions(points, 1.5); +// auto collidingPairs = CollisionDetector::getCollidingPairs(points, 1.5); +class CollisionDetector { +public: + template + static int countCollisions(const std::vector &positions, + double threshold) { + int collisions = 0; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + collisions++; + } + } + } + return collisions; + } + + template + static std::vector> + getCollidingPairs(const std::vector &positions, double threshold) { + std::vector> pairs; + for (size_t i = 0; i < positions.size(); i++) { + for (size_t j = i + 1; j < positions.size(); j++) { + if (positions[i].distanceTo(positions[j]) <= threshold) { + pairs.push_back({i, j}); + } + } + } + return pairs; + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/5/include/IO.hpp b/exams/AxelRubini/2026-01-14/5/include/IO.hpp new file mode 100644 index 0000000..1a79cc6 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/5/include/IO.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace SELib { + +// ============================================================================ +// 11. PARAMETER PARSER +// ============================================================================ + +// ParameterParser: A utility to parse and retrieve parameters from +// configuration files. Purpose: +// - Reads a file and stores key-value pairs for parameters. +// - Allows retrieval of parameters as integers, doubles, or strings. +// - Provides structured data for advanced parsing. +// Usage: +// ParameterParser parser; +// if (parser.parseFile("config.txt")) { +// int param = parser.getInt("example_key", 42); +// } +class ParameterParser { +private: + std::map params; + std::vector> structuredData; + +public: + bool parseFile(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) + return false; + + std::string line; + while (std::getline(file, line)) { + if (line.empty()) + continue; + + std::istringstream iss(line); + std::string key; + iss >> key; + + std::string value; + std::getline(iss, value); + if (!value.empty() && value[0] == ' ') { + value = value.substr(1); + } + + params[key] = value; + + std::vector tokens; + std::istringstream tokenStream(line); + std::string token; + while (tokenStream >> token) { + tokens.push_back(token); + } + structuredData.push_back(tokens); + } + return true; + } + + int getInt(const std::string &key, int defaultValue = 0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stoi(it->second); + } + return defaultValue; + } + + double getDouble(const std::string &key, double defaultValue = 0.0) const { + auto it = params.find(key); + if (it != params.end()) { + return std::stod(it->second); + } + return defaultValue; + } + + std::string getString(const std::string &key, + const std::string &defaultValue = "") const { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return defaultValue; + } + + const std::vector> &getStructuredData() const { + return structuredData; + } + + bool hasKey(const std::string &key) const { + return params.find(key) != params.end(); + } +}; + +// ============================================================================ +// 12. OUTPUT WRITER +// ============================================================================ + +// OutputWriter: A utility class for writing data to output files. +// Purpose: +// - Simplifies the process of writing data to files. +// - Supports writing lines, key-value pairs, and managing file headers. +// Usage: +// OutputWriter writer; +// if (writer.open("output.txt", "Header Info")) { +// writer.writeLine("Example Line"); +// writer.writeKeyValue("Key", 42.0); +// writer.close(); +// } +class OutputWriter { +private: + std::ofstream file; + +public: + bool open(const std::string &filename, const std::string &header) { + file.open(filename); + if (!file.is_open()) + return false; + file << header << std::endl; + return true; + } + + template void writeLine(const T &value) { + file << value << std::endl; + } + + template + void writeLine(const T &first, const Args &...args) { + file << first << " "; + writeLine(args...); + } + + void writeKeyValue(const std::string &key, double value, int precision = 2) { + file << key << " " << std::fixed << std::setprecision(precision) << value + << std::endl; + } + + void close() { + if (file.is_open()) { + file.close(); + } + } + + ~OutputWriter() { close(); } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/5/include/Inventory.hpp b/exams/AxelRubini/2026-01-14/5/include/Inventory.hpp new file mode 100644 index 0000000..c82d776 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/5/include/Inventory.hpp @@ -0,0 +1,423 @@ +#pragma once + +#include "DES.hpp" +#include "Random.hpp" +#include +#include + +namespace SELib { +// ============================================================================ +// 16. CUSTOMER MODEL +// ============================================================================ + +// Customer: Represents a customer model in a simulation. +// Purpose: +// - Simulates customer requests over time. +// - Generates random requests for products at specific intervals. +// Usage: +// Customer customer(5.0, 10.0, 3, rng); +// int productId = customer.step(0.1); // Simulates a time step of 0.1 seconds. +class Customer { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer(double A, double B, int N, RandomGenerator &generator) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator) { + nextRequestTime = rng.uniform(A, B); + } + + int step(double dt) { + nextRequestTime -= dt; + if (nextRequestTime <= 0) { + nextRequestTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } +}; +class Customer_Event_Driven { +private: + double nextRequestTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Customer_Event_Driven(double A, double B, int N, RandomGenerator &generator, + double nextRequestTime) + : minInterval(A), maxInterval(B), numProducts(N), rng(generator), + nextRequestTime(nextRequestTime) {} + + int step_event_driven(double *current_time) { + if (*current_time >= nextRequestTime) { + nextRequestTime = *current_time + rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + double getNextRequestTime() { return nextRequestTime; }; +}; + +// ============================================================================ +// 17. SERVER MODEL +// ============================================================================ + +// Server: Represents a server in a simulated environment. +// Purpose: +// - Handles product requests and manages inventory. +// - Simulates server busy time during request processing. +// Usage: +// Server server(numProducts, maxInventory, processingTimeNormal, +// processingTimeSupplier, rng); int result = +// server.processRequest(productId, needsSupply); +// server.addInventory(productId, quantityAdded); +class Server { +private: + std::vector inventory; + int busyTime; + int processingTimeNormal; + int processingTimeSupplier; + RandomGenerator *rng; + +public: + Server(int numProducts, int K, int F, int G, RandomGenerator &rng) + : inventory(numProducts + 1, 0), busyTime(0), processingTimeNormal(F), + processingTimeSupplier(G), rng(&rng) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + + bool isBusy() const { return busyTime > 0; } + + void decrementBusyTime() { + if (busyTime > 0) + busyTime--; + } + + int processRequest(int productId, bool needsSupply) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return productId; + } else { + busyTime = needsSupply ? processingTimeSupplier : processingTimeNormal; + return -productId; + } + } + + int processRequest2(int productId, double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (inventory[productId] > 0) { + inventory[productId]--; + busyTime = processingTimeNormal; + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + busyTime = processingTimeSupplier; + return -productId; + } + } + + void addInventory(int productId, int quantity) { + inventory[productId] += quantity; + } + + int getInventory(int productId) const { return inventory[productId]; } +}; +// ============================================================================ +// 17.5 SERVER-EVENT-DRIVEN +// ============================================================================ +class Server_Event_Driven { +private: + std::vector inventory; + RandomGenerator *rng; + double next_step_time; + double min_interval; + double max_interval; + +public: + Server_Event_Driven(int numProducts, int K, double min_interval, + double max_interval, RandomGenerator &rng, + double nextStepTime) + : inventory(numProducts + 1, 0), rng(&rng), min_interval(min_interval), + max_interval(max_interval), next_step_time(nextStepTime) { + for (int i = 1; i <= numProducts; i++) { + inventory[i] = rng.uniformInt(0, K); + } + } + int processRequestEventDriven(double current_time, int productId, + double supply_prob, int min_prod_refill, + int max_prod_refill) { + if (current_time >= next_step_time) { + if (inventory[productId] > 0) { + inventory[productId]--; + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + return productId; + } else { + if (rng->bernoulli(supply_prob)) { + int refill_amount = rng->uniformInt(min_prod_refill, max_prod_refill); + inventory[productId] += refill_amount; + } + next_step_time = + current_time + + rng->uniform( + min_interval, + max_interval); // assuming processing time normal is 1.0 + + return -productId; + } + } else { + return 0; + } + } + double getNextStepTime() { return next_step_time; } +}; + +// ============================================================================ +// 18. SUPPLIER MODEL +// ============================================================================ + +// Supplier: Represents a supplier in a simulation. +// Purpose: +// - Simulates the supply of products at specific intervals. +// - Generates random supply requests periodically based on the defined +// intervals. +// Usage: +// Supplier supplier(minInterval, maxInterval, numProducts, rng); +// int suppliedProduct = supplier.step(timeStep); +// supplier.reset(); +class Supplier { +private: + double nextSupplyTime; + double minInterval; + double maxInterval; + int numProducts; + RandomGenerator &rng; + +public: + Supplier(double V, double Q, int N, RandomGenerator &generator) + : minInterval(V), maxInterval(Q), numProducts(N), rng(generator) { + nextSupplyTime = rng.uniform(V, Q); + } + + int step(double dt) { + nextSupplyTime -= dt; + if (nextSupplyTime <= 0) { + nextSupplyTime = rng.uniform(minInterval, maxInterval); + return rng.uniformInt(1, numProducts); + } + return 0; + } + + void reset() { nextSupplyTime = rng.uniform(minInterval, maxInterval); } +}; + +// Server di inventario con messaggi (customer/server network-style) +class InventoryServerProcess : public DiscreteEventProcess { +public: + InventoryServerProcess(int pid, int numItems, int initialStockPerItem, + double serviceTime, MessageBus &bus, + RandomGenerator &rng) + : pid_(pid), serviceTime_(serviceTime), bus_(bus), rng_(rng), + nextEventTime_(0.0), + storage_(static_cast(numItems), initialStockPerItem), + totalRequests_(0), missedRequests_(0) {} + + void initialize(double startTime) override { + nextEventTime_ = startTime + serviceTime_; + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // processa TUTTI i messaggi arrivati a questo server + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + totalRequests_++; + + // sicurezza sugli indici prodotto + if (m.item < 0 || static_cast(m.item) >= storage_.size()) { + continue; // scarta richieste invalide + } + + double servedQty = 0.0; + int &stock = storage_[static_cast(m.item)]; + + if (stock >= static_cast(m.quantity)) { + // richiesta soddisfatta + stock -= static_cast(m.quantity); + servedQty = m.quantity; + } else { + // non abbastanza stock + servedQty = static_cast(stock); + missedRequests_++; + stock = 0; + } + + // risposta al cliente + Message reply; + reply.time = currentTime; + reply.sender = pid_; + reply.receiver = m.sender; // rimanda al mittente + reply.item = m.item; + reply.quantity = servedQty; + + bus_.procToNet(pid_).push(reply); + + // eventuale logica di refill: esempio semplice + if (stock == 0) { + int refill = rng_.uniformInt(1, 10); // batch di rifornimento random + stock += refill; + } + } + + nextEventTime_ = currentTime + serviceTime_; + } + + const std::vector &storage() const { return storage_; } + + int totalRequests() const { return totalRequests_; } + int missedRequests() const { return missedRequests_; } + +private: + int pid_; + double serviceTime_; + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + std::vector storage_; + + int totalRequests_; + int missedRequests_; +}; + +// ============================================================================ +// 19. INVENTORY CUSTOMER PROCESS (DES-compatible) +// ============================================================================ +// +// InventoryCustomerProcess: +// - Processo a eventi discreti che rappresenta un cliente. +// - Invia richieste al server tramite MessageBus (procToNet -> NetworkRouter). +// - Legge le risposte dal canale netToProc. +// - Statistiche: quante richieste ha mandato e quante risposte ha ricevuto. +// +class InventoryCustomerProcess : public DiscreteEventProcess { +public: + // pid : id di questo processo (indice nei canali del MessageBus) + // serverPid : pid del server a cui mandare le richieste + // numItems : numero di prodotti gestiti (item in [0, numItems-1]) + // minInterval/maxInterval : intervallo di tempo tra una richiesta e la + // successiva + InventoryCustomerProcess(int pid, int serverPid, int numItems, + double minInterval, double maxInterval, + MessageBus &bus, RandomGenerator &rng) + : pid_(pid), serverPid_(serverPid), numItems_(numItems), + minInterval_(minInterval), maxInterval_(maxInterval), bus_(bus), + rng_(rng), nextEventTime_(0.0), issuedRequests_(0), receivedReplies_(0), + totalRequestedQty_(0.0), totalServedQty_(0.0) {} + + // inizializza il prossimo evento (prima richiesta) + void initialize(double startTime) override { + nextEventTime_ = startTime + rng_.uniform(minInterval_, maxInterval_); + } + + double nextEventTime() const override { return nextEventTime_; } + + void handleEvent(double currentTime) override { + if (currentTime < nextEventTime_) { + return; + } + + // 1) genera una nuova richiesta al server + const int item = rng_.uniformInt( + 0, numItems_ - 1); // coerente con InventoryServerProcess + const int qty = rng_.uniformInt(1, 3); // quantità richiesta (1..3) + + Message req; + req.time = currentTime; + req.sender = pid_; + req.receiver = serverPid_; + req.item = item; + req.quantity = static_cast(qty); + + bus_.procToNet(pid_).push(req); + issuedRequests_++; + totalRequestedQty_ += req.quantity; + + // 2) processa tutte le risposte arrivate su questo cliente + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); + inbox.pop(); + + // opzionale: possiamo filtrare solo messaggi dal serverPid_ + // if (m.sender != serverPid_) continue; + + receivedReplies_++; + totalServedQty_ += m.quantity; + } + + // 3) pianifica la prossima richiesta + nextEventTime_ = currentTime + rng_.uniform(minInterval_, maxInterval_); + } + + // --- metriche accessibili dall’esterno --- + + int pid() const { return pid_; } + int serverPid() const { return serverPid_; } + + int issuedRequests() const { return issuedRequests_; } + int receivedReplies() const { return receivedReplies_; } + + double totalRequestedQty() const { return totalRequestedQty_; } + double totalServedQty() const { return totalServedQty_; } + + // fill-rate lato customer + double serviceLevel() const { + if (totalRequestedQty_ <= 0.0) + return 1.0; + return totalServedQty_ / totalRequestedQty_; + } + +private: + int pid_; + int serverPid_; + int numItems_; + double minInterval_; + double maxInterval_; + + MessageBus &bus_; + RandomGenerator &rng_; + + double nextEventTime_; + + int issuedRequests_; + int receivedReplies_; + double totalRequestedQty_; + double totalServedQty_; +}; + +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/5/include/Queue.hpp b/exams/AxelRubini/2026-01-14/5/include/Queue.hpp new file mode 100644 index 0000000..0c12ce8 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/5/include/Queue.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 9. FIFO QUEUE +// ============================================================================ + +// FIFOQueue: Implements a first-in-first-out (FIFO) queue with a fixed maximum +// size. Purpose: +// - Enqueue and dequeue items in FIFO order. +// - Track statistics such as total items enqueued and dequeued. +// - Support dynamic size constraints. +// Usage: +// FIFOQueue queue(10); +// queue.enqueue(5); +// int item; +// if (queue.dequeue(item)) { +// // Process item. +// } +template class FIFOQueue { +private: + std::queue queue; + size_t maxSize; + size_t totalEnqueued; + size_t totalDequeued; + +public: + FIFOQueue(size_t max = SIZE_MAX) + : maxSize(max), totalEnqueued(0), totalDequeued(0) {} + + bool enqueue(const T &item) { + if (queue.size() >= maxSize) + return false; + queue.push(item); + totalEnqueued++; + return true; + } + + bool dequeue(T &item) { + if (queue.empty()) + return false; + item = queue.front(); + queue.pop(); + totalDequeued++; + return true; + } + + bool isEmpty() const { return queue.empty(); } + bool isFull() const { return queue.size() >= maxSize; } + size_t size() const { return queue.size(); } + size_t getTotalEnqueued() const { return totalEnqueued; } + size_t getTotalDequeued() const { return totalDequeued; } + std::queue getQueue() const { return queue; } + T getFirst() const { + if (queue.empty()) + throw std::runtime_error("Queue is empty"); + return queue.front(); + } + + void clear() { + while (!queue.empty()) + queue.pop(); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/5/include/Random.hpp b/exams/AxelRubini/2026-01-14/5/include/Random.hpp new file mode 100644 index 0000000..52356f9 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/5/include/Random.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include // std::isfinite +#include // std::accumulate +#include +#include +#include + +namespace SELib { + +class RandomGenerator { +private: + std::mt19937 gen; + +public: + explicit RandomGenerator(unsigned seed = std::random_device{}()) + : gen(seed) {} + + double uniform(double min, double max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_real_distribution dis(lo, high); + return dis(gen); + } + + int uniformInt(int min, int max) { + auto lo = std::min(min, max); + auto high = std::max(min, max); + std::uniform_int_distribution dis(lo, high); + return dis(gen); + } + + double gaussian(double mean, double stddev) { + if (stddev < 0.0) { + throw std::invalid_argument("gaussian: stddev must be >= 0"); + } + std::normal_distribution dis(mean, stddev); + return dis(gen); + } + + bool bernoulli(double p) { + if (p < 0.0 || p > 1.0 || !std::isfinite(p)) { + throw std::invalid_argument("bernoulli: p must be in [0,1]"); + } + std::bernoulli_distribution dis(p); + return dis(gen); + } + + int discrete(const std::vector &probabilities) { + if (probabilities.empty()) { + throw std::invalid_argument("discrete: empty probability vector"); + } + + for (double w : probabilities) { + if (w < 0.0 || !std::isfinite(w)) { + throw std::invalid_argument( + "discrete: weights must be non-negative and finite"); + } + } + + double sum = + std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + if (sum <= 0.0) { + throw std::invalid_argument("discrete: sum of weights must be > 0"); + } + + std::discrete_distribution dis(probabilities.begin(), + probabilities.end()); + return dis(gen); + } + + void setSeed(unsigned seed) { gen.seed(seed); } + + // (opzionale) accesso al motore se ti serve per altre distribuzioni + std::mt19937 &engine() { return gen; } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/5/include/Stat.hpp b/exams/AxelRubini/2026-01-14/5/include/Stat.hpp new file mode 100644 index 0000000..27b4656 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/5/include/Stat.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace SELib { +// ============================================================================ +// 2. STATISTICS +// ============================================================================ + +// Statistics: A utility class for managing and analyzing numerical data +// samples. Purpose: +// - Compute statistical metrics such as mean and standard deviation. +// - Estimate probabilities of custom conditions. +// - Check convergence of data over iterations. +// Usage: +// Statistics stats; +// stats.addSample(5.0); +// double meanValue = stats.mean(); +class Statistics { +private: + std::vector samples; + +public: + void addSample(double value) { samples.push_back(value); } + + void clear() { samples.clear(); } + + double mean() const { + if (samples.empty()) + return 0.0; + double sum = 0.0; + for (double s : samples) + sum += s; + return sum / samples.size(); + } + + double stddev() const { + if (samples.size() < 2) + return 0.0; + double m = mean(); + double variance = 0.0; + for (double s : samples) { + variance += (s - m) * (s - m); + } + return std::sqrt(variance / (samples.size() - 1)); + } + + double probability(std::function condition) const { + if (samples.empty()) + return 0.0; + int count = 0; + for (double s : samples) { + if (condition(s)) + count++; + } + return static_cast(count) / samples.size(); + } + + size_t size() const { return samples.size(); } + const std::vector &getSamples() const { return samples; } + + bool hasConverged(double tolerance = 0.1) const { + if (samples.size() < 30) + return false; + double m = mean(); + double sd = stddev(); + return sd <= tolerance * std::abs(m); + } +}; +} // namespace SELib diff --git a/exams/AxelRubini/2026-01-14/5/main b/exams/AxelRubini/2026-01-14/5/main new file mode 100755 index 0000000..67513e5 Binary files /dev/null and b/exams/AxelRubini/2026-01-14/5/main differ diff --git a/exams/AxelRubini/2026-01-14/5/main.cpp b/exams/AxelRubini/2026-01-14/5/main.cpp new file mode 100644 index 0000000..98fb26d --- /dev/null +++ b/exams/AxelRubini/2026-01-14/5/main.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include +#include "include/DES.hpp" +#include "include/IO.hpp" +#include "include/Random.hpp" +#include "include/Stat.hpp" +#include "include/Decision.hpp" + +namespace SELib { + +class CustomerProcess : public DiscreteEventProcess { +public: + CustomerProcess(int pid, int S, int P, int Q, double A, double B, MessageBus &bus, RandomGenerator &rng) + : pid_(pid), S_(S), P_(P), Q_(Q), A_(A), B_(B), bus_(bus), rng_(rng) {} + void initialize(double startTime) override { nextTime_ = startTime + rng_.uniform(A_, B_); } + double nextEventTime() const override { return nextTime_; } + void handleEvent(double currentTime) override { + Message m; m.time = currentTime; m.sender = pid_; m.receiver = 100 + rng_.uniformInt(0, S_ - 1); + m.item = rng_.uniformInt(1, P_); m.quantity = (double)rng_.uniformInt(1, Q_); + bus_.procToNet(pid_).push(m); + nextTime_ = currentTime + rng_.uniform(A_, B_); + } +private: + int pid_, S_, P_, Q_; double A_, B_, nextTime_; MessageBus &bus_; RandomGenerator &rng_; +}; + +class SupplierProcess : public DiscreteEventProcess { +public: + SupplierProcess(int pid, int S, int P, int Q, double V, double W, MessageBus &bus, RandomGenerator &rng) + : pid_(pid), S_(S), P_(P), Q_(Q), V_(V), W_(W), bus_(bus), rng_(rng) {} + void initialize(double startTime) override { nextTime_ = startTime + rng_.uniform(V_, W_); } + double nextEventTime() const override { return nextTime_; } + void handleEvent(double currentTime) override { + Message m; m.time = currentTime; m.sender = pid_; m.receiver = 100 + rng_.uniformInt(0, S_ - 1); + m.item = -rng_.uniformInt(1, P_); m.quantity = (double)rng_.uniformInt(0, Q_); + bus_.procToNet(pid_).push(m); + nextTime_ = currentTime + rng_.uniform(V_, W_); + } +private: + int pid_, S_, P_, Q_; double V_, W_, nextTime_; MessageBus &bus_; RandomGenerator &rng_; +}; + +class ServerProcess : public DiscreteEventProcess { +public: + ServerProcess(int pid, int C, int F, int P, int Q, MessageBus &bus, RandomGenerator &rng, long long &ms) + : pid_(pid), C_(C), F_(F), P_(P), Q_(Q), bus_(bus), rng_(rng), ms_(ms) { + inv_.assign(P + 1, 0); + } + void initialize(double startTime) override { + for(int i=1; i<=P_; ++i) inv_[i] = rng_.uniformInt(0, Q_); + nextTime_ = startTime + 0.1; + } + double nextEventTime() const override { return nextTime_; } + void handleEvent(double currentTime) override { + auto &inbox = bus_.netToProc(pid_); + while (!inbox.empty()) { + Message m = inbox.front(); inbox.pop(); + bool isSupply = (m.item < 0); + if (isSupply) { inv_[-m.item] += (int)m.quantity; } + else { + int req = (int)m.quantity; + if (inv_[m.item] < req) ms_++; + int prov = std::min(req, inv_[m.item]); inv_[m.item] -= prov; + } + } + nextTime_ = currentTime + 0.1; + } +private: + int pid_, C_, F_, P_, Q_; MessageBus &bus_; RandomGenerator &rng_; long long &ms_; + double nextTime_; std::vector inv_; +}; + +struct Result { double R, V, W, J; }; + +Result runMsims(double V_val, double H, int M, int C, int S, int P, int Q, int F_suppliers, double A, double B, RandomGenerator &rng, double a, double b) { + Statistics s; double W_val = V_val + 5.0; + for (int m = 0; m < M; ++m) { + MessageBus bus(200); DiscreteEventSystem system; long long ms = 0; + for(int i=0; i(i, S, P, Q, A, B, bus, rng); + for(int i=0; i(C+i, S, P, Q, V_val, W_val, bus, rng); + for(int i=0; i(100+i, C, F_suppliers, P, Q, bus, rng, ms); + system.emplaceProcess(bus, rng, 0.01); + DiscreteEventSimulator sim(system); sim.run(H); + s.addSample((double)ms / H); + } + double R_mean = s.mean(); return {R_mean, V_val, W_val, a * V_val + b * R_mean}; +} + +} + +int main() { + SELib::RandomGenerator rng(0); SELib::ParameterParser parser; if (!parser.parseFile("parameters.txt")) return 1; + double H = parser.getDouble("H"); + int M = parser.getInt("M"), C = parser.getInt("C"), S = parser.getInt("S"), P = parser.getInt("P"), Q = parser.getInt("Q"), F_suppliers = parser.getInt("F"), G = parser.getInt("G"); + double A = parser.getDouble("A"), B = parser.getDouble("B"), a_cost = parser.getDouble("a"), b_cost = parser.getDouble("b"); + + SELib::Result bestRes = {0, 0, 0, 1e18}; + for (int i = 0; i < G; ++i) { + double V_cand = rng.uniform(0.1, 10.0); + auto res = SELib::runMsims(V_cand, H, M, C, S, P, Q, F_suppliers, A, B, rng, a_cost, b_cost); + if (res.J < bestRes.J) bestRes = res; + } + std::ofstream outFile("results.txt"); outFile << "2026-01-14-Axel-Rubini-2158099" << std::endl; + outFile << "R " << bestRes.R << std::endl; outFile << "V " << bestRes.V << std::endl; outFile << "W " << bestRes.W << std::endl; outFile << "J " << bestRes.J << std::endl; + return 0; +} diff --git a/exams/AxelRubini/2026-01-14/5/parameters.txt b/exams/AxelRubini/2026-01-14/5/parameters.txt new file mode 100644 index 0000000..0eaf7e4 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/5/parameters.txt @@ -0,0 +1,17 @@ +T 1.0 +M 10 +H 1000 +C 30 +S 1 +F 10 +Q 1 +A 1.5 +B 2.5 +V 10.1 +W 20.1 +P 1 +G 10 +a -0.9 +b 0.1 + + diff --git a/exams/AxelRubini/2026-01-14/5/results.txt b/exams/AxelRubini/2026-01-14/5/results.txt new file mode 100644 index 0000000..a4d7fe5 --- /dev/null +++ b/exams/AxelRubini/2026-01-14/5/results.txt @@ -0,0 +1,5 @@ +2026-01-14-Axel-Rubini-2158099 +R 14.4923 +V 9.9602 +W 14.9602 +J -7.51495