diff --git a/projet E2/main.c b/projet E2/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..8a175baad89c6a4dc9f47cfd25f018fa57a25db7
--- /dev/null
+++ b/projet E2/main.c	
@@ -0,0 +1,512 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <semaphore.h>
+#include <pthread.h>
+#include <limits.h>
+#include <time.h>
+#include "main.h"
+
+#define NTYPE unsigned long long
+#define NTYPE_POURCENT "%llu"
+#define LIMITE pow(10,19)
+/*
+long unsigned : "%lu"
+long long lli
+unsigned long long llu
+...*/
+/*
+FAIRE :
+- faudrait véirifier qu'on a jamais 2 fois le même nombre dans l'algo final (et que tous les nombres y sont)
+- combien de threads faut-il prendre par défaut ?
+- actuellement si on a un très grands nombres, il va prendre tout le temps et une mémoire énorme pcq on travaille sur un thread par nombre
+	à mon avis on va devoir faire du multi-thread par nombre
+	 ou au moins réduire sensiblement la quantité de ram qu'on demande pcq elle est beaucoup trop gande il me semble
+==============================================================================
+QUESTIONS:
+- tests pour les autres fonctions, avec le prinf ds l'output
+- est-ce que Jenkins doit être bloquant au niveau des push ? => on a le choix
+- le README
+- fonctionnement en sigle-thread à vérif, faire des tests sur le code avec plusiers ?
+- déterminer le nombre de thread à utiliser si on a le choix
+- pas nécessaires mais éviter le crash => OK
+- enlever les anti-erreurs
+- véirif pass test sur ingi
+- curseurs de lecture au début des fichiers?
+- pourquoi mutex dans la fct_file_out?
+*/
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+//STRUCTURES
+
+//pour un nombre number
+typedef struct outputstruct{
+	NTYPE number;
+	int number_div; //le nombre de diviseurs de number
+	NTYPE *tab_div; //le pointeur vers le tableau qui contiendra les diviseurs premiers de number
+	} outputstruct_t;
+//pour stocker les buffers
+typedef struct buffers{
+	NTYPE *buf_1; //buffer des nombres de l'input, rempli par la fonction fct_file_in et lu par la fonction fct_calcul
+	outputstruct_t **buf_2; //buffer des structures de chaque nombre, rempli par fct_calcul et lu par fct_file_out
+	} buffers_t;
+
+// FONCTIONS
+/* tout ceci est dans le fichier .h
+NTYPE *prime_divs(   NTYPE*, NTYPE);
+void  *fct_calcul(   void*);
+void  *fct_file_in(  void*);
+void  *fct_file_out( void*);
+*/
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//FILES
+
+FILE* file_in;
+FILE* file_out;
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+//SEMAPHORES ET MUTEX
+
+pthread_mutex_t mutex_buffer_1;		// mutex pour les consommateurs de buffer 1
+sem_t empty_1;				// sémaphore pour le buffer 1, pour déterminer quand le buffer 1 est vide
+sem_t full_1;				// idem, pour déterminer quand le buffer 1 est plein
+int index_remplissage_1 =0;		// pas thread-safe pcq un seul thread qui le modif (producer)
+int index_vidage_1      =0; 		// doit être thread-safe pcq plusieurs threads qui calculent (consumer)
+
+pthread_mutex_t mutex_buffer_2;		// mutex pour les producteurs de buffer 2
+sem_t empty_2;				// sémaphore pour le buffer 2, pour déterminer quand le buffer 2 est vide
+sem_t full_2;				// sémaphore pour le buffer 2, pour déterminer quand le buffer 2 est vide
+int index_remplissage_2 =0;		// pas thread-safe pcq un seul thread qui le modif (producer)
+int index_vidage_2      =0; 		// doit être thread-safe pcq plusieurs threads qui calculent (consumer)
+
+int size1;				// taille de buffer 1 et de buffer 2
+int n_threads;
+
+
+int main(int argc, char** argv){
+
+	fprintf(stdout,"types actuels:");fprintf(stdout,"%s",NTYPE_POURCENT);
+
+	////////////////////////////////////////////////////////////////////////////////////////
+	//GESTION DES ARGUMENTS DONNÉS À LA MAIN
+
+	if(argc != 3 && argc != 5){    fprintf(stderr,"not right amount of arguments\n");   return -1;	}
+	//CASE [ prog_name file1 file2 ]
+	if(argc == 3){
+		n_threads = 3;// nombre le plus intéressant à déterminer
+		//open
+		if( (file_in =fopen(argv[1],"r" ))  == NULL ){	    fprintf(stderr,"error in open file_in\n"); 	}
+		if( (file_out=fopen(argv[2],"w" )) == NULL ){	    fprintf(stderr,"error in open file_out\n");	}//écrit dans le file, elle crée si besoin
+
+	}
+	//CASE [ prog_name -N n_threads file1 file2 ]
+	else{
+		if( (argv[1][0] != '-') || (argv[1][1] != 'N') ){	fprintf(stderr,"not right amount of arguments\n");   return -1;}
+		n_threads = atoi(argv[2]); // *(((int*)(* argv) + 3));
+		//open
+		if( (file_in =fopen(argv[3],"r" ))  == NULL ){	    fprintf(stderr,"error in open file_in\n"); 	}
+		if( (file_out=fopen(argv[4],"w" )) == NULL ){	    fprintf(stderr,"error in open file_out\n");	}//écrit dans le file, elle crée si besoin
+	}
+
+	// placer les curseurs de lecture des fichiers au début de ces derniers
+	if( fseek(file_in , 0, SEEK_SET) != 0){ 	fprintf(stderr,"error dans la fct seek in\n"); 	}
+	if( fseek(file_out, 0, SEEK_SET) != 0){		fprintf(stderr,"error dans la fct seek out\n");	}
+
+	//////////////////////////////////////////////////////////////////////////////////////////////
+	//INITIALISATIONS
+
+	//mutex
+	if(pthread_mutex_init( &mutex_buffer_2, NULL) !=0){	fprintf(stderr,"error in m utex init de buffer 2\n");	}
+	if(pthread_mutex_init( &mutex_buffer_1, NULL) !=0){	fprintf(stderr,"error in m utex init de buffer 1\n");	}
+
+	//buffers 1,2
+	size1 = 2*n_threads;
+
+	NTYPE*           buffer_1 =(NTYPE*)           malloc(sizeof(NTYPE           )*size1);
+	outputstruct_t** buffer_2 =(outputstruct_t**) malloc(sizeof(outputstruct_t* )*size1);
+
+	if(buffer_1 == NULL){ 		fprintf(stderr,"erreur dans le malloc de buffer_1\n");  	}
+	if(buffer_2 == NULL){		fprintf(stderr,"erreur dans le malloc de buffer_2\n");    	}
+
+	//structures outputstruct_t dans le buffer 2
+	for (int p=0; p<size1;p++){
+		buffer_2[p]= ( outputstruct_t* ) malloc(sizeof(outputstruct_t));
+	}
+
+	//structure buffer_t
+	buffers_t *buffers = (buffers_t*) malloc(sizeof(buffers_t));
+	buffers->buf_1 = buffer_1;
+	buffers->buf_2 = buffer_2;
+
+	//sémaphores
+	if(sem_init(&empty_1     , 0 , size1) != 0){	fprintf(stderr,"erreur dans le sem_init empty_1\n"  	);}
+	if(sem_init(&full_1      , 0 , 0    ) != 0){	fprintf(stderr,"erreur dans le sem_init full_1 \n"  	);}
+	if(sem_init(&empty_2     , 0 , size1) != 0){	fprintf(stderr,"erreur dans le sem_init empty_2\n"  	);}
+	if(sem_init(&full_2      , 0 , 0    ) != 0){	fprintf(stderr,"erreur dans le sem_init full_2 \n"  	);}
+
+	//threads
+	pthread_t threads[n_threads];
+	pthread_t thread_ecriture;
+	pthread_t thread_lecture;
+
+	///////////////////////////////////////////////////////////////////////////////////////////////
+	//ON MET LES THREADS AU TRAVAIL
+
+	if (pthread_create(&thread_ecriture,NULL,&fct_file_out ,(void*) buffers) != 0 ){	fprintf(stderr,"erreur create thread ecriture\n");	}
+	if (pthread_create(&thread_lecture, NULL,&fct_file_in  ,(void*) buffers) != 0 ){	fprintf(stderr,"erreur create thread lecture\n");	}
+	for (int i=0; i<n_threads; i++){
+		if (pthread_create(&(threads[i]),NULL, &fct_calcul , (void*)buffers) != 0){	fprintf(stderr,"erreur create_thread[%d]\n",i);		}
+	}
+
+	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	/*
+	- JOIN    des threads,
+	- DESTROY des mutex
+	- DESTROY des sémaphores
+	- FREE    des données
+	- CLOSE   des files
+	*/
+	if (pthread_join(thread_lecture, NULL) != 0) {		fprintf(stderr,"erreur dans appel à pthread_join_lecture\n"); 	}
+	for( int j = 0; j <n_threads ; j++){
+		if( pthread_join(threads[j],NULL ) != 0){	fprintf(stderr,"erreur dans appel à pthread_join_calculs\n");	}
+	}
+	if (pthread_join(thread_ecriture, NULL) != 0) {		fprintf(stderr,"erreur dans appel à pthread_join_ecriture\n");	}
+
+	if(sem_destroy(&empty_1)!=0){	fprintf(stderr,"erreur dans sem destroy empty1 \n");	}
+	if(sem_destroy(&full_1 )!=0){	fprintf(stderr,"erreur dans sem destroy full1  \n");	}
+	if(sem_destroy(&empty_2)!=0){	fprintf(stderr,"erreur dans sem destroy empty2 \n");	}
+	if(sem_destroy(&full_2 )!=0){	fprintf(stderr,"erreur dans sem destroy full2  \n");	}
+
+	if(pthread_mutex_destroy(&mutex_buffer_1)!=0){	fprintf(stderr,"erreur dans sem destroy empty1\n");	}
+	if(pthread_mutex_destroy(&mutex_buffer_2)!=0){	fprintf(stderr,"erreur dans sem destroy full1\n" );	}
+
+	for (int b=0;b<size1;b++){	free(buffer_2[b]);	}
+	free(buffer_1);		free(buffer_2);		free(buffers);
+
+	fclose(file_in);	fclose(file_out);	fprintf(stderr,"fin du programme, exit normal O(∩_∩)O\n");
+
+}//fin de la main
+
+
+//la fonction suivante est dans le fichier prime_divs.c
+/*
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// FCT DE CALCUL DES N PREMIERS (POUR UN NUMBER DONNE)
+NTYPE * prime_divs (NTYPE *diviseur, NTYPE number){
+    NTYPE fix_number = number;
+    NTYPE i, j;
+    NTYPE a = ceil(sqrt(number));
+    *diviseur = 0;
+
+    // on a une légère perte de mémoire vu qu'on utilise pas le 0 et le 1 dans les array on pourrais ganger en taille
+
+
+    //NTYPE clk_tck = CLOCKS_PER_SEC;
+    //clock_t t1, t2, t3;
+    //t1 = clock();
+
+    char *bitarray = (char*)malloc(sizeof(char) * a);
+    //INFO: bitset: array de 0 et 1,
+    //1 => pas un bon nombre (pas premier et ou pas diviseur)
+    //0 => ok on le prends (ou pas encore calculé)
+    //exemple pour 35 => bitarray :  [0,1,1,1,0,1]
+
+
+    if (bitarray == NULL){      fprintf(stderr, "erreur malloc de prime_divs\n");    }
+    memset(bitarray, 0, a);
+    // comment faire pour que le nombre lui-même ne soit pas compté dans les diviseurs
+    //t2 = clock();
+    i = 2;
+    //NTYPE variable = 1;
+    //changer le i<a si possible
+    while (number > 1 && i<a+1) {
+
+	//CASE: pour les nombres qui 0 (sont premiers mais pas forcément diviseurs)
+	if (!bitarray[i]){
+
+	        //tous les mult de i (sauf i lui-même) à 1 pcq se sont PAS des nombres premiers
+		for(j = 2*i; j < a; j += i){          if (!bitarray[j]){    bitarray[j] = 1;     }        }
+
+		//SWITCH on sait que i est nombre premier => est-il un diviseur ?
+
+		//CASE oui => les nombres premiers qu'il reste à identifier sont les diviseurs premiers de number/i
+		// (divisés par i autant de fois que nécessaire pour
+		//	- éviter les redondances de calcul
+		//	- trouver plus aisément le dernier nombre premier)
+
+        	if ((number % i) == 0){
+	            //printf("%llu ", i);
+	            //variable*=i;
+	            (*diviseur)++;
+	            number /= i;
+		    while(number % i == 0){       number /= i;
+		    //variable*=i;
+		    }
+
+		//CASE non => on met à 1 pour dire pas compté après
+		}else{	bitarray[i]=1;	}
+        }
+        i++;
+    }
+
+    //t3 = clock();
+    //printf("tout le code prend : %lf ", (t3-t1)/clk_tck);
+    //printf("bitarray prend : %lf ", (t2-t1)/clk_tck);
+
+    NTYPE* prime_dividers;
+    if((   prime_dividers=      (NTYPE*) malloc(sizeof(NTYPE)*((*diviseur)+1) )    )==NULL){      fprintf(stderr,"erreur dans le malloc de prime_divs\n");    }
+
+
+    //Remplir prime_dividers avec les nombres premiers, diviseurs de number
+    NTYPE k = 0;// nombre de diviseurs => PQ pas utiliser diviseurs lui-même ?
+    NTYPE m;
+
+    for(m = 2; m < i; m++) {
+	// on add à prime_dividers le nombres dont l'indice vaut 0 dans le bitset
+        if(!bitarray[m] && (fix_number != 2) ) {
+            //printf(NTYPE_POURCENT, m);
+            prime_dividers[k] = m;
+            k++;
+        }
+    }
+    if (fix_number == 2){
+	(*diviseur)--;
+    // ajoute le denier nombre dans la liste de diviseurs dans un cas particulier de l'algo (à détailler)
+    }if (number != 1 && (number != fix_number)) {
+	prime_dividers[k] = number;
+        (*diviseur)++;
+        //printf("\n");
+    }
+    free(bitarray);
+
+    return prime_dividers;
+}
+*/
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// FONCTION DE LA THREAD DE LECTURE
+
+void* fct_file_in(void* buffs){
+	//BUT: lecture de l'input et remplissage du buffer_1 de buffs par le thread de lecture
+
+	///////////////////////////////////////////////////////////////////////////////////////////////////
+	//PRODUCER BUFFER 1
+
+	//DONNEES
+	buffers_t *buffers = (buffers_t *) buffs;
+	NTYPE* buffer_1 = buffers->buf_1;
+
+	int eof = 0; // eof étant le return de fscan est soit le nombre de int détectés soit EOF
+	int caractere = 0; //pour gérer un cas particulier
+	NTYPE input_number; // contiendra le nombre lu
+
+	//CODE
+	//avec prise en compte des caractères spéciaux dans l'input
+	while (eof != EOF){
+        	// RAPPEL: fscan met le curseur de lecture avant le caractère qu'il n'a pas su lire (un string alors qu'il devait lire un int par ex)
+                // RAPPEL: fgetc lit le caractère et met le curseur de lecture après le caractère
+
+		//gère le cas où on a un nombre négatif
+		char nana; //contiendra le premier caractere d'une ligne du fichier input
+		fscanf(file_in,"%c",&nana);
+
+		if(nana == '-'){	while( fgetc(file_in) !='\n'){}
+		}else{	fseek(file_in,-1,SEEK_CUR);}
+
+		//lit le nombre
+		eof = fscanf(file_in, NTYPE_POURCENT, &input_number);
+
+		//ne prend pas en compte les nombres plus grand que LIMITE (car code non fonctionnel pour ceux-là)
+	        if (input_number > LIMITE){
+        	    continue;
+       		}
+
+		//CASE: fin de lecture de l'input => remplissage de buffer 1 avec n_threads "1" pour lui indiquer que fin de lecture
+		if (eof == EOF){
+			for(int i =0; i< n_threads; i++){
+
+				if(sem_wait(&empty_1)!=0){	fprintf(stderr,"erreur dans le sem wait empty_1 in read fct\n");		}
+
+					buffer_1[index_remplissage_1] = 1;
+					index_remplissage_1++;
+					index_remplissage_1 %= size1;
+
+				if(sem_post(&full_1) != 0){	fprintf(stderr,"erreur dans le sem post full_1 in read fct\n" );		}
+			}
+			continue; //à la prochaine itération, on est sûr de sortir
+		}
+
+		//CASE: pas bon format (caractères spéciaux dans la ligne)
+		if( (eof != 1)  ){		while( fgetc(file_in) !='\n'){}		}
+
+		//CASE: "int\n" bon format et nombre plus grand que 2
+		//met le nombre dans le buffer 1
+		else if( (input_number >= 2) && (caractere=fgetc(file_in))=='\n'){
+				if(sem_wait(&empty_1)!=0){	fprintf(stderr,"erreur dans le sem wait empty_1 in read fct\n");		}
+
+					buffer_1[index_remplissage_1] = input_number;
+					index_remplissage_1++;
+					index_remplissage_1 %= size1;
+
+				if(sem_post(&full_1) != 0){	fprintf(stderr,"erreur dans le sem post full_1 in read fct\n" );		}
+
+ 		}
+
+		/*
+		reste encore un cas particulier, si la ligne de l'input est :
+		4-2\n
+		*/
+		//CASE: on lit le 4 (fscanf), mais le caractère suivant stocké dans
+		//caractere n'est pas \n, donc on passe la ligne
+		else if(caractere != '\n'){
+			while(fgetc(file_in)!='\n'){}
+		}
+
+	}
+	return (void*) NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// FONCTION DES THREADS DE CALCUL
+
+void* fct_calcul(void* buffs){
+	/* BUT :
+	1) prend le nombre dans le buffer 1 de buffs
+	2) appelle prime_divs
+	3) écrit dans buffer_2 de buffs avec le tableau des nombres premiers remplis
+	*/
+
+	//DONNEES
+	buffers_t* buffers = (buffers_t*) buffs;
+	outputstruct_t** buffer_2 = buffers->buf_2;
+	NTYPE* buffer_1 = buffers->buf_1;
+
+	NTYPE nombre;
+	NTYPE diviseurs[1]; //stockera le nombre de diviseurs de nombre
+	diviseurs[0] = 0;
+	NTYPE* array; //contiendra les nombres premiers
+
+	//CODE
+	while(1){
+		//////////////////////////////////////////////////////////////////////////////////////////
+		//A. CONSUMER BUFFER 1
+
+		int index; // indice auxquel on peut aller prendre le nombre dans buffer_1
+
+		//prend le nombre dans le buffer 1
+		if(sem_wait(&full_1)!=0){	fprintf(stderr,"erreur dans le sem wait full_1 in fct calcul\n");	}
+
+			pthread_mutex_lock(&mutex_buffer_1);
+
+				index = index_vidage_1;
+				index_vidage_1++;
+				index_vidage_1 %= size1;
+
+			pthread_mutex_unlock(&mutex_buffer_1);
+
+			nombre = buffer_1[index];
+
+		if(sem_post(&empty_1) != 0){fprintf(stderr,"erreur dans le sem _post empty_1\n" );}
+
+		//CASE number=1: signifie que la lecture de l'input est terminée, donc l'indique dans le buffer 2
+		if(nombre == 1){
+
+			if(sem_wait(&empty_2)!=0){	fprintf(stderr,"erreur dans le sem empty_2 in fct calcul\n");	} // attente d’une place libre
+
+				pthread_mutex_lock(&mutex_buffer_2);
+
+					buffer_2[index_remplissage_2]->number = 1;
+					buffer_2[index_remplissage_2]->number_div = 1;
+					buffer_2[index_remplissage_2]->tab_div = NULL;
+
+					index_remplissage_2++;
+					(index_remplissage_2) %= size1;
+
+				pthread_mutex_unlock(&mutex_buffer_2);
+
+			if(sem_post(&full_2) != 0){	fprintf(stderr,"erreur dans le sem _post full_2\n" );		}
+
+			break;
+		}
+
+		//////////////////////////////////////////////////////////////////////////
+		//B. CALCUL
+
+		//appel de prime_divs pour remplir le tableau des diviseurs premiers de nombre
+		array = prime_divs(diviseurs, nombre);
+
+		/////////////////////////////////////////////////////////////////////////////////////////
+		//C. PRODUCER BUFFER 2
+
+		//remplit le buffer 2 avec la structure complétée pour le nombre
+		if(sem_wait(&empty_2)!=0){	fprintf(stderr,"erreur dans le sem empty_2 in fct calcul\n");	} // attente d’une place libre
+
+			pthread_mutex_lock(&mutex_buffer_2);
+
+				//(le tableau précédent à déjà été free, PAS la structure,cfr thread fct_file_out)
+				buffer_2[index_remplissage_2]->number = nombre;
+				buffer_2[index_remplissage_2]->number_div = *diviseurs;
+				buffer_2[index_remplissage_2]->tab_div = array;
+
+				index_remplissage_2++;
+				(index_remplissage_2) %= size1;
+
+			pthread_mutex_unlock(&mutex_buffer_2);
+
+		if(sem_post(&full_2) != 0){	fprintf(stderr,"erreur dans le sem _post full_2\n" );		}
+	}
+	return (void*) NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// FONCTION DE LA THREAD D'ECRITURE
+
+void* fct_file_out(void* buffs){
+	//BUT: prend les structures dans buffer_2 de buffs et écrit dans l'output
+
+	////////////////////////////////////////////////////////////////////////////////////////
+	// CONSUMER BUFFER 2
+
+	//DONNEES
+	buffers_t* buffers = (buffers_t *) buffs;
+	outputstruct_t** buffer_2 = buffers->buf_2;
+
+	//CODE
+	int n_moinsun = 0; //pour gérer la fin de la boucle while
+
+	while (n_moinsun < n_threads ){
+		if(sem_wait(&full_2)!=0){	fprintf(stderr,"erreur dans le sem wait in fct calcul\n");	} // attente d’une place remplie
+
+		//pthread_mutex_lock(&mutex_buffer_2);
+
+			//écrit dans l'output si ce n'est pas fini
+			if(buffer_2[index_vidage_2]->number != 1){
+				fprintf(file_out,NTYPE_POURCENT,buffer_2[index_vidage_2]->number);
+				for( int i=0; i < buffer_2[index_vidage_2]->number_div ; i++){// number->div est utile ici
+					fprintf(file_out," ");
+					fprintf(file_out,NTYPE_POURCENT,   (buffer_2[index_vidage_2]->tab_div)[i]   );
+				}
+				fprintf(file_out,"\n");
+			//CASE : il n'y a plus de structures à écrire dans l'output
+			//pour le signaler, incrémente n_moinsun; lorsque toutes les threads
+			//sont passées par là, la boucle s'arrête
+			}else{		n_moinsun++;	}
+
+			// pas besoin pcq index_vidage n'est utilisé que par la fct file_out
+			//pthread_mutex_lock(&mutex_buffer_2);
+
+				free(buffer_2[index_vidage_2]->tab_div);//on free le pointeur de l'ancien tableau de nombres premiers de la struct
+
+			//pthread_mutex_unlock(&mutex_buffer_2);
+
+			index_vidage_2++;
+			index_vidage_2 %= size1;
+
+		//pthread_mutex_unlock(&mutex_buffer_2);
+		if(sem_post(&empty_2) != 0){	fprintf(stderr,"erreur dans le sem _post empty_2\n" );	 }
+	}
+	return (void*) NULL;
+}