function [y,e,w,u,ref,symbolCounter,weightUpdateCounter,...
  numTrainSymbols,P,trainingDelayBuffer] = equalizerrlscore(x,sps,u,ref,...
  symbolCounter,weightUpdateCounter,w,...
  sigConst,numTrainSymbols,trainingSymbols,activeDelay,...
  adaptAfterTraining,adaptWeights,trainingFlag,weightUpdatePeriod,...
  lambda,P,nff,nfb,trainingDelayBuffer)
% Core RLS equalizer algorithm

%   Copyright 2018 The MathWorks, Inc.

%#codegen

% Preallocate output
outputLength = (length(x) / sps);
y = zeros([outputLength 1], 'like', x);
e = zeros([outputLength 1], 'like', x);


lambdaInverse = (1/lambda);
for p=1:outputLength
  % Update tap delay line
  for idx=nff:-1:sps+1
    u(idx) = u(idx-sps);
  end
  for q=1:sps
    u(q,1) = x((p-1)*sps+q,1);
  end
  if nfb > 0
    for idx=nff+nfb:-1:nff+2
      u(idx) = u(idx-1);
    end
    u(nff+1) = ref;
  end
  symbolCounter = symbolCounter + 1;
  weightUpdateCounter = weightUpdateCounter + 1;
  
  % Filter
  yTemp = w' * u;
  
  % Calculate error signal
  if symbolCounter <= numTrainSymbols
    if p <= length(trainingSymbols)
      dRef = trainingSymbols(p,1);
    else
      % If we ran out of training symbols, we need to push dummy
      % symbols until all the training is flushed out.
      dRef = trainingSymbols(1,1);
    end
    
    if trainingDelayBuffer.Length > 0
      ref = trainingDelayBuffer.Buffer(trainingDelayBuffer.Pointer);
      trainingDelayBuffer.Buffer(trainingDelayBuffer.Pointer) = dRef;
      trainingDelayBuffer.Pointer = trainingDelayBuffer.Pointer + 1;
      if trainingDelayBuffer.Pointer > trainingDelayBuffer.Length
        trainingDelayBuffer.Pointer = 1;
      end
    else
      ref = dRef;
    end
    
    if symbolCounter <= activeDelay
      % Use decisions as reference
      [~,idx] = min(abs(sigConst - yTemp));
      ref = sigConst(idx);
    end
  else
    % Decide on the received signal (Detector/Slicer)
    [~,idx] = min(abs(sigConst - yTemp));
    ref = sigConst(idx);
    
    if ~adaptAfterTraining
      adaptWeights = false;
    end
  end
  eTemp = ref - yTemp;
  
  if adaptWeights
    % Update tap weights
    if (trainingFlag == true) ...
        || (weightUpdateCounter == weightUpdatePeriod)
      weightUpdateCounter = 0;
      UP = u'*P;
      K = (P*u) / (lambda + UP*u);
      w = w + K*conj(eTemp);
      P = lambdaInverse * (P - K*UP);
    end
  end
  
  % Assign output
  y(p,1) = yTemp;
  e(p,1) = eTemp;
end
