c
c     from netlib/a/stl: no authorship nor copyright claim in the source;
c     presumably by the authors of
c
c     R.B. Cleveland, W.S.Cleveland, J.E. McRae, and I. Terpenning,
c     STL: A Seasonal-Trend Decomposition Procedure Based on Loess,
c     Statistics Research Report, AT&T Bell Laboratories.
c
c     Converted to double precision by B.D. Ripley 1999.
c     Indented, goto labels renamed, many goto's replaced by `if then {else}'
c     (using Emacs), many more comments;  by M.Maechler 2001-02.
c
      subroutine stl(y,n,np,ns,nt,nl, isdeg,itdeg,ildeg,
     &     nsjump,ntjump,nljump, ni,no, rw,season,trend,work)

c     implicit none
c Arg
      integer n, np, ns,nt,nl, isdeg,itdeg,ildeg, nsjump,ntjump,nljump,
     &     ni, no
c       n                   : length(y)
c       ns, nt, nl          : spans        for `s', `t' and `l' smoother
c       isdeg, itdeg, ildeg : local degree for `s', `t' and `l' smoother
c       nsjump,ntjump,nljump: ........     for `s', `t' and `l' smoother
c       ni, no              : number of inner and outer (robust) iterations

      double precision y(n), rw(n), season(n), trend(n),
     &     work(n+2*np,5)
c Var
      integer i,k, newns, newnt, newnl, newnp
      logical userw

      userw = .false.
      do i = 1,n
         trend(i) = 0.d0
      end do
c the three spans must be at least three and odd:
      newns = max0(3,ns)
      newnt = max0(3,nt)
      newnl = max0(3,nl)
      if(mod(newns,2) .eq. 0) newns = newns + 1
      if(mod(newnt,2) .eq. 0) newnt = newnt + 1
      if(mod(newnl,2) .eq. 0) newnl = newnl + 1
c periodicity at least 2:
      newnp = max0(2,np)

      k = 0
c --- outer loop -- robustnes iterations
 100  continue
      call stlstp(y,n, newnp,newns,newnt,newnl, isdeg,itdeg,ildeg,
     &     nsjump,ntjump,nljump, ni,userw,rw,season, trend, work)
      k = k+1
      if(k .gt. no) goto 10

      do i = 1,n
         work(i,1) = trend(i)+season(i)
      end do
      call stlrwt(y,n,work(1,1),rw)
      userw = .true.
      goto 100
c --- end Loop
 10   continue

c     robustness weights when there were no robustness iterations:
      if(no .le. 0) then
         do i = 1,n
            rw(i) = 1.d0
         end do
      endif
      return
      end

      subroutine stless(y,n,len,ideg,njump,userw,rw,ys,res)

c     implicit none
c Arg
      integer n, len, ideg, njump
      double precision y(n), rw(n), ys(n), res(n)
c Var
      integer newnj, nleft, nright, nsh, k, i, j
      double precision delta
      logical ok, userw

      if(n .lt. 2) then
         ys(1) = y(1)
         return
      endif

      newnj = min0(njump, n-1)
      if(len .ge. n) then
         nleft = 1
         nright = n
         do i = 1,n,newnj
            call stlest(y,n,len,ideg,dble(i),ys(i),nleft,nright,res,
     &           userw,rw,ok)
            if(.not. ok) ys(i) = y(i)
         end do

      else

         if(newnj .eq. 1) then
            nsh = (len+1)/2
            nleft = 1
            nright = len
            do i = 1,n
               if(i .gt. nsh  .and.  nright .ne. n) then
                  nleft = nleft+1
                  nright = nright+1
               endif
               call stlest(y,n,len,ideg,dble(i),ys(i),nleft,nright,res,
     &              userw,rw,ok)
               if(.not. ok) ys(i) = y(i)
            end do
         else
            nsh = (len+1)/2
            do i = 1,n,newnj
               if(i .lt. nsh) then
                  nleft = 1
                  nright = len
               else if(i .ge. n-nsh+1) then
                  nleft = n-len+1
                  nright = n
               else
                  nleft = i-nsh+1
                  nright = len+i-nsh
               endif

               call stlest(y,n,len,ideg,dble(i),ys(i),nleft,nright,res,
     &              userw,rw,ok)
               if(.not. ok) ys(i) = y(i)
            end do

         endif

      endif

      if(newnj .ne. 1) then
         do i = 1,n-newnj,newnj
            delta = (ys(i+newnj)-ys(i))/dble(newnj)
            do j = i+1,i+newnj-1
               ys(j) = ys(i)+delta*dble(j-i)
            end do
         end do
         k = ((n-1)/newnj)*newnj+1

         if(k .ne. n) then
            call stlest(y,n,len,ideg,dble(n),ys(n),nleft,nright,res,
     &           userw,rw,ok)
            if(.not. ok) ys(n) = y(n)

            if(k .ne. n-1) then
               delta = (ys(n)-ys(k))/dble(n-k)
               do j = k+1,n-1
                  ys(j) = ys(k)+delta*dble(j-k)
               end do
            endif
         endif
      endif
      return
      end

      subroutine stlest(y,n,len,ideg,xs,ys,nleft,nright,w,
     &     userw,rw,ok)

c     implicit none
c Arg
      integer n, len, ideg, nleft, nright
      double precision y(n), w(n), rw(n), xs, ys
      logical userw,ok
c Var
      double precision range, h, h1, h9, a, b, c, r
      integer j

      range = dble(n)-dble(1)
      h = max(xs - dble(nleft), dble(nright) - xs)
      if(len .gt. n) h = h + dble((len-n)/2)
      h9 = 0.999d0*h
      h1 = 0.001d0*h
      a = 0.d0
      do j = nleft,nright
         r = abs(dble(j)-xs)
         if(r .le. h9) then
            if(r .le. h1) then
               w(j) = 1.d0
            else
               w(j) = (1.d0 - (r/h)**3)**3
            endif
            if(userw) w(j) = rw(j)*w(j)
            a = a+w(j)
         else
            w(j) = 0.d0
         endif
      end do

      if(a .le. 0.d0) then
         ok = .false.
      else
         ok = .true.
         do j = nleft,nright
            w(j) = w(j)/a
         end do
         if((h .gt. 0.d0) .and. (ideg .gt. 0)) then
            a = 0.d0
            do j = nleft,nright
               a = a+w(j)*dble(j)
            end do
            b = xs-a
            c = 0.d0
            do j = nleft,nright
               c = c+w(j)*(dble(j)-a)**2
            end do
            if(sqrt(c) .gt. 0.001d0*range) then
               b = b/c
               do j = nleft,nright
                  w(j) = w(j)*(b*(dble(j)-a)+1.0d0)
               end do
            endif
         endif
         ys = 0.d0
         do j = nleft,nright
            ys = ys+w(j)*y(j)
         end do
      endif

      return
      end

      subroutine stlfts(x,n,np,trend,work)
      integer n, np
      double precision x(n), trend(n), work(n)

      call stlma(x,    n,      np, trend)
      call stlma(trend,n-np+1, np, work)
      call stlma(work, n-2*np+2,3, trend)
      return
      end


      subroutine stlma(x, n, len, ave)

c Moving Average (aka "running mean")
c ave(i) := mean(x{j}, j = max(1,i-k),..., min(n, i+k))
c           for i = 1,2,..,n

c     implicit none
c Arg
      integer n, len
      double precision x(n), ave(n)
c Var
      double precision flen, v
      integer i, j, k, m, newn
      newn = n-len+1
      flen = dble(len)
      v = 0.d0
      do i = 1,len
         v = v+x(i)
      end do
      ave(1) = v/flen
      if(newn .gt. 1) then
         k = len
         m = 0
         do j = 2, newn
            k = k+1
            m = m+1
            v = v-x(m)+x(k)
            ave(j) = v/flen
         end do
      endif
      return
      end


      subroutine stlstp(y,n,np,ns,nt,nl,isdeg,itdeg,ildeg,nsjump,
     &     ntjump,nljump,ni,userw,rw,season,trend,work)

c     implicit none
c Arg
      integer n,np,ns,nt,nl,isdeg,itdeg,ildeg,nsjump,ntjump,nljump,ni
      logical userw
      double precision y(n),rw(n),season(n),trend(n),work(n+2*np,5)
c Var
      integer i,j

      do j = 1,ni
         do i = 1,n
            work(i,1) = y(i)-trend(i)
         end do
         call stlss(work(1,1),n,np,ns,isdeg,nsjump,userw,rw,work(1,2),
     &        work(1,3),work(1,4),work(1,5),season)
         call stlfts(work(1,2),n+2*np,np,work(1,3),work(1,1))
         call stless(work(1,3),n,nl,ildeg,nljump,.false.,work(1,4),
     &        work(1,1),work(1,5))
         do i  = 1,n
            season(i) = work(np+i,2)-work(i,1)
         end do
         do i = 1,n
            work(i,1) = y(i)-season(i)
         end do
         call stless(work(1,1),n,nt,itdeg,ntjump,userw,rw,trend,
     &        work(1,3))
      end do
      return
      end

      subroutine stlrwt(y,n,fit,rw)
c Robustness Weights
c       rw_i := B( |y_i - fit_i| / (6 M) ),   i = 1,2,...,n
c               where B(u) = (1 - u^2)^2  * 1[|u| < 1]   {Tukey's biweight}
c               and   M := median{ |y_i - fit_i| }
c     implicit none
c Arg
      integer n
      double precision y(n), fit(n), rw(n)
c Var
      integer mid(2), i
      double precision cmad, c9, c1, r

      do i = 1,n
         rw(i) = abs(y(i)-fit(i))
      end do
      mid(1) = n/2+1
      mid(2) = n-mid(1)+1
      call psort(rw,n,mid,2)
      cmad = 3.0d0*(rw(mid(1))+rw(mid(2)))
c     = 6 * MAD
      c9 = 0.999d0*cmad
      c1 = 0.001d0*cmad
      do i = 1,n
         r = abs(y(i)-fit(i))
         if(r .le. c1) then
            rw(i) = 1.d0
         else if(r .le. c9) then
            rw(i) = (1.d0 - (r/cmad)**2)**2
         else
            rw(i) = 0.d0
         endif
      end do
      return
      end

      subroutine stlss(y,n,np,ns,isdeg,nsjump,userw,rw,season,
     &     work1,work2,work3,work4)
c
c       called by stlstp() at the beginning of each (inner) iteration
c
c     implicit none
c Arg
      integer n, np, ns, isdeg, nsjump
      double precision y(n), rw(n), season(n+2*np),
     &     work1(n), work2(n), work3(n), work4(n)
      logical userw
c Var
      integer nright, nleft, i, j, k, m
      logical ok
      double precision xs

      if(np .lt. 1) return

      do j = 1, np
         k = (n-j)/np+1
         do i = 1,k
            work1(i) = y((i-1)*np+j)
         end do
         if(userw) then
            do i = 1,k
               work3(i) = rw((i-1)*np+j)
            end do
         endif
         call stless(work1,k,ns,isdeg,nsjump,userw,work3,work2(2),work4)
         xs = 0
         nright = min0(ns,k)
         call stlest(work1,k,ns,isdeg,xs,work2(1),1,nright,work4,
     &        userw,work3,ok)
         if(.not. ok) work2(1) = work2(2)
         xs = k+1
         nleft = max0(1,k-ns+1)
         call stlest(work1,k,ns,isdeg,xs,work2(k+2),nleft,k,work4,
     &        userw,work3,ok)
         if(.not. ok) work2(k+2) = work2(k+1)
         do m = 1,k+2
            season((m-1)*np+j) = work2(m)
         end do

      end do

      return
      end


c  STL E_Z_ : "Easy" user interface  -- not called from R

      subroutine stlez(y, n, np, ns, isdeg, itdeg, robust, no, rw,
     &     season, trend, work)

c     implicit none
c Arg
      integer n, np, ns, isdeg, itdeg, no
      logical robust
      double precision y(n), rw(n), season(n), trend(n), work(n+2*np,7)
c Var
      integer i, j, ildeg, nt, nl, ni, nsjump, ntjump, nljump,
     &     newns, newnp
      double precision maxs, mins, maxt, mint, maxds, maxdt, difs, dift

      ildeg = itdeg
      newns = max0(3,ns)
      if(mod(newns,2) .eq. 0) newns = newns+1
      newnp = max0(2,np)
      nt = int((1.5d0*newnp)/(1.d0 - 1.5d0/newns) + 0.5d0)
      nt = max0(3,nt)
      if(mod(nt,2) .eq. 0) nt = nt+1
      nl = newnp
      if(mod(nl,2) .eq. 0) nl = nl+1

      if(robust) then
         ni = 1
      else
         ni = 2
      endif

      nsjump = max0(1,int(float(newns)/10 + 0.9))
      ntjump = max0(1,int(float(nt)/10 + 0.9))
      nljump = max0(1,int(float(nl)/10 + 0.9))
      do i = 1,n
         trend(i) = 0.d0
      end do
      call stlstp(y,n,newnp,newns,nt,nl,isdeg,itdeg,ildeg,nsjump,
     &     ntjump,nljump,ni,.false.,rw,season,trend,work)

      no = 0
      if(robust) then
         j=1
C        Loop  --- 15 robustness iterations
 100     if(j .le. 15) then
            do i = 1,n
               work(i,6) = season(i)
               work(i,7) = trend(i)
               work(i,1) = trend(i)+season(i)
            end do
            call stlrwt(y,n,work(1,1),rw)
            call stlstp(y, n, newnp, newns, nt,nl, isdeg,itdeg,ildeg,
     &           nsjump,ntjump,nljump, ni, .true.,
     &           rw, season, trend, work)
            no = no+1
            maxs = work(1,6)
            mins = work(1,6)
            maxt = work(1,7)
            mint = work(1,7)
            maxds = abs(work(1,6) - season(1))
            maxdt = abs(work(1,7) - trend(1))
            do i = 2,n
               if(maxs .lt. work(i,6)) maxs = work(i,6)
               if(maxt .lt. work(i,7)) maxt = work(i,7)
               if(mins .gt. work(i,6)) mins = work(i,6)
               if(mint .gt. work(i,7)) mint = work(i,7)
               difs = abs(work(i,6) - season(i))
               dift = abs(work(i,7) - trend(i))
               if(maxds .lt. difs) maxds = difs
               if(maxdt .lt. dift) maxdt = dift
            end do
            if((maxds/(maxs-mins) .lt. 0.01d0) .and.
     &         (maxdt/(maxt-mint) .lt. 0.01d0))   goto 300
            continue
            j=j+1
            goto 100
         endif
C        end Loop
 300     continue

      else
c       .not. robust

         do i = 1,n
            rw(i) = 1.0d0
         end do
      endif

      return
      end

      subroutine psort(a,n,ind,ni)
c
c Partial Sorting ; used for Median (MAD) computation only
c
c     implicit none
c Arg
      integer n,ni
      double precision a(n)
      integer ind(ni)
c Var
      integer indu(16),indl(16),iu(16),il(16),p,jl,ju,i,j,m,k,ij,l
      double precision t,tt

      if(n .lt. 0 .or. ni .lt. 0) return

      if(n .lt. 2 .or. ni .eq. 0) return

      jl = 1
      ju = ni
      indl(1) = 1
      indu(1) = ni
      i = 1
      j = n
      m = 1

c Outer Loop
 161  continue
      if(i .lt. j) go to 10

c  _Loop_
 166  continue
      m = m-1
      if(m .eq. 0) return
      i = il(m)
      j = iu(m)
      jl = indl(m)
      ju = indu(m)
      if(.not.(jl .le. ju))  goto 166

c     while (j - i > 10)
 173  if(.not.(j-i .gt. 10)) goto 174

 10   k = i
      ij = (i+j)/2
      t = a(ij)
      if(a(i) .gt. t) then
         a(ij) = a(i)
         a(i) = t
         t = a(ij)
      endif
      l = j
      if(a(j) .lt. t) then
         a(ij) = a(j)
         a(j) = t
         t = a(ij)
         if(a(i) .gt. t) then
            a(ij) = a(i)
            a(i) = t
            t = a(ij)
         endif
      endif

 181  continue
      l = l-1
      if(a(l) .le. t)then
         tt = a(l)
 186     continue
         k = k+1
         if(.not.(a(k) .ge. t)) goto 186

         if(k .gt. l) goto 183

         a(l) = a(k)
         a(k) = tt
      endif
      goto   181

 183  continue
      indl(m) = jl
      indu(m) = ju
      p = m
      m = m+1
      if(l-i .le. j-k) then
         il(p) = k
         iu(p) = j
         j = l

 193     continue
         if(jl .gt. ju) goto 166
         if(ind(ju) .gt. j) then
            ju = ju-1
            goto 193
         endif
         indl(p) = ju+1
      else
         il(p) = i
         iu(p) = l
         i = k

 200     continue
         if(jl .gt. ju) goto 166
         if(ind(jl) .lt. i) then
            jl = jl+1
            goto 200
         endif
         indu(p) = jl-1
      endif

      goto   173
c     end while
 174  continue

      if(i .ne. 1) then
         i = i-1
 209     continue
         i = i+1
         if(i .eq. j) goto 166
         t = a(i+1)
         if(a(i) .gt. t) then
            k = i
c           repeat
 216        continue
            a(k+1) = a(k)
            k = k-1
            if(.not.(t .ge. a(k))) goto 216
c           until  t >= a(k)
            a(k+1) = t
         endif
         goto 209

      endif

      goto 161
c End Outer Loop

      end
