code: @Override
      public List<SubsetMove> getAllMoves(SubsetSolution solution) {
          // get set of candidate IDs for deletion and addition (fixed IDs are discarded)
          Set<Integer> removeCandidates = getRemoveCandidates(solution);
          Set<Integer> addCandidates = getAddCandidates(solution);
          // create empty list of moves
          List<SubsetMove> moves = new ArrayList<>();
          // generate all addition moves, if valid
          if(canAdd(solution, addCandidates)){
              // create addition move for each add candidate
              addCandidates.forEach(add -> moves.add(new AdditionMove(add)));
          }
          // generate all deletion moves, if valid
          if(canRemove(solution, removeCandidates)){
              // create deletion move for each remove candidate
              removeCandidates.forEach(remove -> moves.add(new DeletionMove(remove)));
          }
          // generate all swap moves, if valid
          if(canSwap(solution, addCandidates, removeCandidates)){
              // create swap move for each combination of add and remove candidate
              addCandidates.forEach(add -> {
                  removeCandidates.forEach(remove -> {
                      moves.add(new SwapMove(add, remove));
                  });
              });
          }
          // return generated moves
          return moves;
      }
docs: Generate all valid swap, deletion and addition moves that transform the given subset solution into
  a neighbour within the minimum and maximum allowed subset size. The returned list may be empty,
  if no valid moves exist. If any fixed IDs have been specified, these will not be considered
  for deletion nor addition.
  
  @param solution solution for which a set of all valid moves is generated
  @return list of all valid swap, deletion and addition moves