commit e89839f281cac8a05411172f4294b3e714e6377d
parent b0bd63f5cbe875194b8929d20802394ca794da5a
Author: Martin J. Klöckner <64109770+klewer-martin@users.noreply.github.com>
Date: Thu, 29 Jul 2021 22:28:39 -0300
Merge pull request #3 from klewer-martin/segunda_entrega
Segunda entrega
Diffstat:
M | Makefile | | | 25 | +++++++++++++++++-------- |
M | examples/input_gen.py | | | 90 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- |
M | include/cla.h | | | 19 | +++++++++---------- |
D | include/io.h | | | 67 | ------------------------------------------------------------------- |
D | include/sort.h | | | 18 | ------------------ |
M | include/status.h | | | 11 | ++++++----- |
M | include/user.h | | | 28 | ++++++++++++++++++---------- |
A | include/utils.h | | | 43 | +++++++++++++++++++++++++++++++++++++++++++ |
A | include/vector.h | | | 40 | ++++++++++++++++++++++++++++++++++++++++ |
M | source/cla.c | | | 74 | ++++++++++++++++++++++++++++++++++++++++++-------------------------------- |
D | source/io.c | | | 247 | ------------------------------------------------------------------------------- |
M | source/main.c | | | 220 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------- |
D | source/sort.c | | | 53 | ----------------------------------------------------- |
M | source/status.c | | | 23 | ----------------------- |
M | source/user.c | | | 110 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------- |
A | source/utils.c | | | 106 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | source/vector.c | | | 187 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
17 files changed, 812 insertions(+), 549 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,13 +1,13 @@
CC=gcc
-CFLAGS=-pedantic -Wall
+CFLAGS=-pedantic -Wall -g
SRCFOLDER=source
HFOLDER=include
PROGNAME=main
all: main clean
-main: cla.o status.o sort.o io.o main.o user.o
- $(CC) $(CFLAGS) main.o cla.o status.o io.o sort.o user.o -o $(PROGNAME)
+main: cla.o status.o utils.o main.o user.o vector.o
+ $(CC) $(CFLAGS) main.o cla.o status.o utils.o user.o vector.o -o $(PROGNAME)
main.o: $(HFOLDER)/cla.h $(HFOLDER)/status.h $(HFOLDER)/user.h
$(CC) $(CFLAGS) -c $(SRCFOLDER)/main.c
@@ -15,14 +15,14 @@ main.o: $(HFOLDER)/cla.h $(HFOLDER)/status.h $(HFOLDER)/user.h
cla.o: $(HFOLDER)/cla.h $(HFOLDER)/status.h
$(CC) $(CFLAGS) -c $(SRCFOLDER)/cla.c
+vector.o: $(HFOLDER)/vector.h $(HFOLDER)/status.h
+ $(CC) $(CFLAGS) -c $(SRCFOLDER)/vector.c
+
status.o: $(HFOLDER)/status.h
$(CC) $(CFLAGS) -c $(SRCFOLDER)/status.c
-io.o: $(HFOLDER)/status.h $(HFOLDER)/user.h
- $(CC) $(CFLAGS) -c $(SRCFOLDER)/io.c
-
-sort.o: $(HFOLDER)/status.h $(HFOLDER)/user.h
- $(CC) $(CFLAGS) -c $(SRCFOLDER)/sort.c
+utils.o: $(HFOLDER)/status.h $(HFOLDER)/user.h
+ $(CC) $(CFLAGS) -c $(SRCFOLDER)/utils.c
user.o: $(HFOLDER)/status.h $(HFOLDER)/user.h
$(CC) $(CFLAGS) -c $(SRCFOLDER)/user.c
@@ -30,6 +30,9 @@ user.o: $(HFOLDER)/status.h $(HFOLDER)/user.h
clean:
rm *.o
+run5:
+ ./main -fmt csv -out output.csv -in examples/test_file_5.csv -ti 1320498000 -tf 1320498046
+
run20:
./main -fmt csv -out output.csv -in examples/test_file_20.csv -ti 1320498000 -tf 1320498046
@@ -48,9 +51,15 @@ 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
+run50m:
+ ./main -fmt csv -out output.csv -in examples/test_file_50m.csv -ti 1320498000 -tf 1420529000
+
run50xml:
./main -fmt xml -out output.xml -in examples/test_file_50.csv -ti 1320498000 -tf 1320498049
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 = 500000
+LINES = 10000
# OUTPUT:
# ID_TRANSACCION, ID_USUARIO, FECHA, MONTO, NUMERO DE TRAJETA, DESCRIPCION
@@ -10,19 +10,99 @@ LINES = 500000
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/cla.h b/include/cla.h
@@ -21,19 +21,18 @@ typedef enum {
} flags_t;
typedef struct {
- char *fmt, *fi, *fo;
- unsigned long ti, tf, parsed_lines;
+ char *fmt;
+ FILE *fi, *fo;
+ unsigned long ti, tf;
} ADT_cla_t, *cla_t;
-status_t validate_arguments(int argc, char **argv);
-status_t check_flags_position(int argc, char **argv);
-status_t check_flags_repeated(int argc, char **argv);
+status_t validate_arguments(int, char **, cla_t);
+status_t check_flags_position(int, char **);
+status_t check_flags_repeated(int, char **);
-status_t cla_create(cla_t *cla);
-status_t cla_setup(int argc, char **argv, cla_t *cla);
-status_t cla_destroy(cla_t *cla);
-
-void clean(cla_t cla);
+status_t cla_create(cla_t *);
+status_t cla_setup(int, char **, cla_t);
+status_t cla_destroy(cla_t);
extern const char *available_flags[FLAGS_MAX];
extern const char *available_formats[FORMATS_MAX];
diff --git a/include/io.h b/include/io.h
@@ -1,67 +0,0 @@
-#ifndef IO__H
-#define IO__H
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "cla.h"
-#include "user.h"
-#include "status.h"
-
-#define _XOPEN_SOURCE
-#define __USE_XOPEN
-#include <time.h>
-
-#define INIT_SIZE 1000
-#define INPUT_FILE_FIELDS 6
-
-#define BUFFER_SIZE 1000
-#define GROWTH_FACTOR 2
-
-#define INPUT_FILE_DELIM ","
-#define CSV_OUTPUT_DELIM ","
-
-#define XML_HEADER "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
-#define XML_ROOT_OPEN "<root>"
-#define XML_ROOT_CLOSE "</root>"
-
-#define XML_ROW_OPEN "<row>"
-#define XML_ROW_CLOSE "</row>"
-
-#define XML_ID_OPEN "<user>"
-#define XML_ID_CLOSE "</user>"
-
-#define XML_CREDIT_OPEN "<credits>"
-#define XML_CREDIT_CLOSE "</credits>"
-
-#define XML_DEBT_OPEN "<debits>"
-#define XML_DEBT_CLOSE "</debits>"
-
-/* typedef enum { */
-/* POS_ID_TXN, */
-/* POS_USER_ID, */
-/* POS_TXN_DATE, */
-/* POS_AMOUNT, */
-/* POS_CARD_NUMBER, */
-/* POS_DESC */
-/* } csv_pos_t; */
-
-status_t process_file(cla_t cla, user_t **users, size_t *i);
-
-status_t string_split(char *s, char **data, char *delim);
-
-status_t load_values(FILE *, cla_t *data);
-
-status_t export_data(cla_t cla, const user_t *users, size_t size);
-void clean_data(char **data);
-
-status_t export_data_as_csv(FILE *fo, const user_t *users, size_t size);
-status_t export_data_as_xml(FILE *fo, const user_t *users, size_t size);
-
-status_t destroy_data(char **data);
-status_t get_date(time_t *e, char **data);
-
-void clean_buffer(char *buf);
-
-#endif
diff --git a/include/sort.h b/include/sort.h
@@ -1,18 +0,0 @@
-#ifndef SORT__H
-#define SORT__H
-
-#include "cla.h"
-#include "user.h"
-#include "status.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-
-int credit_minmax(const void *, const void *);
-int credit_maxmix(const void *, const void *);
-
-int debt_minmax(const void *, const void *);
-int debt_maxmin(const void *, const void *);
-
-
-#endif
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
@@ -17,6 +18,7 @@ typedef enum {
ERROR_MEMORY,
ERROR_WRONG_FLAGS,
ERROR_WRONG_TIME,
+ ERROR_INVALID_POS,
ERROR_MISSING_ARGS,
ERROR_OPENING_FILE,
ERROR_CORRUPT_DATA,
@@ -37,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/user.h b/include/user.h
@@ -2,21 +2,29 @@
#define USER__H
#include "status.h"
+#include "utils.h"
+
+#define OUT_FILE_DELIM ","
+
+typedef unsigned long ulong;
typedef struct {
- size_t id;
- long credit, debt;
-} ADT_user_t, *user_t;
+ /* id: user id / c: user credits / d: user debits */
+ ulong id, c, d;
+} ADT_user_t;
+
+status_t user_create(ADT_user_t **);
+status_t user_destroy(ADT_user_t **);
+status_t user_set_data(ADT_user_t *, char **);
+status_t user_add_amount(ADT_user_t *, long);
-user_t user_find(const user_t *users, int id, size_t size);
-user_t user_dup(user_t src);
+int user_equals(const void *, const void *);
-status_t user_create(user_t *usr);
-status_t user_set_data(user_t *user, char **data);
+status_t user_print_as_csv(const void *, FILE *);
+status_t user_print_as_xml(const void *, FILE *);
-void user_clean(user_t usr);
+int user_comparator_credits_minmax(const void *, const void *);
+int user_comparator_credits_maxmin(const void *, const void *);
-status_t sort_users(user_t *users, size_t size, char *order);
-status_t destroy_users(user_t *users, size_t size);
#endif
diff --git a/include/utils.h b/include/utils.h
@@ -0,0 +1,43 @@
+#ifndef UTILS__H
+#define UTILS__H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "cla.h"
+#include "user.h"
+#include "status.h"
+
+#define _XOPEN_SOURCE
+#define __USE_XOPEN
+#include <time.h>
+
+#define IN_FILE_MAX_LEN 100
+#define IN_FILE_DELIM ","
+#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"
+#define XML_STR_ID "user"
+#define XML_STR_CREDIT "credits"
+#define XML_STR_DEBIT "debits"
+
+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
@@ -0,0 +1,40 @@
+#ifndef VECTOR__H
+#define VECTOR__H
+
+#define VECTOR_INIT_SIZE 10
+#define VECTOR_GROWTH_FACTOR 2
+
+#include <stdlib.h>
+
+#include "utils.h"
+#include "status.h"
+
+typedef status_t (*printer_t)(const void *, FILE *);
+typedef int (*comparator_t)(const void *, const void *);
+
+typedef struct {
+ void **a;
+ size_t size, alloc;
+
+ printer_t printer;
+ comparator_t comparator;
+
+} ADT_Vector_t;
+
+status_t ADT_Vector_create(ADT_Vector_t **);
+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 *);
+status_t ADT_Vector_sort(ADT_Vector_t *, comparator_t);
+
+void *ADT_Vector_get_elem(const ADT_Vector_t *v, void *e);
+
+status_t ADT_Vector_set_printer(ADT_Vector_t *, printer_t);
+status_t ADT_Vector_set_comparator(ADT_Vector_t *, comparator_t);
+
+status_t ADT_Vector_export_as_xml(const ADT_Vector_t *, FILE *);
+status_t ADT_Vector_export_as_csv(const ADT_Vector_t *, FILE *);
+
+#endif
diff --git a/source/cla.c b/source/cla.c
@@ -3,17 +3,20 @@
const char *available_flags[] = { "-fmt", "-out", "-in", "-ti", "-tf" };
const char *available_formats[] = { "csv", "xml" };
-status_t validate_arguments(int argc, char **argv)
+status_t validate_arguments(int argc, char **argv, cla_t cla)
{
status_t st;
- if(argv == NULL) return ERROR_NULL_POINTER;
+ if(argv == NULL || cla == NULL) return ERROR_NULL_POINTER;
if((argc == NO_ARGS_ENTERED) || (argc != NORMAL_AMOUNT_ARGS))
return ERROR_MISSING_ARGS;
if((st = check_flags_position(argc, argv))) return st;
if((st = check_flags_repeated(argc, argv))) return st;
+ /* Asigna a la estructura 'cla' los argumentos ingresados */
+ if((st = cla_setup(argc, argv, cla))) return st;
+
return OK;
}
@@ -60,25 +63,45 @@ status_t check_flags_repeated(int argc, char **argv)
return OK;
}
-status_t cla_setup(int argc, char **argv, cla_t *cla)
+status_t cla_setup(int argc, char **argv, cla_t cla)
{
char *endptr;
size_t i;
+ FILE *fp;
flags_t f;
for(i = 1; i < argc; i += 2) {
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: strcpy((*cla)->fo, argv[i + 1]); break;
- case FLAG_IN: strcpy((*cla)->fi, argv[i + 1]); 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;
}
}
@@ -101,34 +124,21 @@ status_t cla_create(cla_t *cla)
return ERROR_MEMORY;
}
- if(((*cla)->fo = calloc(sizeof(char), 100)) == NULL) {
- free((*cla)->fmt);
- free(cla);
- cla = NULL;
- return ERROR_MEMORY;
- }
-
- if(((*cla)->fi = calloc(sizeof(char), 100)) == NULL) {
- free((*cla)->fo);
- free((*cla)->fmt);
- free(cla);
- cla = NULL;
- return ERROR_MEMORY;
- }
+ (*cla)->fi = NULL;
+ (*cla)->fo = NULL;
return OK;
}
-status_t cla_destroy(cla_t *cla)
+status_t cla_destroy(cla_t cla)
{
if(cla == NULL) return ERROR_NULL_POINTER;
- free((*cla)->fmt);
- free((*cla)->fo);
- free((*cla)->fi);
- free(*cla);
+ if(cla->fi != NULL) fclose(cla->fi);
+ if(cla->fo != NULL) fclose(cla->fo);
- *cla = NULL;
+ free(cla->fmt);
+ free(cla);
return OK;
}
diff --git a/source/io.c b/source/io.c
@@ -1,247 +0,0 @@
-#include "../include/io.h"
-
-/* Lee los datos del archivo de entrada linea por linea mientras los procesa y asigna a un arreglo de usuarios */
-status_t process_file(cla_t cla, user_t **users, size_t *size)
-{
- FILE *fpi;
- char *buffer, **data;
- time_t epoch;
- size_t alloc_size;
- user_t user, user_found, *tmp;
- status_t st;
-
- *size = cla->parsed_lines = 0;
- alloc_size = INIT_SIZE;
- user = user_found = NULL;
-
- if((fpi = fopen(cla->fi, "rt")) == NULL) {
- *users = NULL;
- return ERROR_OPENING_FILE;
- }
-
- if((data = (char **)malloc(sizeof(char *) * INPUT_FILE_FIELDS)) == NULL) {
- fclose(fpi);
- return ERROR_MEMORY;
- }
-
- if((buffer = calloc(sizeof(char), BUFFER_SIZE)) == NULL) {
- destroy_data(data);
- fclose(fpi);
- return ERROR_MEMORY;
- }
-
- if(user_create(&user) != OK) {
- free(buffer);
- destroy_data(data);
- fclose(fpi);
- }
-
- /* En caso de haber algun error users no es liberado, se libera luego en main */
- if(((*users) = (user_t *)malloc(sizeof(user_t) * INIT_SIZE)) == NULL) {
- free(buffer);
- free(user);
- destroy_data(data);
- fclose(fpi);
- }
-
- /* Lee los datos de el archivo de entrada linea por linea */
- while(fgets(buffer, BUFFER_SIZE, fpi) != NULL) {
- /* Extiende el arreglo de usuarios en caso de que haya poca memoria */
- if(*size == alloc_size) {
- alloc_size *= GROWTH_FACTOR;
- if((tmp = realloc((*users), alloc_size * sizeof(user_t))) == NULL) {
- fclose(fpi);
- free(buffer);
- destroy_data(data);
- return ERROR_MEMORY;
- }
- (*users) = tmp;
- }
-
- /* Divide el buffer en subarreglos segun un caracter delimitador */
- if((st = string_split(buffer, data, INPUT_FILE_DELIM)) != OK) {
- fclose(fpi);
- free(buffer);
- destroy_data(data);
- return st;
- }
-
- /* Asigna a 'user' id, creditos y debitos cargados en data */
- if((st = user_set_data(&user, data)) != OK) {
- destroy_data(data);
- free(buffer);
- fclose(fpi);
- return st;
- }
-
- /* Transforma la fecha leida a tiempo UNIX en segundos */
- if((st = get_date(&epoch, data)) != OK) {
- destroy_data(data);
- free(buffer);
- fclose(fpi);
- return st;
- }
-
- /* Comprueba que la fecha leida este dentro del rango de fechas ingresadas en CLA */
- if(epoch < cla->ti) continue;
- else if(epoch > cla->tf) {
- free(user);
- destroy_data(data);
- free(buffer);
- fclose(fpi);
- return OK;
- }
-
- /* Busca el numero de id en los usuarios ya ingresados, si no lo encuentra agrega un usuario nuevo al arreglo de usuarios */
- if((user_found = user_find(*users, user->id, *size)) != NULL) {
- user_found->credit += user->credit;
- user_found->debt += user->debt;
- } else {
- (*users)[(*size)++] = user_dup(user);
- }
-
- cla->parsed_lines++;
-
- user_clean(user);
- clean_data(data);
- clean_buffer(buffer);
- } /* End while */
-
- free(user);
- destroy_data(data);
- free(buffer);
- fclose(fpi);
- return OK;
-}
-
-
-status_t string_split(char *s, char **data, char *delim)
-{
- char *p, *tmp;
- size_t fields = 0;
-
- for(p = s; (tmp = strtok(p, delim)); p = NULL)
- data[fields++] = strdup(tmp);
-
- if(fields != INPUT_FILE_FIELDS)
- return ERROR_CORRUPT_DATA;
-
- return OK;
-}
-
-status_t destroy_data(char **data)
-{
- size_t i;
-
- if(data == NULL) return ERROR_NULL_POINTER;
-
- for(i = 0; i < INPUT_FILE_FIELDS; i++) {
- free(data[i]);
- data[i] = NULL;
- }
-
- free(data);
- return OK;
-}
-
-status_t export_data(cla_t cla, const user_t *users, size_t size)
-{
- FILE *fo;
-
- if(users == NULL) return ERROR_NULL_POINTER;
-
- if((fo = fopen(cla->fo, "wt")) == NULL)
- return ERROR_OPENING_FILE;
-
- if(!strcmp(cla->fmt, "csv")) {
- export_data_as_csv(fo, users, size);
- } else if(!strcmp(cla->fmt, "xml")) {
- export_data_as_xml(fo, users, size);
- } else return ERROR_FORMAT_NOT_FOUND;
-
- fclose(fo);
- return OK;
-}
-
-status_t export_data_as_csv(FILE *fo, const user_t *users, size_t size)
-{
- size_t i;
-
- if(fo == NULL || users == NULL)
- return ERROR_NULL_POINTER;
-
- for(i = 0; i < size; i++)
- fprintf(fo, "%ld%s%ld%s%ld\n", users[i]->id, CSV_OUTPUT_DELIM,\
- users[i]->credit, CSV_OUTPUT_DELIM, users[i]->debt);
-
- return OK;
-}
-
-status_t export_data_as_xml(FILE *fo, const user_t *users, size_t size)
-{
- size_t i;
-
- if(fo == NULL || users == NULL)
- return ERROR_NULL_POINTER;
-
- fprintf(fo, "%s\n%s\n", XML_HEADER, XML_ROOT_OPEN);
-
- for(i = 0; i < size; i++) {
- fprintf(fo, "\t%s\n", XML_ROW_OPEN);
- fprintf(fo, "\t\t%s%ld%s\n", XML_ID_OPEN, users[i]->id, XML_ID_CLOSE);
- fprintf(fo, "\t\t%s%ld%s\n", XML_CREDIT_OPEN, users[i]->credit, XML_CREDIT_CLOSE);
- fprintf(fo, "\t\t%s%ld%s\n", XML_DEBT_OPEN, users[i]->debt, XML_DEBT_CLOSE);
- fprintf(fo, "\t%s\n", XML_ROW_CLOSE);
- }
-
- fprintf(fo, "%s\n", XML_ROOT_CLOSE);
- return OK;
-}
-
-void clean_buffer(char *buf)
-{
- size_t i;
-
- for(i = 0; i < BUFFER_SIZE; i++)
- buf[i] = '\0';
-}
-
-void clean_data(char **data)
-{
- size_t i;
-
- for(i = 0; i < INPUT_FILE_FIELDS; i++) {
- free(data[i]);
- data[i] = NULL;
- }
-}
-
-status_t get_date(time_t *e, char **data)
-{
- struct tm tm;
-
- if(e == NULL || data == NULL) return ERROR_NULL_POINTER;
-
- /* Desactiva el horario de verano */
- tm.tm_isdst = 0;
-
- switch(data[POS_TXN_DATE][2]) {
- case '/':
- strptime(data[2], "%d/%m/%Y %H:%M:%S", &tm);
- *e = mktime(&tm);
- break;
- case '-':
- strptime(data[2], "%d-%m-%Y %H:%M:%S", &tm);
- *e = mktime(&tm);
- break;
- case '.':
- strptime(data[2], "%d.%m.%Y %H:%M:%S", &tm);
- *e = mktime(&tm);
- break;
-
- default: return ERROR_CORRUPT_DATA; break;
- }
-
- return OK;
-}
-
diff --git a/source/main.c b/source/main.c
@@ -1,75 +1,221 @@
-#include "../include/io.h"
#include "../include/cla.h"
#include "../include/user.h"
-#include "../include/sort.h"
+#include "../include/utils.h"
#include "../include/status.h"
-
-/* "ca" - creditos ascendentes | "cd" - creditos descendentes
- * "da" - debitos ascendentes | "dd" - debitos descendentes */
-#define SORTING_ORDER "cd"
-#define PRINT_EXIT_SUCCESS_MSG
+#include "../include/vector.h"
int main (int argc, char *argv[])
{
status_t st;
cla_t cla;
- user_t *users;
- size_t size;
+ ADT_Vector_t *v;
+ ADT_user_t *user, *user_tmp;
+ char buffer[IN_FILE_MAX_LEN];
+ char *endptr, **data;
+ size_t i;
+ long amount;
+ time_t epoch;
- /* Valida que los argumentos sean correctos */
- if((st = validate_arguments(argc, argv)) != OK) {
+ /* Asigna memoria a cla (c.l.a.: "command line arguments") */
+ if((st = cla_create(&cla)) != OK) {
show_status(st);
return st;
}
- /* Asigna memoria a cla */
- if((st = cla_create(&cla)) != OK) {
+ /* Valida que los argumentos sean correctos y asigna valores a cla */
+ if((st = validate_arguments(argc, argv, cla)) != OK) {
show_status(st);
+ cla_destroy(cla);
return st;
}
- /* Asigna a la estructura 'cla' los argumentos ingresados */
- if((st = cla_setup(argc, argv, &cla)) != OK) {
+ /* Crea un vector */
+ if((st = ADT_Vector_create(&v)) != OK) {
show_status(st);
+ cla_destroy(cla);
return st;
}
- /* Carga en users los usuarios desde el archivo de entrada */
- if((st = process_file(cla, &users, &size)) != OK) {
- show_status(st);
- cla_destroy(&cla);
- destroy_users(users, size);
- return st;
+ /* Crea un arreglo de cadenas de caracteres */
+ if((data = malloc(sizeof(char *) * IN_FILE_FIELDS)) == NULL) {
+ show_status(ERROR_MEMORY);
+ cla_destroy(cla);
+ ADT_Vector_destroy(&v);
+ return ERROR_MEMORY;
}
- /* Ordena los usuarios con orden SORTING_ORDER */
- if((st = sort_users(users, size, SORTING_ORDER)) != OK) {
+ for(i = 0; i < IN_FILE_FIELDS; i++) {
+ if((data[i] = calloc(sizeof(char), IN_FILE_FIELDS_MAX_LEN)) == NULL) {
+ show_status(ERROR_MEMORY);
+ cla_destroy(cla);
+ ADT_Vector_destroy(&v);
+ array_destroy(data, IN_FILE_FIELDS);
+ return ERROR_MEMORY;
+ }
+ }
+
+ /* Setea el comparador a ADT_Vector */
+ if((st = ADT_Vector_set_comparator(v, user_equals)) != OK) {
show_status(st);
- cla_destroy(&cla);
- destroy_users(users, size);
+ cla_destroy(cla);
+ ADT_Vector_destroy(&v);
+ array_destroy(data, IN_FILE_FIELDS);
return st;
}
- /* Imprime los datos cargados en users a un archivo de salida */
- if((st = export_data(cla, users, size)) != OK) {
+ /* Crea un usuario temporal */
+ if((st = user_create(&user_tmp)) != OK) {
show_status(st);
- cla_destroy(&cla);
- destroy_users(users, size);
+ cla_destroy(cla);
+ ADT_Vector_destroy(&v);
+ array_destroy(data, IN_FILE_FIELDS);
return st;
}
+ /* Lee linea por linea del archivo de entrada */
+ while(fgets(buffer, IN_FILE_MAX_LEN, cla->fi)) {
+
+ /* Separa la linea leida segun un caracter delimitador */
+ 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);
+ free(user_tmp);
+ return st;
+ }
+
+ /* Setea el usuario temporal con los datos obtenidos de la linea */
+ 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);
+ free(user_tmp);
+ return st;
+ }
+
+ amount = strtol(data[POS_AMOUNT], &endptr, 10);
+ if(*endptr != '\0') {
+ show_status(st);
+ cla_destroy(cla);
+ ADT_Vector_destroy(&v);
+ array_destroy(data, IN_FILE_FIELDS);
+ free(user_tmp);
+ return ERROR_CORRUPT_DATA;
+ }
-#ifdef PRINT_EXIT_SUCCESS_MSG
+ /* Transforma el tiempo de la linea leida a formato UNIX */
+ get_date(&epoch, data);
- /* Imprime un mensaje para darle a conocer al usuario
- * que todo se ejecuto correctamente */
- printf("\n%s\n%s%ld\n%s%ld\n", EXIT_SUCCESS_MSG, USERS_REGISTERED_MSG,\
- size, PROCESED_LINES_MSG, cla->parsed_lines);
+ /* Comprueba que el tiempo de la linea leida no supere los argumentos ingresados */
+ if(epoch < cla->ti) continue;
+ else if(epoch > cla->tf) break;
-#endif
+ /* 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;
+ }
- cla_destroy(&cla);
- destroy_users(users, size);
+ /* 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 */
+ if((st = user_add_amount(user, amount)) != OK) {
+ show_status(st);
+ cla_destroy(cla);
+ ADT_Vector_destroy(&v);
+ array_destroy(data, IN_FILE_FIELDS);
+ free(user_tmp);
+ return st;
+ }
+ }
+
+ /* Si no lo encuentra crea un usuario nuevo */
+ else {
+ if((st = user_create(&user)) != OK) {
+ show_status(st);
+ cla_destroy(cla);
+ ADT_Vector_destroy(&v);
+ array_destroy(data, IN_FILE_FIELDS);
+ free(user_tmp);
+ 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);
+ free(user_tmp);
+ return st;
+ }
+
+ /* Y lo agrega al vector */
+ if((st = ADT_Vector_add(&v, user)) != OK){
+ show_status(st);
+ free(user_tmp);
+ cla_destroy(cla);
+ ADT_Vector_destroy(&v);
+ array_destroy(data, IN_FILE_FIELDS);
+ free(user_tmp);
+ return st;
+ }
+ }
+ clean_buffer(buffer);
+ clean_array(data);
+ } /* End while */
+
+ /* Ordena el vector con los usuarios */
+ if((st = ADT_Vector_sort(v, user_comparator_credits_maxmin)) != OK) {
+ show_status(st);
+ free(user_tmp);
+ cla_destroy(cla);
+ ADT_Vector_destroy(&v);
+ array_destroy(data, IN_FILE_FIELDS);
+ 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;
+ }
+ /* E imprime el vector con los usuarios */
+ if((st = ADT_Vector_export_as_csv(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;
+ }
+ }
+ 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;
+ }
+ if((st = ADT_Vector_export_as_xml(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);
+ cla_destroy(cla);
+ ADT_Vector_destroy(&v);
+ array_destroy(data, IN_FILE_FIELDS);
return OK;
}
diff --git a/source/sort.c b/source/sort.c
@@ -1,53 +0,0 @@
-#include "../include/sort.h"
-
-int credit_minmax(const void *a, const void *b)
-{
- user_t *A = (user_t *)a;
- user_t *B = (user_t *)b;
-
- return ((*A)->credit > (*B)->credit) ? 1 : 0;
-}
-
-int credit_maxmin(const void *a, const void *b)
-{
- user_t *A = (user_t *)a;
- user_t *B = (user_t *)b;
-
- return ((*A)->credit > (*B)->credit) ? 0 : 1;
-}
-
-int debt_minmax(const void *a, const void *b)
-{
- user_t *A = (user_t *)a;
- user_t *B = (user_t *)b;
-
- return ((*A)->debt > (*B)->debt) ? 1 : 0;
-}
-
-int debt_maxmin(const void *a, const void *b)
-{
- user_t *A = (user_t *)a;
- user_t *B = (user_t *)b;
-
- return ((*A)->debt > (*B)->debt) ? 0 : 1;
-}
-
-status_t sort_users(user_t *users, size_t l, char *order)
-{
- if(users == NULL || order == NULL)
- return ERROR_NULL_POINTER;
-
- if(!strcmp(order, "ca"))
- qsort(users, l, sizeof(user_t), credit_minmax);
-
- if(!strcmp(order, "cd"))
- qsort(users, l, sizeof(user_t), credit_maxmin);
-
- if(!strcmp(order, "da"))
- qsort(users, l, sizeof(user_t), debt_minmax);
-
- if(!strcmp(order, "dd"))
- qsort(users, l, sizeof(user_t), debt_maxmin);
-
- return OK;
-}
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/user.c b/source/user.c
@@ -1,76 +1,118 @@
#include "../include/user.h"
-status_t user_create(user_t *usr)
+status_t user_create(ADT_user_t **user)
{
- if(((*usr) = (user_t)malloc(sizeof(ADT_user_t))) == NULL)
+ if((*user = (ADT_user_t *)malloc(sizeof(ADT_user_t))) == NULL)
return ERROR_MEMORY;
- (*usr)->id = 0;
- (*usr)->credit = 0;
- (*usr)->debt = 0;
+ (*user)->id = 0;
+ (*user)->c = 0;
+ (*user)->d = 0;
return OK;
}
-void user_clean(user_t usr)
+status_t user_destroy(ADT_user_t **user)
{
- usr->id = 0;
- usr->credit = 0;
- usr->debt = 0;
+ if(user == NULL) return ERROR_NULL_POINTER;
+
+ free(*user);
+ *user = NULL;
+
+ return OK;
}
-status_t user_set_data(user_t *user, char **data)
+status_t user_set_data(ADT_user_t *user, char **data)
{
char *endptr;
long amount;
if(data == NULL || user == NULL) return ERROR_NULL_POINTER;
- (*user)->id = strtol(data[POS_USER_ID], &endptr, 10);
+ user->id = strtol(data[POS_USER_ID], &endptr, 10);
if(*endptr != '\0') return ERROR_CORRUPT_DATA;
amount = strtol(data[POS_AMOUNT], &endptr, 10);
if(*endptr != '\0') return ERROR_CORRUPT_DATA;
- if(amount > 0) (*user)->credit = amount;
- else if(amount < 0) (*user)->debt = -amount; /* '-=' Para eliminar el menos */
+ if(amount > 0) user->c = amount;
+ else if(amount < 0) user->d = -amount; /* '-=' Para eliminar el menos */
+
+ return OK;
+}
+
+status_t user_add_amount(ADT_user_t *user, long amount)
+{
+ if(user == NULL) return ERROR_NULL_POINTER;
+
+ if(amount > 0) user->c += amount;
+ else if(amount < 0) user->d -= amount; /* '-=' Para eliminar el menos */
return OK;
}
-user_t user_dup(user_t src)
+int user_equals(const void *a, const void *b)
{
- user_t dst = NULL;
+ ADT_user_t *A, *B;
- user_create(&dst);
+ if(a == NULL || b == NULL) return ERROR_NULL_POINTER;
- dst->id = src->id;
- dst->credit = src->credit;
- dst->debt = src->debt;
+ A = (ADT_user_t *)a;
+ B = (ADT_user_t *)b;
- return dst;
+ if(A->id == B->id) return 1;
+
+ return 0;
}
-user_t user_find(const user_t *users, int id, size_t size)
+status_t user_print_as_csv(const void *u, FILE *fp)
{
- size_t i;
- for(i = 0; i < size; i++) {
- if(users[i]->id == id) {
- return users[i];
- }
- }
- return NULL;
+ if(u == NULL || fp == NULL) return ERROR_NULL_POINTER;
+
+ ADT_user_t *user = (ADT_user_t *)u;
+
+ fprintf(fp, "%ld%s%ld%s%ld\n", user->id, OUT_FILE_DELIM, user->c, OUT_FILE_DELIM, user->d);
+
+ return OK;
}
-status_t destroy_users(user_t *users, size_t size)
+status_t user_print_as_xml(const void *u, FILE *fp)
{
- size_t i;
+ if(u == NULL || fp == NULL) return ERROR_NULL_POINTER;
- if(users == NULL) return ERROR_NULL_POINTER;
+ ADT_user_t *user = (ADT_user_t *)u;
- for(i = 0; i < size; i++)
- free(users[i]);
+ fprintf(fp, "\t\t<%s>%ld</%s>\n", XML_STR_ID, user->id, XML_STR_ID);
+ fprintf(fp, "\t\t<%s>%ld</%s>\n", XML_STR_CREDIT, user->c, XML_STR_CREDIT);
+ fprintf(fp, "\t\t<%s>%ld</%s>\n", XML_STR_DEBIT, user->d, XML_STR_DEBIT);
- free(users);
return OK;
}
+
+int user_comparator_credits_minmax(const void *a, const void *b)
+{
+ ADT_user_t *A, *B;
+
+ if(a == NULL || b == NULL) return ERROR_NULL_POINTER;
+
+ A = *(ADT_user_t **)a;
+ B = *(ADT_user_t **)b;
+
+ if(A->c > B->c) return 1;
+ else if(A->c == B->c) return 0;
+ return -1;
+}
+
+int user_comparator_credits_maxmin(const void *a, const void *b)
+{
+ ADT_user_t *A, *B;
+
+ if(a == NULL || b == NULL) return ERROR_NULL_POINTER;
+
+ A = *(ADT_user_t **)a;
+ B = *(ADT_user_t **)b;
+
+ if(A->c < B->c) return 1;
+ else if(A->c == B->c) return 0;
+ return -1;
+}
diff --git a/source/utils.c b/source/utils.c
@@ -0,0 +1,106 @@
+#include "../include/utils.h"
+
+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);
+
+ if(fields != IN_FILE_FIELDS)
+ return ERROR_CORRUPT_DATA;
+
+ return OK;
+}
+
+
+void clean_buffer(char *buf)
+{
+ size_t i;
+
+ for(i = 0; i < IN_FILE_MAX_LEN; i++)
+ buf[i] = '\0';
+}
+
+void clean_array(char **data)
+{
+ size_t i, j;
+
+ for(i = 0; i < IN_FILE_FIELDS; i++) {
+ for(j = 0; j < IN_FILE_FIELDS_MAX_LEN; j++)
+ data[i][j] = '\0';
+ }
+}
+
+status_t array_destroy(char **data, size_t fields)
+{
+ size_t i;
+
+ if(data == NULL) return ERROR_NULL_POINTER;
+
+ for(i = 0; i < fields; i++) {
+ free(data[i]);
+ data[i] = NULL;
+ }
+
+ free(data);
+ return OK;
+}
+
+status_t get_date(time_t *e, char **data)
+{
+ struct tm tm;
+
+ if(e == NULL || data == NULL) return ERROR_NULL_POINTER;
+
+ /* Desactiva el horario de verano */
+ tm.tm_isdst = 0;
+
+ switch(data[POS_TXN_DATE][2]) {
+ case '/':
+ strptime(data[2], "%d/%m/%Y %H:%M:%S", &tm);
+ *e = mktime(&tm);
+ break;
+ case '-':
+ strptime(data[2], "%d-%m-%Y %H:%M:%S", &tm);
+ *e = mktime(&tm);
+ break;
+ case '.':
+ strptime(data[2], "%d.%m.%Y %H:%M:%S", &tm);
+ *e = mktime(&tm);
+ break;
+
+ default: return ERROR_CORRUPT_DATA; break;
+ }
+ 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
@@ -0,0 +1,187 @@
+#include "../include/vector.h"
+
+status_t ADT_Vector_create(ADT_Vector_t **v)
+{
+ if(v == NULL) return ERROR_NULL_POINTER;
+
+ if((*v = (ADT_Vector_t *)malloc(sizeof(ADT_Vector_t))) == NULL)
+ return ERROR_MEMORY;
+
+ if(((*v)->a = (void **)malloc(sizeof(void *) * VECTOR_INIT_SIZE)) == NULL)
+ return ERROR_MEMORY;
+
+ (*v)->size = 0;
+ (*v)->alloc = VECTOR_INIT_SIZE;
+ (*v)->printer = NULL;
+ (*v)->comparator = NULL;
+
+ return OK;
+}
+
+status_t ADT_Vector_add(ADT_Vector_t **v, void *e)
+{
+ void *tmp;
+
+ if(v == NULL || e == NULL) return ERROR_NULL_POINTER;
+
+ /* Extends the array in case low memory left */
+ if(((*v)->size + 1) == (*v)->alloc) {
+ if((tmp = (void **)realloc((*v)->a, sizeof(void *) * (*v)->alloc * VECTOR_GROWTH_FACTOR)) == NULL)
+ return ERROR_MEMORY;
+ else {
+ (*v)->a = tmp;
+ (*v)->alloc *= VECTOR_GROWTH_FACTOR;
+ }
+ }
+
+ (*v)->a[(*v)->size++] = e;
+
+ return OK;
+}
+
+status_t ADT_Vector_destroy(ADT_Vector_t **v)
+{
+ if(v == NULL) return ERROR_NULL_POINTER;
+
+ for(size_t i = 0; i < (*v)->size; i++) {
+ free((*v)->a[i]);
+ (*v)->a[i] = NULL;
+ }
+
+ free((*v)->a);
+ free(*v);
+ *v = NULL;
+
+ return OK;
+}
+
+status_t ADT_Vector_set(ADT_Vector_t **v, void *e, size_t pos)
+{
+ void ** aux;
+ size_t i, alloc_size;
+
+ if(v == NULL || e == NULL) return ERROR_NULL_POINTER;
+ else if(pos > (*v)->size) return ERROR_INVALID_POS;
+
+ alloc_size = (*v)->alloc;
+
+ /* Cuenta cuanto tiene que alargar el arreglo p/ que entre el nuevo elemento */
+ while(pos >= alloc_size) {
+ alloc_size *= VECTOR_GROWTH_FACTOR;
+ }
+
+ /* Extiende el arreglo en caso de que no haya memoria suficiente */
+ if(pos > (*v)->alloc) {
+ if((aux = (void **)realloc((*v)->a, sizeof(void *) * alloc_size)) == NULL)
+ return ERROR_MEMORY;
+ else {
+ (*v)->a = aux;
+ (*v)->alloc = alloc_size;
+ }
+ }
+
+ for(i = (*v)->size; i < pos; i++) {
+ (*v)->a[pos] = NULL;
+ }
+
+ (*v)->a[pos] = e;
+
+ if(pos >= (*v)->size) {
+ (*v)->size = (pos + 1);
+ }
+
+ return OK;
+}
+
+void *ADT_Vector_get_elem(const ADT_Vector_t *v, void *e)
+{
+ size_t i;
+
+ if(v == NULL || e == NULL) return NULL;
+
+ for(i = 0; i < v->size; i++) {
+ if(v->comparator(e, v->a[i])) {
+ return v->a[i];
+ }
+ }
+
+ return NULL;
+}
+
+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++)
+ if((st = v->printer(v->a[i], fp)) != OK)
+ return st;
+
+ return OK;
+}
+
+status_t ADT_Vector_sort(ADT_Vector_t *v, comparator_t pf)
+{
+ if(v == NULL) return ERROR_NULL_POINTER;
+
+ /* Quick sort */
+ qsort(v->a, v->size, sizeof(void *), pf);
+
+ return OK;
+}
+
+status_t ADT_Vector_set_printer(ADT_Vector_t *v, printer_t pf)
+{
+ if(v == NULL) return ERROR_NULL_POINTER;
+
+ v->printer = pf;
+
+ return OK;
+}
+
+status_t ADT_Vector_set_comparator(ADT_Vector_t *v, comparator_t pf)
+{
+ if(v == NULL) return ERROR_NULL_POINTER;
+
+ v->comparator = pf;
+
+ return OK;
+}
+
+status_t ADT_Vector_export_as_csv(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++)
+ if((st = v->printer(v->a[i], fp)) != OK)
+ return st;
+
+ return OK;
+}
+
+status_t ADT_Vector_export_as_xml(const ADT_Vector_t *v, FILE *fp)
+{
+ status_t st;
+ size_t i;
+
+ if(v == NULL) return ERROR_NULL_POINTER;
+
+ fprintf(fp, "%s\n", XML_STR_HEADER);
+ fprintf(fp, "<%s>\n", XML_STR_ROOT);
+
+ for(i = 0; i < v->size; i++){
+ fprintf(fp, "\t<%s>\n", XML_STR_ROW);
+ if((st = v->printer(v->a[i], fp)) != OK)
+ return st;
+
+ fprintf(fp, "\t</%s>\n", XML_STR_ROW);
+ }
+
+ fprintf(fp, "</%s>\n", XML_STR_ROOT);
+ return OK;
+}