/*
 * Lista generalizadas
 */

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

enum elem_t {tipo_int, tipo_char, tipo_sublista};

union info_lista {
  int i;
  char c;
  struct No* sublista;
};

typedef struct No {
  enum elem_t tipo;
  union info_lista info;
  struct No* prox;
} No;



int existe_neg(No *l) {
  if (l == NULL)
    return 0;
  if (l->tipo == tipo_int &&
      l->info.i < 0)
    return 1;
  if (l->tipo == tipo_sublista)
    return (existe_neg(l->info.sublista) ||
	    existe_neg(l->prox));
  return existe_neg(l->prox);
}


No* inverte(No* p) {  
  if (p == NULL)
    return NULL;
  if (p->tipo == tipo_sublista)
    p->info.sublista = inverte(p->info.sublista);
  if (p->prox == NULL)
    return p;
  No *r = inverte(p->prox);
  p->prox->prox = p;
  p->prox = NULL;
  return r;
}


/* Imprime com parĂȘnteses */

void imprime_simples(No* l) {
  printf("(");
  while(l!=NULL) {
    switch(l->tipo){
    case tipo_int:
      printf("%d", l->info.i);
      break;
    case tipo_char:
      printf("%c", l->info.c);
      break;
    case tipo_sublista:
      imprime_simples(l->info.sublista);
      break;
    }
    l = l->prox;
    if (l != NULL)
      printf(", ");
  }
  printf(")");
}

int max(int x, int y) {
  if (x >= y)
    return x;
  return y;
}

int prof(No* l) {
  if (l == NULL)
    return 0;
  if (l->tipo == tipo_sublista) 
    return max (1 + prof(l->info.sublista),
	 prof(l->prox));
  return max (1, prof(l->prox));
}


char abre[4] = {'(','(','[','{'};
char fecha[4] = {')',')',']','}'};
 
void imprime(No* l) {
  int p = prof(l);
  if (p > 3) p = 3;
  printf("%c",abre[p]);
  while(l!=NULL) {
    switch(l->tipo){
    case tipo_int:
      printf("%d", l->info.i);
      break;
    case tipo_char:
      printf("%c", l->info.c);
      break;
    case tipo_sublista:
      imprime(l->info.sublista);
      break;
    }
    l = l->prox;
    if (l != NULL)
      printf(", ");
  }
  printf("%c",fecha[p]);
}

No* concat(No* ap_elem, No* ap_lista) {
  ap_elem->prox = ap_lista;
  return ap_elem;
}

No* atomo(int i) {
  No* n = (No*) malloc (sizeof(No));
  n->info.i = i;
  n->tipo = tipo_int;
  return n;
}

No* sublista(No* sublista) {
  No* n = (No*) malloc (sizeof(No));
  n->info.sublista = sublista;
  n->tipo = tipo_sublista;
  return n;
}


int main() {

  /* Monta a lista (1) */
  No* l0 = concat(atomo(1), NULL);
  
  /* Monta a lista (1,2) */
  No* l1 = concat(atomo(1), concat(atomo(2), NULL));

  /* Monta a lista (1,(2,3)) */
  No* l2 = 
    concat(atomo(1), 
	   concat(sublista(concat(atomo(2), concat(atomo(3), NULL))),
		  NULL));

  /* Como montar a lista
     (1,(2,3),4,5,(6,(7,8))) ? */

  /* (1,(2, (3, -1)), 5, 6 ) */ 

  No* l3 = concat(atomo(3), concat(atomo(-1), NULL)); 
  No* l4 = concat(atomo(2), concat(sublista(l3), NULL));
  No* l5 = concat(atomo(5), concat(atomo(6), NULL)); 
  l5 = concat(sublista(l4), l5);
  l5 = concat(atomo(1), l5);
  imprime(l5);
  
  return 0;
  
}