9511_project03

project 3 for algorithms & programming I (9511) prof. Cardozo
Index Commits Files Refs README LICENSE
commit 5f508c961494cb93003b230621e5a7706f450416
parent 6b416dc25a00a138a8d458d092ec067917d62755
Author: klewer-martin <martin.cachari@gmail.com>
Date:   Thu, 29 Jul 2021 16:43:34 -0300

Updated source code and input file generator

Diffstat:
MMakefile | 3+++
Mexamples/input_gen.py | 90++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Minclude/status.h | 10+++++-----
Minclude/utils.h | 9++++++++-
Minclude/vector.h | 2+-
Msource/cla.c | 52+++++++++++++++++++++++++++++++++-------------------
Msource/main.c | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Msource/status.c | 23-----------------------
Msource/utils.c | 29+++++++++++++++++++++++++++++
Msource/vector.c | 6++++--
10 files changed, 239 insertions(+), 86 deletions(-)
diff --git a/Makefile b/Makefile
@@ -51,6 +51,9 @@ run5k:
 run50k:
     ./main -fmt csv -out output.csv -in examples/test_file_50k.csv -ti 1320498000 -tf 1420529000
 
+run100k:
+    ./main -fmt csv -out output.csv -in examples/test_file_100k.csv -ti 1320498000 -tf 1420529000
+
 run500k:
     ./main -fmt csv -out output.csv -in examples/test_file_500k.csv -ti 1320498000 -tf 1420529000
 
diff --git a/examples/input_gen.py b/examples/input_gen.py
@@ -2,7 +2,7 @@ from string import digits
 from time import strftime, gmtime
 from random import randint, choice
 
-LINES = 5
+LINES = 100000
 
 # OUTPUT:
 #     ID_TRANSACCION, ID_USUARIO, FECHA, MONTO, NUMERO DE TRAJETA, DESCRIPCION
@@ -10,19 +10,99 @@ LINES = 5
 
 descs = [ "Compras supermercado", "Pago tarjeta", "Compras libreria", "Pago Mecanico", "Pago Dentista", "Pago Servicios online", "Compras Ferreteria",  "Compras Accesorios Informatica", "Compras farmacia", "Ventas online", "Extraccion cajero" ]
 
-def card_number_generator():
-    return ''.join(choice(digits) for i in range(1, 16))
+def calculate_luhn(cc):
+    num = list(map(int, str(cc)))
+    check_digit = 10 - sum(num[-2::-2] + [sum(divmod(d * 2, 10)) for d in num[::-2]]) % 10
+    return 0 if check_digit == 10 else check_digit
+
+def generate_card(type):
+    """
+    Prefill some values based on the card type
+    """
+    card_types = ["americanexpress","visa13", "visa16","mastercard","discover"]
+
+    def prefill(t):
+        # typical number of digits in credit card
+        def_length = 16
+
+        """
+        Prefill with initial numbers and return it including the total number of digits
+        remaining to fill
+        """
+        if t == card_types[0]:
+            # american express starts with 3 and is 15 digits long
+            # override the def lengths
+            return [3, randint(4,7)], 13
+
+        elif t == card_types[1] or t == card_types[2]:
+            # visa starts with 4
+            if t.endswith("16"):
+                return [4], def_length - 1
+            else:
+                return [4], 12
+
+        elif t == card_types[3]:
+            # master card start with 5 and is 16 digits long
+            return [5, randint(1,5)], def_length - 2
+
+        elif t == card_types[4]:
+            # discover card starts with 6011 and is 16 digits long
+            return [6, 0, 1, 1], def_length - 4
+
+        else:
+            # this section probably not even needed here
+            return [], def_length
+
+    def finalize(nums):
+        """
+        Make the current generated list pass the Luhn check by checking and adding
+        the last digit appropriately bia calculating the check sum
+        """
+        check_sum = 0
+
+        #is_even = True if (len(nums) + 1 % 2) == 0 else False
+
+        """
+        Reason for this check offset is to figure out whether the final list is going
+        to be even or odd which will affect calculating the check_sum.
+        This is mainly also to avoid reversing the list back and forth which is specified
+        on the Luhn algorithm.
+        """
+        check_offset = (len(nums) + 1) % 2
+
+        for i, n in enumerate(nums):
+            if (i + check_offset) % 2 == 0:
+                n_ = n*2
+                check_sum += n_ -9 if n_ > 9 else n_
+            else:
+                check_sum += n
+        return nums + [10 - (check_sum % 10) ]
+
+    # main body
+    t = type.lower()
+    if t not in card_types:
+        print("Unknown type: '%s'" % type)
+        print("Please pick one of these supported types: %s" % card_types)
+        return
+
+    initial, rem = prefill(t)
+    so_far = initial + [randint(1,9) for x in range(rem - 1)]
+    return ("".join(map(str,finalize(so_far))))
 
 def generate_file(max_lines):
     id_transaction_base = 123400
     id_user_base = 1
-    id_user_max = 20000
+    id_user_max = 30000
     amount_base = 1
     amount_max = 10000
     for i in range(max_lines):
         id_transaction = (i + id_transaction_base)
         id_user = randint(id_user_base, id_user_max)
-        card_nr = card_number_generator() 
+
+        card_nr = generate_card("visa16")
+        while calculate_luhn(card_nr) != 0:
+            card_nr = generate_card("visa16")
+
         date = strftime("%d.%m.%Y %H:%M:%S", gmtime(1320487200 + i))
         
         j = randint(1, len(descs) - 1)
diff --git a/include/status.h b/include/status.h
@@ -5,10 +5,11 @@
 #include <stdlib.h>
 #include <stdarg.h>
 
-#define ERROR_RETRY_MSG "Verifique y vuelva a intentar"
-#define EXIT_SUCCESS_MSG "Ejecución terminada exitosamente"
-#define USERS_REGISTERED_MSG    "Usuarios registrados: "
-#define PROCESED_LINES_MSG        "Lineas procesadas: "
+#define MSG_ERROR_RETRY                "Verifique y vuelva a intentar"
+#define MSG_EXIT_SUCCESS            "Ejecución terminada exitosamente"
+#define MSG_USERS_REGISTERED        "Usuarios registrados"
+#define MSG_PROCESED_LINES            "Lineas procesadas"
+#define STR_INVALID_CARD_NUMBER        "Número no válido"
 
 #define STATUS_T_MAX 12
 
@@ -38,7 +39,6 @@ typedef enum {
 } csv_pos_t;
 
 void show_status(status_t st);
-void free_arrays(size_t num,...);
 
 extern const char *status_strings[STATUS_T_MAX];
 
diff --git a/include/utils.h b/include/utils.h
@@ -4,6 +4,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <stdbool.h>
 
 #include "cla.h"
 #include "user.h"
@@ -18,6 +19,11 @@
 #define IN_FILE_FIELDS    6
 #define IN_FILE_FIELDS_MAX_LEN    50
 
+#define STR_FMT_CSV "csv"
+#define STR_FMT_XML "xml"
+
+#define CARD_NO_VALID_LEN 16
+
 #define XML_STR_HEADER    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
 #define XML_STR_ROOT    "root"
 #define XML_STR_ROW        "row"
@@ -29,8 +35,9 @@ status_t get_date(time_t *, char **);
 status_t array_destroy(char **, size_t);
 status_t string_split(char *, char **, char *);
 
+bool is_valid_card(char *);
+
 void clean_array(char **);
 void clean_buffer(char *);
 
-
 #endif
diff --git a/include/vector.h b/include/vector.h
@@ -26,7 +26,7 @@ status_t ADT_Vector_add(ADT_Vector_t **, void *);
 status_t ADT_Vector_destroy(ADT_Vector_t **);
 
 status_t ADT_Vector_set(ADT_Vector_t **, void *, size_t);
-status_t ADT_Vector_print(const ADT_Vector_t *, FILE *, printer_t);
+status_t ADT_Vector_print(const ADT_Vector_t *, FILE *);
 status_t ADT_Vector_sort(ADT_Vector_t *, comparator_t);
 
 void *ADT_Vector_get_elem(const ADT_Vector_t *v, void *e);
diff --git a/source/cla.c b/source/cla.c
@@ -74,23 +74,34 @@ status_t cla_setup(int argc, char **argv, cla_t cla)
         for(f = FLAG_FMT; f < FLAGS_MAX; f++) {
             if(!strcmp(available_flags[f], argv[i])) {
                 switch (f) {
-                    case FLAG_FMT: strcpy(cla->fmt, argv[i + 1]); break;
-                    case FLAG_OUT: if((fp = fopen(argv[i + 1], "wt")) == NULL)
-                                       return ERROR_OPENING_FILE;
-
-                                   cla->fo = fp; 
-                                   break;
-                    case FLAG_IN: if((fp = fopen(argv[i + 1], "rt")) == NULL)
-                                      return ERROR_OPENING_FILE;
-
-                                   cla->fi = fp; 
-                                   break;
-                    case FLAG_TI: cla->ti = strtoul(argv[i + 1], &endptr, 10); 
-                                  if(*endptr != '\0') return ERROR_WRONG_TIME;
-                                  break;
-                    case FLAG_TF: cla->tf = strtoul(argv[i + 1], &endptr, 10); 
-                                  if(*endptr != '\0') return ERROR_WRONG_TIME;
-                                  break;
+                    case FLAG_FMT: 
+                        strcpy(cla->fmt, argv[i + 1]);
+                       break;
+
+                    case FLAG_OUT:
+                       if((fp = fopen(argv[i + 1], "wt")) == NULL)
+                           return ERROR_OPENING_FILE;
+
+                       cla->fo = fp; 
+                       break;
+
+                    case FLAG_IN:
+                       if((fp = fopen(argv[i + 1], "rt")) == NULL)
+                           return ERROR_OPENING_FILE;
+
+                       cla->fi = fp; 
+                       break;
+
+                    case FLAG_TI:
+                       cla->ti = strtoul(argv[i + 1], &endptr, 10); 
+                       if(*endptr != '\0') return ERROR_WRONG_TIME;
+                       break;
+
+                    case FLAG_TF:
+                       cla->tf = strtoul(argv[i + 1], &endptr, 10); 
+                       if(*endptr != '\0') return ERROR_WRONG_TIME;
+                       break;
+
                     default: return ERROR_FLAG_NOT_FOUND;
                 }
             }
@@ -113,6 +124,9 @@ status_t cla_create(cla_t *cla)
         return ERROR_MEMORY;
     }
 
+    (*cla)->fi = NULL;
+    (*cla)->fo = NULL;
+
     return OK;
 }
 
@@ -120,8 +134,8 @@ status_t cla_destroy(cla_t cla)
 {
     if(cla == NULL) return ERROR_NULL_POINTER;
 
-    fclose(cla->fo);
-    fclose(cla->fi);
+    if(cla->fi != NULL) fclose(cla->fi);
+    if(cla->fo != NULL)    fclose(cla->fo);
 
     free(cla->fmt);
     free(cla);
diff --git a/source/main.c b/source/main.c
@@ -4,11 +4,6 @@
 #include "../include/status.h"
 #include "../include/vector.h"
 
-#define IN_FILE_MAX_LEN    100
-#define IN_FILE_DELIM    ","
-#define IN_FILE_FIELDS    6
-#define IN_FILE_FIELDS_MAX_LEN    50
-
 int main (int argc, char *argv[])
 {
     status_t st;
@@ -68,6 +63,26 @@ int main (int argc, char *argv[])
         return st;
     }
 
+    /* Setea el impresor a ADT_Vector */
+    if(!strcmp(cla->fmt, STR_FMT_CSV)) {
+        if((st = ADT_Vector_set_printer(v, user_print_as_csv)) != OK) {
+            show_status(st);
+            cla_destroy(cla);
+            ADT_Vector_destroy(&v);
+            array_destroy(data, IN_FILE_FIELDS);
+            return st;
+        }
+    }
+    else if(!strcmp(cla->fmt, STR_FMT_XML)) {
+        if((st = ADT_Vector_set_printer(v, user_print_as_xml)) != OK) {
+            show_status(st);
+            cla_destroy(cla);
+            ADT_Vector_destroy(&v);
+            array_destroy(data, IN_FILE_FIELDS);
+            return st;
+        }
+    }
+
     /* Crea un usuario temporal */
     if((st = user_create(&user_tmp)) != OK) {
         show_status(st);
@@ -81,10 +96,22 @@ int main (int argc, char *argv[])
     while(fgets(buffer, IN_FILE_MAX_LEN, cla->fi)) {
 
         /* Separa la linea leida segun un caracter delimitador */
-        string_split(buffer, data, IN_FILE_DELIM);
+        if((st = string_split(buffer, data, IN_FILE_DELIM)) != OK) {
+            show_status(st);
+            cla_destroy(cla);
+            ADT_Vector_destroy(&v);
+            array_destroy(data, IN_FILE_FIELDS);
+            return st;
+        }
 
         /* Setea el usuario temporal con los datos obtenidos de la linea */
-        user_set_data(user_tmp, data);
+        if((st = user_set_data(user_tmp, data)) != OK) {
+            show_status(st);
+            cla_destroy(cla);
+            ADT_Vector_destroy(&v);
+            array_destroy(data, IN_FILE_FIELDS);
+            return st;
+        }
 
         amount = strtol(data[POS_AMOUNT], &endptr, 10);
         if(*endptr != '\0') return ERROR_CORRUPT_DATA;
@@ -96,15 +123,41 @@ int main (int argc, char *argv[])
         if(epoch < cla->ti) continue;
         else if(epoch > cla->tf) break;
 
+        /* Solo imprime en el archivo de salida las transacciones realizadas con una tarjeta valida */
+        if(!is_valid_card(data[POS_CARD_NUMBER])) {
+            fprintf(stderr, "%s: %s\n",STR_INVALID_CARD_NUMBER, data[POS_CARD_NUMBER]);
+            continue;
+        }
+
         /* Busca el id del usuario en el vector */
         if((user = ADT_Vector_get_elem(v, user_tmp)) != NULL) {
             /* Si lo encuentra le suma el monto correspondiente */
-            user_add_amount(user, amount);
+            if((st = user_add_amount(user, amount)) != OK) {
+                show_status(st);
+                cla_destroy(cla);
+                ADT_Vector_destroy(&v);
+                array_destroy(data, IN_FILE_FIELDS);
+                return st;
+            }
         }
+
+        /* Si no lo encuentra crea un usuario nuevo */
         else { 
-            /* Si no lo encuentra crea un usuario nuevo */
-            user_create(&user);
-            user_set_data(user, data);
+            if((st = user_create(&user)) != OK) {
+                show_status(st);
+                cla_destroy(cla);
+                ADT_Vector_destroy(&v);
+                array_destroy(data, IN_FILE_FIELDS);
+                return st;
+            }
+
+            if((st = user_set_data(user, data))) {
+                show_status(st);
+                cla_destroy(cla);
+                ADT_Vector_destroy(&v);
+                array_destroy(data, IN_FILE_FIELDS);
+                return st;
+            }
 
             /* Y lo agrega al vector */
             if((st = ADT_Vector_add(&v, user)) != OK){
@@ -131,25 +184,13 @@ int main (int argc, char *argv[])
     }
 
     /* Imprime el vector con los usuarios de acuerdo al argumento recibido */
-    if(!strcmp(cla->fmt, "xml")) {
-        if((st = ADT_Vector_export_as_xml(v, cla->fo, user_print_as_xml)) != OK) {
-            show_status(st);
-            free(user_tmp);
-            cla_destroy(cla);
-            ADT_Vector_destroy(&v);
-            array_destroy(data, IN_FILE_FIELDS);
-            return st;
-        }
-    }
-    else if(!strcmp(cla->fmt,"csv")) {
-        if((st = ADT_Vector_export_as_csv(v, cla->fo, user_print_as_csv)) != OK) {
-            show_status(st);
-            free(user_tmp);
-            cla_destroy(cla);
-            ADT_Vector_destroy(&v);
-            array_destroy(data, IN_FILE_FIELDS);
-            return st;
-        }
+    if((st = ADT_Vector_print(v, cla->fo)) != OK) {
+        show_status(st);
+        free(user_tmp);
+        cla_destroy(cla);
+        ADT_Vector_destroy(&v);
+        array_destroy(data, IN_FILE_FIELDS);
+        return st;
     }
 
     free(user_tmp);
diff --git a/source/status.c b/source/status.c
@@ -18,27 +18,4 @@ const char *status_strings[] = {
 void show_status(status_t st)
 {
     fprintf(stderr, "\n%s\n", status_strings[st]);
-    fprintf(stderr, "%s\n", ERROR_RETRY_MSG);
-}
-
-void free_arrays(size_t num,...)
-{
-    va_list valist;
-    size_t i;
-
-    va_start(valist, num);
-
-    for(i = 0; i < num; i++)
-        free(va_arg(valist, char *));
-}
-
-void close_streams(size_t num,...)
-{
-    va_list valist;
-    size_t i;
-
-    va_start(valist, num);
-
-    for(i = 0; i < num; i++)
-        free(va_arg(valist, FILE *));
 }
diff --git a/source/utils.c b/source/utils.c
@@ -5,6 +5,9 @@ status_t string_split(char *s, char **data, char *delim)
     char *p, *tmp;
     size_t fields = 0;
 
+    if(s == NULL || data == NULL || delim == NULL)
+        return ERROR_NULL_POINTER;
+
     for(p = s; (tmp = strtok(p, delim)); p = NULL)
         strcpy(data[fields++], tmp);
 
@@ -75,3 +78,29 @@ status_t get_date(time_t *e, char **data)
     }
     return OK;
 }
+
+bool is_valid_card(char *card_no)
+{
+    size_t i, j, k;
+    int arr[4][4], sum;
+    char tmp[2];
+
+    if(card_no == NULL) return false;
+    if(strlen(card_no) != CARD_NO_VALID_LEN) return false;
+
+    for(i = 0, k = 0; i < 4; i++) {
+        for(j = 0; j < 4; j++) {
+            arr[i][j] = (card_no[k++] - '0');
+            if(j % 2) {
+                arr[i][j] *= 2;
+                sprintf(tmp, "%d", arr[i][j]);
+                if(!(strlen(tmp) - 1)) arr[i][j] = (tmp[0] - '0');
+                else arr[i][j] = ((tmp[0] - '0') + (tmp[1] - '0'));
+
+            }
+            sum += arr[i][j];
+        }
+    }
+
+    return (sum % 10) ? false : true;
+}
diff --git a/source/vector.c b/source/vector.c
@@ -108,14 +108,16 @@ void *ADT_Vector_get_elem(const ADT_Vector_t *v, void *e)
     return NULL;
 }
 
-status_t ADT_Vector_print(const ADT_Vector_t *v, FILE *fp, printer_t pf)
+status_t ADT_Vector_print(const ADT_Vector_t *v, FILE *fp)
 {
+    status_t st;
     size_t i;
 
     if(v == NULL) return ERROR_NULL_POINTER;
 
     for(i = 0; i < v->size; i++)
-        (*pf)(v->a[i], fp);
+        if((st = v->printer(v->a[i], fp)) != OK)
+            return st;
 
     putchar('\n');
     return OK;