/* --------------------------------------------------------------
                    a   l   b   i   n   .   c                                  
Il  programma  gestisce  in maniera completa -inserzione, cancel-
lazione, visita- un albero binario non del  tipo  AVL.  E'  stato
scritto con la tecnica BKF (=Bast Ke Funz). Pertanto potrebbe es-
sere migliorabile. Segnatamente  la cancellazione.  Oltre alle o-
perazioni  citate,  e'  prevista la  stampa. La cosa piu' sensata
sarebbe stata quella di scrivere un applet Java.  Poiche' non  me
la  sento  di affrontare lo studio di quest'ultimo ma conosco La-
TeX, ho sfruttato le sue limitate capacita' grafiche.  Sufficien-
ti tuttavia, in quanto si tratta di tracciare dei box e delle li-
nee. L'operazione di stampa crea un file temporaneo. Da  un'altra
finestra terminale va lanciato uno script che elabora il contenu-
to trasformandolo, tramite un programma C, in statement LaTeX (e'
necessaria la  presenza di  questo  pacchetto).  Da  LaTeX si ot-
tiene un file .pdf. Ogni volta che la funzione stampa viene invo-
cata, c'e' la sovrascrittura del file temporaneo. La stampa quin-
di e' asincrona.

Ho messo la massima cura, ma non posso garantire l'assenza di er-
rori. Pertanto il tutto va usato cosi' com'e' e a proprio rischio
e pericolo.

Per la compilazione usare:
  gcc -ansi -DSTAMPA -O -pedantic -s -W -Wall -o albero albero.c 
Lo script per la generazione dell'albero grafico e quanto connes-
so non sono online. Basta richiederli.
-------------------------------------------------------------- */

#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

#define False   0
#define True    !False

#define albero  main

#ifdef STAMPA
int             altbox = 10, altfr = 5, basebox = 10;
int             curv = 2, trabox = 5;
const int       box = 5, frdx = 11, frsx = 10;
#endif

jmp_buf restore;

struct foglia
{
  struct foglia *sx;
  int           valore;
  struct foglia *retro;
  struct foglia *dx;
};
typedef struct foglia* nodo;

#ifdef STAMPA
void arco (nodo n, char *fn)
{
  char          *riga;
  FILE          *f;
  int           a[4], da[4], dim;
  int           isempty (nodo);
  nodo          retro;
  void          vertici (FILE *, int, char *, int);

  dim = 40;
  riga = (char *) malloc ((unsigned int) dim);
  if (isempty (n) == False)
    {
      arco (n->sx, fn);
      if (isempty (n->retro) == False)
        {
          f = fopen (fn, "r+");
          retro = n->retro;   
          vertici (f, retro->valore, riga, dim);
          (void) sscanf (riga, "%*i %i %i %i %i",
            &da[0], &da[1], &da[2], &da[3]);
          vertici (f, n->valore, riga, dim);
          (void) sscanf (riga, "%*i %i %i %i %i",
            &a[0], &a[1], &a[2], &a[3]);
          a[2] /= 2;
          fseek (f, (long) 0, SEEK_END);
          if (retro->sx == n)
            (void) fprintf (f, "%i %i %i %i %i %i\n", frsx,
              a[0] + a[2], a[1] + a[3], da[0] - (a[0] + a[2]),
              altfr, curv); 
          else
            (void) fprintf (f, "%i %i %i %i %i %i\n", frdx,
              da[0] + da[2], da[1], a[0] + a[2] - (da[0] + da[2]),
              altfr, curv); 
          fclose (f);
        }
      arco (n->dx, fn);
    }
  free ((void *) riga);
}
#endif

int between (int inf, int valore, int sup)
{
  if (valore < inf)
    return (inf);
  if (valore > sup)
    return (sup);
  return (valore);
}

nodo cancella (nodo n, int valore)
{
  unsigned int  destra, sinistra;
  int           isempty (nodo);
  nodo          local, pred, retro, succ;
  nodo          predecessore (nodo, int), ricerca (nodo, int);

  local = ricerca (n, valore);
  sinistra = isempty (local->sx) == True;
  destra = isempty (local->dx) == True; 
    /* foglia */
  if (sinistra && destra)
    {
      if (isempty (local->retro) == True)
        return ((nodo) NULL);
      retro = local->retro;
      if (retro->sx == local)
        retro->sx = (nodo) NULL;
      else
        retro->dx = (nodo) NULL;
      return (n);
    } 
    /* 1 sottoalbero */
  if (sinistra ^ destra)
    {
      succ = sinistra ? local->dx : local->sx;
      retro = local->retro;
      if (isempty (retro) == True)
        {
          succ->retro = (nodo) NULL;
          return (succ);
        }
      if (retro->sx == local)
        retro->sx = succ;
      else
        retro->dx = succ;
      return (n);
    }
    /* 2 sottoalberi */
  pred = predecessore (n, valore);
  if (local->sx == pred)
    local->sx = (nodo) NULL;
  local->valore = pred->valore;
  retro = pred->retro;
  if (retro->sx == pred)
    retro->sx = (nodo) NULL;
  else
    if (retro->dx == pred)
      retro->dx = (nodo) NULL;
  return (n);
}

#ifdef STAMPA
void disegna (nodo n, int livello, FILE *f, int maxord, int *asc)
{
  int           isempty (nodo);
  
  if (isempty (n) == False)
    {
      disegna (n->sx, livello + 1, f, maxord, asc);
      (void) fprintf (f, "%i %i %i %i %i %i :%i\n",
        box, *asc * (basebox + trabox),
        (maxord - 1 - livello) * altbox,
        basebox, altbox, curv, n->valore);
      *asc += 1;
      disegna (n->dx, livello + 1, f, maxord, asc);
    }
}
#endif

void insert (nodo *n, int valore, nodo provenienza)
{
  int           between (int, int, int), isempty (nodo);

  if (isempty (*n) == True)
    {
        /* nuovo nodo */
      *n = (nodo) malloc (sizeof (struct foglia));
      (*n)->sx = (nodo) NULL;
      (*n)->valore = valore;
      (*n)->retro = provenienza;
      (*n)->dx = (nodo) NULL;
    }
  else
    {
      switch (between (-1, valore - (*n)->valore, +1))
        {
          case -1: /* piu' piccolo - inserisci a sinistra */
            insert (&((*n)->sx), valore, *n);
            break;
          case 0:  /* duplicato */
            break;
          case +1: /* piu' grande - inserisci a destra */
            insert (&((*n)->dx), valore, *n);
            break;
        }
    }
}

int isempty (nodo n)
{
  if (n == (nodo) NULL)
    return (True);
  else
    return (False);
}

#ifdef STAMPA
void level (nodo n, int livello, int *maxord)
{
  int           isempty (nodo);
  
  if (isempty (n) == False)
    {
      if (livello > *maxord)
        *maxord = livello;
      level (n->sx, livello + 1, maxord);
      level (n->dx, livello + 1, maxord);
    }
}
#endif

nodo Massimo (nodo n)
{
  int           isempty (nodo);
  
  if (isempty (n) == True)
    longjmp (restore, 0);
  if (isempty (n->dx) == False)
    return (Massimo (n->dx));
  return (n);
}

#ifdef STAMPA
void maxbox (nodo n, int *maxasc)
{
  int           isempty (nodo);
  
  if (isempty (n) == False)
    {
      maxbox (n->sx, maxasc);
      *maxasc += 1;
      maxbox (n->dx, maxasc);
    }
}
#endif

nodo minimo (nodo n)
{
  int           isempty (nodo);
  
  if (isempty (n) == True)
    longjmp (restore, 0);
  if (isempty (n->sx) == False)
    return (minimo (n->sx));
  return (n);
}

nodo predecessore (nodo n, int valore)
{
  extern int    isempty (nodo);
  nodo          local, tmp;
  extern nodo   Massimo (nodo), minimo (nodo), ricerca (nodo, int);

  local = ricerca (n, valore);
  if (isempty (local->sx) == False)
    return (Massimo (local->sx));
  if (local == minimo (n))
    longjmp (restore, 0);
  tmp = local->retro;
  while (isempty (tmp) == False && local == tmp->sx)
    {
      local = tmp;
      tmp = local->retro;
    }
  return (tmp);
}

nodo ricerca (nodo n, int valore)
{
  extern int    between (int, int, int), isempty (nodo);
  nodo          tmp;

  if (isempty (n) == True)
    longjmp (restore, 0);
  tmp = n;
  switch (between (-1, valore - n->valore, +1))
    {
      case -1: /* piu' piccolo - cerca a sinistra */
        tmp = (ricerca (n->sx, valore));
        break;
      case 0:  /* trovato */
        break;
      case +1: /* piu' grande - cerca a destra */
        tmp = (ricerca (n->dx, valore));
        break;
    }
  return (tmp);
}

#ifdef STAMPA
void stampa (nodo n)
{
  char          fn[L_tmpnam];
  FILE          *f;
  int           maxasc, maxord;
  void          arco (nodo, char *), maxbox (nodo, int *),
                disegna (nodo, int, FILE *, int, int *),
                level (nodo, int, int *);
  
  (void) sprintf (fn, "%s", __FILE__);
  (void) strcpy (strrchr (fn, '.'), ".txt");
  remove (fn);
  if (isempty (n) == True)
    longjmp (restore, 0);
  if ((f = fopen (fn, "w")) == NULL)
    {
      (void) fprintf (stderr, "errore apertura %s\n", fn);
      exit (EXIT_FAILURE);
    }
  maxasc = maxord = 0;
  level (n, 1, &maxord);
  maxbox (n, &maxasc);
  (void) fprintf (f, "%i %i\n", maxasc * (basebox + trabox) - trabox,
    maxord * altbox);
  (void) fprintf (f, "albero\n");
  (void) fprintf (f, "Diagramma albero\n");
  maxasc = 0;
  disegna (n, 0, f, maxord, &maxasc);
  fclose (f);
  arco (n, fn);
}

void vertici (FILE *f, int kiave, char *ombra, int dim)
{
  char          *p, *riga;
  int           ind;

  riga = (char *) malloc ((unsigned int) dim);
  fseek (f, (long) 0, SEEK_SET);
  (void) fgets (riga, dim, f);
  while (! feof (f))
    {
      riga[strlen (riga) - 1] = '\0';
      (void) strcpy (ombra, riga);
      p = strtok (riga, " ");
      if (atoi (p) == box)
        {
          for (ind = 0; ind < 6; ind++)
            p = strtok ((char *) NULL, " ");
          if (atoi (++p) == kiave)
            break;
        }
      (void) fgets (riga, dim, f);
    }
  free ((void *) riga);
}
#endif

void visita (nodo n, int offset)
{
  int           incr, ind, isempty (nodo);

  incr = 2;
  if (isempty (n) == False)
    {
      visita (n->sx, offset + incr);
      for (ind = 0; ind < offset; ind++)
        (void) printf (" ");
      (void) printf ("%*i\n", incr, n->valore);
      visita (n->dx, offset + incr);
    }
}

int albero (void) /* main program */
{
  char          *menu, *oper[] =
    {
      "[c]ancella ", "[f]ine ", "[i]nserisci ", "[v]isita "
#ifdef STAMPA
      , "[s]tampa "
#endif
    };
  int           fine, ind, jump, nop, op, valore;
  nodo          n, cancella (nodo, int);
  void          insert (nodo *, int, nodo);
#ifdef STAMPA
  void          stampa (nodo);
#endif
  void          visita (nodo, int);

  valore = 0;
  nop = sizeof oper / sizeof oper[0];
  for (ind = 0; ind < nop; ind++)
    valore += strlen (oper[ind]);
  menu = (char *) malloc (valore);
  bzero (menu, valore);
  for (ind = 0; ind < nop; ind++)
    (void) strcat (menu, oper[ind]);
  fine = True;
  n = (nodo) NULL; /* inizializzazione albero */
  jump = setjmp (restore); /* punto di ritorno */
  if (jump)
    (void) getchar ();
  do
    {
      (void) printf ("%s", menu); (void) printf ("> ");
      op = fgetc (stdin);
      switch (op)
        {
          default:  /* errore */
            (void) printf ("\noperazioni ammesse: ");
            (void) printf ("%s\n\n", menu);
            break;
          case 'c': /* cancellazione */
            (void) fscanf (stdin, "%i", &valore);
            n = cancella (n, valore);
            break;
          case 'f': /* fine */
            fine = False;
            break;
          case 'i': /* inserzione */
            (void) fscanf (stdin, "%i", &valore);
            insert (&n, valore, (nodo) NULL);
            break;
#ifdef STAMPA
          case 's': /* stampa */
            stampa (n);
            break;       
#endif
          case 'v': /* visita */
            visita (n, 0);
            break;
        }
      (void) getchar ();
    }
  while (fine == True);
  free ((void *) menu);
  exit (EXIT_SUCCESS);
}