Skip to content

Latest commit

 

History

History
1243 lines (912 loc) · 27.5 KB

TME.org

File metadata and controls

1243 lines (912 loc) · 27.5 KB

#+TITLE : Prise de notes TME 4I400 PSCR

Yann Thierry-Mieg ([email protected]) 4I400

TME 1 : 27/09/2019

1.2 Prise en main de non-Eclipse

Question 1

Non applicable, on ne se sert pas d’Eclipse.

Question 2

On fait un hello world :

#include <iostream>

using namespace std;

int main()
{
	  cout << "Hello World !" << endl;
	  return 0;
}

Question 3

Non applicable, on ne se sert pas d’Eclipse.

Question 4

Afficher le contenu d’un tableau créé :

#include <iostream>

using namespace std;

int main()
{
	  int tab[10];

	  for (int i = 0; i < 10; ++i) {
		  tab[i] = i;
		  cout << i << " ";
	  }

	  cout << endl;

	  return 0;
}

Question 5

#include <iostream>

using namespace std;

int main()
{
	  int tab[10];

	  for (int i = 0; i < 10; ++i) {
		  tab[i] = i;
	  }

	  for (size_t i=9; i > 0 ; i--) {

		  if (tab[i] - tab[i-1] != 1) {
			  cout << "probleme !";
		  }
	  }

	  return 0;
}

On est censé découvrir le debugger intégré.

On se servira de gdb : compiler avec le flag -g, mettre le breakpoint à la bonne ligne.

Question 6

On se sert de gdb pour regarder ce qu’il se passe :

Au breakpoint :

(gdb) print i $1 = 0 (gdb) print tab $2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

Un cycle de boucle plus tard :

(gdb) print i $3 = 18446744073709551615

En effet : size_t correspond à l’entier non-signé qu’on peut écrire sur un mot mémoire de la machine (8 octets), donc unsigned long int.

Quand i passe en dessous de 0 (car la condition d’arrêt le permet), il est interprété comme le plus grand nombre écrivable sur 8 octets, soit 2^64 (on imagine on a vérifié 18446744073709551615)

Il faut et il suffit d’écrire correctement la condition d’arrêt, pour ne pas permettre i de passer en-dessous de 0 :

i > 0 sur la condition d’arrêt.

Question 7

Je ne sais pas ce qu’on a oublié de faire de mal, mais on n’a pas de possibilité de fuite mémoire d’après valgrind.

Question 8

Débogage

#ifndef SRC_LIST_H_
#define SRC_LIST_H_

#include <cstddef>
#include <string>
#include <ostream>

namespace pr {

	  class Chainon {
	  public :
		  std::string data;
		  Chainon * next;
		  Chainon (const std::string & data, Chainon * next=nullptr);
		  size_t length() ;
		  void print (std::ostream & os) const;
	  };

	  class List {
	  public:
		  Chainon * tete;
		  List(): tete(nullptr) {}
		  ~List() {
			  for (Chainon * c = tete ; c ; ) {
				  Chainon * tmp = c->next;
				  delete c;
				  c = tmp;
			  }
		  }
		  const std::string & operator[] (size_t index) const ;
		  void push_back (const std::string& val) ;
		  void push_front (const std::string& val) {
			  tete = new Chainon(val,tete);
		  }
		  bool empty() ;
		  size_t size() const ;
	  };
	  std::ostream & operator<< (std::ostream & os, const List & vec) ;
} /* namespace pr */
#endif /* SRC_LIST_H_ */

TME 2 : 04/10/2019

std::vector, std::pair

On s’appuiera sur la documentation de référence du langage.

Le code du parseur :

#include <iostream>
#include <fstream>
#include <regex>
#include <chrono>

int main ()
{
	  using namespace std;
	  using namespace std::chrono;
	  ifstream input = ifstream("./WarAndPeace.txt");
	  auto start = steady_clock::now();
	  cout << "Parsing War and Peace" << endl;
	  size_t nombre_lu = 0;
	  // prochain mot lu
	  string word;
	  // une regex qui reconnait les caractères anormaux (négation des lettres)
	  regex re( R"([^a-zA-Z])");
	  while (input >> word) {
		  // élimine la ponctuation et les caractères spéciaux
		  word = regex_replace ( word, re, "");
		  // passe en lowercase
		  transform(word.begin(),word.end(),word.begin(),::tolower);
		  // word est maintenant "tout propre"
		  if (nombre_lu % 100 == 0)
			  // on affiche un mot "propre" sur 100
			  cout << nombre_lu << ": "<< word << endl;
		  nombre_lu++;
	  }
	  input.close();
	  cout << "Finished Parsing War and Peace" << endl;
	  auto end = steady_clock::now();
	  cout << "Parsing took "
	       << duration_cast<milliseconds>(end - start).count()
	       << "ms.\n";
	  cout << "Found a total of " << nombre_lu << " words." << endl;
	  return 0;
}

Question 1

Finished Parsing War and Peace Parsing took 1522ms. Found a total of 566193 words.

Question 2

#include <iostream>
#include <fstream>
#include <regex>
#include <chrono>

#include <vector>


int main ()
{
	  using namespace std;
	  using namespace std::chrono;

	  ifstream input = ifstream("./WarAndPeace.txt");
	  auto start = steady_clock::now();

	  cout << "Parsing War and Peace" << endl;

	  size_t nombre_lu = 0;
	  // prochain mot lu
	  string word;
	  // une regex qui reconnait les caractères anormaux (négation des lettres)
	  regex re( R"([^a-zA-Z])");

	  // On met ici le vecteur qui stockera les mots
	  vector <string> unique_words;

	  while (input >> word) {
		  // élimine la ponctuation et les caractères spéciaux
		  word = regex_replace ( word, re, "");
		  // passe en lowercase
		  transform(word.begin(),word.end(),word.begin(),::tolower);
		  // word est maintenant "tout propre"

		  // on affiche un mot "propre" sur 100
		  if (nombre_lu % 100 == 0) cout << nombre_lu << ": "<< word << endl;

		  nombre_lu++;

		  bool trouve = false;
		  for (int i = 0 ; i < unique_words.size(); i++) {
			  if (unique_words[i] == word) {
				  trouve = true;
				  break;
			  }
		  }
		  if (trouve == false) {
			  unique_words.push_back(word);
		  }

	  }

	  input.close();

	  cout << "Finished Parsing War and Peace" << endl;
	  auto end = steady_clock::now();
	  cout << "Parsing took "
	       << duration_cast<milliseconds>(end - start).count()
	       << "ms.\n";
	  cout << "Found a total of " << nombre_lu << " words." << endl;
	  cout << "Found a total of " << unique_words.size() << " unique words." << endl;

	  return 0;
}

Sans le break dans le if, en vérifiant encore après avoir trouvé Finished Parsing War and Peace Parsing took 71808ms. (71 secondes, sur le NFS) Found a total of 566193 words. Found a total of 20333 unique words.

Avec le break. Finished Parsing War and Peace Parsing took 11318ms. (11 secondes, sur le NFS) Found a total of 566193 words. Found a total of 20333 unique words.

Question 3

#include <iostream>
#include <fstream>
#include <regex>
#include <chrono>

#include <vector>
#include <utility>

using namespace std;
using namespace std::chrono;

int occurences_mot(const string mot, vector <pair<string,int>> tab_mot);

int main ()
{

	  ifstream input = ifstream("./WarAndPeace.txt");
	  auto start = steady_clock::now();

	  cout << "Parsing War and Peace" << endl;

	  size_t nombre_lu = 0;
	  // prochain mot lu
	  string word;
	  // une regex qui reconnait les caractères anormaux (négation des lettres)
	  regex re( R"([^a-zA-Z])");

	  // On met ici le vecteur qui stockera les mots
	  vector <pair <string,int>> unique_words_and_count;

	  while (input >> word) {
		  // élimine la ponctuation et les caractères spéciaux
		  word = regex_replace ( word, re, "");
		  // passe en lowercase
		  transform(word.begin(),word.end(),word.begin(),::tolower);
		  // word est maintenant "tout propre"

		  // on affiche un mot "propre" sur 100
		  if (nombre_lu % 100 == 0) cout << nombre_lu << ": "<< word << endl;

		  nombre_lu++;

		  bool trouve = false;
		  for (int i = 0 ; i < unique_words_and_count.size(); i++) {
			  if (unique_words_and_count[i].first == word) {
				  trouve = true;
				  unique_words_and_count[i].second++;
				  break;
			  }
		  }
		  if (trouve == false) {
			  unique_words_and_count.emplace_back(word,1);
		  }

	  }

	  input.close();

	  cout << "Finished Parsing War and Peace" << endl;
	  auto end = steady_clock::now();
	  cout << "Parsing took "
	       << duration_cast<milliseconds>(end - start).count()
	       << "ms.\n";
	  cout << "Found a total of " << nombre_lu << " words." << endl;

	  cout << "Found a total of " << unique_words_and_count.size() << " unique words." << endl;

	  cout << "Found a total of " << occurences_mot("war", unique_words_and_count) << " occurrences of the word war." << endl;

	  cout << "Found a total of " << occurences_mot("peace", unique_words_and_count) << " occurrences of the word peace." << endl;

	  cout << "Found a total of " << occurences_mot("napoleon", unique_words_and_count) << " occurrences of the word napoleon." << endl;

	  cout << "Found a total of " << occurences_mot("pierre", unique_words_and_count) << " occurrences of the word pierre." << endl;

	  return 0;
}

int occurences_mot(const string mot, const vector <pair<string,int>> tab_mot)
{
	  for (int i = 0; i < tab_mot.size(); ++i) {
		  if (mot == tab_mot[i].first) {
			  return tab_mot[i].second;
		  }
	  }

	  return 0;
}

Finished Parsing War and Peace Parsing took 11661ms. Found a total of 566193 words. Found a total of 20333 unique words. Found a total of 298 occurrences of the word war. Found a total of 114 occurrences of the word peace. Found a total of 475 occurrences of the word napoleon. Found a total of 1784 occurrences of the word pierre.

Question 4

Pour chacun des n mots du texte, on a besoin de faire [la suite manque]

1.2 Table de hachage

Question 5

(Version Thierry-Mieg, au tableau)

#include <iostream>
#include <fstream>
#include <regex>
#include <chrono>

using namespace std;

template <typename K, typename V>
class HashMap {

	  struct Entry {
		  const K key;
		  V value;
		  Entry(const K& key, const V& value) : key(key), value(value) {}
	  };

	  std::vector <std::forward_list <Entry> > buckets;

	  V* get(const K& key) {
		  size_t h = std::hash <K>()(key);
		  h = h % buckets.size();

		  for (Entry& ent : buckets[h]) {
			  if (ent.key == key) {
				  return & ent.value;
			  }
		  }

		  return nullptr;
	  }


};

int main ()
{
	  using namespace std;
	  using namespace std::chrono;

	  ifstream input = ifstream("./WarAndPeace.txt");
	  auto start = steady_clock::now();

	  cout << "Parsing War and Peace" << endl;

	  size_t nombre_lu = 0;
	  // prochain mot lu
	  string word;
	  // une regex qui reconnait les caractères anormaux (négation des lettres)
	  regex re( R"([^a-zA-Z])");

	  while (input >> word) {
		  // élimine la ponctuation et les caractères spéciaux
		  word = regex_replace ( word, re, "");
		  // passe en lowercase
		  transform(word.begin(),word.end(),word.begin(),::tolower);
		  // word est maintenant "tout propre"
		  if (nombre_lu % 100 == 0)
			  // on affiche un mot "propre" sur 100
			  cout << nombre_lu << ": "<< word << endl;
		  nombre_lu++;
	  }

	  input.close();

	  cout << "Finished Parsing War and Peace" << endl;
		  auto end = steady_clock::now();
		  cout << "Parsing took "
		       << duration_cast<milliseconds>(end - start).count()
		       << "ms.\n";
		  cout << "Found a total of " << nombre_lu << " words." << endl;

		  return 0;
	  }

(Version Agon-Rambosson)

#include <iostream>
#include <fstream>
#include <regex>
#include <chrono>

#include <vector>
#include <utility>
#include <forward_list>

using namespace std;

template <typename K, typename V>
class HashMap {

	  struct Entry {
		  const K key;
		  V value;
		  Entry(const K& key, const V& value) : key(key), value(value) {}
	  };
	  std::vector <std::forward_list<Entry>> buckets;
	  size_t nb_stored_values;

public:

	  HashMap(size_t size) {
		  buckets.resize(size);
		  nb_stored_values = 0;
	  }

	  HashMap() {
		  buckets.resize(256);
		  nb_stored_values = 0;
	  }

	  size_t nb_buckets() const {
		  return buckets.size();
	  }

	  size_t size() const {
		  return nb_stored_values;
	  }

	  void grow() {
		  size_t former_size = buckets.size();

		  HashMap nouvelle_map(2 * former_size);

		  K temp_key;
		  V temp_value;
		  size_t temp_nb_values = 0;

		  for (int i = 0; i < former_size ; i++) {
			  while (buckets[i].empty() != 1) {
				  temp_key = buckets[i].front().key;
				  temp_value = buckets[i].front().value;
				  buckets[i].pop_front();
				  nb_stored_values--;
				  nouvelle_map.put(temp_key, temp_value);
				  temp_nb_values++;
			  }
		  }

		  buckets.swap(nouvelle_map.buckets);
		  this->nb_stored_values = temp_nb_values;
		  nouvelle_map.buckets.clear();
	  }


	  V* get(const K& key) {
		  size_t h = std::hash <K>()(key);
		  h = h % buckets.size();

		  for (Entry& ent : buckets[h]) {
			  if (ent.key == key) {
				  return &ent.value;
			  }
		  }

		  return nullptr;
	  }

	  bool put(const K& key, const V& value) {
		  size_t h = std::hash <K>()(key);
		  h = h % buckets.size();

		  for (Entry& ent : buckets[h]) {
			  if (ent.key == key) {
				  ent.value = value;
				  return true;
			  }
		  }

		  if (((this->size() + 0.0) / (this->nb_buckets() + 0.0)) > 0.8) {
			  this->grow();
		  }
		  buckets[h].push_front(Entry(key, value));
		  this->nb_stored_values++;
		  return false;
	  }

	  bool increment(const K& key) {
		  size_t h = std::hash <K>()(key);
		  h = h % buckets.size();

		  for (Entry& ent : buckets[h]) {
			  if (ent.key == key) {
				  ent.value++;
				  return true;
			  }
		  }

		  if (((this->size() + 0.0) / (this->nb_buckets() + 0.0)) > 0.8) {
			  this->grow();
		  }
		  buckets[h].push_front(Entry(key, 1));
		  this->nb_stored_values++;
		  return false;

	  }

	  bool del(const K& key) {
		  size_t h = std::hash <K>()(key);
		  h = h % buckets.size();

		  auto prev = buckets[h].before_begin();
		  for (auto it = buckets[h].before_begin();
		       it != buckets[h].end();) {
			  prev = it;
			  if ((++it)->key == key) {
				  buckets[h].erase_after(prev);
				  nb_stored_values--;
				  return true;
			  }
		  }
		  return false;
	  }
};


int main ()
{
	  using namespace std;
	  using namespace std::chrono;

	  ifstream input = ifstream("./WarAndPeace.txt");
	  auto start = steady_clock::now();

	  cout << "Parsing War and Peace" << endl;

	  size_t nombre_lu = 0;
	  // prochain mot lu
	  string word;
	  // une regex qui reconnait les caractères anormaux (négation des lettres)
	  regex re( R"([^a-zA-Z])");

	  // On créé la structure qui accueillera les données
	  HashMap<string,int> table;

	  while (input >> word) {
		  // élimine la ponctuation et les caractères spéciaux
		  word = regex_replace ( word, re, "");
		  // passe en lowercase
		  transform(word.begin(),word.end(),word.begin(),::tolower);
		  // word est maintenant "tout propre"

		  // on affiche un mot "propre" sur 100
		  if (nombre_lu % 100 == 0) cout << nombre_lu << ": "<< word << endl;

		  nombre_lu++;

		  table.increment(word);
	  }

	  cout << "Finished Parsing War and Peace" << endl;
	  auto end = steady_clock::now();
	  cout << "Parsing took "
	       << duration_cast<milliseconds>(end - start).count()
	       << "ms.\n";
	  cout << "Found a total of " << nombre_lu << " words." << endl;
	  cout << "Found a total of " << table.size() << " unique words." << endl;

	  cout << "We need " << table.nb_buckets() << " buckets" << endl;

	  cout << "On trouve " << *(table.get("pierre")) << " occurences du mot pierre" << endl;

	  return 0;
}

Finished Parsing War and Peace Parsing took 3225ms. Found a total of 566193 words. Found a total of 20333 unique words. We need 32768 buckets

On peut déjà remarquer qu’on a bien les mêmes valeurs qu’avec le vecteur.

Le parsing n’a pris que 3 secondes, alors même qu’on est sur un ordinateur bien moins puissant (à tester en condition similaire).

Le nombre de buckets est bien tel que le nombre de valeur uniques / le nombre de buckets est en deçà de 0.8. On a choisi de réallouer le double à chaque itération.

De cette manière, le coût des réallocations est en log_2(n).

TME 3 : 11/10/2019

Question 1

#include <iostream>
#include <fstream>
#include <regex>
#include <chrono>

#include <vector>
#include <utility>
#include <forward_list>

using namespace std;

template <typename K, typename V>
class HashMap {

public:
	  struct Entry {
		  const K key;
		  V value;
		  Entry(const K& key, const V& value) : key(key), value(value) {}
	  };

private:
	  typedef std::vector<std::forward_list<Entry>> buckets_t;
	  buckets_t buckets;
	  size_t nb_stored_values;

public:

	  HashMap(size_t size) {
		  buckets.resize(size);
		  nb_stored_values = 0;
	  }

	  HashMap() {
		  buckets.resize(256);
		  nb_stored_values = 0;
	  }

	  size_t nb_buckets() const {
		  return buckets.size();
	  }

	  size_t size() const {
		  return nb_stored_values;
	  }

	  void grow() {
		  size_t former_size = buckets.size();

		  HashMap nouvelle_map(2 * former_size);

		  K temp_key;
		  V temp_value;
		  size_t temp_nb_values = 0;

		  for (size_t i = 0; i < former_size ; i++) {
			  while (buckets[i].empty() != 1) {
				  temp_key = buckets[i].front().key;
				  temp_value = buckets[i].front().value;
				  buckets[i].pop_front();
				  nb_stored_values--;
				  nouvelle_map.put(temp_key, temp_value);
				  temp_nb_values++;
			  }
		  }

		  buckets.swap(nouvelle_map.buckets);
		  this->nb_stored_values = temp_nb_values;
		  nouvelle_map.buckets.clear();
	  }


	  V* get(const K& key) {
		  size_t h = std::hash <K>()(key);
		  h = h % buckets.size();

		  for (Entry& ent : buckets[h]) {
			  if (ent.key == key) {
				  return &ent.value;
			  }
		  }

		  return nullptr;
	  }

	  bool put(const K& key, const V& value) {
		  size_t h = std::hash <K>()(key);
		  h = h % buckets.size();

		  for (Entry& ent : buckets[h]) {
			  if (ent.key == key) {
				  ent.value = value;
				  return true;
			  }
		  }

		  if (((this->size() + 0.0) / (this->nb_buckets() + 0.0)) > 0.8) {
			  this->grow();
		  }
		  buckets[h].push_front(Entry(key, value));
		  this->nb_stored_values++;
		  return false;
	  }

	  bool increment(const K& key) {
		  size_t h = std::hash <K>()(key);
		  h = h % buckets.size();

		  for (Entry& ent : buckets[h]) {
			  if (ent.key == key) {
				  ent.value++;
				  return true;
			  }
		  }

		  if (((this->size() + 0.0) / (this->nb_buckets() + 0.0)) > 0.8) {
			  this->grow();
		  }
		  buckets[h].push_front(Entry(key, 1));
		  this->nb_stored_values++;
		  return false;

	  }

	  bool del(const K& key) {
		  size_t h = std::hash <K>()(key);
		  h = h % buckets.size();

		  auto prev = buckets[h].before_begin();
		  for (auto it = buckets[h].before_begin();
		       it != buckets[h].end();) {
			  prev = it;
			  if ((++it)->key == key) {
				  buckets[h].erase_after(prev);
				  nb_stored_values--;
				  return true;
			  }
		  }
		  return false;
	  }

	  struct iterator {
		  typename std::vector<std::forward_list<Entry>>::iterator vit;
		  typename std::forward_list<Entry>::iterator lit;

		  buckets_t *buckets;

		  iterator (buckets_t &buckets) : buckets(&buckets) {}

		  iterator &operator++() {

			  ++lit;
			  if (lit != vit->begin()) return *this;

			  while (++vit -> empty() && vit != buckets->end()) {
				  if (vit != buckets->end()) {
					  lit = vit->begin();
				  }
			  }

			  return *this;
		  }

		  bool operator!=(iterator & other) {
			  return ((vit != other.vit) || (lit != other.lit));
		  }
	  };

	  iterator begin() {
		  auto temp = iterator(&buckets);

		  for (size_t i = 0; i < buckets.size() ; ++i) {
			  if (buckets[i].empty()) {
				  temp.vit = buckets.begin() + i;
				  temp.lit = temp.vit->begin();
				  break;
			  }
		  }

		  return temp;
	  }

	  iterator end() {
		  auto temp = iterator(&buckets);

		  temp.lit = buckets[buckets.size() - 1].end();
		  temp.vit = buckets.end();

		  return temp;
	  }

	  size_t count(iterator begin, iterator end) {
		  size_t compteur = 0;

		  for (auto it = begin; it != end; ++it, ++compteur);

		  return compteur;
	  }


};

int main ()
{
	  using namespace std;
	  using namespace std::chrono;

	  ifstream input = ifstream("./WarAndPeace.txt");
	  auto start = steady_clock::now();

	  cout << "Parsing War and Peace" << endl;

	  size_t nombre_lu = 0;
	  // prochain mot lu
	  string word;
	  // une regex qui reconnait les caractères anormaux (négation des lettres)
	  regex re( R"([^a-zA-Z])");

	  // On créé la structure qui accueillera les données
	  HashMap<string,int> table;

	  while (input >> word) {
		  // élimine la ponctuation et les caractères spéciaux
		  word = regex_replace ( word, re, "");
		  // passe en lowercase
		  transform(word.begin(),word.end(),word.begin(),::tolower);
		  // word est maintenant "tout propre"

		  // on affiche un mot "propre" sur 100
		  if (nombre_lu % 100 == 0) cout << nombre_lu << ": "<< word << endl;

		  nombre_lu++;

		  table.increment(word);
	  }

	  cout << "Finished Parsing War and Peace" << endl;
	  auto end = steady_clock::now();
	  cout << "Parsing took "
	       << duration_cast<milliseconds>(end - start).count()
	       << "ms.\n";
	  cout << "Found a total of " << nombre_lu << " words." << endl;
	  cout << "Found a total of " << table.size() << " unique words." << endl;

	  cout << "We need " << table.nb_buckets() << " buckets" << endl;

	  cout << "On trouve " << *(table.get("pierre")) << " occurences du mot pierre" << endl;

	  cout << "Il y a " << table.count(table.begin(), table.end()) << " mots" << endl;

	  return 0;
}

TME 4 : 18/10/2019

Question 1

On rappelle toutes les classes sous-jacentes :

Le fichier d’en-tête de la classe Compte :

#pragma once
#include <thread>
#include <mutex>

namespace pr {

	  class Compte {
		  mutable std::mutex m;
		  int solde;
	  public :
		  Compte(int solde=0):solde(solde) {}
		  Compte(const Compte & other);
		  void crediter (unsigned int val) ;
		  bool debiter (unsigned int val) ;
		  int getSolde() const;
		  mutex &getMutex();
	  };

}

L’implémentation des méthodes :

#include "Compte.h"

using namespace std;

namespace pr {
	  void Compte::crediter (unsigned int val) {
		  unique_lock<mutex> g(m);
		  solde+=val ;
	  }
	  bool Compte::debiter (unsigned int val) {
		  unique_lock<mutex> g(m);
		  bool doit = solde >= val;
		  if (doit) {
			  solde-=val ;
		  }
		  return doit;
	  }
	  int Compte::getSolde() const  {
		  unique_lock<mutex> g(m);
		  return solde;
	  }
// NB : vector exige Copyable, mais mutex ne l'est pas...
	  Compte::Compte(const Compte & other) {
		  other.m.lock();
		  solde = other.solde;
		  other.m.unlock();
	  }

	  Compte::getmutex() {
		
	  }

}

On rajoute la classe Banque :

#pragma once

#include "Compte.h"
#include <vector>

namespace pr {

	  class Banque {
		  typedef std::vector<Compte> comptes_t;
		  comptes_t comptes;
	  public :
		  Banque (size_t ncomptes, size_t solde) : comptes (ncomptes, Compte(solde)){
		  }
		  void transfert(size_t deb, size_t cred, unsigned int val) ;
		  size_t size() const ;
		  bool comptabiliser (int attendu) const ;
		  int get_solde(size_t compte) ;
	  };

}

L’implémentation des méthodes :

#include "Banque.h"

#include <iostream>

using namespace std;

namespace pr {

	  void Banque::transfert(size_t deb, size_t cred, unsigned int val) {
		  Compte & debiteur = comptes[deb];
		  Compte & crediteur = comptes[cred];
		  if (debiteur.debiter(val)) {
			  crediteur.crediter(val);
		  }
	  }
	  size_t Banque::size() const {
		  return comptes.size();
	  }
	  bool Banque::comptabiliser(int attendu) const {
		  int bilan = 0;
		  int id = 0;
		  for (const auto & compte : comptes) {
			  if (compte.getSolde() < 0) {
				  cout << "Compte " << id << " en négatif : " << compte.getSolde() << endl;
			  }
			  bilan += compte.getSolde();
			  id++;
		  }
		  if (bilan != attendu) {
			  cout << "Bilan comptable faux : attendu " << attendu << " obtenu : " << bilan << endl;
		  }
		  return bilan == attendu;
	  }

	  int Banque::get_solde(size_t compte) {
		  return comptes[compte].getSolde();
	  }
}
#include <thread>
#include <iostream>

#include "Banque.h"

using namespace std;

const int NB_THREAD = 20;
const int K = 100;
const int SOLDEINITIAL = 1000;

// En fait, il fallait bien écrire la fonction ici :

void transferts_aleatoires(pr::Banque *banque) {

	  int compteur = 1000;

	  for (; compteur > 0; --compteur) {
		  auto i = ::rand() % banque->size();
		  auto j = ::rand() % banque->size();

		  // On part du principe qu'on peut se faire des transferts à soi-même
		  auto m = ::rand() % 100;

		  // cout << m << endl;

		  banque->transfert(i,j,m);

		  // On s'endort pendant une durée aléatoire entre 0 et 200ms
		  auto r = ::rand() % 20;

		  std::this_thread::sleep_for(std::chrono::milliseconds(r));
	  }
}

int bilan_naif(pr::Banque *banque)
{
	  int bilan = 0;

	  for (int i = 0; i < K ; ++i) {
		   bilan += banque->get_solde(i);
	   }

	   return bilan;
 }

int main()
{
	  // On fixe la graine
	  ::srand(::time(nullptr));

	  // On créé une instance de banque
	  pr::Banque LBP(K, SOLDEINITIAL);
	  // On vérifie que le comptable est content
	  // cout << LBP.comptabiliser(K * SOLDEINITIAL) << endl;
	  cout << bilan_naif(&LBP) << endl;
	  // On vérifie qu'un compte random est bien à la bonne valeur
	  cout << LBP.get_solde(12) << endl;


	  vector<thread> threads;

	  // TODO : creer des threads qui font ce qui est demandé
	  threads.reserve(NB_THREAD);
	  for (int i = 0; i < NB_THREAD; ++i) {
		  threads.push_back(thread(transferts_aleatoires, &LBP));
	  }
	  // Threads créés

	  for (auto & t : threads) {
		  t.join();
	  }

	  // Est-ce que le comptable est content ?
	  // cout << LBP.comptabiliser(K * SOLDEINITIAL) << endl;
	  cout << bilan_naif(&LBP) << endl;
	  // Est-ce que notre fonction a vraiment fait qqch ?
	  cout << LBP.get_solde(12) << endl;
	  cout << LBP.get_solde(37) << endl;
	  cout << LBP.get_solde(15) << endl;
	  cout << LBP.get_solde(87) << endl;

	  // TODO : tester solde = NB_THREAD * JP
	  return 0;
}

Pour compiler tout ça, on écrit un makefile :

tme4: Banque.cpp Compte.cpp main.cpp
	  g++ -std=c++1y -O0 -g3 -Wall -c -lpthread -o ./Compte.o ./Compte.cpp
	  g++ -std=c++1y -O0 -g3 -Wall -c -lpthread -o ./Banque.o ./Banque.cpp
	  g++ -std=c++1y -O0 -g3 -Wall -c -lpthread -o ./tme4.o ./main.cpp
	  g++ -lpthread -o ./tme4 ./Compte.o ./Banque.o ./tme4.o

Question 2

Déjà, on arrive à compiler avec les codes donnés plus haut.

On n’a pas de problème pour le moment, avec K = 100, 10 threads, et 1000 itérations dans le thread.

On passe bien l’instance de la banque par pointeur, et on vérifie bien que les comptes sont modifiés.

Comment forcer une erreur ?

Question 3

Lignes de réponse :

On fait un truc style d.lock, c.lock, section critique

recursive_mutex doit être utilisé (on se rebloque entre le premier verrou qu’on vient de définir, et le deuxième dans le corps des méthodes)

Ensuite :

Deadlock introduit par interblocage.

Il faut bien ordonner les verrous : on peut demander au système de faire ça, ou on peut le faire à la main.

Sinon Big Fat Lock : on met un mutex sur la banque (mais l’intérêt est nul)

Pour le comptable :

v0 la fonction comptabiliser, lock tous les comptes.

Lock progressif : lock dans l’ordre d’indice.

[à reprendre quand on a le temps]