; OGGETTI CONNETTIBILI in LISP  (crivello di Eratostene)

; Parte per il debugging su video
(defconstant Debug 1)     ; 0=no debug;  1=si' debug

  (defun debug1 (msg)
            (cond ((= Debug 1) (print msg)) ))
  (defun debug2 (m1 m2)
            (cond ((= Debug 1) (prin1 m1)(print m2)) ))
  (defun debug3 (m1 m2 m3)
            (cond ((= Debug 1) (prin1 m1)(prin1 m2)(print m3)) ))
  (defun debug4 (m1 m2 m3 m4)
            (cond ((= Debug 1) (prin1 m1)(prin1 m2)(prin1 m3)(print m4)) ))

; PRIMITIVE SUGLI STREAM:
;Costruzione; avendo in ingresso il primo elemento
;             e la funzione generatrice (una chiusura)
(defun ConsStream  (PrimoEl FunGen) (cons PrimoEl FunGen))
; Rende l'elemento in testa allo stream
(defun Head        (Stream)         (car Stream))
; Rende lo stream ottenuto rimuovendo l'elemento in testa
(defun Tail        (Stream)         (funcall (cdr Stream)))

; Dice se uno stream e' vuoto
(defun EmptyS      (Stream)         (null Stream))
; Rende (sempre) lo stream vuoto
(setq  EmptyStream  nil)


; UTILIZZO DEGLI STREAM
; GENERATORE: crea uno stream di interi
(defun IntStream (n)
         (debug2 'generazione_ n)
         (ConsStream n 
                     (lambda () (IntStream (+ 1 n)))
         ))
; idem ma e' uno stream limitato
(defun IntStreamLimit (n max)
         (debug2 'generazione_ n)
         (cond ((eq n max) EmptyStream)
               (   t       (ConsStream n
                                       (lambda() (IntStreamLimit (+ 1 n) max))))
         ))

; STAMPA: stampa uno a uno tutti gli elementi di uno stream
(defun StampaStream (S)
         (cond ((EmptyS S)    EmptyStream)
               (    t         (print (Head S))(StampaStream (Tail S)) )
         ))

; FILTRO: lascia passare o arresta un numero a seconda
;         che verifichi il predicato Pred
(defun Filtro (S Pred)
         (cond (  (EmptyS S)            EmptyStream)
               ((funcall Pred (Head S)) (debug4 'filtro_ (funcall Pred -1) '_passa_ (Head S))
                                        (ConsStream (Head S) 
                                                    (lambda () (Filtro (Tail S) Pred))))
               (     t                  (debug4 'filtro_ (funcall Pred -1) '_blocca_ (Head S))
                                        (Filtro (Tail S) Pred))
         ))


; ACCUMULATORE: esegue l'operazione Oper (con elemento neutro ElNeutro) su 
;               tutti gli elementi di uno stream 
(defun Accum (S ElNeutro Oper)
         (cond ((EmptyS S) ElNeutro)
               (     t     (debug2 'accumulatore_usa_ (Head S))
                           (funcall Oper (Head S) (Accum (Tail S) ElNeutro Oper)))
         ))

; LIMITATORE: limita il numero di elementi resi da uno stream
(defun Limita (S Conta MaxConta)
         (cond ((eq Conta MaxConta) EmptyStream)
               (       t            (ConsStream (Head S)
                                                (lambda () (Limita (Tail S) (+ Conta 1) MaxConta))) )
         ))

; TRASFORMA: rende uno stream costituito dagli elementi dello stream
;            dato a cui e' applicata la funzione Fun
(defun Trasforma (S Fun)
         (cond ((EmptyS S) EmptyStream)
               (     t     (ConsStream (funcall Fun (Head S))
                                       (lambda () (Trasforma (Tail S) Fun))))
         ))

; OPERAZIONI "UTILI":
; Operazione di "non divisibilita'" tra due numeri
; (e' usata per il crivello di Eratostane)
(defun NoDivisibili (x y)
         (cond  ((/= x -1)  (/= 0 (rem x y)))
                (    t      y)   ; rende il valore del divisore per x=-1
         ))
; Operazione "un numero e' pari"
(defun Pari (x)
         (= 0 (rem x 2)))
; Operazioni "dimostrative" per trasformazione
(defun PiuUno (x) (+ x 1) )
(defun PerDue (x) (* x 2) )
(defun Somma (x y) (+ x y) )

; SETACCIO per Eratostane: rende uno stream con i numeri primi
(defun Sift (S)
         (cond ((EmptyS S) S)
               (    t      (debug2 'Nuovo_Numero_Primo_Trovato_ (Head S))
                           (ConsStream (Head S) 
                                       (lambda () (Sift (Filtro (Tail S) (lambda (x) (NoDivisibili x (Head S))) ))) )
               )
         ))

; Stampa i numeri primi tra 2 e 19
(defun prova0 () (StampaStream (Sift (IntStreamLimit 2 20))))
; Stampa i primi 3 numeri primi
(defun prova1 () (StampaStream (Limita (Sift (IntStream 2)) 0 3)))
; Stampa i primi 5 numeri primi
(defun prova2 () (StampaStream (Limita (Sift (IntStream 2)) 0 5)))
; Stampa i primi 31 numeri primi
(defun prova3 () (StampaStream (Limita (Sift (IntStream 2)) 0 31)))
; Stampa i numeri pari tra 2 e 5
(defun prova4 () (StampaStream (Filtro (IntStreamLimit 2 6) (lambda (x) (Pari x))) ))
; Stampa l'intervallo 2-4 sommando uno a ogni elemento (cioe' intrvallo 3-5)
(defun prova5 () (StampaStream (Trasforma (IntStreamLimit 2 8) (lambda (x) (PiuUno x)))))
; Stampa l'intervallo 2-4 moltiplicando per due ogni elemento
(defun prova6 () (StampaStream (Trasforma (IntStreamLimit 2 8) (lambda (x) (PerDue x)))))
; Stampa la somma dei numeri tra 1 e 6
(defun prova7 () (Accum (IntStreamLimit 1 7) 0 (lambda (x y) (Somma x y))))
; Idem (versione alternativa)
(defun prova8 () (Accum (IntStreamLimit 1 7) 0 (lambda (x y) (+ x y))))
; Stampa la somma dei numeri tra 1 e 19
(defun prova9 () (Accum (IntStreamLimit 1 20) 0 (lambda (x y) (+ x y))))
; Stampa la somma dei primi 4 numeri primi
(defun prova10() (Accum (Limita (Sift (IntStream 2)) 0 4) 0 (lambda (x y) (+ x y))))
; Stampa la somma dei primi 20 numeri primi
(defun prova11() (Accum (Limita (Sift (IntStream 2)) 0 20) 0 (lambda (x y) (+ x y))))