diff --git a/TP1/CPP/Makefile b/TP1/CPP/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..8d18471febbaa741a65975278594e89e7c1175db --- /dev/null +++ b/TP1/CPP/Makefile @@ -0,0 +1,12 @@ +CC= g++ +CXXFLAGS= -Wall -ansi --pedantic -g + +CPP_O_FILE = arraylist.o main.o + +all: $(CPP_O_FILE) + $(CC) $(CXXFLAGS) -o arraylist_analysis $(CPP_O_FILE) + +clean: + rm -rf *.o + rm -rf *~ + rm -rf arraylist_analysis diff --git a/TP1/CPP/analyzer.hpp b/TP1/CPP/analyzer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..34546d877b7ab77d9f5eb1d556685dc1ded0c38d --- /dev/null +++ b/TP1/CPP/analyzer.hpp @@ -0,0 +1,120 @@ +#ifndef __ANALYZER_HPP__ +#define __ANALYZER_HPP__ + +#include<cmath> +#include <vector> +#include <iostream> +#include <fstream> + +/** + Classe utilisée pour faire des statistiques élémentaires + sur une séquence d'opérations. +*/ +class Analyzer{ +public: + /** + Constructeur de la classe analyse + Complexité en temps/espace, pire et meilleur cas : O(1) + */ + Analyzer():cumulative_square(0){} + + /** + Ajoute un coût, une valeur à l'analyse. + Complexité en temps/espace, pire cas : O(size) + Complexité en temps/espace, meilleur cas : O(1) + Complexité amortie : O(1) + @param x est la valeur que l'on souhaite ajouter à l'analyse. + */ + void append(const double & x){ + cost.push_back(x); + cumulative_cost.push_back( (cumulative_cost.size()) ? cumulative_cost.back()+x : x); + cumulative_square += x*x; + } + + /** + Renvoie la somme des coûts enregistrés dans cette analyse. + Complexité en temps/espace, meilleur cas : O(1) + @returns la somme des coûts enregistrés dans cette analyse. + */ + double get_total_cost(){ + return cumulative_cost.back(); + } + + /** + Renvoie le coût amorti d'une opération. + Complexité en temps/espace, meilleur cas : O(1) + @param pos est l'indice de l'opération pour laquelle on veut connaître le coût amorti. + @returns le coût amorti d'une opération. + */ + double get_amortized_cost(size_t pos){ + return (pos)? cumulative_cost.at(pos)/pos : cumulative_cost.at(pos); + } + + /** + Renvoie la moyenne des coûts de toutes les opérations enregistrées dans l'analyse. + Complexité en temps/espace, meilleur cas : O(1) + @returns la moyenne des coûts de toutes les opérations enregistrées dans l'analyse. + */ + double get_average_cost(){ + if(cumulative_cost.empty()) + throw std::runtime_error("List is empty"); + return cumulative_cost.back()/cumulative_cost.size(); + } + + /** + Renvoie la variance des coûts de toutes les opérations enregistrées dans l'analyse. + Complexité en temps/espace, meilleur cas : O(1) + @returns la variance des coûts de toutes les opérations enregistrées dans l'analyse. + */ + double get_variance(){ + double mean, mean_square; + mean = get_average_cost(); + mean_square = mean * mean; + return cumulative_square - mean_square; + } + + /** + Renvoie l'écart-type des coûts de toutes les opérations enregistrées dans l'analyse. + Complexité en temps/espace, meilleur cas : O(1) + @returns l'écart-type des coûts de toutes les opérations enregistrées dans l'analyse. + */ + double get_standard_deviation(){ + return std::sqrt(get_variance()); + } + + /** + Sauvegarde la liste des coûts et des coûts amortis dans un fichier. + Complexité en temps, meilleur/pire cas : O(size) + @param path est le chemin du fichier dans lequel la sauvegarde est faite. + */ + void save_values(const std::string & path){ + std::ofstream f; + size_t i; + f.open(path.c_str()); + for (i = 0; i < cost.size(); ++i) + f<<i<<" "<<cost.at(i)<<" "<<get_amortized_cost(i)<<std::endl; + f.close(); + } + + /** + Affiche la liste des coûts et des coûts amortis sur la sortie standard. + Complexité en temps, meilleur/pire cas : O(size) + */ + void plot_values(){ + size_t i; + for (i = 0; i < cost.size(); ++i) + std::cout<<i<<" "<<cost.at(i)<<" "<<get_amortized_cost(i)<<std::endl; + } + +private: + // Coût de chaque opération. Peut représenter du temps ou une autre mesure. + std::vector<double> cost; + // Coût cumulatif. La case i contient la somme des coûts des i premières opérations. + // Permet de calculer le coût amorti d'une opération. + std::vector<double> cumulative_cost; + // Carré du coût cumulatif. Sert à calculer la variance. On ne garde que la dernière valeur. + double cumulative_square; +}; + + +#endif diff --git a/TP1/CPP/arraylist.cpp b/TP1/CPP/arraylist.cpp new file mode 100644 index 0000000000000000000000000000000000000000..67a6a394026fdc1118b9e97ae14442a19355c0f1 --- /dev/null +++ b/TP1/CPP/arraylist.cpp @@ -0,0 +1,9 @@ +#include "arraylist.hpp" + + +template <typename P> +P ArrayList<P>::get(const int & pos){ + return data[pos]; +} + + diff --git a/TP1/CPP/arraylist.hpp b/TP1/CPP/arraylist.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1ee96aa19b8b7ad19dec12fe281dcb4b44e808c4 --- /dev/null +++ b/TP1/CPP/arraylist.hpp @@ -0,0 +1,97 @@ +#ifndef __ARRAYLIST_HPP +#define __ARRAYLIST_HPP + +#include<iostream> +#include<vector> + +/** + Cette classe est un proxy pour les vecteurs, c'est à dire les tableaux dynamiques en C++. + On utilise cette classe afin d'avoir le contrôle sur la gestion de la mémoire. +*/ +template <typename P> +class ArrayList{ +public: + /** + Constructeur de la classe des tableaux dynamiques. + Complexité en temps/espace, pire et meilleur cas : O(1) + */ + ArrayList(){ + data.reserve(4); + } + + /** + Ajoute une valeur dans le tableau. + Complexité en temps/espace, pire cas : O(size) + Complexité en temps/espace, meilleur cas : O(1) + Complexité amortie : O(1) + @param x est la valeur que l'on souhaite ajouter. + @returns true si le tableau a été agrandit, false sinon + */ + bool append(P x){ + bool memory_allocation = false; + if( enlarging_capacity() ){ + memory_allocation = true; + data.reserve( data.capacity() *2 ); + } + data.push_back(x); + return memory_allocation; + } + + /** + Supprime la dernière valeur du tableau. + Complexité en temps, pire cas : O(size) + Complexité en temps, meilleur cas : O(1) + Complexité amortie : O(1) + @returns true si le tableau a été réduit, false sinon + */ + bool pop_back(){ + bool memory_reduction = false; + if(!data.empty()){ + if( reducing_capacity() ){ + memory_reduction = true; + data.reserve( data.capacity() /2 ); + } + data.pop_back(); + } + return memory_reduction; + } + + /** + Renvoie la valeur située à la position donnée par l'utilisateur. + Complexité en temps/espace, pire cas : O(1) + @param pos est la l'indice de la case on l'utilisateur veut connaître la valeur. + @returns la valeur située à la position donnée par l'utilisateur. + */ + P get(const int & pos); + + /** + Renvoie le nombre d'éléments stockés dans le tableau. + Complexité en temps/espace, pire cas : O(1) + @returns le nombre d'éléments stockés dans le tableau. + */ + const size_t get_size(){ return data.size(); } + +private: + // Vecteur contenant les données. + std::vector<P> data; + + /** + Cette fonction détermine la règle selon laquelle un espace mémoire plus grand sera alloué ou non. + @returns true si le tableau doit être agrandi, false sinon. + */ + bool enlarging_capacity(){ + return data.size() >= (data.capacity() * 3)/4; + } + + /** + Cette fonction détermine la règle selon laquelle un espace mémoire plus petit sera alloué ou non. + @returns true si le tableau doit être réduit, false sinon. + */ + bool reducing_capacity(){ + return data.size() <= data.capacity()/4 && data.size() >4; + } + +}; + + +#endif diff --git a/TP1/CPP/main.cpp b/TP1/CPP/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..490771380eec2d1c31e9d1920136667b3e24d5bf --- /dev/null +++ b/TP1/CPP/main.cpp @@ -0,0 +1,44 @@ +#include<iostream> +#include<time.h> +#include<cstdlib> +#include "arraylist.hpp" +#include "analyzer.hpp" + + +int main(int argc, char ** argv){ + int i; + // Tableau dynamique. + ArrayList<int> a; + // Analyse du temps pris par les opérations. + Analyzer time_analysis; + // Analyse du nombre de copies faites par les opérations. + Analyzer copy_analysis; + struct timespec before, after; + // Booléen permettant de savoir si une allocation a été effectuée. + bool memory_allocation; + + for(i = 0; i < 1000000 ; i++){ + // Ajout d'un élément et mesure du temps pris par l'opération. + timespec_get(&before, TIME_UTC); + memory_allocation = a.append(i); + timespec_get(&after, TIME_UTC); + + // Enregistrement du temps pris par l'opération + time_analysis.append(after.tv_nsec - before.tv_nsec); + // Enregistrement du nombre de copies efféctuées par l'opération. + // S'il y a eu réallocation de mémoire, il a fallu recopier tout le tableau. + copy_analysis.append( (memory_allocation)? i:1 ); + } + + // Affichage de quelques statistiques sur l'expérience. + std::cerr<<"Total cost :"<<time_analysis.get_total_cost()<<std::endl; + std::cerr<<"Average cost :"<<time_analysis.get_average_cost()<<std::endl; + std::cerr<<"Variance :"<<time_analysis.get_variance()<<std::endl; + std::cerr<<"Standard deviation :"<<time_analysis.get_standard_deviation()<<std::endl; + + // Sauvegarde les données de l'expérience: temps et nombre de copies effectuées par opération. + time_analysis.save_values("../dynamic_array_time_cpp.plot"); + copy_analysis.save_values("../dynamic_array_copy_cpp.plot"); + + return 0; +}