class LoessSmoother {

	public double[] smooth() {
		if (fData.length == 1) {
			fSmoothed[0] = fData[0];
			return fSmoothed;
		}

		int left = -1, right = -1;
		if (fWidth >= fData.length) {
			left = 0;
			right = fData.length - 1;
			for (int i = 0; i < fData.length; i += fJump) {
				final Double y = fInterpolator.smoothOnePoint(i, left, right);
				fSmoothed[i] = y == null ? fData[i] : y;
				// logSmoothedPoint(i, smooth[i]);
			}
		} else if (fJump == 1) {
			final int halfWidth = (fWidth + 1) / 2;
			left = 0;
			right = fWidth - 1;
			for (int i = 0; i < fData.length; ++i) {
				if (i >= halfWidth && right != fData.length - 1) {
					++left;
					++right;
				}
				final Double y = fInterpolator.smoothOnePoint(i, left, right);
				fSmoothed[i] = y == null ? fData[i] : y;
				// logSmoothedPoint(i, smooth[i]);
			}
		} else {
			// For reference, the original RATFOR:
			// else { # newnj greater than one, len less than n
			// nsh = (len+1)/2
			// do i = 1,n,newnj { # fitted value at i
			// if(i<nsh) {              // i     = [1, 2, 3, 4, 5, 6, 7, 8, 9]; 9 points
			// nleft = 1                // left  = [1, 1, 1, 1, 1, 1, 1, 1, 1];
			// nright = len             // right = [19, 19, 19, 19, 19, 19, 19, 19, 19]; right - left = 18
			// }
			// else if(i>=n-nsh+1) {    // i     = [135, 136, 137, 138, 139, 140, 141, 142, 143, 144]; 10 points
			// nleft = n-len+1          // left  = [126, 126, 126, 126, 126, 126, 126, 126, 126, 126];
			// nright = n               // right = [144, 144, 144, 144, 144, 144, 144, 144, 144, 144]; right - left = 18
			// }
			// else {                   // i     = [10, 11, 12, ..., 132, 133, 134]; 125 points
			// nleft = i-nsh+1          // left  = [1, 2, 3, ..., 123, 124, 125]
			// nright = len+i-nsh       // right = [19, 20, 21, ..., 141, 142, 143]; right - left = 18
			// }
			// call est(y,n,len,ideg,float(i),ys(i),nleft,nright,res,userw,rw,ok)
			// if(!ok) ys(i) = y(i)
			// }
			// }
			// Note that RATFOR/Fortran are indexed from 1
			//
			// test: data.length == 144, fWidth = 19
			//   --> halfWidth = 10
			// Ignoring jumps...
			// First branch for  i = [0, 1, 2, 3, 4, 5, 6, 7, 8]; 9 points
			//                left = [0, 0, 0, 0, 0, 0, 0, 0, 0]
			//               right = [18, 18, 18, 18, 18, 18, 18, 18, 18]; right - left = 18
			// Second branch for i = [134, 135, 136, 137, 138, 139, 140, 141, 142, 143]; 10 points
			//                left = [125, 125, 125, 125, 125, 125, 125, 125, 125, 125];
			//               right = [143, 143, 143, 143, 143, 143, 143, 143, 143, 143]; right - left = 18
			// Third branch for  i = [ 9, 10, 11, ..., 131, 132, 133]; 125 points
			//                left = [ 0,  1,  2, ..., 122, 123, 124]
			//               right = [18, 19, 20, ..., 140, 141, 142]; right - left = 18
			final int halfWidth = (fWidth + 1) / 2;
			for (int i = 0; i < fData.length; i += fJump) {
				if (i < halfWidth - 1) {
					left = 0;
				} else if (i >= fData.length - halfWidth) {
					left = fData.length - fWidth;
				} else {
					left = i - halfWidth + 1;
				}
				right = left + fWidth - 1;
				final Double y = fInterpolator.smoothOnePoint(i, left, right);
				fSmoothed[i] = y == null ? fData[i] : y;
				// logSmoothedPoint(i, smooth[i]);
			}
		}

		if (fJump != 1) {
			for (int i = 0; i < fData.length - fJump; i += fJump) {
				final double slope = (fSmoothed[i + fJump] - fSmoothed[i]) / (double) fJump;
				for (int j = i + 1; j < i + fJump; ++j) {
					fSmoothed[j] = fSmoothed[i] + slope * (j - i);
					// logInterpolatedPoint(j, smooth[j]);
				}
			}

			final int last = fData.length - 1;
			int lastSmoothedPos = (last / fJump) * fJump;
			if (lastSmoothedPos != last) {
				final Double y = fInterpolator.smoothOnePoint(last, left, right);
				fSmoothed[last] = y == null ? fData[last] : y;
				// logSmoothedPoint(last, smooth[last]);

				if (lastSmoothedPos != last - 1) {
					final double slope = (fSmoothed[last] - fSmoothed[lastSmoothedPos]) / (last - lastSmoothedPos);
					for (int j = lastSmoothedPos + 1; j < last; ++j) {
						fSmoothed[j] = fSmoothed[lastSmoothedPos] + slope * (j - lastSmoothedPos);
						// logInterpolatedPoint(j, smooth[j]);
					}
				}
			}
		}

		return fSmoothed;
	}

	private  LoessSmoother(int width, int jump, int degree, double[] data, double[] externalWeights);

}