#
#  Risolvere graficamente e numericamente EQ. DIFFERENZIALI del 1º ORDINE
#
## ========  Copia da qui a "FINE"  =========
# CAMPO DIREZIONALE
# Tracciamento in (x1,x2)*(y1,y2) del c. direz. di y' = f(x,y) con f messo in Dy:
# Dy <- function(x,y) ... ;  prova con m=25, n=25 (m+1 e n+1 sono il numero dei
# trattini in orizz. e in vert.; se metti  m=0, n=0  puoi tracciare solo il
# reticolo e limitarti ad usare soledifc per tracciare la curva soluz.)
col1 <<- "orange"; col2 <<- "grey52"      # colori cambiabili di griglia e campo
soledif <- function(x1,x2,y1,y2,m,n) {
  plot(c(x1,x2), c(y1,y2), type="n",xlab="",ylab="")
  abline(v=axTicks(1),h=axTicks(2),lty=3,col=col1)
  abline(h=0,v=0,col="blue"); if(n*m > 0) {
  dx <- (x2-x1)/m; dy <- (y2-y1)/n
  a <- 0; b <- 0; f <- function(x) Dy(a,b)*(x-a)+b
  for(i in 0:m) for(j in 0:n) {a <- x1+dx*i; b <- y1+dy*j
   Dx <- dx; for(k in 1:100)
    {if(abs(f(a+Dx/2.5)-f(a-Dx/2.5)) > dy) Dx <- Dx*0.9 else k <- 100}
   plot(f,a-Dx/2.5,a+Dx/2.5,ylim=c(b-dy/2.5,b+dy/2.5),add=TRUE,col=col2)}}}
#
# CURVA SOLUZIONE
# Soluzione di  y' = f(x,y)  tale che  y(x0) = y0  fino a xf col metodo di
# Eulero in N passi: sia h = (xf-x0)/N, approssimo y(x) col segmento che parte
# da (x0,y0), ha pendenza f(x0,y0), arriva a x1=x0+h; sia y1 la corrispondente
# ordinata. Poi col segmento da (x1,y1) con pendenza f(x1,y1) fino a x2=x1+h; …
### Il tracciamento del poligono approssimante il graf. della sol. y(x)
### passante per (x0,y0), da x0 a xf.
soledifc <- function(x0,y0, xf, n, col) {
  x <- NULL; y <- NULL; h <- (xf-x0)/n; y[1] <- y0
  for (i in 1:(n+1)) x[i] <- x0+h*(i-1)
  for (i in 2:(n+1)) y[i] <- y[i-1]+Dy(x[i-1],y[i-1])*h
  lines(x[1:(n+1)],y[1:(n+1)],lwd=2,col=col); points(x0,y0,pch=21,bg="black") }
### Il tracciamento dei suoi vertici
soledifp <- function(x0,y0, xf, n, col) {
  x <- NULL; y <- NULL; h <- (xf-x0)/n; y[1] <- y0
  for (i in 1:(n+1)) x[i] <- x0+h*(i-1)
  for (i in 2:(n+1)) y[i] <- y[i-1]+Dy(x[i-1],y[i-1])*h
  for(i in 1:(n+1)) points(x[i],y[i],pch=20,col=col)}
### Il valore di y(x) in p approssimato in n passi
soledifv <- function(x0,y0, p, n) {xxx <<- NULL; xx <- 0; D2 <- 0
  x<- NULL; y<- NULL; h<- (p-x0)/n; y[1]<- y0; for (i in 1:(n+1)) x[i]<- x0+h*(i-1)
  for (i in 2:(n+1)) { y[i] <- y[i-1]+Dy(x[i-1],y[i-1])*h; D1 <- D2; D2 <- y[i]-y[i-1];
  if(xx==0) {if(sign(D1)+sign(D2)==0){xx<- 1; xxx<<- c(x[i-1],x[i],y[i-1],y[i]) } } }
  # in xxx sono messi i punti in cui cambia segno Δy; vedi Esempio 3
  y[n+1] }                        ### In breve i comandi:
# Dy<- function(x,y) … soledif(,, ,, ,) soledifc(,, , ,) soledifp(,, , ,) soledifv(,, ,)
## ===============  "FINE"  ================
#
# ESEMPIO 1
# y'(x) = x - y(x)   Trovare il campo direzionale in (-1,5)*(-2,5)
# e tracciare le curve soluzioni tali che y(1/2)=3 e che y(2)=1/2.
#
# Definisco l'eq. differenziale  [nota: metto solo x-y, non x-y(x)]:
#
Dy <- function(x,y) x-y
#
# Scelgo le dimensioni della finestra e il margine (cosa non necessaria)
dev.new(width=4,height=3); par( mai = c(0.5,0.5,0.2,0.2) )
#
col1 <<- "orange"; col2 <<- "grey52"
soledif(-1,5, -2,4, 30,25)
   
# Traccio la soluz. approssimata fino a x=5 in 3 passi
soledifc(1/2,3, 5, 3, "red")
# Traccio i pallini estremi dei segmenti
soledifp(1/2,3, 5, 3, "blue")
# Aumento il numero dei passi (7)
soledifc(1/2,3, 5, 7, "green2")
soledifp(1/2,3, 5, 7, "blue")
# Con un numero alto di passi ho il grafico OK
soledifc(1/2,3, 5, 1e4, "green4")
# Traccio la soluzione anche fino a x=-1
soledifc(1/2,3, -1, 1e4, "brown")
   
#
# Per avere solo i grafici delle soluzioni definisco la scala e uso
# soledifc. Ma posso sceglere la scala anche usando soledif con m=n=0
soledif(-1,5, -2,4, 0,0)
soledifc(1/2,3, 5, 1e4, "green4")
soledifc(1/2,3, -1, 1e4, "brown")
soledifc(2,1/2, 5, 1e4, "green4")
soledifc(2,1/2, -1, 1e4, "brown")
   
#
# Per avere il valore della soluzione in un qualunque punto uso solediv
# Vediamo ad esempio come trovare il valore che la soluzione passante
# per (0,1) assume nel punto 2
soledif(-1,5, -2,4, 0,0)
soledifc(0,1, 5, 1e4, "green4")
soledifc(0,1, -1, 1e4, "brown")
# Faccio un po' di prove e assumo 1.2707 come valore
soledifv(0,1, 2,1e4)
#  1.270616
soledifv(0,1, 2,2e4)
#  1.270643
soledifv(0,1, 2,4e4)
#  1.270657
points(2,1.2707, col="red",pch=20)
   
#
## Se so risolvere l'equazione - la soluzione in questo caso è:
f <- function(x) x-1+(C+1)*exp(-x); C <- 1
## - posso verificare che il valore è corretto:
f(2)
#  1.270671
# E sovrapporre il grafico:
curve(f,col="yellow",add=TRUE,lty=3,lwd=2)
#
# ESEMPIO 2
# Vediamo come trovare con più precisione il valore che la soluzione della
# eq. diff.le precedente  passante per (0,1) assume nel punto 2
v0 <- soledifv(0,1, 2, 5e3); v0
#  1.270562
v1 <- soledifv(0,1, 2, 1e4); c(v1, v1-v0)
#  1.270616e+00   5.413953e-05
v2 <- soledifv(0,1, 2, 2e4); c(v2, v2-v1)
#  1.270643e+00   2.706841e-05
v3 <- soledifv(0,1, 2, 4e4); c(v3, v3-v2)
#  1.270657e+00   1.353387e-05
## In questo caso le variazioni man mano si dimezzano: 1/2+1/4+1/16+... = 1
## Quindi l'ultima variazione fornisce una stima di quanto manca al valore esatto
v3 + (v3-v2)
#  1.270671
## Ritrovo il valore trovato risolvendo l'equazione differenziale
#
#
# ESEMPIO 3
# y'(x) = x/y   Trovare il campo direzionale in (-3,3)*(-3,3)
# e tracciare le curve soluzioni tali che y(-2)=3 e che y(-3)=2.
#
Dy <- function(x,y) x/y
col1 <<- "red"; col2 <<- "grey50"
soledif(-3,3, -3,3, 25,25)
soledifc(-2,3, 3, 1e4, "brown")
   
v0 <- soledifv(-2,3, 3, 5e3); v0
#  3.741161
v0 <- soledifv(-2,3, 2, 5e3); v0
#  2.999565
v1 <- soledifv(-2,3, 2, 1e4); c(v1, v1-v0)
#  2.9997824339 0.0002175728
v2 <- soledifv(-2,3, 2, 2e4); c(v2, v2-v1)
#  2.9998912178 0.0001087839
v3 <- soledifv(-2,3, 2, 4e4); c(v3, v3-v2)
#  2.999946e+00 5.439132e-05
# Anche in questo caso le variazioni si dimezzano; quindi assumo
v3 + (v3-v2)
#  3    Come era ovvio, data la simmetria della curva soluzione
#
#  Vediamo l'altro problema:
soledifc(-3,2, 3, 1e4, "green4")
   
# Era evidente dal campo direzionale e dalla eq. stessa che la curva non è
# definita per x≥0. Studiamo meglio il dominio (potremmo farlo diversamente,
# date le simmetrie del problema, ma facciamolo prescindendo dalle particolarità
# di esso).
# Sfrutto il fatto che, chiamato soledifv (vedi il commento presente in soledifv),
# xxx mi dà i punti in cui la pendenza della curva soluzione cambia segno;
# Capisco dal grafico verde ottenuto che sarà circa 2.25.
soledifv(-3,2, 3, 1e4); xxx
#  -2.234400000  -2.233800000   -0.034917451  0.003477104
# Tra (-2.2344,-0.034917451) e (-2.2338,0.003477104) cambia segno la pendenza; preciso
# l'intervallo
soledifv(-3,2, -2.2, 2e4); xxx
#  -2.23592000  -2.23588000   -0.02072017  -0.01640376.  x è circa -2.2359
# Evidenzio il punto
xp <- mean(xxx[1],xxx[2]); yp <- mean(xxx[3],xxx[4])
points(xp,yp, pch=19,col="red")
# Traccio il grafico della soluzione e il grafico di quella passante per (-3,-2):
soledifc(-3,2, xp, 1e4, "black")
soledifc(-3,-2, xp, 1e4, "black")
points(xp,yp, pch=19,col="red")
   
#
# Riferendoci alla stessa equazione illustriamo un altro problema:
# se vi sono dei punti in cui l'equazione non è definita può capitare che
# essi non vengano individuati dall'algoritmo (anche in questo caso occorre
# usare le nostre conoscenze matematiche per controllare le uscite):
soledif(-3,3, -3,3, 25,25)
soledifc(-3,3, 3, 1e4, "brown")
soledifc(-3,-3, 3, 1001, "green4")