docs: def add_user(self, user_id, counts_df, update_existing=False, maxiter=10, ncores=1,
  				 random_seed=1, stop_thr=1e-3, update_all_params=None):
  		"""
  		Add a new user to the model or update parameters for a user according to new data
  		
  		Note
  		----
  		This function will NOT modify any of the item parameters.
  
  		Note
  		----
  		This function only works with one user at a time. For updating many users at the same time,
  		use 'partial_fit' instead.
  
  		Note
  		----
  		For betters results, refit the model again from scratch.
  
  		Note
  		----
  		This function is prone to producing all NaNs values.
  
  		Parameters
  		----------
  		user_id : obj
  			Id to give to be user (when adding a new one) or Id of the existing user whose parameters are to be
  			updated according to the data in 'counts_df'. **Make sure that the data type is the same that was passed
  			in the training data, so if you have integer IDs, don't pass a string as ID**.
  		counts_df : data frame or array (nsamples, 2)
  			Data Frame with columns 'ItemId' and 'Count'. If passing a numpy array, will take the first two columns
  			in that order. Data containing user/item interactions **from one user only** for which to add or update
  			parameters. Note that you need to pass *all* the user-item interactions for this user when making an update,
  			not just the new ones.
  		update_existing : bool
  			Whether this should be an update of the parameters for an existing user (when passing True), or
  			an addition of a new user that was not in the model before (when passing False).
  		maxiter : int
  			Maximum number of iterations to run.
  		ncores : int
  			Number of threads/cores to use. With data for only one user, it's unlikely that using
  			multiple threads would give a significant speed-up, and it might even end up making
  			the function slower due to the overhead.
  		random_seed : int
  			Random seed used to initialize parameters.
  		stop_thr : float
  			If the l2-norm of the difference between values of Theta_{u} between interations is less
  			than this, it will stop. Smaller values of 'k' should require smaller thresholds.
  		update_all_params : bool
  			Whether to update also the item parameters in each iteration. If passing True, will update them
  			with a step size given determined by the number of iterations already taken and the step_size function
  			given as input in the model constructor call.
  
  		Returns
  		-------
  		True : bool
  			Will return True if the process finishes successfully.
  		"""
  
  		ncores, random_seed, stop_thr, maxiter = self._check_input_predict_factors(ncores, random_seed, stop_thr, maxiter)
  
  		if update_existing:
  			## checking that the user already exists
  			if self.produce_dicts and self.reindex:
  				user_id = self.user_dict_[user_id]
  			else:
  				if self.reindex:
  					user_id = pd.Categorical(np.array([user_id]), self.user_mapping_).codes[0]
  					if user_id == -1:
  						raise ValueError("User was not present in the training data.")
  
  		## processing the data
  		counts_df = self._process_data_single(counts_df)
  		
  		if update_all_params:
  			counts_df['UserId'] = user_id
  			counts_df['UserId'] = counts_df.UserId.astype(cython_loops.obj_ind_type)
  			self.partial_fit(counts_df, new_users=(not update_existing))
  			Theta_prev = self.Theta[-1].copy()
  			for i in range(maxiter - 1):
  				self.partial_fit(counts_df)
  				new_Theta = self.Theta[-1]
  				if np.linalg.norm(new_Theta - Theta_prev) <= stop_thr:
  					break
  				else:
  					Theta_prev = self.Theta[-1].copy()
  		else:
  			## calculating the latent factors
  			Theta = np.empty(self.k, dtype='float32')
  			temp = cython_loops.calc_user_factors(
  								 self.a, self.a_prime, self.b_prime,
  								 self.c, self.c_prime, self.d_prime,
  								 counts_df.Count.values,
  								 counts_df.ItemId.values,
  								 Theta, self.Beta,
  								 self.Lambda_shp,
  								 self.Lambda_rte,
  								 cython_loops.cast_ind_type(counts_df.shape[0]), cython_loops.cast_ind_type(self.k),
  								 cython_loops.cast_int(maxiter), cython_loops.cast_int(ncores),
  								 cython_loops.cast_int(random_seed), cython_loops.cast_int(stop_thr),
  								 cython_loops.cast_int(self.keep_all_objs)
  								 )
  
  			if np.isnan(Theta).sum() > 0:
  				raise ValueError("NaNs encountered in the result. Failed to produce latent factors.")
  
  			## adding the data to the model
  			if update_existing:
  				self.Theta[user_id] = Theta
  				if self.keep_all_objs:
  					self.Gamma_shp[user_id] = temp[0]
  					self.Gamma_rte[user_id] = temp[1]
  					self.k_rte[user_id] = self.a_prime/self.b_prime + \
  										(temp[0].reshape((1,-1))/temp[1].reshape((1,-1))).sum(axis=1, keepdims=True)
  			else:
  				if self.reindex:
  					new_id = self.user_mapping_.shape[0]
  					self.user_mapping_ = np.r_[self.user_mapping_, user_id]
  					if self.produce_dicts:
  						self.user_dict_[user_id] = new_id
  				self.Theta = np.r_[self.Theta, Theta.reshape((1, self.k))]
  				if self.keep_all_objs:
  					self.Gamma_shp = np.r_[self.Gamma_shp, temp[0].reshape((1, self.k))]
  					self.Gamma_rte = np.r_[self.Gamma_rte, temp[1].reshape((1, self.k))]
  					self.k_rte = np.r_[self.k_rte, self.a_prime/self.b_prime + \
  									(temp[0].reshape((1,-1))/temp[1].reshape((1,-1))).sum(axis=1, keepdims=True)]
  				self.nusers += 1
  
  		## updating the list of seen items for this user
  		if self.keep_data:
  			if update_existing:
  				n_seen_by_user_before = self._n_seen_by_user[user_id]
  				self._n_seen_by_user[user_id] = counts_df.shape[0]
  				self.seen = np.r_[self.seen[:user_id], counts_df.ItemId.values, self.seen[(user_id + 1):]]
  				self._st_ix_user[(user_id + 1):] += self._n_seen_by_user[user_id] - n_seen_by_user_before
  			else:
  				self._n_seen_by_user = np.r_[self._n_seen_by_user, counts_df.shape[0]]
  				self._st_ix_user = np.r_[self._st_ix_user, self.seen.shape[0]]
  				self.seen = np.r_[self.seen, counts_df.ItemId.values]
  
  		return True