#include <stdio.h>
#include <stdlib.h>
/*
Liste - definizione
Liste - funzioni di creazione, distruzione, stampa, lunghezza, lista vuota
Liste - funzioni di accesso, inserimento, ordinamento
Liste - reverse, liste doppie, ricerca
Liste - agenda
*/

typedef struct _elem {
    int data;
    struct _elem * next;
} elem;

/*
typedef struct _elem {
    int data;
    struct _elem *next;
    struct _elem *prev;
}
 
typedef struct _lista {
    elem *head;
    elem *tail;
} lista;
*/


elem *get(elem *l, int i);

/* Crea un nuovo elemento con valore d */
elem *new(int d) {
    elem *p=malloc(sizeof(elem));
    p->next=NULL;
    p->data=d;
    return p;
}

/* Inserisce un elemento con valore d in testa alla lista */
elem *ins_head(elem *l, int d){
    elem *p=new(d);
    p->next=l;
    return p;
}

/* Inserisce un elemento con valore d in coda alla lista (forma ricorsiva) */
elem *ins_tail(elem *l, int d){
    if (l==NULL) {
        return new(d);
    } else if (l->next==NULL) {
        l->next=new(d);
        return l;
    } else { 
        l->next=ins_tail(l->next, d); /* il valore restituito è l->next! */
        return l;
    }
}

/* Inserisce un elemento con valore d in coda alla lista (forma iterativa) */
elem *ins_tail_iter(elem *l, int d){
    elem *p=new(d);
    elem *curr;
    if (l==NULL) return p;
    for(curr=l; curr->next!=NULL; curr=curr->next);
    /* curr=get(l,length(l)-1); */
    curr->next=p;
    return l;
}

/* Inserisce un nuovo elemento con valore d alla posizione i-esima della lista */
elem *ins_at(elem *l, int i, int d){
    elem *curr, *p;
    if (i==0) return ins_head(l,d);
    curr=get(l, i-1);
    p=new(d);
    p->next=curr->next;
    curr->next=p;
    return l;
}

/* l1 -> l2 aggancia la seconda lista in coda alla prima */
elem *cat(elem *l1, elem *l2){
    elem *curr;
    if (l1==NULL) return l2;
    if (l2==NULL) return l1;
    for(curr=l1; curr->next!=NULL; curr=curr->next);
    curr->next=l2;
    return l1;
}

/* Rimuove l'elemento item dalla lista l */
elem *remv(elem *l, elem *item){
    elem *curr;
    if (l==item) {
        l=l->next;
        free(item);
        return l;
    }
    for(curr=l; curr->next!=item; curr=curr->next);
    if (curr==NULL) return l;
    curr->next=item->next;
    free(item);
    return l;
}

/* Rimuove l'elemento i-esimo dalla lista l */
elem *remove_at(elem *l, int i){
    elem *curr, *p;
    if (l==NULL) return NULL;
    if (i==0) {
        curr=l->next;
        free(l);
        return curr;
    }
    curr=get(l,i-1);
    if (curr==NULL) return l;
    p=curr->next;
    if (p!=NULL){ 
        curr->next=p->next;
        free(p);
    } else curr->next=NULL;
    return l;
}

/* Dealloca l'intera lista */
void destroy(elem *l){
    if (l==NULL) return;
    destroy(l->next);
    free(l); /* Dealloca solo l'elemento l! */
}

/* Restituisce l'indirizzo dell'i-esimo elemento, se esiste */
elem *get(elem *l, int i){
    if (l==NULL) return NULL;
    if (i==0) return l;
    if (l->next==NULL) return NULL;
    return get(l->next, i-1);
}

/* Cambia il valor dell'i-esimo elemento in val, se esiste tale elemento */
elem *set(elem *l, int i, int val){
    elem *to_set = get(l,i);
    if (to_set==NULL) return NULL;
    to_set->data=val;
    return to_set;
}

/* Lunghezza della lista (forma ricorsiva) */ 
int length(elem *l){
    if (l==NULL) return 0;
    return 1 + length(l->next);
}

/* Lunghezza della lista (forma iterativa) */
int len(elem *l){
    int count;
    for(count=0; l!=NULL; l=l->next)
        count+=1;
    return count;
}

/* Stampa la lista*/
void print(elem *l){
    if (l==NULL) {
        printf("--|\n");
    } else if (l->next==NULL) {
        printf ("%d --|\n", l->data);
    } else {
        printf ("%d --> ", l->data);
        print(l->next);
    }
}

elem *find(elem *l, int d){
    if (l==NULL) return NULL;
    if (l->data==d) return l;
    return find(l->next, d);
}

elem *find_iter(elem *l, int d){
    for( ; l!=NULL && l->data!=d; l=l->next);
    return l;
}

elem *reverse(elem *l){
    elem *res=NULL;
    elem *curr=l;
    for(curr=l; curr!=NULL; curr=curr->next)
        res=ins_head(res,curr->data);
    /*
    while(curr!=NULL){
        int d=curr->data;
        elem *p=curr;
        curr=curr->next;
        free(p);
        res=ins_head(res,d);
    }
    */
    return res;
}

elem *reverse2(elem *l){
    elem *head;
    if (l==NULL || l->next == NULL) return l;
    head=l;
    l=l->next;
    return ins_tail(reverse2(l),head->data);
}

elem *ins_order(elem *l, int d){
    elem *curr, *p;
    if (l==NULL || d<=l->data) return ins_head(l,d); 
    for(curr=l; curr->next!=NULL && curr->next->data<d; curr=curr->next);
    p=new(d);
    p->next=curr->next;
    curr->next=p;
    return l;
}

elem *ins_sort(elem *l){
    elem *res=NULL;
    for( ; l!=NULL; l=l->next){
        res=ins_order(res,l->data);
    }
    return res;
}

int main(){
    int i;
    elem *lista=NULL, *inversa=NULL;
    for(i=0; i<5; i++){
        lista=ins_head(lista,i);
    }
    printf("%d %d\n", length(lista), len(lista));
    print(lista);
    for(i=0; i<5; i++){
        set(lista,i,i+1);
    }
    print(lista);
    destroy(lista);
    lista=NULL;
    print(lista);
    for(i=0; i<5; i++)
        lista=ins_tail(lista, i);
    print(lista);
    for(i=1; i<6; i+=2)
        lista=ins_at(lista, i, i*10);
    print(lista);
    for(i=0; i<9; i+=3){
        printf("%d\n", i);
        lista=remove_at(lista, i);
        print(lista);
    }
    inversa=reverse(lista);
    print(inversa);
    lista=cat(lista,reverse2(inversa));
    print(lista);
    inversa=ins_sort(lista);
    print(inversa);
    print(lista);
    destroy(lista);
    destroy(inversa);
}

