diff --git a/.gitignore b/.gitignore
index 63fd01b7e0ff4cff41d4cdab8ae1f8270f45d408..6f3007984042ed242514f672889e8f278a7576c3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,6 @@
 *.o
 .vscode/
-test_logs/
+tests_logs/*
 log_files/
 *.zip
 linksimulator/
diff --git a/Makefile b/Makefile
index e96ac90eeb313fc61506c9a147802d8d74f65992..2e5aabb1e5fe2ce0a3820b4ca918ac4bcc3c05ea 100644
--- a/Makefile
+++ b/Makefile
@@ -7,8 +7,8 @@ HEADERS_DIR = -Iheaders
 LDFLAGS = -lz
 
 # Adapt these as you want to fit with your project
-SENDER_SOURCES = $(wildcard src/sender.c src/log.c src/our_utils.c src/packet_interface.c src/sender_utils.c)
-RECEIVER_SOURCES = $(wildcard src/receiver.c src/log.c src/our_utils.c src/packet_interface.c src/receiver_utils.c)
+SENDER_SOURCES = $(wildcard src/sender.c src/log.c src/utils.c src/packet_interface.c src/sender_utils.c)
+RECEIVER_SOURCES = $(wildcard src/receiver.c src/log.c src/utils.c src/packet_interface.c src/receiver_utils.c)
 
 SENDER_OBJECTS = $(SENDER_SOURCES:.c=.o)
 RECEIVER_OBJECTS = $(RECEIVER_SOURCES:.c=.o)
diff --git a/src/log.c b/src/log.c
index 25874a85e483f67b32d15a94547d30ba7ee6684a..e73784e105bf809c2fad0489348988d3e51dd2cd 100644
--- a/src/log.c
+++ b/src/log.c
@@ -4,6 +4,7 @@
 
 #include "log.h"
 
+
 /* Prints `len` bytes starting from `bytes` to stderr */
 void dump(const uint8_t *bytes, size_t len) {
     for (size_t i = 0; i < len;) {
diff --git a/src/our_utils.h b/src/our_utils.h
deleted file mode 100644
index 421c0dae01cc65dc592bf4cadfe457a498f5519a..0000000000000000000000000000000000000000
--- a/src/our_utils.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/**
- * @file our_utils.h
- * @brief This header contains utils that were coded for the packet sending on inginious
- * @date 2022-03-17
- */
-#ifndef __OUR_UTILS_H_
-#define __OUR_UTILS_H_
-
-#include <arpa/inet.h>
-#include <errno.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <unistd.h>
-
-
-/* Resolve the resource name to an usable IPv6 address
- * @address: The name to resolve
- * @rval: Where the resulting IPv6 address descriptor should be stored
- * @return: NULL if it succeeded, or a pointer towards
- *          a string describing the error if any.
- *          (const char* means the caller cannot modify or free the return value,
- *           so do not use malloc!)
- */
-const char * real_address(const char *address, struct sockaddr_in6 *rval);
-
-
-/* Creates a socket and initialize it
- * @source_addr: if !NULL, the source address that should be bound to this socket
- * @src_port: if >0, the port on which the socket is listening
- * @dest_addr: if !NULL, the destination address to which the socket should send data
- * @dst_port: if >0, the destination port to which the socket should be connected
- * @return: a file descriptor number representing the socket,
- *         or -1 in case of error (explanation will be printed on stderr)
- */
-int create_socket(struct sockaddr_in6 *source_addr, int src_port, struct sockaddr_in6 *dest_addr, int dst_port);
-
-
-/* Block the caller until a message is received on sfd,
- * and connect the socket to the source addresse of the received message
- * @sfd: a file descriptor to a bound socket but not yet connected
- * @return: 0 in case of success, -1 otherwise
- * @POST: This call is idempotent, it does not 'consume' the data of the message,
- * and could be repeated several times blocking only at the first call.
- */
-int wait_for_client(int sfd);
-
-
-#endif
\ No newline at end of file
diff --git a/src/packet_interface.c b/src/packet_interface.c
index 7193b58b4576d683aeaca5b315a60f789ebad22c..a255a01a67b5e0921fc35806f8cfb9950da3e79a 100644
--- a/src/packet_interface.c
+++ b/src/packet_interface.c
@@ -55,27 +55,33 @@ pkt_status_code pkt_decode_ack_nack(const char *data, const size_t len, pkt_t *p
 
 pkt_status_code pkt_decode_data_fec(const char *data, const size_t len, pkt_t *pkt)
 {
-    uint16_t length;
-    memcpy((void *) &length, (void *) &data[1], 2);
-    length = ntohs(length);
+    uint16_t payload_length, actual_indicated_length;
+    ptypes_t type = (data[0] & TYPE_MASK) >> TYPE_SHIFT; 
+
+    memcpy((void *) &actual_indicated_length, (void *) &data[1], 2);
+    actual_indicated_length = ntohs(actual_indicated_length);
+    payload_length = (type == PTYPE_DATA) ? actual_indicated_length : MAX_PAYLOAD_SIZE; // Fec are always of length 512
 
     if ( len < PKT_MIN_HEADERLEN )
         return E_NOHEADER;
 
-    if ( len > PKT_MAX_LEN || length > MAX_PAYLOAD_SIZE )
+    if ( len > PKT_MAX_LEN || payload_length > MAX_PAYLOAD_SIZE )
         return E_LENGTH;
 
-    ptypes_t type = (data[0] & TYPE_MASK) >> TYPE_SHIFT; 
+    
     if ( type == PTYPE_FEC  && (data[0] & TR_MASK ) != 0 )
         return E_TR;
         
-    size_t expected_len = sizeof(header_t) + TIMESTAMP_SIZE + CRC_SIZE + length;
+    size_t expected_len = sizeof(header_t) + TIMESTAMP_SIZE + CRC_SIZE + payload_length;
 
-    if ( length > 0 )
+    if ( payload_length > 0 )
         expected_len += CRC_SIZE;
      
-    if ( len != expected_len )
+    if ( len != expected_len && (data[0] & TR_MASK) == 0)
+    {
         return E_UNCONSISTENT; 
+    }
+        
 
     // We set the TR to 0 in order to calculcate the CRC on the header
     char modified_header[8]; 
@@ -94,11 +100,11 @@ pkt_status_code pkt_decode_data_fec(const char *data, const size_t len, pkt_t *p
     @brief : We don't check the checksum and the window here
     **/
     uint32_t crc2;
-    if ( (type == PTYPE_DATA && (data[0] & TR_MASK ) == 0 && length > 0) || type == PTYPE_FEC )
+    if ((data[0] & TR_MASK ) == 0 && payload_length > 0 )
     {
-        memcpy((void *) &crc2, &data[12+length], 4);
+        memcpy((void *) &crc2, &data[12+payload_length], 4);
         crc2 = ntohl((uint32_t) crc2);
-        if (calculate_crc(&data[12], length) != crc2)
+        if (calculate_crc(&data[12], payload_length) != crc2)
             return E_CRC;
         pkt_set_crc2(pkt, crc2);
     } 
@@ -107,18 +113,18 @@ pkt_status_code pkt_decode_data_fec(const char *data, const size_t len, pkt_t *p
     memcpy((void *) &timestamp, &data[4], 4);
 
     memcpy(&(pkt->header.front), data, 1);
-    pkt_set_length(pkt, length);
+    pkt_set_length(pkt, actual_indicated_length);
     pkt_set_seqnum(pkt, seqnum);
     pkt_set_timestamp(pkt, timestamp);
     pkt_set_crc1(pkt, crc1);
-    pkt_set_payload(pkt, &data[12], length);
+    pkt_set_payload(pkt, &data[12], payload_length);
     return PKT_OK;
 }
 
 pkt_status_code pkt_decode(const char *data, const size_t len, pkt_t *pkt)
 {
     ptypes_t type = ((data[0]) & TYPE_MASK) >> TYPE_SHIFT; 
-    if ( type == PTYPE_ACK || type == PTYPE_NACK)
+    if (type == PTYPE_ACK || type == PTYPE_NACK)
     {
         return pkt_decode_ack_nack(data, len, pkt);
     } else
@@ -145,7 +151,7 @@ pkt_status_code pkt_encode_ACK_NACK(const pkt_t *pkt, char *buf, size_t *len)
 pkt_status_code pkt_encode_DATA_FEC(const pkt_t *pkt, char *buf, size_t *len)
 {   
     // Let's first copy the header
-    if ( *len < 12 ) return E_NOMEM;
+    if ( *len < PKT_MIN_HEADERLEN ) return E_NOMEM;
     memcpy((void *) &buf[0],   (void *) &(pkt->header.front),   1);
     uint16_t n_length  = htons(pkt_get_length(pkt));
     memcpy((void *) &buf[1], (void *) &n_length,                2);
@@ -157,20 +163,20 @@ pkt_status_code pkt_encode_DATA_FEC(const pkt_t *pkt, char *buf, size_t *len)
 
     size_t required_size;
     ptypes_t type = pkt_get_type(pkt);
-    uint16_t length = (type == PTYPE_DATA && pkt_get_tr(pkt) == 0) ?  pkt_get_length(pkt) : MAX_PAYLOAD_SIZE;
-    required_size = sizeof(header_t) + TIMESTAMP_SIZE + CRC_SIZE + length;
+    uint16_t payload_length = (type == PTYPE_DATA && pkt_get_tr(pkt) == 0) ?  pkt_get_length(pkt) : MAX_PAYLOAD_SIZE; // Cause FEC payload is always max size
+    required_size = sizeof(header_t) + TIMESTAMP_SIZE + CRC_SIZE + payload_length;
     if ( pkt_get_length(pkt) > 0 )
         required_size += CRC_SIZE;
 
     if ( *len < required_size ) 
         return E_NOMEM;
 
-    *len =  sizeof(header_t) + TIMESTAMP_SIZE + CRC_SIZE + length;
-    if ( pkt_get_length(pkt) > 0 )
+    *len =  sizeof(header_t) + TIMESTAMP_SIZE + CRC_SIZE + payload_length;
+    if ( payload_length > 0 )
     {
-        memcpy((void *) &buf[12], (void *) pkt->payload, length);
-        uint32_t crc2 = htonl(calculate_crc(&buf[12], length));
-        memcpy((void *) &buf[12 + length], (void *) &crc2, CRC_SIZE);
+        memcpy((void *) &buf[12], (void *) pkt->payload, payload_length);
+        uint32_t crc2 = htonl(calculate_crc(&buf[12], payload_length));
+        memcpy((void *) &buf[12 + payload_length], (void *) &crc2, CRC_SIZE);
         *len = *len + CRC_SIZE;
     }
     return PKT_OK;
diff --git a/src/packet_interface.h b/src/packet_interface.h
index 181dc94a21d823b623621c5a7de5645da6d627d9..930f455041fe902838849fdeab72af186c6faca1 100644
--- a/src/packet_interface.h
+++ b/src/packet_interface.h
@@ -10,6 +10,7 @@
 #include <stdio.h>  /* ssize_t */
 #include <zlib.h>
 
+#include "log.h"
 
 /* Taille maximale permise pour le payload */
 #define MAX_PAYLOAD_SIZE 512
@@ -68,7 +69,7 @@ typedef enum {
 #define TR_MASK                         0x20
 #define WINDOW_MASK                     0x1F
 #define TR_SETTER_TO_ZERO               0xDF
-#define SECOND_TO_LAST_PKT              0xFFFFFFFF // Use to specify it was the second to last pkt sent by the sender
+#define SECOND_TO_LAST_PKT              0x3AB33922 // Use to specify it was the second to last pkt sent by the sender -> Need to be random ?
 
 #define TYPE_SHIFT                      6
 #define TR_SHIFT                        5
diff --git a/src/receiver.c b/src/receiver.c
index b4b3b0f53cc59e30e192b8b87c58e51552fba512..899f07301471cdf7219921c7c8952bb3e5d579c5 100644
--- a/src/receiver.c
+++ b/src/receiver.c
@@ -60,9 +60,8 @@ int main(int argc, char **argv) {
         return (EXIT_FAILURE);
     }
 
-    ASSERT(stats_filename == NULL);
+    receiver_read_write_loop(socket, (const char *) stats_filename);
 
-    receiver_read_write_loop(socket);
     close(socket);
     free(addr);
     return EXIT_SUCCESS;
diff --git a/src/receiver_utils.c b/src/receiver_utils.c
index eead2ab97dfad023412beaa5d31143df41de6f83..7a0f85d848b418bf60dc47bdfa7584afbf4fa591 100644
--- a/src/receiver_utils.c
+++ b/src/receiver_utils.c
@@ -1,5 +1,17 @@
 #include "receiver_utils.h"
 
+
+/**
+ * @brief The function is going to check if we need to send an ACK or NACK on the @pfd, 
+ * and will send at most one packet [either an ACK or NACK or nothing].
+ * 
+ * @param pfd: The pollfd struct that we used to get the file descriptor to send the messsage on.
+ * @param state: The current stae of the receiver.
+ * 
+ * @return 0 upon success, else -1 with a message printed on the stderr.
+ * 
+ * @modifies: state
+*/
 int send_if_inneed(struct pollfd * pfd, receiver_state_t * state)
 {
     // Priority to NACKs
@@ -23,8 +35,8 @@ int send_if_inneed(struct pollfd * pfd, receiver_state_t * state)
             return -1;
         }   
         DEBUG("Sent NACK that [%d] has been truncated", pkt_get_seqnum(state->nack_to_send[nack_idx]));
-        DEBUG_DUMP(buffer, len);
         pkt_del(state->nack_to_send[nack_idx]);
+        state->stats->nack_sent++;
         state->nack_to_send[nack_idx] = NULL;
     } else if (state->ack_to_send != NULL)  /* There's an ack to send */                                 
     {
@@ -42,66 +54,144 @@ int send_if_inneed(struct pollfd * pfd, receiver_state_t * state)
             return -1;
         }   
         DEBUG("Sent ACK saying we are waiting for %d, timestamp %d", pkt_get_seqnum(state->ack_to_send), pkt_get_timestamp(state->ack_to_send));
-        DEBUG_DUMP(buffer, len);
         pkt_del(state->ack_to_send);
+        state->stats->ack_sent++;
         state->ack_to_send = NULL;
     }
     return 0;
 } 
 
-int consume_data(receiver_state_t * state, uint8_t seqnum_to_consume)
+/**
+ * @brief Write the payload of the packet in the state->recvd_data_buf[seqnum_to_consume] to the stdout.
+ * 
+ * @param state: The current stae of the receiver.
+ * @param seqnum_to_consume: The seqnum of the packet to consume.
+ * 
+ * @return 0 upon success, else -1 with a message printed on the stderr.
+ * 
+ * @modifies: state.
+ */
+int consume_data_pkt(receiver_state_t * state, uint8_t seqnum_to_consume)
 {
-    size_t written = fwrite((void *) pkt_get_payload(state->recvd_buf[seqnum_to_consume]), sizeof(char), (size_t) pkt_get_length(state->recvd_buf[seqnum_to_consume]), stdout);
-    if (written != pkt_get_length(state->recvd_buf[seqnum_to_consume]))
+    ASSERT(state->recvd_data_buf[seqnum_to_consume] != NULL);
+    size_t written = fwrite((void *) pkt_get_payload(state->recvd_data_buf[seqnum_to_consume]), sizeof(char), (size_t) pkt_get_length(state->recvd_data_buf[seqnum_to_consume]), stdout);
+    if (written != pkt_get_length(state->recvd_data_buf[seqnum_to_consume]))
     {
         ERROR("Couldn't write the full packet content");
         return -1;
     }
     fflush(stdout);
-    state->latest_timestamp  = pkt_get_timestamp(state->recvd_buf[seqnum_to_consume]);
-    pkt_del(state->recvd_buf[seqnum_to_consume]);
-    state->recvd_buf[seqnum_to_consume] = NULL;
+    state->latest_timestamp  = pkt_get_timestamp(state->recvd_data_buf[seqnum_to_consume]);
+    pkt_del(state->recvd_data_buf[seqnum_to_consume]);
+    state->recvd_data_buf[seqnum_to_consume] = NULL;
     return 0;
+}        
+
+/**
+ * @brief This function checks if 4 consecutive packets, starting from the positions_to_start, have been received.
+ * 
+ * @param state: The current stae of the receiver.
+ * @param position_to_start: The position from which to start the inspection.
+ * 
+ * @returns 1 Upon true else 0.
+ */ 
+uint16_t next_four_packets_received(receiver_state_t * state, uint16_t position_to_start)
+{
+    /* 
+       256 is dividable by 4 if the the sender respects the constraint of sending a FEC each 4 packets then 
+       we just have to check increasingly only. Hence why the loop consition checks if the next 4 packets are received
+       and doesn't check the circular condition.
+    */
+    ASSERT(position_to_start < TWO_EXP_EIGHT);
+    if ( position_to_start > TWO_EXP_EIGHT - FEC_CALCULATED_ON)
+        return 0;
+
+    for (uint16_t i = position_to_start; i < position_to_start + FEC_CALCULATED_ON; i++)
+    {
+        if (state->recvd_data_buf[i] == NULL)
+            return 0;
+    } 
+    return 1;
+}
+
+/**
+ * @brief Checks if we can consume data from the buffer. 
+ * 
+ * @param state: The current receiver state.
+ * @param latest_packet_received: The latest valid packet we received 
+ * 
+ * @return The number of packets to consume.
+ * @modifies: state.
+ */ 
+uint16_t can_consume(receiver_state_t * state, const pkt_t * latest_packet_received){
+    // We have received the last 4 paquets harmless or we just received the last packet
+    int to_consume = 0;
+    ASSERT(state->next_to_consume >= 0 && state->next_to_consume < TWO_EXP_EIGHT);
+    if (pkt_get_length(latest_packet_received) == 0)
+    {   // Last packet we read everything from the buffer
+        for (uint16_t i = state->next_to_consume; state->recvd_data_buf[i] != NULL; i = (i + 1) % TWO_EXP_EIGHT )
+            to_consume++;
+    } else 
+    {   // We only read blocks of 4 packets.
+        uint16_t start_to_consume = state->next_to_consume;
+        for (; next_four_packets_received(state, start_to_consume % TWO_EXP_EIGHT); start_to_consume+=4)
+            to_consume+=4;
+    }
+    return to_consume;
 }
 
+/**
+ * @brief Updates the state, upon new data entrance. This function is the core function that handles the writing of the
+ * date to the stdout. Updates the receiver window, last_received_in_order; etc.
+ * 
+ * @param state: The current receiver state.
+ * @param pkt: The data packet that just been received.
+ * 
+ * @returns: 0 upon success else -1 with an error message on the stderr.
+ * 
+ * @modifies: state 
+ */
 int update_buffer_upon_new_data(receiver_state_t * state, const pkt_t * pkt)
 {
     // Find free space 
-    state->curr_recv_window = (state->curr_recv_window > 0) ? state->curr_recv_window-1 : 0;
-
     uint8_t seqnum = pkt_get_seqnum(pkt);
+
     // New packet
-    if (state->recvd_buf[seqnum] == NULL)
+    if (state->recvd_data_buf[seqnum] == NULL)
     {
         pkt_t * pkt_to_store = pkt_new();
         if (pkt_to_store == NULL)
             return -1;
         memcpy((void *) pkt_to_store, (void *) pkt, sizeof(pkt_t));
-        state->recvd_buf[seqnum] = pkt_to_store;
+        state->recvd_data_buf[seqnum] = pkt_to_store;
+        state->curr_recv_window  = (state->curr_recv_window > 0) ? state->curr_recv_window-1 : 0;
+    } else {
+        state->stats->packet_duplicated++;
     }
 
+    // Update last received in order
     if (seqnum == (state->last_received_in_order + 1) % TWO_EXP_EIGHT)
     {
         state->last_received_in_order = seqnum;
-    } else 
-    {
-        DEBUG("Out of sequence");
-        return 0;
+        uint16_t idx = seqnum;
+        for (;state->recvd_data_buf[idx] != NULL; idx = (idx + 1) % TWO_EXP_EIGHT)
+            state->last_received_in_order = idx; 
     }
+    uint16_t to_consume = can_consume(state, pkt);
+    if (to_consume == 0)
+        return 0;
 
-    // We only reach here when we received packets in sequence.
-
-    uint16_t next_wait = state->last_received_in_order;
-    while (state->recvd_buf[next_wait] != NULL)
+    DEBUG("Going to consume the next %d packets.", to_consume);
+    while (to_consume > 0)
     {
         DEBUG("Consuming packet : %d | curr_recv_window = %d, recv_window_start = %d", 
-                                next_wait, state->curr_recv_window, state->recv_window_start);
+                                state->next_to_consume, state->curr_recv_window, state->recv_window_start);
 
-        if (consume_data(state, next_wait) != 0)    return -1;
-        state->last_received_in_order = next_wait;
-        state->curr_recv_window  = (state->curr_recv_window < RECV_MAX_SLCTV_RPT_WDW) ? state->curr_recv_window+1 : RECV_MAX_SLCTV_RPT_WDW;
+        if (consume_data_pkt(state, state->next_to_consume) != 0)    return -1;
+        state->curr_recv_window  = (state->curr_recv_window < RECV_MAX_SLCTV_RPT_WDW) ? state->curr_recv_window + 1 : RECV_MAX_SLCTV_RPT_WDW;
         state->recv_window_start = (state->recv_window_start + 1) % TWO_EXP_EIGHT; 
-        next_wait                = (next_wait + 1) % TWO_EXP_EIGHT; 
+        state->next_to_consume   = (state->next_to_consume + 1) % TWO_EXP_EIGHT; 
+        to_consume--;
     }
     return 0;
 }
@@ -111,7 +201,8 @@ int update_buffer_upon_new_data(receiver_state_t * state, const pkt_t * pkt)
  * there will be an ACK to send.
  * 
  * @param state:    The connection state structure.
- * @param pkt:      The packet for which we should prepare ack. Can be nullable in that case we just send the ack latest received
+ * @param pkt:      The packet for which we should prepare ack. Can be nullable in that case we just send the ack latest received.
+ * 
  * @returns         0 upon success else -1
  * 
  * @modifies: @state.
@@ -152,16 +243,21 @@ int prepare_ack_to_send(receiver_state_t * state)
 }
 
 /**
- * @brief This function handles PTYPE_DATA arriving packets amd updates the state. 
+ * @brief This function handles PTYPE_DATA arriving packets and updates the state. 
+ * 
+ * @param state: The receiver state.
+ * @param pkt: The DATA packet.
  * 
- * @param state: The receiver state
- * @param pkt: The DATA packet
  * @returns 0 upon success else -1.
  * 
  * @modifies: state.
  */
 int handle_data_pkt(receiver_state_t * state, const pkt_t * pkt)
 {
+    state->stats->data_received++;
+    DEBUG("Received data packet seqnum %d with timestamp %d | current_window_size : %d, current_window_start : %d", 
+               pkt_get_seqnum(pkt), pkt_get_timestamp(pkt), state->curr_recv_window, state->recv_window_start);
+               
     if (update_buffer_upon_new_data(state, pkt) != 0)  return -1;
 
     if (prepare_ack_to_send(state) != 0)   return -1;
@@ -182,6 +278,145 @@ int handle_data_pkt(receiver_state_t * state, const pkt_t * pkt)
         state->last_data_packet = pkt_get_seqnum(pkt);
 
     return 0;
+}   
+
+/**
+ * @brief: This function uses the fec packet to recover the missing packet with missing_seqnum as seqnum.
+ * 
+ * @param state: The receiver state.
+ * @param pkt: The FEC packet we just received.
+ * @param missing_seqnum: The packet we are going to recover from the fec packet.
+ * 
+ * @returns 0 upon success else -1 with an error message on stderr.
+ * 
+ * @modifies: state. 
+ */ 
+int use_fec(receiver_state_t * state, const pkt_t * fec, uint16_t missing_seqnum)
+{
+    pkt_t * new_packet = pkt_new();
+    if ( new_packet == NULL )
+    {
+        ERROR("An error occured when initiating a new packet in order to use FEC");
+        return -1;
+    }
+    uint16_t fec_seqnum = pkt_get_seqnum(fec);
+    pkt_set_type(new_packet, PTYPE_DATA);
+    pkt_set_seqnum(new_packet, missing_seqnum);
+    pkt_set_length(new_packet, pkt_get_length(fec));
+    for ( uint16_t idx = fec_seqnum; idx < fec_seqnum + FEC_CALCULATED_ON; idx++ )
+    {
+        if ( state->recvd_data_buf[idx] != NULL)
+            pkt_set_length(new_packet,  pkt_get_length(new_packet) ^ pkt_get_length(state->recvd_data_buf[idx]));
+    }
+    uint16_t payload_length = pkt_get_length(new_packet);
+    uint8_t new_payload[payload_length];
+    uint8_t * fec_payload = (uint8_t *) pkt_get_payload(fec);
+    uint8_t * other_payloads[FEC_CALCULATED_ON-1];
+    
+    for ( uint16_t idx = fec_seqnum, idx_other_payloads = 0; idx < fec_seqnum + FEC_CALCULATED_ON; idx++ )
+    {
+        if ( state->recvd_data_buf[idx] != NULL)
+        {
+            other_payloads[idx_other_payloads] = (uint8_t *) pkt_get_payload(state->recvd_data_buf[idx]);
+            idx_other_payloads++;
+        }
+    }
+    
+    for ( uint16_t i = 0; i < payload_length; i++ )
+    {
+        new_payload[i] = fec_payload[i];
+        for ( uint16_t idx_other_payloads = 0; idx_other_payloads < FEC_CALCULATED_ON-1; idx_other_payloads++ )
+            new_payload[i] = new_payload[i] ^ other_payloads[idx_other_payloads][i];
+    }
+    pkt_set_payload(new_packet, (char *) new_payload, payload_length);
+        
+    DEBUG("Used FEC packet [%d] to recover data packet with seqnum [%d] | The new packet payload length is %d", 
+            fec_seqnum, missing_seqnum, payload_length);
+
+    // Add packet to received data buffer and treat the packet
+    // We pass the packet to the rest as if it had just came from the network
+    if (handle_data_pkt(state, new_packet) != 0) return -1;
+
+    pkt_del(new_packet);
+    state->stats->packet_recovered++;
+    return 0;
+}
+
+/**
+ * @brief: This function checks whether fec with seqnum as fec_seqnum, can be used to recover 
+ * a missing packet. 
+ * 
+ * @param state: The receiver state.
+ * @param fec_seqnum: The seqnum of the fec packet that we just received.
+ * @param missing_seqnum: The pointer to the variable to which put the missind_seqnum if the the fec can be used.
+ * 
+ * @returns: 1 upon True else 0. if True missing_seqnum set to the packet that can be recoverd by the fec.
+ */ 
+int can_fec_be_used(receiver_state_t * state, uint16_t fec_seqnum, uint16_t * missing_seqnum)
+{
+    uint16_t idx = fec_seqnum;
+    uint16_t in_range_missing   = 0;
+    uint16_t in_range_available = 0;
+    for (; idx < fec_seqnum + FEC_CALCULATED_ON; idx++)
+    {
+        if (state->recvd_data_buf[idx] != NULL)
+        {    
+            in_range_available++;
+        } else
+        {
+            in_range_missing++;
+            *missing_seqnum = idx;
+        }
+    }
+    return (in_range_available == FEC_CALCULATED_ON-1 && in_range_missing == 1);
+}
+
+/**
+ * @brief: This function checks the potential usage of the fec, if the fec can be used then it uses the fec else it doesn't. 
+ * 
+ * @param state: The receiver state.
+ * @param pkt: The fec packet.
+ * 
+ * @returns: 0 upon success else -1 
+ */ 
+int potential_usage_of_fec(receiver_state_t * state, const pkt_t * fec)
+{
+    uint16_t fec_seqnum = pkt_get_seqnum(fec);
+    uint16_t missing_data_seqnum = 0;
+    if ( can_fec_be_used(state, fec_seqnum, &missing_data_seqnum) )
+    {
+        if ( use_fec(state, fec, missing_data_seqnum) != 0 ) return -1;
+    } else
+    {
+        DEBUG("Received FEC with seqnum [%d] but wasn't used", fec_seqnum);
+    }
+    return 0;
+}
+
+/**
+ * @brief This function handles PTYPE_FEC arriving packets and updates the state. 
+ * 
+ * @param state: The receiver state.
+ * @param pkt: The DATA packet. 
+ * @returns 0 upon success else -1.
+ * 
+ * @requires: The packet is in the current receiving window
+ * 
+ * @modifies: state.
+ */
+int handle_fec_pkt(receiver_state_t * state, const pkt_t * pkt)
+{
+    state->stats->fec_received++;
+    ASSERT(state != NULL && pkt != NULL);
+    uint8_t seqnum = pkt_get_seqnum(pkt); 
+    if (seqnum + 3 < state->last_received_in_order )
+    {
+        DEBUG("Received FEC with seqnum [%d] but wasn't used since last received in order : %d", 
+                pkt_get_seqnum(pkt), state->last_received_in_order);
+        state->stats->packet_ignored++;
+        return 0;
+    }
+    return potential_usage_of_fec(state, pkt);
 }
 
 /**
@@ -200,10 +435,14 @@ int handle_data_pkt(receiver_state_t * state, const pkt_t * pkt)
  */
 int handle_truncated_pkt(receiver_state_t * state, const pkt_t * pkt)
 {
+    state->stats->data_truncated_received++;
+    DEBUG("Received a truncated packet with seqnum %d", pkt_get_seqnum(pkt));
+    if (pkt_get_type(pkt) != PTYPE_DATA) return 0;
+
     int free_idx = 0;
     for (; free_idx < RECV_MAX_SLCTV_RPT_WDW && state->nack_to_send[free_idx] != NULL; free_idx++);
     
-    if ( free_idx == 0 )    return 0;
+    if ( free_idx == 32 )    return 0;
 
     pkt_t * pkt_to_send = pkt_new();  
     if (pkt_to_send == NULL)                return -1;
@@ -223,7 +462,7 @@ int handle_truncated_pkt(receiver_state_t * state, const pkt_t * pkt)
  * and modifies @state.
  * 
  * @requires:
- *  - @pkt was validly decoded by pkt_decoded().
+ *  - @pkt was validly decoded by pkt_decoded().l
  * 
  * @param pkt:        The packet received.
  * @param state:      The current connection state.
@@ -233,9 +472,6 @@ int handle_truncated_pkt(receiver_state_t * state, const pkt_t * pkt)
  */ 
 int handle_valid_pkt(receiver_state_t * state, const pkt_t * pkt)
 {
-    // Feck - Not handled for now
-    if (pkt_get_type(pkt) != PTYPE_DATA)    return 0;
-
     // Is it in the receive_window ?
     uint8_t seqnum = pkt_get_seqnum(pkt);
     int in_window  = (seqnum >= state->recv_window_start);
@@ -247,16 +483,22 @@ int handle_valid_pkt(receiver_state_t * state, const pkt_t * pkt)
 
     if (!in_window)
     {
-        DEBUG("Received packet [%d] Out of window with %d | receive window start at : %d (included) till %d (excluded)", 
-               seqnum, pkt_get_timestamp(pkt), state->recv_window_start, seqnum < (state->recv_window_start + RECV_MAX_SLCTV_RPT_WDW) % TWO_EXP_EIGHT);
+        DEBUG("Received packet [%d] Out of window with timestamp %d | receive window start at : %d (included) till %d (excluded)", 
+               seqnum, pkt_get_timestamp(pkt), state->recv_window_start, (state->recv_window_start + RECV_MAX_SLCTV_RPT_WDW) % TWO_EXP_EIGHT);
         return prepare_ack_to_send(state);
     }
         
     uint8_t tr = pkt_get_tr(pkt);
-    if (tr == 1)    /* If truncated     */
+    if (tr == 1)
+    {   /* If truncated */
         return handle_truncated_pkt(state, pkt);
-    else            /* If not truncated */
+    } else if (pkt_get_type(pkt) == PTYPE_DATA)
+    {   /* Type DATA    */
         return handle_data_pkt(state, pkt);
+    } else if (pkt_get_type(pkt) == PTYPE_FEC) 
+    {   /* Type FEC     */
+        return handle_fec_pkt(state, pkt);
+    }
     return 0;
 }
 
@@ -266,7 +508,7 @@ int handle_valid_pkt(receiver_state_t * state, const pkt_t * pkt)
  * 
  * @warning Modifiers state
  * 
- * @param pfd:        The struct pollfd sur lequel cherche le fd et lire.
+ * @param pfd:  The struct pollfd sur lequel cherche le fd et lire.
  * @returns     0 upon success and -1 in cas of failure. 
  */ 
 int handle_incoming(struct pollfd * pfd, receiver_state_t * state)
@@ -285,16 +527,14 @@ int handle_incoming(struct pollfd * pfd, receiver_state_t * state)
     pkt_status_code pkt_status = pkt_decode(buffer, read_bytes, pkt);
     if (pkt_status == 0)
     {
-        DEBUG("Received packet seqnum %d with timestamp %d | current_window_size : %d, current_window_start : %d", 
-               pkt_get_seqnum(pkt), pkt_get_timestamp(pkt), state->curr_recv_window, state->recv_window_start);
-        
         if (handle_valid_pkt(state, (const pkt_t *) pkt) != 0) return -1;
-    } else 
+    } else
     {            
-        /* If the packet has been damaged we can discuss if it's better to send a ACK
-        or not but for now we just ignore it */
-        DEBUG("Received a damaged packet with %d status.", pkt_status);
-        return prepare_ack_to_send(state);
+        DEBUG("Received a damaged packet with %d status. and seqnum as %d", pkt_status, pkt_get_seqnum(pkt));
+        // See if there's a FEC that can be used and update buffer
+        pkt_del(pkt);
+        state->stats->packet_ignored++;
+        return 0;
     }
     pkt_del(pkt);
     return 0;
@@ -315,7 +555,7 @@ void reception_loop(struct pollfd * pfd, receiver_state_t * state)
     {
         int ready = poll(pfd, 1, -1);
 
-        if (ready == -1 || pfd->revents & POLLERR)      /* In case of error on socket */
+        if (ready == -1 || pfd->revents & POLLERR)              /* In case of error on socket */
         {
             DEBUG("Error on socket");
             return;
@@ -325,7 +565,7 @@ void reception_loop(struct pollfd * pfd, receiver_state_t * state)
         {
             if (handle_incoming(pfd, state) != 0)   
                 return;
-            if (state->last_data_packet < TWO_EXP_EIGHT)     /* This means we received last data packet transfer */     
+            if (state->last_data_packet < TWO_EXP_EIGHT)        /* This means we received last data packet transfer */     
                 gettimeofday(&last_packet_received, NULL);
  
         } else if (pfd->revents & POLLOUT) 
@@ -334,7 +574,7 @@ void reception_loop(struct pollfd * pfd, receiver_state_t * state)
                 return;
         } 
 
-        if (state->last_data_packet < TWO_EXP_EIGHT)
+        if (state->last_data_packet < TWO_EXP_EIGHT)            /* This is a signal we use between us in order to mark the last datapacket containing data */
         {
             gettimeofday(&current_time, NULL);
             if (current_time.tv_sec - last_packet_received.tv_sec > RECEIVER_INACTIVE_TIMEOUT)
@@ -353,45 +593,51 @@ void reception_loop(struct pollfd * pfd, receiver_state_t * state)
  */
 receiver_state_t * state_new()
 {
-    receiver_state_t * toReturn = (receiver_state_t *) calloc(1, sizeof(receiver_state_t));
-    if (toReturn == NULL)   return NULL;
+    receiver_state_t * to_return = (receiver_state_t *) calloc(1, sizeof(receiver_state_t));
+    if (to_return == NULL)   return NULL;
+
+    to_return->stats = calloc(sizeof(transfer_stats_t), 1);
+    if (to_return->stats == NULL) 
+    {
+        free(to_return);
+        return NULL;    
+    }
 
     for (size_t i = 0; i < TWO_EXP_EIGHT; i++)
-        toReturn->recvd_buf[i] = NULL;
+        to_return->recvd_data_buf[i] = NULL;
 
     for (size_t i = 0; i < RECV_MAX_SLCTV_RPT_WDW; i++)
-        toReturn->nack_to_send[i] = NULL;
+        to_return->nack_to_send[i] = NULL;
     
-    toReturn->curr_recv_window          = RECV_MAX_SLCTV_RPT_WDW;
-    toReturn->recv_window_start         = 0;
-    toReturn->last_received_in_order    = -1;   // Sign indicating that it hasn't received something
-    toReturn->ack_to_send               = NULL; // Sign that there's no ack to send
-    toReturn->transfer_done             = 0;
-    toReturn->last_packet               = 256;  // No  validseqnum will reach this value, max value 255
-    toReturn->last_data_packet          = 256;
-    return toReturn;
+    to_return->curr_recv_window          = RECV_MAX_SLCTV_RPT_WDW;
+    to_return->recv_window_start         = 0;
+    to_return->last_received_in_order    = -1;   // Sign indicating that it hasn't received something
+    to_return->ack_to_send               = NULL; // Sign that there's no ack to send
+    to_return->transfer_done             = 0;
+    to_return->next_to_consume           = 0;
+    to_return->last_packet               = 256;  // No valid seqnum will reach this value, max value 255
+    to_return->last_data_packet          = 256;    
+    return to_return;
 }
 
 /**
  * @brief This function frees the memory allocated by the state_new() function.
- * @state: The structure to free. 
+ * @param state: The structure to free. 
+ * 
  */
 void state_del(receiver_state_t * state)
 {
     ASSERT(state != NULL);
     for (int i = 0; i < 31; i++)
-        pkt_del(state->recvd_buf[i]);
+        pkt_del(state->recvd_data_buf[i]);
 
     free(state->ack_to_send);
+    free(state->stats);
     free(state);
+
 }
 
-/**
- * @brief This main loop for the receiver.
- * @param sfd: A valid socket.
- * @returns As soon as an error occurs or, a the total transfer came through.
- */
-void receiver_read_write_loop(int sfd)
+void receiver_read_write_loop(int sfd, const char * pathname)
 {
     receiver_state_t * state = state_new(); 
     struct pollfd * pfd = (struct pollfd *) calloc(1, sizeof(struct pollfd));
@@ -405,7 +651,9 @@ void receiver_read_write_loop(int sfd)
     pfd->events         = POLLIN | POLLOUT;
     if (wait_for_client(sfd) == 0)
         reception_loop(pfd, state);
-    DEBUG("Done with done status equal to %d", state->transfer_done);
+    DEBUG("Done the transfer with done status being %s", (state->transfer_done) ? "true" : "false");
+
+    write_stats_to_file(pathname, state->stats, RECEIVER);
     state_del(state);
     free(pfd);
 }
\ No newline at end of file
diff --git a/src/receiver_utils.h b/src/receiver_utils.h
index ba9152f0607f94594082a58005d923c597d8ae73..1f77b62006483afcfa2f59ea326724f3ae2a1a43 100644
--- a/src/receiver_utils.h
+++ b/src/receiver_utils.h
@@ -21,7 +21,7 @@
 #include <unistd.h>
 
 #include "log.h"
-#include "our_utils.h"
+#include "utils.h"
 #include "packet_interface.h"
 
 
@@ -31,6 +31,7 @@
 #define TIMER_MULTIPLIER_BY_1 1
 #define TIMER_MULTIPLIER_BY_2 2
 #define RECEIVER_INACTIVE_TIMEOUT 20
+#define FEC_CALCULATED_ON 4
 
 
 /* Represent the receiver's connection state */
@@ -40,21 +41,25 @@ typedef struct __attribute__((__packed__))
     uint8_t recv_window_start;        
     uint8_t last_received_in_order;
     uint32_t latest_timestamp;
-    pkt_t * recvd_buf[TWO_EXP_EIGHT]; 
+    pkt_t * recvd_data_buf[TWO_EXP_EIGHT]; 
     pkt_t * ack_to_send;
     pkt_t * nack_to_send[RECV_MAX_SLCTV_RPT_WDW];
     uint8_t transfer_done;
     uint16_t last_packet;
     uint16_t last_data_packet;
+    uint16_t next_to_consume;
+    transfer_stats_t * stats;
 } receiver_state_t;
 
 
 /**
- * Loop reading on socket and printing to the stdout
- * @sfd : The socket file descriptor. It is both bound and connected.
- * @return: as soon as the whole transfer is done.
+ * @brief Loop reading on socket and printing to the stdout
+ * @param sfd : The socket file descriptor. It is both bound and connected.
+ * @param pathname: Pathname to where write the stats.
+ * 
+ * @returns As soon as an error occurs or, the total transfer came through.
  */
-void receiver_read_write_loop(int sfd);
+void receiver_read_write_loop(int sfd, const char * pathname);
 
 /**
  * Creates and initialize a receiver_state_t structure
@@ -66,7 +71,7 @@ receiver_state_t * state_new();
 /**
  * Deletes the receiver_state_t structure pointed by the
  * state argument.
- * @state: the state to delete.
+ * @param state: the state to delete.
  * @return: / 
  */
 void state_del(receiver_state_t * state);
diff --git a/src/sender.c b/src/sender.c
index 9fe8f3692788c8612d4c4080ad87053d185b2c4e..4a809651f2bb5476072fccc9bb1ac4f35899f1b9 100644
--- a/src/sender.c
+++ b/src/sender.c
@@ -46,17 +46,10 @@ int main(int argc, char **argv) {
         return print_usage(argv[0]);
     }
 
-    // DEBUG_DUMP("Some bytes", 11); // You can use it with any pointer type
-    ASSERT(fec_enabled == false);
-    ASSERT(stats_filename == NULL);
     DEBUG("Sender has following arguments: \n\t\tfilename is %s,\n\t\tstats_filename is %s,\n\t\tfec_enabled is %d,\n\t\treceiver_ip is %s,\n\t\treceiver_port is %u",
         filename, stats_filename, fec_enabled, receiver_ip, receiver_port);
 
 
-    // Now let's code! 
-    // Alright :-)
-
-
     // *** Step 1: Create a socket AND connect ***
     struct sockaddr_in6 receiver_addr;
     if (real_address(receiver_ip, &receiver_addr) != NULL)
@@ -65,7 +58,7 @@ int main(int argc, char **argv) {
         return EXIT_FAILURE;
     }
 
-    // create_socket also create a connection using connection() !!
+    // create_socket() also creates a connection using connect()
     int socket_fd = create_socket(NULL, 0, &receiver_addr, receiver_port);
     if (socket_fd == -1)
     {
@@ -106,7 +99,7 @@ int main(int argc, char **argv) {
     pfd[0].fd     = socket_fd;
     pfd[0].events = POLLIN | POLLOUT;
 
-    sender_state_t *state = state_new();
+    sender_state_t *state = state_new(fec_enabled);
     if (state == NULL)
     {
         free(pfd);
@@ -118,7 +111,7 @@ int main(int argc, char **argv) {
 
     struct timeval closing_pkt_sent_time;
     struct timeval curr_time;
-    while (state->last_pkt_sent != CLOSING_PKT || state->s_window_size != MAX_WINDOW_SIZE)
+    while ((state->last_pkt_sent != CLOSING_PKT) || (state->s_window_size != MAX_WINDOW_SIZE))
     {
         // Blocking system call
         int rvalue = poll(pfd, 1, -1); // -1 means that there are no setted time out
@@ -134,15 +127,15 @@ int main(int argc, char **argv) {
 
         // Setting a timer only when waiting for the very last ACK
         gettimeofday(&curr_time, NULL);
-        if (state->last_pkt_sent == CLOSING_PKT && ((curr_time.tv_sec - closing_pkt_sent_time.tv_sec) > SENDER_INACTIVE_TIMEOUT))
+        if (state->last_pkt_sent == CLOSING_PKT && ((time_milliseconds(&curr_time) - time_milliseconds(&closing_pkt_sent_time)) > SENDER_INACTIVE_TIMEOUT))
         {
-            DEBUG("The sender hasn't received any news from the receiver for too long so it TIMEOUT");
+            DEBUG("The sender hasn't received any news from the receiver for too long so it TIMEOUT.");
             break;
         }
 
-        if (pfd->revents & POLLIN)
+        if ((pfd->revents & POLLIN) && (pfd->revents & POLLOUT))
         {
-            DEBUG("The sender is reading from the socket");
+            DEBUG("The sender is reading from the socket.");
             rvalue = handle_returning_ack_nack(state, socket_fd);
             if (rvalue == -1)
             {
@@ -156,22 +149,31 @@ int main(int argc, char **argv) {
         }
         else if (can_send(state) && (pfd->revents & POLLOUT))
         {
-            DEBUG("The sender will send a pkt on the socket, the current sender window size is: %d", state->s_window_size);
+            DEBUG("The sender will send a pkt on the socket, the current sender window size is: %d | receiver window size: %d", 
+            state->s_window_size, state->r_window_size);
             rvalue = read_and_send(state, sending_fd, socket_fd);
             if (rvalue == -1)
             {
-                free(pfd);
-                state_del(state);
-                close(socket_fd);
-                close(sending_fd);
-                ERROR("read_and_send function failed");
-                return EXIT_FAILURE;
+                if (state->fec_enabled && state->last_pkt_sent == CLOSING_PKT)
+                {
+                    DEBUG("The very last PTYPE_FEC could not be send because the receiver probably disconnected which is not a problem !");
+                    break;
+                }
+                else
+                {
+                    free(pfd);
+                    state_del(state);
+                    close(socket_fd);
+                    close(sending_fd);
+                    ERROR("read_and_send function failed");
+                    return EXIT_FAILURE;
+                }
             }
-            // Let's start the timer for the last pkt sent
+            // Let's start the timer after the closing pkt (PTYPE_DATA with length = 0)
             if (state->last_pkt_sent == CLOSING_PKT)
             {
                 gettimeofday(&closing_pkt_sent_time, NULL);
-                DEBUG("A timer of -> %ds <- has started after sending the closing pkt for the first time !", SENDER_INACTIVE_TIMEOUT);
+                DEBUG("A timer of -> %dms <- has started after sending the last PTYPE_DATA pkt !", SENDER_INACTIVE_TIMEOUT);
             }
         }
         else if (pfd->revents & POLLOUT)
@@ -179,11 +181,11 @@ int main(int argc, char **argv) {
             rvalue = checking_timer(state, socket_fd);
             if (rvalue == -1)
             {
-                // If an error occured when trying to send back the CLOSING_PKT,
+                // If an error occured when trying to send back the CLOSING_PKT (so when the last FEC has been sent),
                 // we guess that the receiver has simply disconnected and the ACK of the CLOSING_PKT was lost.
                 if (state->last_pkt_sent == CLOSING_PKT)
                 {
-                    DEBUG("The sender can't send anything to the receiver anymore (which has probably disconnected) so it'll disconnect !");
+                    DEBUG("The sender can't send anything to the receiver anymore (which has probably disconnected) so the sender will also disconnect !");
                     break;
                 }
                 else
@@ -198,7 +200,13 @@ int main(int argc, char **argv) {
             }
         }
     }
-    DEBUG("Sender disconnected");
+    DEBUG("Sender disconnected"); 
+    
+    if (stats_filename != NULL)
+    {
+        write_stats_to_file(stats_filename, state->stats, SENDER);
+    }
+
     free(pfd);
     state_del(state);
     close(sending_fd);
diff --git a/src/sender_utils.c b/src/sender_utils.c
index b70b96b5c9c20d74874c69f39e99978bc04338e0..18acddf0131e7c1b910a14fc15eec27d8ebdb249 100644
--- a/src/sender_utils.c
+++ b/src/sender_utils.c
@@ -1,7 +1,7 @@
 #include "sender_utils.h"
 
 
-sender_state_t *state_new()
+sender_state_t *state_new(bool fec_enabled)
 {
     sender_state_t *state = (sender_state_t *) calloc(1, sizeof(sender_state_t));
     if (state == NULL)
@@ -21,13 +21,20 @@ sender_state_t *state_new()
     {
         state->map_seqnum_to_buffer_place[i] = OUT_OFF_WINDOW;
     }
-    state->last_pkt_sent = RANDOM_DATA_PKT; // default
+    state->last_pkt_sent = RANDOM_PKT; // default
+    state->fec_enabled = fec_enabled;
+    if (fec_enabled)
+    {
+        state->FEC = pkt_new();
+        state->FEC_nbr = 0;
+    }
+    state->stats = calloc(sizeof(transfer_stats_t), 1);
     return state;
 }
 
 void state_del(sender_state_t *state)
 {
-    // To be clean, we free the pkt that might not have been freed (in case of an error or timeout)
+    // To be sure, we free the pkt that might not have been freed (in case of an error or timeout)
     for (uint8_t i = 0; i < WINDOW_SIZE; i++)
     {
         if (state->buffer[i] != NULL)
@@ -35,12 +42,17 @@ void state_del(sender_state_t *state)
             pkt_del(state->buffer[i]);
         }
     }
+    if (state->fec_enabled)
+    {
+        pkt_del(state->FEC);
+    }
+    free(state->stats);
     free(state);
 }
 
 bool can_send(sender_state_t *state)
 {
-    if (state->last_pkt_sent == RANDOM_DATA_PKT)
+    if (state->last_pkt_sent == RANDOM_PKT)
     {
         return (state->r_window_size > 0) && (state->s_window_size > 0);
     }
@@ -50,6 +62,12 @@ bool can_send(sender_state_t *state)
         // it was the end of the file (so that I can set a timer for timeout)
         return state->s_window_size == MAX_WINDOW_SIZE;
     }
+    // Case: we're in FEC mode, the closing pkt has been sent but we will still send a last FEC if possible
+    else if ((state->fec_enabled) && (state->last_pkt_sent == CLOSING_PKT) && (state->FEC_nbr > 0))
+    {
+        return true;
+    }
+    // Case last FEC has been sended
     else
     {
         return false;
@@ -80,7 +98,8 @@ int send_pkt(sender_state_t *state, pkt_t *pkt, uint8_t position, int socket_fd)
     state->buffer[position] = pkt;
     struct timeval time;
     gettimeofday(&time, NULL);
-    state->timers[position] = time.tv_sec;
+    state->timers[position] = time;
+    state->stats->data_sent++;
     return 0;
 }
 
@@ -94,6 +113,7 @@ int handle_returning_ack_nack(sender_state_t *state, int socket_fd)
     pkt_status_code pkt_status = pkt_decode(buffer, read_bytes, pkt);
     if (pkt_status != PKT_OK)
     {
+        state->stats->packet_ignored++;
         pkt_del(pkt);
         DEBUG("Decode function on a received pkt failed with status: %d so we discard the pkt", pkt_status);
         return 0;
@@ -103,23 +123,35 @@ int handle_returning_ack_nack(sender_state_t *state, int socket_fd)
     uint8_t r_window  = pkt_get_window(pkt);
     pkt_del(pkt);
 
+    if (pkt_type != PTYPE_ACK && pkt_type != PTYPE_NACK)
+    {
+        state->stats->packet_ignored++;
+        DEBUG("The sender has received a pkt with the type PTYPE_ACK or PTYPE_NACK so it discard it !");
+        return 0;
+    }
+
     // Handling NACK:
     if (pkt_type == PTYPE_NACK) 
     {
-        if (state->map_seqnum_to_buffer_place[seqnum] == OUT_OFF_WINDOW)
+        state->stats->nack_received++;
+        uint8_t place = state->map_seqnum_to_buffer_place[seqnum];
+        if (place == OUT_OFF_WINDOW)
         {
             DEBUG("The NACK with the seqnum: %d is out of the sended window so it has been discarded", seqnum);
         }
         else
         {
-            DEBUG("The NACK with the seqnum: %d has been received", seqnum);
-            state->r_window_size = r_window;
-            if (send_pkt(state, pkt, seqnum, socket_fd) == -1) return -1;
+            state->stats->packet_retransmitted++;
+            DEBUG("The NACK with the seqnum: %d has been received, so the pkt will be sent back", seqnum);
+            state->r_window_size = r_window - 1; // -1 Because the receiver doesn't count yet the sended pkt
+            pkt_t *n_pkt = state->buffer[place];
+            if (send_pkt(state, n_pkt, place, socket_fd) == -1) return -1;
         }
     }
     // Handling ACK:
     else
     {
+        state->stats->ack_received++;
         DEBUG("The ACK with the seqnum: %d has been received", seqnum);
         uint8_t seqnum_nack = seqnum;
         uint8_t seqnum_ack  = seqnum == 0 ? MAX_SEQNUM_SIZE : seqnum - 1;
@@ -127,7 +159,7 @@ int handle_returning_ack_nack(sender_state_t *state, int socket_fd)
         uint8_t place_last_ack  = state->map_seqnum_to_buffer_place[seqnum_ack];
         uint8_t place_last_nack = state->map_seqnum_to_buffer_place[seqnum_nack];
         
-        // Nothing need to be acked
+        // Nothing need to be acknowledged
         if (place_last_ack == OUT_OFF_WINDOW)
         {
             // Checking if it's in my window:
@@ -138,23 +170,37 @@ int handle_returning_ack_nack(sender_state_t *state, int socket_fd)
             }
             else
             {
-                DEBUG("The receiver is asking AGAIN, the seqnum: %d", seqnum_nack);
-                state->r_window_size = r_window;
+                state->stats->packet_retransmitted++;
+                DEBUG("The receiver is asking AGAIN the seqnum: %d so the sender sends it back", seqnum_nack);
+                state->r_window_size = r_window - 1; // -1 Because the receiver doesn't count yet the sended pkt
                 pkt_t *n_pkt = state->buffer[place_last_nack];
                 if (send_pkt(state, n_pkt, place_last_nack, socket_fd) == -1) return -1;
                 return 0;
             }
         }
-        // Some pkt need to be acked
+        // Some pkt need to be acknowledged
         else
         {
             state->r_window_size = r_window;
 
             uint8_t upper_bound = (place_last_ack + 1) % WINDOW_SIZE; // The oldest seqnum sended
-            DEBUG("The sender is cumulatively acknowledging [%d : %d[ (place in the buffer)", state->tail, upper_bound);
+            DEBUG("The sender is cumulatively acknowledging [%d : %d[ (place in the buffer) | [%d, %d[ (seqnum)", 
+                    state->tail, upper_bound, pkt_get_seqnum(state->buffer[state->tail]), seqnum_nack);
             // A do while is necessary here in case we want to make a full revolution (ack from 1 to 1 not included for example)
             do 
             {
+                struct timeval time;
+                gettimeofday(&time, NULL);
+                unsigned long long int delta_time = time_milliseconds(&time) - time_milliseconds(&state->timers_first_send[state->tail]);
+                if (state->stats->min_rtt == 0 || state->stats->min_rtt > delta_time)
+                {
+                    state->stats->min_rtt = delta_time;
+                }
+                if (state->stats->max_rtt < delta_time)
+                {
+                    state->stats->max_rtt = delta_time;
+                }
+
                 pkt_t *n_pkt = state->buffer[state->tail];
                 seqnum = pkt_get_seqnum(n_pkt);
                 state->map_seqnum_to_buffer_place[seqnum] = OUT_OFF_WINDOW;
@@ -169,6 +215,7 @@ int handle_returning_ack_nack(sender_state_t *state, int socket_fd)
             // Send back the asked ACK if there is one to send back
             if (place_last_nack != OUT_OFF_WINDOW)
             {
+                state->stats->packet_retransmitted++;
                 pkt_t *n_pkt = state->buffer[place_last_nack];
                 if (send_pkt(state, n_pkt, place_last_nack, socket_fd) == -1) return -1;
             }
@@ -184,10 +231,10 @@ int checking_timer(sender_state_t *state, int socket_fd)
     if (state->buffer[state->tail] != NULL)
     {
         gettimeofday(&time, NULL);
-        time_t curr_time = time.tv_sec;
         // When the timer is over, we send the packet back
-        if ((curr_time - state->timers[state->tail]) >= TIMER_LIMIT)
+        if ((time_milliseconds(&time) - time_milliseconds(&state->timers[state->tail])) >= TIMER_LIMIT)
         {
+            state->stats->packet_retransmitted++;
             pkt_t *pkt = state->buffer[state->tail];
             DEBUG("The pkt with seqnum: %d has timeout", pkt_get_seqnum(pkt));
             if (send_pkt(state, pkt, state->tail, socket_fd) == -1) return -1;
@@ -196,69 +243,140 @@ int checking_timer(sender_state_t *state, int socket_fd)
     return 0;
 }
 
-int read_and_send(sender_state_t *state, int sending_fd, int socket_fd)
+bool is_it_EOF(int sending_fd)
 {
-    state->s_window_size--;
-    state->r_window_size--;
-
     // Checking if we're at the end of the file
     off_t curr_position = lseek(sending_fd, 0, SEEK_CUR);
     off_t end_position  = lseek(sending_fd, 0, SEEK_END);
     // put the reader cursor back
     lseek(sending_fd, curr_position, SEEK_SET);
+    return (bool) (curr_position == end_position);
+}
 
-    pkt_t *pkt = pkt_new(); 
-    ssize_t nbr_byte_read;
+void construct_FEC(sender_state_t *state, pkt_t *pkt)
+{
+    if (state->FEC_nbr == 0)
+    {
+        pkt_set_seqnum(state->FEC, pkt_get_seqnum(pkt));
+    }
+
+    uint16_t length = pkt_get_length(state->FEC) ^ pkt_get_length(pkt);
+    pkt_set_length(state->FEC, length);
+
+    uint8_t payload[MAX_PAYLOAD_SIZE];
+    uint8_t *p1 = (uint8_t *) pkt_get_payload(state->FEC);
+    uint8_t *p2 = (uint8_t *) pkt_get_payload(pkt);
+    for (int i = 0; i < MAX_PAYLOAD_SIZE; i++)
+    {
+        payload[i] = p1[i] ^ p2[i];
+    }
+    pkt_set_payload(state->FEC, (const char *) payload, MAX_PAYLOAD_SIZE);
+
+    state->FEC_nbr++;
+}
 
-    // The file has already been read but we need to send an empty DATA
-    if (curr_position == end_position) 
+int send_FEC(sender_state_t *state, int socket_fd)
+{
+    if (state->last_pkt_sent == CLOSING_PKT)
     {
-        nbr_byte_read = 0;
-        state->last_pkt_sent = CLOSING_PKT;
-        DEBUG("The CLOSING pkt has been sent !");
+        DEBUG("Sending LAST FEC pkt with seqnum: %d", pkt_get_seqnum(state->FEC));
     }
     else 
     {
-        nbr_byte_read = read(sending_fd, pkt->payload, MAX_PAYLOAD_SIZE);
+        DEBUG("Sending FEC pkt with seqnum: %d", pkt_get_seqnum(state->FEC));
+    }
+    
+    char packet_to_be_sent[PKT_MAX_LEN];
+    size_t len = PKT_MAX_LEN;
+    pkt_status_code pkt_status = pkt_encode(state->FEC, packet_to_be_sent, &len);
+    if (pkt_status != PKT_OK) 
+    {
+        ERROR("pkt_encode failed with status: %d", pkt_status);
+        return -1;
     }
+    
+    ssize_t sent = send(socket_fd, packet_to_be_sent, len, 0);
+    if (sent == -1) 
+    {
+        ERROR("The sending (using the function send from <sys/socket.h>) of the pkt failed");
+        return -1;
+    }
+    memset(state->FEC, 0, sizeof(pkt_t));
+    state->FEC_nbr = 0;
+    state->stats->fec_sent++;
+    return 0;
+}
 
-    pkt_set_type(pkt, PTYPE_DATA);
-    pkt_set_tr(pkt, 0);
-    pkt_set_window(pkt, state->s_window_size);
-    pkt_set_length(pkt, nbr_byte_read);
-    pkt_set_seqnum(pkt, state->next_seqnum);
-    // Sending a specific timestamp to let the receiver knows, it is the second to last pkt
-    curr_position = lseek(sending_fd, 0, SEEK_CUR);
-    if (state->last_pkt_sent == RANDOM_DATA_PKT && curr_position == end_position)
+int read_and_send(sender_state_t *state, int sending_fd, int socket_fd)
+{
+    // Checking whether I need to send a PTYPE_FEC or PTYPE_DATA
+    if ((state->fec_enabled) && ((state->FEC_nbr == 4) || ((state->last_pkt_sent == CLOSING_PKT) && (state->FEC_nbr > 0))))
     {
-        pkt_set_timestamp(pkt, SECOND_TO_LAST_PKT);
-        state->last_pkt_sent = LAST_DATA_PKT;
-        DEBUG("The LAST DATATYPE is being sent !");
+        return send_FEC(state, socket_fd);
     }
     else
     {
-        pkt_set_timestamp(pkt, 0);
-    }
-    // put the reader cursor back
-    lseek(sending_fd, curr_position, SEEK_SET);
-    
-    // We set the TR to 0 in order to calculcate the CRC on the header
-    char modified_header[8];
-    memcpy((void *) &modified_header, (void *) &pkt->header, 8);
-    modified_header[0] = modified_header[0] & TR_SETTER_TO_ZERO;
-    uint32_t crc1 = htonl(calculate_crc(modified_header, 8));
-    pkt_set_crc1(pkt, crc1);
+        state->s_window_size--;
+        state->r_window_size--;
+
+        pkt_t *pkt = pkt_new(); 
+        ssize_t nbr_byte_read;
+        bool is_EOF = is_it_EOF(sending_fd);
 
-    uint32_t crc2 = htonl(calculate_crc((char *) pkt->payload, (uint32_t) pkt_get_length(pkt)));
-    pkt_set_crc2(pkt, crc2);
+        // The file has already been read but we need to send an empty DATA pkt with length = 0
+        if (is_EOF) 
+        {
+            nbr_byte_read = 0;
+            state->last_pkt_sent = CLOSING_PKT;
+            DEBUG("The CLOSING pkt is being sent !");
+        }
+        else 
+        {
+            nbr_byte_read = read(sending_fd, pkt->payload, MAX_PAYLOAD_SIZE);
+        }
+
+        pkt_set_type(pkt, PTYPE_DATA);
+        pkt_set_tr(pkt, 0);
+        pkt_set_window(pkt, state->s_window_size);
+        pkt_set_length(pkt, nbr_byte_read);
+        pkt_set_seqnum(pkt, state->next_seqnum);
+        // Sending a specific timestamp to let the receiver know, it is the second to last pkt
+        is_EOF = is_it_EOF(sending_fd);
+        if (state->last_pkt_sent == RANDOM_PKT && is_EOF)
+        {
+            pkt_set_timestamp(pkt, SECOND_TO_LAST_PKT);
+            state->last_pkt_sent = LAST_DATA_PKT;
+            DEBUG("The LAST PTYPE_DATA is being sent !");
+        }
+        else
+        {
+            pkt_set_timestamp(pkt, 0);
+        }
+        
+        // We set the TR to 0 in order to calculcate the CRC on the header
+        char modified_header[8];
+        memcpy((void *) &modified_header, (void *) &pkt->header, 8);
+        modified_header[0] = modified_header[0] & TR_SETTER_TO_ZERO;
+        uint32_t crc1 = htonl(calculate_crc(modified_header, 8));
+        pkt_set_crc1(pkt, crc1);
 
-    if (send_pkt(state, pkt, state->head, socket_fd) == -1) return -1;
+        uint32_t crc2 = htonl(calculate_crc((char *) pkt->payload, (uint32_t) pkt_get_length(pkt)));
+        pkt_set_crc2(pkt, crc2);
 
-    state->map_seqnum_to_buffer_place[pkt_get_seqnum(pkt)] = state->head;
-    state->head = (state->head + 1) % WINDOW_SIZE;
+        if (send_pkt(state, pkt, state->head, socket_fd) == -1) return -1;
+        if (state->fec_enabled)
+        {
+            construct_FEC(state, pkt);
+        }
+        struct timeval timer;
+        gettimeofday(&timer, NULL);
+        state->timers_first_send[state->head] = timer;
 
-    // Careful we need to convert to uint16_t to avoid overflow
-    state->next_seqnum = (uint8_t) (((uint16_t) state->next_seqnum) + 1) % SEQNUM_RANGE;
+        state->map_seqnum_to_buffer_place[pkt_get_seqnum(pkt)] = state->head;
+        state->head = (state->head + 1) % WINDOW_SIZE;
 
-    return 0;
+        // Careful we need to convert to uint16_t to avoid overflow
+        state->next_seqnum = (uint8_t) (((uint16_t) state->next_seqnum) + 1) % SEQNUM_RANGE;
+        return 0;
+    }
 }
\ No newline at end of file
diff --git a/src/sender_utils.h b/src/sender_utils.h
index c6eb106e9197e1f4d5ed78279f9436a67fce29da..4013d8b54294ce0d4898ee2eb5763cec5445c765 100644
--- a/src/sender_utils.h
+++ b/src/sender_utils.h
@@ -13,20 +13,23 @@
 #include <unistd.h>
 
 #include "log.h"
-#include "our_utils.h"
+#include "utils.h"
 #include "packet_interface.h"
 
 
 #define SEQNUM_RANGE            256
-#define TIMER_LIMIT             2   // It's in seconds, in this network, the latency to send is = [0, 2s]
-#define SENDER_INACTIVE_TIMEOUT 30  // Only waits 30sec for the ACK of the CLOSING_PKT (the very last sended pkt)
+#define TIMER_LIMIT             2000   // It's in milli seconds, in this network, the latency to send is = [0, 2000ms]
+#define SENDER_INACTIVE_TIMEOUT 30000  // Only waits 30sec (it's in milliseconds) for the ACK of the CLOSING_PKT (the very last sended pkt)
 #define WINDOW_SIZE             31
 #define OUT_OFF_WINDOW          255
 
-// It is used to identify what kind of pkt was sent lately
-#define RANDOM_DATA_PKT 0
-#define LAST_DATA_PKT   1
-#define CLOSING_PKT     2
+// It is used to identify what kind of PTYPE_DATA pkt was sent lately
+// /!\ It doesn't trace the sended FEC !
+typedef enum {
+	RANDOM_PKT    = 0,
+	LAST_DATA_PKT = 1,
+	CLOSING_PKT   = 2
+} last_sended_pkt_t;
 
 
 /** 
@@ -35,22 +38,28 @@
 typedef struct state {
     uint8_t r_window_size; // receiver buffer space
     uint8_t s_window_size; // sender (our) buffer space
-    time_t  timers[WINDOW_SIZE];  // Time in seconds (corresponds to the timers of the sended pkt)
-    pkt_t  *buffer[WINDOW_SIZE];  // When the buffer fields are not used, they MUST be se to NULL
+    struct timeval timers[WINDOW_SIZE];            // timeval struct corresponding to the last time the pkt was sended
+    struct timeval timers_first_send[WINDOW_SIZE]; // timeval struct corresponding to the first time the pkt was sended (only used for the stats)
+    pkt_t *buffer[WINDOW_SIZE];  // When the buffer fields are not used, they MUST be se to NULL
     uint8_t head;  // place last element insert +1 in the buffer (free place)  | Head and tail are used to know
     uint8_t tail;  // place oldest element insert in the buffer                | the start and end of the sender window (of the sended pkt)
     uint8_t next_seqnum;
     uint8_t map_seqnum_to_buffer_place[SEQNUM_RANGE]; // Default value is: OUT_OFF_WINDOW
-    uint8_t last_pkt_sent;  // Can either be: RANDOM_DATA_PKT, LAST_DATA_PKT or CLOSING_PKT
+    last_sended_pkt_t last_pkt_sent;  // Can either be: RANDOM_PKT, LAST_DATA_PKT or CLOSING_PKT
+    bool fec_enabled;
+    pkt_t *FEC;      // The pkt FEC in construction
+    uint8_t FEC_nbr; // The number of PTYPE_DATA stacked on FEC
+    transfer_stats_t *stats;
 } sender_state_t;
 
 
 /**
- * @brief Creation of the UNIQUE structure representing the sender state. 
+ * @brief Creation of the UNIQUE structure representing the sender state.
  * 
- * @return sender_state_t* : The structure reprensting the state of the sender.
+ * @param fec_enabled : bool: variable telling whether the FEC are enabled or not
+ * @return sender_state_t* : The structure representing the state of the sender.
  */
-sender_state_t *state_new();
+sender_state_t *state_new(bool fec_enabled);
 
 /**
  * @brief Deletion if the structure representing the sender state.
@@ -72,7 +81,7 @@ void state_del(sender_state_t *state);
 bool can_send(sender_state_t *state);
 
 /**
- * @brief Cautious this function is used for sending and sending back pkt !
+ * @brief Cautious this function is used for sending and sending back PTYPE_DATA pkt !
  * 
  * @param state     : The variable representing the sender (state).
  * @param pkt       : The pkt to be sent.
@@ -102,8 +111,34 @@ int handle_returning_ack_nack(sender_state_t *state, int socket_fd);
 int checking_timer(sender_state_t *state, int socket_fd);
 
 /**
- * @brief When this function is called, the sender MUST be allowed to send a data pkt.
- *        It sends the next pkt and update the variable 'last_pkt_sent' of state.
+ * @brief Determines wheter the read file offset is at the end of the file
+ * 
+ * @param sending_fd : The file descriptor of the file you want to know if you're at the end.
+ * @return true  : The offset of sending_fd is at the end of the file
+ * @return false : The offset of sending_file is not at the end of the file
+ */
+bool is_it_EOF(int sending_fd);
+
+/**
+ * @brief It performs the XOR operation between state->FEC and pkt and stores the result in state->FEC
+ * 
+ * @param state : The variable representing the sender (state).
+ * @param pkt 
+ */
+void construct_FEC(sender_state_t *state, pkt_t *pkt);
+
+/**
+ * @brief It sends the FEC pkt contains in state->FEC.
+ * 
+ * @param state     : The variable representing the sender (state).
+ * @param socket_fd : The socket on which pkt are sent.
+ * @return int      : 0 if no error, -1 otherwise
+ */
+int send_FEC(sender_state_t *state, int socket_fd);
+
+/**
+ * @brief When this function is called, the sender MUST be allowed to send a pkt.
+ *        It sends the next pkt and update (if necessary) the variable 'last_pkt_sent' of state.
  * 
  * @param state      : The variable representing the sender (state).
  * @param sending_fd : The file descriptor of the file to be send
diff --git a/src/test.c b/src/test.c
deleted file mode 100644
index 7ba56388dc88091c8738bc7fd085b6de874f57fc..0000000000000000000000000000000000000000
--- a/src/test.c
+++ /dev/null
@@ -1,19 +0,0 @@
-#include <arpa/inet.h>
-#include <zlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "packet_interface.h"
-
-
-int main(int argc, char const *argv[])
-{
-    if (argc < 2)
-        return 1;
-    pkt_t * pkt = pkt_new();
-    int err = pkt_decode(argv[1], 10, pkt);
-    printf("Returned %d\n", err);
-    return 0;
-}
diff --git a/src/our_utils.c b/src/utils.c
similarity index 57%
rename from src/our_utils.c
rename to src/utils.c
index 6793cb48f51901ab1eb31bc9126cb5b3a3ac3356..435fe402c2030ea21878c5dabceba6823af36347 100644
--- a/src/our_utils.c
+++ b/src/utils.c
@@ -1,19 +1,5 @@
-#include <arpa/inet.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <poll.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <unistd.h>
-
-#include "log.h"
-#include "our_utils.h"
+#include "utils.h"
+
 
 const char * real_address(const char *address, struct sockaddr_in6 *rval)
 {
@@ -107,4 +93,77 @@ int wait_for_client(int sfd)
     ipv6_to_str_unexpanded(text, &(cast->sin6_addr));
     DEBUG("Successfully connected to IPv6 addresss: %s, port : %d", text, ntohs(cast->sin6_port));
     return 0;
+}
+
+unsigned long long int time_milliseconds(struct timeval *time)
+{
+    return ((unsigned long long int) time->tv_sec * 1000) + ((unsigned long long int) time->tv_usec / 1000);
+}
+
+void write_stats_to_file(const char * pathname, transfer_stats_t * stats_file, agents_t caller)
+{
+    if (pathname ==  NULL || stats_file == NULL)
+        return;
+
+    int fd = open(pathname, O_RDWR|O_CREAT|O_TRUNC, S_IRWXU);
+    if (fd == -1)
+    {
+        // We can use strerror cause here the error is known since we are using standard library open
+        DEBUG("%s", strerror(errno));
+        return;
+    }
+    int ret = 0;
+    char buffer[100];
+    ret = sprintf((char *) &buffer, "data_sent:%llu\n", stats_file->data_sent);
+    ret = write(fd, buffer, strlen(buffer));
+
+    ret = sprintf((char *) &buffer, "data_received:%llu\n", stats_file->data_received);
+    ret = write(fd, buffer, strlen(buffer));
+
+    ret = sprintf((char *) &buffer, "data_truncated_received:%llu\n", stats_file->data_truncated_received);
+    ret = write(fd, buffer, strlen(buffer));
+
+    ret = sprintf((char *) &buffer, "fec_sent:%llu\n", stats_file->fec_sent);
+    ret = write(fd, buffer, strlen(buffer));
+
+    ret = sprintf((char *) &buffer, "fec_received:%llu\n", stats_file->fec_received);
+    ret = write(fd, buffer, strlen(buffer));
+
+    ret = sprintf((char *) &buffer, "ack_sent:%llu\n", stats_file->ack_sent);
+    ret = write(fd, buffer, strlen(buffer));
+
+    ret = sprintf((char *) &buffer, "ack_received:%llu\n", stats_file->ack_received);
+    ret = write(fd, buffer, strlen(buffer));
+
+    ret = sprintf((char *) &buffer, "nack_received:%llu\n", stats_file->nack_received);
+    ret = write(fd, buffer, strlen(buffer));
+
+    ret = sprintf((char *) &buffer, "packet_ignored:%llu\n", stats_file->packet_ignored);
+    ret = write(fd, buffer, strlen(buffer));
+
+    if (caller == RECEIVER)
+    {
+        ret = sprintf((char *) &buffer, "packet_duplicated:%llu\n", stats_file->packet_duplicated);
+        ret = write(fd, buffer, strlen(buffer));
+
+        ret = sprintf((char *) &buffer, "packet_recovered:%llu\n", stats_file->packet_recovered);
+        ret = write(fd, buffer, strlen(buffer));
+    }
+
+    if (caller == SENDER)
+    {
+        ret = sprintf((char *) &buffer, "min_rtt:%llu\n", stats_file->min_rtt);
+        ret = write(fd, buffer, strlen(buffer));
+
+        ret = sprintf((char *) &buffer, "max_rtt:%llu\n", stats_file->max_rtt);
+        ret = write(fd, buffer, strlen(buffer));
+
+        ret = sprintf((char *) &buffer, "packet_retransmitted:%llu\n", stats_file->packet_retransmitted);
+        ret = write(fd, buffer, strlen(buffer));
+    }    
+    
+    close(fd);
+    ret--; // dump
+    DEBUG("Wrote the transfer statistics to %s.", pathname);
+    return;
 }
\ No newline at end of file
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000000000000000000000000000000000000..4d07cc142cc7f67fb7b42b10931d56dbdcccf150
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,103 @@
+#ifndef __UTILS_H_
+#define __UTILS_H_
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <poll.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "log.h"
+
+
+typedef struct __attribute__((__packed__))
+{
+    unsigned long long int data_sent;
+    unsigned long long int data_received;
+    unsigned long long int data_truncated_received;
+    unsigned long long int fec_sent;
+    unsigned long long int fec_received;
+    unsigned long long int ack_sent;
+    unsigned long long int ack_received;
+    unsigned long long int nack_sent;
+    unsigned long long int nack_received;
+    unsigned long long int packet_ignored;
+    unsigned long long int packet_duplicated;
+    unsigned long long int packet_recovered;
+    unsigned long long int min_rtt;
+    unsigned long long int max_rtt;
+    unsigned long long int packet_retransmitted;
+} transfer_stats_t;
+
+/* The type of agents */ 
+typedef enum{
+    SENDER,
+    RECEIVER,
+} agents_t;
+
+
+/**
+ * @brief Resolve the resource name to an usable IPv6 address
+ * @param address: The name to resolve
+ * @param rval: Where the resulting IPv6 address descriptor should be stored
+ * @param return: NULL if it succeeded, or a pointer towards
+ *          a string describing the error if any.
+ *          (const char* means the caller cannot modify or free the return value,
+ *           so do not use malloc!)
+ */
+const char * real_address(const char *address, struct sockaddr_in6 *rval);
+
+
+/** 
+ * @brief: Creates a socket and initialize it
+ * @param source_addr: if !NULL, the source address that should be bound to this socket.
+ * @param src_port: if >0, the port on which the socket is listening.
+ * @param dest_addr: if !NULL, the destination address to which the socket should send data.
+ * @param dst_port: if >0, the destination port to which the socket should be connected.
+ * 
+ * @return: a file descriptor number representing the socket
+ *         or -1 in case of error (explanation will be printed on stderr)
+ */
+int create_socket(struct sockaddr_in6 *source_addr, int src_port, struct sockaddr_in6 *dest_addr, int dst_port);
+
+
+/**
+ * @brief  Block the caller until a message is received on sfd,
+ * and connect the socket to the source addresse of the received message
+ * @param sfd: a file descriptor to a bound socket but not yet connected
+ * @param return: 0 in case of success, -1 otherwise
+ *
+ * @warning: This call is idempotent, it does not 'consume' the data of the message,
+ * and could be repeated several times blocking only at the first call.
+ */
+int wait_for_client(int sfd);
+
+/**
+ * @brief Return the in milliseconds
+ * 
+ * @param time : Structure representing the in seconds an mico seconds
+ * @return long long int : return the time in milliseconds
+ */
+unsigned long long int time_milliseconds(struct timeval *time);
+
+/**
+ * @brief Writes the content of the stats_file in the pathname file.
+ * 
+ * @param pathname: The pathname to the stats file.
+ * @param stats_file: Pointer to the stats file output.
+ * @param caller: To know the caller of the function.
+ * 
+ * 
+ * @returns In case of an error it is printed on the stderr.
+ */ 
+void write_stats_to_file(const char * pathname, transfer_stats_t * stats_file, agents_t caller);
+
+#endif
\ No newline at end of file
diff --git a/tests/advanced_test.sh b/tests/advanced_test.sh
index 297f4975a7d70e7169204bf2d55e9e46bdbfa445..92dfd19a8e06cd45dd4d8ac729a3633747ac09fd 100755
--- a/tests/advanced_test.sh
+++ b/tests/advanced_test.sh
@@ -1,84 +1,134 @@
 #!/bin/bash
 
+GREEN='\033[0;32m'
+RED='\033[0;31m'
+NC='\033[0m'
+
 if [ -z "$1" ]; then 
   echo "Not given the file to send"
   exit 1
 fi 
 
+# If the directory does not exist, we create it
+if [ ! -d "tests_logs/advanced_tests/" ]; then
+  mkdir 'tests_logs/advanced_tests/' 2>/dev/null
+fi
+
 FILENAME=$1
 BASENAME=$(basename $FILENAME)
 BSN_PRE="${BASENAME%.*}"
 BSN_EXT="${BASENAME##*.}"
-TEST_OUTPUT_FILES="unwanted_logs/advanced_tests/${BSN_PRE}"
-GREEN='\033[0;32m'
-NC='\033[0m'
 
-# If the directory does not exist, we create it
-if [ ! -d "unwanted_logs/advanced_tests/" ]; then
-  mkdir 'unwanted_logs/advanced_tests/' 2>/dev/null
-fi
-mkdir "${TEST_OUTPUT_FILES}/" 2>/dev/null  
+TEST_OUTPUT_FILES="tests_logs/advanced_tests/${BSN_PRE}"
+mkdir "${TEST_OUTPUT_FILES}/" 2>/dev/null
 
-touch "${TEST_OUTPUT_FILES}/adv_${BSN_PRE}_received_file.${BSN_EXT}" \
-      "${TEST_OUTPUT_FILES}/adv_valgrind_${BSN_PRE}_receiver.log"    \
-      "${TEST_OUTPUT_FILES}/adv_valgrind_${BSN_PRE}_sender.log"
+MODES=(
+  'with_FEC'
+  'without_FEC'
+)
 
+ERROR_RATE=20
+CUT_RATE=30
+DELAY=0
+JITTER=0
+LOSS_RATE=25
 
-# The next 2 lines come from: https://unix.stackexchange.com/questions/55913/whats-the-easiest-way-to-find-an-unused-local-port
-# We use this to be sure we're using unused port
-port1=$(comm -23 <(seq 65000 65200 | sort) <(ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n 1)
-port2=$(comm -23 <(seq 65000 65200 | sort) <(ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n 1)
+echo -e "The linksimulator paramateres are:
+        \t\t-ERROR RATE: ${ERROR_RATE}%    \t-DELAY:  ${DELAY}ms
+        \t\t-CUT RATE:   ${CUT_RATE}%      \t-JITTER: ${JITTER}ms 
+        \t\t-LOSS RATE:  ${LOSS_RATE}%\n"
 
-# We first launch the link simulator 
-./linksimulator/link_sim -p $port2 -P $port1 -l 20 -d 300 -e 8 -c 12 -R \
-  &>${TEST_OUTPUT_FILES}/adv_${BSN_PRE}_link.log & link_pid=$!
+for MODE in "${MODES[@]}"; do
+  
+  mkdir "${TEST_OUTPUT_FILES}/${MODE}/" 2>/dev/null
+  DIR="${TEST_OUTPUT_FILES}/${MODE}"
+
+  touch "${DIR}/adv_${BSN_PRE}_received_file.${BSN_EXT}" \
+        "${DIR}/adv_valgrind_${BSN_PRE}_receiver.log"    \
+        "${DIR}/adv_valgrind_${BSN_PRE}_sender.log"      \
+        "${DIR}/${BSN_PRE}_receiver_stats.csv"          \
+        "${DIR}/${BSN_PRE}_sender_stats.csv"
 
-# We launch the receiver and capture its output
-valgrind --leak-check=full --log-file=${TEST_OUTPUT_FILES}/adv_${BSN_PRE}_valgrind_receiver.log \
-              ./receiver ::1 $port1 1> ${TEST_OUTPUT_FILES}/adv_${BSN_PRE}_received_file.${BSN_EXT} \
-              2> ${TEST_OUTPUT_FILES}/adv_${BSN_PRE}_receiver.log & receiver_pid=$!
 
-cleanup()
-{
-    kill -9 $receiver_pid
-    kill -9 $link_pid
-    exit 0
-}
-trap cleanup SIGINT  # Kill the background procces in case of ^-C
-
-# We start the transfer
-if ! valgrind --leak-check=full --log-file=${TEST_OUTPUT_FILES}/adv_valgrind_${BSN_PRE}_receiver.log \
-    ./sender -f ${FILENAME} ::1 $port2 2> ${TEST_OUTPUT_FILES}/adv_${BSN_PRE}_sender.log ; then
-  echo "Crash du sender!"
-  cat ${TEST_OUTPUT_FILES}/adv_${BSN_PRE}_sender.log
-  err=1  # We record the error
-fi
 
-sleep 5 # We wait 5s for the receiver to finish up
+  # The next 2 lines come from: https://unix.stackexchange.com/questions/55913/whats-the-easiest-way-to-find-an-unused-local-port
+  # We use this to be sure we're using unused port
+  port1=$(comm -23 <(seq 65000 65200 | sort) <(ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n 1)
+  port2=$(comm -23 <(seq 65000 65200 | sort) <(ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n 1)
 
-if kill -0 $receiver_pid &> /dev/null ; then
-  echo "The receiver didn't stop at the end of the transfer!"
-  kill -9 $receiver_pid
-  err=1
-else  # We check the return value of the receiver
-  if ! wait $receiver_pid ; then
-    echo "Crash of the receiver!"
-    cat ${TEST_OUTPUT_FILES}/adv_${BSN_PRE}_receiver.log
+
+  #####   Launching the link simulator   #####
+  ./linksimulator/link_sim -p $port2 -P $port1 -l $LOSS_RATE -d $DELAY -e $ERROR_RATE -c $CUT_RATE -j $JITTER \
+    &>${DIR}/adv_${BSN_PRE}_link.log & link_pid=$!
+
+  #####   Launching the receiver and capturinig its output   #####
+  valgrind --leak-check=full --log-file=${DIR}/adv_valgrind_${BSN_PRE}_receiver.log \
+                ./receiver ::1 $port1 -s ${DIR}/${BSN_PRE}_receiver_stats.csv 1> ${DIR}/adv_${BSN_PRE}_received_file.${BSN_EXT} \
+                2> ${DIR}/adv_${BSN_PRE}_receiver.log & receiver_pid=$!
+
+  cleanup()
+  {
+      kill -9 $receiver_pid
+      kill -9 $link_pid
+      exit 0
+  }
+  trap cleanup SIGINT  # Kill the background procces in case of ^-C
+
+  # Checking the mode (with out without FEC)
+  if [ $MODE = "with_FEC" ]; then
+    # We start the transfer
+    if ! valgrind --leak-check=full --log-file=${DIR}/adv_valgrind_${BSN_PRE}_sender.log \
+        ./sender -f ${FILENAME} ::1 $port2 -c -s ${DIR}/${BSN_PRE}_sender_stats.csv 2> ${DIR}/adv_${BSN_PRE}_sender.log ; then
+      echo "The sender crashed!"
+      cat ${DIR}/adv_${BSN_PRE}_sender.log
+      err=1  # We record the error
+    fi
+  else
+    # We start the transfer
+    if ! valgrind --leak-check=full --log-file=${DIR}/adv_valgrind_${BSN_PRE}_sender.log \
+        ./sender -f ${FILENAME} ::1 $port2 -s ${DIR}/${BSN_PRE}_sender_stats.csv 2> ${DIR}/adv_${BSN_PRE}_sender.log ; then
+      echo "The sender crashed!"
+      cat ${DIR}/adv_${BSN_PRE}_sender.log
+      err=1  # We record the error
+    fi
+  fi
+
+  sleep 5 # We wait 5s for the receiver to finish up
+
+  if kill -0 $receiver_pid &> /dev/null ; then
+    echo "The receiver didn't stop at the end of the transfer!"
+    kill -9 $receiver_pid
     err=1
+  else  # We check the return value of the receiver
+    if ! wait $receiver_pid ; then
+      echo "Crash of the receiver!"
+      cat ${DIR}/adv_${BSN_PRE}_receiver.log
+      err=1
+    fi
   fi
-fi
 
-# Stop the link simulator
-kill -9 $link_pid
-wait $link_pid 2>/dev/null
+  # Stop the link simulator
+  kill -9 $link_pid
+  wait $link_pid 2>/dev/null
 
-# We verify that the transfer ran through properly
-if [[ "$(md5sum ${FILENAME} | awk '{print $1}')" != "$(md5sum ${TEST_OUTPUT_FILES}/adv_${BSN_PRE}_received_file.${BSN_EXT} | awk '{print $1}')" ]]; then
-  echo "The transfer corrupted the file!"
-  echo "Binary difference between the 2 files: (expected vs actual)"
-  diff -C 9 <(od -Ax -t x1z ${FILENAME}) <(od -Ax -t x1z ${TEST_OUTPUT_FILES}/adv_${BSN_PRE}_received_file.${BSN_EXT})
-  exit 1
-else
-  echo -e "${GREEN}The transfer has succeeded!${NC}"
-  exit ${err:-0}  # In case of error, we return the error code
-fi
\ No newline at end of file
+  # We verify that the transfer ran through properly
+  if [[ "$(md5sum ${FILENAME} | awk '{print $1}')" != "$(md5sum ${DIR}/adv_${BSN_PRE}_received_file.${BSN_EXT} | awk '{print $1}')" ]]; then
+    echo "The transfer corrupted the file!"
+    echo "Binary difference between the 2 files: (expected vs actual)"
+    diff -C 9 <(od -Ax -t x1z ${FILENAME}) <(od -Ax -t x1z ${DIR}/adv_${BSN_PRE}_received_file.${BSN_EXT})
+    if [ $MODE = "with_FEC" ]; then
+      echo -e "${RED}The transfer (with FEC) has failed!${NC}"
+    else
+      echo -e "${RED}The transfer (without FEC) has failed!${NC}"
+    fi
+    exit 1
+  else
+    if [ $MODE = "with_FEC" ]; then
+      echo -e "${GREEN}The transfer (with FEC) has succeeded!${NC}"
+    else
+      echo -e "${GREEN}The transfer (without FEC) has succeeded!${NC}"
+    fi
+  fi
+done
+
+exit ${err:-0}  # In case of error, we return the error code
\ No newline at end of file
diff --git a/tests/performances_tests.py b/tests/performances_tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..92b09e9b858ca3e17992a17a2f7399d105196e57
--- /dev/null
+++ b/tests/performances_tests.py
@@ -0,0 +1,23 @@
+
+"""
+Return a dictionary of the different values found in the receiver stats (.csv) file
+"""
+def read_receiver_stats(filename):
+    dictionary = {}
+    with open(filename, 'r') as f:
+        for line in f.readlines():
+            key, value = line.split(":")
+            dictionary[key] = int(value)
+    return dictionary
+
+"""
+Return a dictionary of the different values found in the sender stats (.csv) file
+"""
+def read_sender_stats(filename):
+    dictionary = {}
+    with open(filename, 'r') as f:
+        for line in f.readlines():
+            key, value = line.split(":")
+            dictionary[key] = int(value)
+    return dictionary
+
diff --git a/tests/performances_tests.sh b/tests/performances_tests.sh
new file mode 100644
index 0000000000000000000000000000000000000000..e13371856e44ad86e2b3e9486af8dadbd01f875d
--- /dev/null
+++ b/tests/performances_tests.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+
+# File use for the test of performance
+file_to_transfer='tests_files/smile.png'
+
+# Chosen parameters for the test of performance
+ERROR_RATE=20
+CUT_RATE=30
+DELAY=0
+JITTER=0
+LOSS_RATE=25
+
+if [ ! -d "tests_logs/performance_tests/" ]; then
+  mkdir 'tests_logs/performance_tests/'
+fi
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 5cf92ee3fbbaddc9875011efa45898e1a9400160..e3317a9a2a1b760f5bd31b05013337a5b27fa908 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1,41 +1,65 @@
 #!/bin/bash
 
+BROWN='\033[0;33m'
+NC='\033[0m'
+
 # Note that this assumes to be called from the Makefile, you may want to adapt it.
 FILESIZE="" 
-TEST_FILES_DIR=./test_files/
+TEST_FILES_DIR=./tests_files/
 
+if [ ! -d ./tests_logs/ ]; then
+  mkdir ./tests_logs
+fi
 # Remove everything
-rm -rf ./unwanted_logs/*
+rm -rf ./tests_logs/*
+
+if [ -d ./unwanted_logs/ ]; then
+  rm -rdf ./unwanted_logs
+fi
+
+simple_test_files=(
+  'tests_files/greeting.txt'
+  'tests_files/long_message.txt'
+  'tests_files/smile.png'
+  'tests_files/thumbs-up-nod.gif'
+)
+
+advanced_test_files=(
+  'tests_files/greeting.txt'
+  'tests_files/long_message.txt'
+  'tests_files/noice.gif'
+)
 
-# We want our tests to always use valgrind 
-#echo -e "\nStarting simple tests ...\n"
-#for FILENAME in "$TEST_FILES_DIR"/*
-#do 
-#  FILESIZE=$(stat -c%s "$FILENAME")
-#  echo -e "Sending $FILENAME \t\t [$FILESIZE bytes], \t\t with Valgrind"
-#  ./tests/simple_test.sh $FILENAME
+# echo -e "\nStarting simple tests ...\n"
+# i=1
+# for FILENAME in "${simple_test_files[@]}"; do 
+#   FILESIZE=$(stat -c%s "$FILENAME")
+#   echo -e "${BROWN}($i/${#simple_test_files[@]}) Sending \"$FILENAME\"  \t[$FILESIZE bytes] with Valgrind${NC}"
+#   ./tests/simple_test.sh $FILENAME
 
-#  if [ $? -ne 0 ]; then 
-#    echo "Tests terminated cause of a failed test"
-#    exit 0
-#  fi
+#   if [ $? -ne 0 ]; then 
+#     echo "Tests terminated cause of a failed test"
+#     exit 0
+#   fi
+#   let i++
 
-#done
-#echo -e "Finished Simple tests."
+# done
+echo -e "Finished Simple tests."
 
 if [ -d linksimulator/ ]; then
   echo -e "\nStarting advanced tests ...\n"
   # Now we ran the advanced tests
-  for FILENAME in "$TEST_FILES_DIR"/*
-  do 
+  i=1
+  for FILENAME in "${advanced_test_files[@]}"; do 
     FILESIZE=$(stat -c%s "$FILENAME")
-    echo -e "Sending $FILENAME \t\t [$FILESIZE bytes], \t\t with linksimulator and Valgrind"
+    echo -e "\n${BROWN}($i/${#advanced_test_files[@]}) Sending the file \"$FILENAME\"  \t[$FILESIZE bytes] with linksimulator and Valgrind${NC}"
     ./tests/advanced_test.sh $FILENAME
 
     if [ $? -ne 0 ]; then 
       echo "Tests terminated cause of a failed test"
       exit 0
-    fi  
+    fi
+    let i++
   done
   echo "Finished Advanced tests."
 else 
diff --git a/tests/simple_test.sh b/tests/simple_test.sh
index 46ffb8a4aa660b273aeb20d5cfe0ce5c4dcfbeb3..db3611d979edb6d89785badc2b4439a60c647bc0 100755
--- a/tests/simple_test.sh
+++ b/tests/simple_test.sh
@@ -9,13 +9,13 @@ FILENAME=$1
 BASENAME=$(basename $FILENAME)
 BSNM_PRE="${BASENAME%.*}"
 BSNM_EXT="${BASENAME##*.}"
-TEST_OUTPUT_FILES="unwanted_logs/simple_tests/${BSNM_PRE}"
+TEST_OUTPUT_FILES="tests_logs/simple_tests/${BSNM_PRE}"
 GREEN='\033[0;32m'
 NC='\033[0m'
 
 # If the directory does not exist, we create it
-if [ ! -d "unwanted_logs/simple_tests/" ]; then
-  mkdir 'unwanted_logs/simple_tests/' 2>/dev/null
+if [ ! -d "tests_logs/simple_tests/" ]; then
+  mkdir 'tests_logs/simple_tests/' 2>/dev/null
 fi
 mkdir "${TEST_OUTPUT_FILES}/" 2>/dev/null  
 
@@ -32,7 +32,7 @@ port=$(comm -23 <(seq 65000 65200 | sort) <(ss -Htan | awk '{print $4}' | cut -d
 
 # We launch the receiver and capture its output
 valgrind --leak-check=full --log-file=${TEST_OUTPUT_FILES}/valgrind_${BSNM_PRE}_receiver.log \
-              ./receiver ::1 $port 1> ${TEST_OUTPUT_FILES}/${BSNM_PRE}_received_file.${BSNM_EXT} \
+              ./receiver ::1 $port -s ${TEST_OUTPUT_FILES}/${BSNM_PRE}_receiver_stats.csv 1> ${TEST_OUTPUT_FILES}/${BSNM_PRE}_received_file.${BSNM_EXT} \
               2> ${TEST_OUTPUT_FILES}/${BSNM_PRE}_receiver.log & receiver_pid=$!
 
 cleanup()
@@ -45,7 +45,7 @@ trap cleanup SIGINT  # Kill the background procces in case of ^-C
 
 # We start the transfer
 if ! valgrind --leak-check=full --log-file=${TEST_OUTPUT_FILES}/valgrind_${BSNM_PRE}_sender.log \
-    ./sender -f ${FILENAME} ::1 $port 2> ${TEST_OUTPUT_FILES}/${BSNM_PRE}_sender.log ; then
+    ./sender -f ${FILENAME} ::1 $port -s ${TEST_OUTPUT_FILES}/${BSNM_PRE}_sender_stats.csv 2> ${TEST_OUTPUT_FILES}/${BSNM_PRE}_sender.log ; then
   echo "The sender crashed!"
   cat ${TEST_OUTPUT_FILES}/${BSNM_PRE}_sender.log
   err=1  # We record the error
diff --git a/test_files/greeting.txt b/tests_files/greeting.txt
similarity index 100%
rename from test_files/greeting.txt
rename to tests_files/greeting.txt
diff --git a/test_files/long_message.txt b/tests_files/long_message.txt
similarity index 70%
rename from test_files/long_message.txt
rename to tests_files/long_message.txt
index 9a4178c362cdafd845cf5179691e0f1c6dccca0e..a18b03ec08fdc53c1f0a5e0fc9187b5a0f18568f 100644
--- a/test_files/long_message.txt
+++ b/tests_files/long_message.txt
@@ -6,14 +6,14 @@ ahead. So good luck have a nice day of testing and remember : Tough Times Never
 PS: Drink a beer when you pass the test. Share your knowledge to others, computer 
 science isn't a field for selfishness. If you want to switch to Law, Economy, some shit
 
-Hi Vany I love reading from you. As you know I'm working sometime a bit late in the night.
-In a few hour you'll be in the famous "Salle Intel" to be really productive as you are.
+Hi Vany I'm pleased to read some news from you. As you know I work sometime a bit late in the night.
+In a few hour you'll show up in the famous "Salle Intel" to be really productive as you are.
 
 Best regards,
 
 Samuel
 
-Sorry to be lazy but I'll past down what is above
+Sorry to be lazy but I'll past down what is above :')
 
 Hello I'm a good friend, I've seen that you are writing a protocol named TRTP. 
 How is it going ? Hard ? Good for you. See life is full of hard things just like your 
@@ -23,9 +23,7 @@ ahead. So good luck have a nice day of testing and remember : Tough Times Never
 PS: Drink a beer when you pass the test. Share your knowledge to others, computer 
 science isn't a field for selfishness. If you want to switch to Law, Economy, some shit
 
-Hi Vany I love reading from you. As you know I'm working sometime a bit late in the night.
-In a few hour you'll be in the famous "Salle Intel" to be really productive as you are.
+Hi Vany I'm pleased to read some news from you. As you know I work sometime a bit late in the night.
+In a few hour you'll show up in the famous "Salle Intel" to be really productive as you are.
 
 Best regards,
-
-Samuel
diff --git a/test_files/noice.gif b/tests_files/noice.gif
similarity index 100%
rename from test_files/noice.gif
rename to tests_files/noice.gif
diff --git a/test_files/smile.png b/tests_files/smile.png
similarity index 100%
rename from test_files/smile.png
rename to tests_files/smile.png
diff --git a/test_files/thumbs-up-nod.gif b/tests_files/thumbs-up-nod.gif
similarity index 100%
rename from test_files/thumbs-up-nod.gif
rename to tests_files/thumbs-up-nod.gif