#include <stdio.h>
#include <stdlib.h>

#define TIPO int
#define IMPRIME_VALOR(X) printf("%d", X)

struct struct_fila {
    TIPO *elems;
    int inicio;
    int fim;
    int qtde;
    int max;
};

typedef struct struct_fila fila;

fila *cria_fila(int n) {
    fila *ret = malloc(sizeof(fila));
    ret->elems = malloc(sizeof(TIPO) * n);
    ret->inicio = 0;
    ret->fim = n - 1;
    ret->qtde = 0;
    ret->max = n;
    return ret;
}

void destroi_fila(fila *f) {
    free(f->elems);
    free(f);    
}

int fila_cheia(fila *f) {
    return f->qtde == f->max;
}

int fila_vazia(fila *f) {
    return f->qtde == 0;
}

void enqueue(fila *f, TIPO e) {
    if (fila_cheia(f)) {
        printf("Fila cheia\n");
        exit(1);
    }
    f->fim = (f->fim + 1) % f->max;
    f->elems[f->fim] = e;    
    f->qtde++;
} 

TIPO dequeue(fila *f) {
    TIPO ret;
    if (fila_vazia(f)) {
        printf("Fila vazia\n");
        exit(1);
    }
    ret = f->elems[f->inicio];
    f->inicio = (f->inicio + 1) % f->max;
    f->qtde--;
    return ret;
}


int main (int argc, char** argv) {
    int i;

    fila* f1 = cria_fila(4);

    for (i = 0; i < 4; i++) 
        enqueue (f1, i);
    
    printf("A fila f1 está cheia: %c\n", fila_cheia(f1) ? 'S' : 'N');
    
    printf("Os elementos devem sair na ordem:\n");
    for (i = 0; i < 4; i++) {
       printf("\t");
       IMPRIME_VALOR(dequeue(f1));
       printf("\n");
    }

    for (i = 0; i < 4; i++) 
        enqueue (f1, i);

    //jogando o 0 e 1 pro fim da fila
    dequeue(f1); 
    dequeue(f1); 
    enqueue(f1, 0);
    enqueue(f1, 1); 
    
    printf("Os elementos deveriam sair na ordem 2 3 0 1:\n");
    for (i = 0; i < 4; i++) {
       printf("\t");
       IMPRIME_VALOR(dequeue(f1));
       printf("\n");
    }
    
    destroi_fila(f1);
    
}