/*****************************************************************************\
 *  eval_nodes.c - Determine order of nodes for job.
 *****************************************************************************
 *  Copyright (C) SchedMD LLC.
 *
 *  This file is part of Slurm, a resource management program.
 *  For details, see <https://slurm.schedmd.com/>.
 *  Please also read the included file: DISCLAIMER.
 *
 *  Slurm is free software; you can redistribute it and/or modify it under
 *  the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2 of the License, or (at your option)
 *  any later version.
 *
 *  In addition, as a special exception, the copyright holders give permission
 *  to link the code of portions of this program with the OpenSSL library under
 *  certain conditions as described in each individual source file, and
 *  distribute linked combinations including the two. You must obey the GNU
 *  General Public License in all respects for all of the code used other than
 *  OpenSSL. If you modify file(s) with this exception, you may extend this
 *  exception to your version of the file(s), but you are not obligated to do
 *  so. If you do not wish to do so, delete this exception statement from your
 *  version.  If you delete this exception statement from all source files in
 *  the program, then also delete it here.
 *
 *  Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
 *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 *  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 *  details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with Slurm; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
\*****************************************************************************/

#include "eval_nodes.h"
#include "gres_filter.h"
#include "gres_sched.h"

#include "src/common/xstring.h"

typedef struct node_weight_struct {
	bitstr_t *node_bitmap;	/* bitmap of nodes with this weight */
	uint64_t weight;	/* priority of node for scheduling work on */
} node_weight_type;

typedef struct {
	uint16_t *avail_cpu_per_node;
	avail_res_t **avail_res_array;
	uint32_t cpus_per_task;
	int i_end;
	int i_start;
	uint32_t *max_nodes;
	int *min_rem_nodes;
	bitstr_t *node_map;
	int *rem_cpus;
	int *rem_nodes;
	int64_t *rem_max_cpus;
	uint16_t *used_cpu_per_node;
} foreach_add_nodes_lln_t;

/* Find node_weight_type element from list with same weight as node config */
static int _node_weight_find(void *x, void *key)
{
	node_weight_type *nwt = x;
	node_record_t *node_ptr = key;
	if (nwt->weight == node_ptr->sched_weight)
		return 1;
	return 0;
}

/* Free node_weight_type element from list */
static void _node_weight_free(void *x)
{
	node_weight_type *nwt = x;
	FREE_NULL_BITMAP(nwt->node_bitmap);
	xfree(nwt);
}

/* Sort list of node_weight_type records in order of increasing node weight */
static int _node_weight_sort(void *x, void *y)
{
	node_weight_type *nwt1 = *(node_weight_type **) x;
	node_weight_type *nwt2 = *(node_weight_type **) y;
	if (nwt1->weight < nwt2->weight)
		return -1;
	if (nwt1->weight > nwt2->weight)
		return 1;
	return 0;
}

/*
 * Given a bitmap of available nodes, return a list of node_weight_type
 * records in order of increasing "weight" (priority)
 */
static list_t *_build_node_weight_list(bitstr_t *node_bitmap)
{
	list_t *node_list;
	node_record_t *node_ptr;
	node_weight_type *nwt;

	xassert(node_bitmap);
	/* Build list of node_weight_type records, one per node weight */
	node_list = list_create(_node_weight_free);
	for (int i = 0; (node_ptr = next_node_bitmap(node_bitmap, &i)); i++) {
		nwt = list_find_first(node_list, _node_weight_find, node_ptr);
		if (!nwt) {
			nwt = xmalloc(sizeof(node_weight_type));
			nwt->node_bitmap = bit_alloc(node_record_count);
			nwt->weight = node_ptr->sched_weight;
			list_append(node_list, nwt);
		}
		bit_set(nwt->node_bitmap, i);
	}

	/* Sort the list in order of increasing node weight */
	list_sort(node_list, _node_weight_sort);

	return node_list;
}

static void _reduce_res_cores(topology_eval_t *topo_eval,
			      uint64_t *maxtasks,
			      uint16_t res_cores_per_gpu,
			      int sockets,
			      uint16_t cores_per_socket,
			      uint16_t cpus_per_core,
			      int node_i)
{
	gres_job_state_t *gres_js;
	gres_state_t *gres_job_state;
	sock_gres_t *sock_gres;
	list_t *sock_list = topo_eval->avail_res_array[node_i]->sock_gres_list;
	bitstr_t *avail_core = topo_eval->avail_core[node_i];
	uint16_t *avail_cores_per_sock =
		topo_eval->avail_res_array[node_i]->avail_cores_per_sock;
	uint16_t *actual_cores_p_s;
	uint32_t tot_cores = 0;

	if (topo_eval->cr_type & SELECT_SOCKET)
		return;

	actual_cores_p_s = xcalloc(sockets, sizeof(uint16_t));
	for (int s = 0; s < sockets; s++) {
		int start_core = s * cores_per_socket;
		int end_core = start_core + cores_per_socket;
		actual_cores_p_s[s] = bit_set_count_range(avail_core,
							  start_core,
							  end_core);
		tot_cores += avail_cores_per_sock[s];
	}

	list_itr_t *sock_list_iter;
	sock_list_iter = list_iterator_create(sock_list);
	while ((sock_gres = list_next(sock_list_iter))) {
		bitstr_t *res_cores;
		uint16_t tot_res_core;
		uint32_t max_res_cores = 0;
		uint64_t max_gres = 0;
		uint32_t max_gres_by_cpu = 0;
		int i = (sockets * cores_per_socket) - 1;
		bool done = false;

		if (!sock_gres->gres_state_job)
			continue;

		gres_job_state = sock_gres->gres_state_job;
		gres_js = gres_job_state->gres_data;
		if (!gres_js->gres_per_task &&
		    (!gres_js->ntasks_per_gres ||
		     (gres_js->ntasks_per_gres == NO_VAL16)))
			continue;
		/* Gres per node takes priority in selection */
		if (gres_js->gres_per_node)
			continue;
		if (gres_js->gres_per_task)
			max_gres = *maxtasks * gres_js->gres_per_task;
		else if (gres_js->ntasks_per_gres) {
			max_gres = *maxtasks / gres_js->ntasks_per_gres;
			*maxtasks = max_gres * gres_js->ntasks_per_gres;
		}

		sock_gres->total_cnt = MIN(sock_gres->total_cnt, max_gres);

		if ((!gres_find_gpu_or_alt(gres_job_state, NULL)) ||
		    !gres_js->res_gpu_cores || !gres_js->res_gpu_cores[node_i])
			continue;

		max_res_cores = max_gres * res_cores_per_gpu;
		res_cores = bit_copy(gres_js->res_gpu_cores[node_i]);
		bit_and(res_cores, avail_core);
		tot_res_core = bit_set_count(res_cores);

		while (!done) {
			while (tot_res_core > max_res_cores) {
				int s;
				/*
				* Must remove restricted cores from the end of
				* the bitmap first since cores are picked from
				* front to back. This helps the needed
				* restricted cores get picked.
				*/
				i  = bit_fls_from_bit(res_cores, i);
				if (i < 0)
					break; /* This should never happen */
				bit_clear(avail_core, i);
				tot_res_core--;

				s = i / cores_per_socket;
				actual_cores_p_s[s]--;
				if (actual_cores_p_s[s] <
				    avail_cores_per_sock[s]) {
					int cnt;
					avail_cores_per_sock[s]--;
					tot_cores--;
					cnt = tot_cores * cpus_per_core;
					if (cnt < topo_eval->avail_cpus)
						topo_eval->avail_cpus = cnt;
				}
				i--;
			}

			if (gres_js->cpus_per_gres) {
				max_gres_by_cpu = topo_eval->avail_cpus /
					gres_js->cpus_per_gres;
				while (max_gres_by_cpu < max_gres) {
					(*maxtasks)--;
					if (gres_js->gres_per_task) {
						max_gres = *maxtasks *
							gres_js->gres_per_task;
					} else if (gres_js->ntasks_per_gres) {
						max_gres = *maxtasks /
							gres_js->
							ntasks_per_gres;
						*maxtasks = max_gres * gres_js->
							ntasks_per_gres;
					}
				}
				sock_gres->total_cnt =
					MIN(sock_gres->total_cnt, max_gres);
				max_res_cores = max_gres * res_cores_per_gpu;
				if (tot_res_core <= max_res_cores)
					done = true;
			} else
				done = true;
		}
		FREE_NULL_BITMAP(res_cores);
	}
	list_iterator_destroy(sock_list_iter);
	xfree(actual_cores_p_s);
}

static uint32_t _reduce_res_core_by_task_cnt(topology_eval_t* topo_eval,
					     uint64_t maxtasks,
					     uint32_t *gres_max_tasks,
					     node_record_t *node_ptr,
					     int node_i,
					     int select_inx)
{
	job_record_t *job_ptr = topo_eval->job_ptr;
	uint16_t plane_size = NO_VAL16;
	uint16_t arbitrary_tasks = NO_VAL16;
	bool one_task_per_node = false;
	uint16_t res_cores_per_gpu =  node_ptr->res_cores_per_gpu;

	if (job_ptr->details->overcommit && !job_ptr->tres_per_task)
		one_task_per_node = true;
	if (((job_ptr->details->task_dist & SLURM_DIST_STATE_BASE) ==
	     SLURM_DIST_PLANE) && !one_task_per_node) {
		plane_size = 1;
		if (job_ptr->details->mc_ptr)
			plane_size = job_ptr->details->mc_ptr->plane_size;
	} else if ((job_ptr->details->task_dist & SLURM_DIST_STATE_BASE) ==
		   SLURM_DIST_ARBITRARY) {
		arbitrary_tasks = job_ptr->details->arbitrary_tpn[select_inx];
	}

	maxtasks = MIN(maxtasks, plane_size);
	maxtasks = MIN(maxtasks, arbitrary_tasks);
	if (maxtasks < *gres_max_tasks) {
		_reduce_res_cores(topo_eval, &maxtasks, res_cores_per_gpu,
				  node_ptr->tot_sockets, node_ptr->cores,
				  node_ptr->tpc, node_i);
		*gres_max_tasks = maxtasks;
	}
	maxtasks = MIN(maxtasks, *gres_max_tasks);
	return maxtasks;
}

/*
 * Reduce the gres_max_tasks and total GRES available to a node based on
 * what will be laid out on the node.
 * If the GRES available gets reduced and RestrictedCoresPerGPU
 * is used, any unusable restricted cores will be removed.
 * If to many cores are removed such that the node is no longer usable
 * in the allocation it returns false, else true.
 */
extern bool eval_nodes_gres(topology_eval_t *topo_eval,
			    uint64_t *max_tasks,
			    job_record_t *job_ptr,
			    node_record_t *node_ptr,
			    int rem_nodes,
			    int node_i,
			    int select_inx)
{
	bool use_node = true;
	uint64_t used_tasks;
	uint32_t save_tasks = MAX((rem_nodes - 1), 0);
	uint16_t min_cpus = job_ptr->details->cpus_per_task;

	*max_tasks -= save_tasks;
	if (!job_ptr->details->overcommit) {
		used_tasks = MIN(*max_tasks, (topo_eval->avail_cpus /
					      job_ptr->details->cpus_per_task));
		if (used_tasks < *max_tasks) {
			save_tasks += *max_tasks - used_tasks;
			*max_tasks = used_tasks;
		}
	}
	*max_tasks = MAX(*max_tasks, 1);
	used_tasks = _reduce_res_core_by_task_cnt(
		topo_eval, *max_tasks,
		&topo_eval->avail_res_array[node_i]->gres_max_tasks, node_ptr,
		node_i, select_inx);

	if (!job_ptr->details->overcommit)
		min_cpus = job_ptr->details->cpus_per_task * used_tasks;
	else
		min_cpus = job_ptr->details->cpus_per_task;
	if (min_cpus < job_ptr->details->pn_min_cpus)
		min_cpus = job_ptr->details->pn_min_cpus;

	if (!used_tasks)
		use_node = false;
	else
		use_node = topo_eval->avail_cpus >= min_cpus;

	if (topo_eval->gres_per_job && use_node) {
		use_node = gres_sched_add(
			&topo_eval->avail_cpus,
			topo_eval->avail_core[node_i],
			topo_eval->avail_res_array[node_i]->
			avail_cores_per_sock,
			topo_eval->avail_res_array[node_i]->sock_gres_list,
			job_ptr->gres_list_req,
			node_ptr->res_cores_per_gpu,
			node_ptr->tot_sockets,
			node_ptr->cores, node_ptr->tpc, topo_eval->cr_type,
			min_cpus, node_i);
	}

	if (use_node)
		*max_tasks -= used_tasks;
	else
		topo_eval->avail_cpus = 0;

	topo_eval->avail_res_array[node_i]->avail_cpus = topo_eval->avail_cpus;
	*max_tasks += save_tasks;
	return use_node;
}

extern uint64_t eval_nodes_set_max_tasks(job_record_t *job_ptr,
					 uint64_t max_cpus,
					 uint32_t max_nodes) {
	uint32_t max_tasks = max_cpus;
	if (!job_ptr->details->overcommit &&
	    (job_ptr->details->cpus_per_task > 1)) {
		if (job_ptr->details->ntasks_per_node == 0) {
			max_tasks = max_tasks / job_ptr->details->cpus_per_task;
		} else {
			max_tasks = job_ptr->details->ntasks_per_node *
				max_nodes;
		}
	}
	return max_tasks;
}

extern void eval_nodes_clip_socket_cores(topology_eval_t *topo_eval)
{
	bitstr_t *avail_core;
	uint16_t *avail_cores_per_sock;
	uint16_t actual_core_cnt;
	node_record_t *node_ptr;
	int start_core;
	int end_core;

	if (!topo_eval->job_ptr->gres_list_req)
		return;

	for (int i = 0;
	     (node_ptr = next_node_bitmap(topo_eval->node_map, &i));
	     i++) {
		avail_core = topo_eval->avail_core[i];
		avail_cores_per_sock =
			topo_eval->avail_res_array[i]->avail_cores_per_sock;
		for (int s = 0; s < node_ptr->tot_sockets; s++) {
			start_core = s * node_ptr->cores;
			end_core = start_core + node_ptr->cores;
			actual_core_cnt = bit_set_count_range(avail_core,
							      start_core,
							      end_core);
			for (int c = node_ptr->cores - 1; c >= 0; c--) {
				int i = (s * node_ptr->cores) + c;
				if (actual_core_cnt <= avail_cores_per_sock[s])
					break;
				if (!bit_test(avail_core, i))
					continue;
				bit_clear(avail_core, i);
				actual_core_cnt--;
			}
		}
	}
}

/*
 * A variation of _eval_nodes() to select resources using busy nodes first.
 */
static int _eval_nodes_busy(topology_eval_t *topo_eval)
{
	int i, i_start, i_end, error_code = SLURM_ERROR;
	int idle_test;
	int rem_cpus, rem_nodes; /* remaining resources desired */
	int min_rem_nodes;	/* remaining resources desired */
	int total_cpus = 0;	/* #CPUs allocated to job */
	int64_t rem_max_cpus;
	job_record_t *job_ptr = topo_eval->job_ptr;
	job_details_t *details_ptr = job_ptr->details;
	bitstr_t *req_map = details_ptr->req_node_bitmap;
	bitstr_t *orig_node_map = bit_copy(topo_eval->node_map);
	avail_res_t **avail_res_array = topo_eval->avail_res_array;
	uint32_t min_nodes = topo_eval->min_nodes;
	uint32_t req_nodes = topo_eval->req_nodes;
	bool all_done = false;
	node_record_t *node_ptr;
	list_t *node_weight_list = NULL;
	node_weight_type *nwt;
	list_itr_t *iter;
	uint64_t maxtasks;

	if (details_ptr->arbitrary_tpn) {
		info("does not support arbitrary tasks distribution");
		error_code = ESLURM_NOT_SUPPORTED;
		goto fini;
	}

	topo_eval->avail_cpus = 0;

	rem_cpus = details_ptr->min_cpus;
	min_rem_nodes = min_nodes;
	if ((details_ptr->num_tasks != NO_VAL) &&
	    (details_ptr->num_tasks != 0))
		topo_eval->max_nodes = MIN(topo_eval->max_nodes,
					   details_ptr->num_tasks);
	if ((topo_eval->gres_per_job = gres_sched_init(job_ptr->gres_list_req)))
		rem_nodes = MIN(min_nodes, req_nodes);
	else
		rem_nodes = MAX(min_nodes, req_nodes);
	rem_max_cpus = eval_nodes_get_rem_max_cpus(details_ptr, rem_nodes);
	maxtasks = eval_nodes_set_max_tasks(job_ptr, rem_max_cpus,
					    topo_eval->max_nodes);

	i_start = bit_ffs(topo_eval->node_map);
	if (i_start >= 0)
		i_end = bit_fls(topo_eval->node_map);
	else
		i_end = i_start - 1;
	if (req_map) {
		for (i = i_start; i <= i_end; i++) {
			if (!bit_test(req_map, i)) {
				bit_clear(topo_eval->node_map, i);
				continue;
			}
			node_ptr = node_record_table_ptr[i];
			if (!avail_res_array[i] ||
			    !avail_res_array[i]->avail_cpus) {
				debug("%pJ required node %s lacks available resources",
				      job_ptr, node_ptr->name);
				topo_eval->eval_action = EVAL_ACTION_BREAK;
				goto fini;
			}
			if (topo_eval->max_nodes <= 0) {
				log_flag(SELECT_TYPE, "%pJ requires nodes exceed maximum node limit",
					 job_ptr);
				topo_eval->eval_action = EVAL_ACTION_BREAK;
				goto fini;
			}
			eval_nodes_select_cores(topo_eval, i, min_rem_nodes);
			(void) eval_nodes_cpus_to_use(topo_eval, i,
						      rem_max_cpus,
						      min_rem_nodes,
						      &maxtasks, true);
			if (topo_eval->avail_cpus <= 0) {
				debug("%pJ required node %s lacks available resources",
				      job_ptr, node_ptr->name);
				topo_eval->eval_action = EVAL_ACTION_BREAK;
				goto fini;
			}
			total_cpus += topo_eval->avail_cpus;
			rem_cpus   -= topo_eval->avail_cpus;
			rem_max_cpus -= topo_eval->avail_cpus;
			rem_nodes--;
			min_rem_nodes--;
			/* leaving bitmap set, decr max limit */
			topo_eval->max_nodes--;
		}
		if ((rem_nodes <= 0) && (rem_cpus <= 0) &&
		    gres_sched_test(job_ptr->gres_list_req, job_ptr->job_id)) {
			error_code = SLURM_SUCCESS;
			bit_and(topo_eval->node_map, req_map);
			goto fini;
		}
		if (topo_eval->max_nodes <= 0) {
			topo_eval->eval_action = EVAL_ACTION_BREAK;
			goto fini;
		}
		bit_and_not(orig_node_map, topo_eval->node_map);
	} else {
		bit_clear_all(topo_eval->node_map);
	}

	/* Compute CPUs already allocated to required nodes */
	if ((details_ptr->max_cpus != NO_VAL) &&
	    (total_cpus > details_ptr->max_cpus)) {
		info("%pJ can't use required nodes due to max CPU limit",
		     job_ptr);
		topo_eval->eval_action = EVAL_ACTION_BREAK;
		goto fini;
	}

	/*
	 * Start by using nodes that already have a job running.
	 * Then try to use idle nodes.
	 */
	if (topo_eval->max_nodes == 0)
		all_done = true;
	node_weight_list = _build_node_weight_list(orig_node_map);
	iter = list_iterator_create(node_weight_list);
	while (!all_done && (nwt = list_next(iter))) {
		for (idle_test = 0; idle_test < 2; idle_test++) {
			for (i = i_start; i <= i_end; i++) {
				if (!avail_res_array[i] ||
				    !avail_res_array[i]->avail_cpus)
					continue;
				/* Node not available or already selected */
				if (!bit_test(nwt->node_bitmap, i) ||
				    bit_test(topo_eval->node_map, i))
					continue;
				if (((idle_test == 0) &&
				     bit_test(idle_node_bitmap, i)) ||
				    ((idle_test == 1) &&
				     !bit_test(idle_node_bitmap, i)))
					continue;
				eval_nodes_select_cores(topo_eval, i,
							min_rem_nodes);
				(void) eval_nodes_cpus_to_use(topo_eval, i,
							      rem_max_cpus,
							      min_rem_nodes,
							      &maxtasks, true);
				if (topo_eval->avail_cpus == 0)
					continue;
				total_cpus += topo_eval->avail_cpus;
				rem_cpus -= topo_eval->avail_cpus;
				rem_max_cpus -= topo_eval->avail_cpus;
				rem_nodes--;
				min_rem_nodes--;
				topo_eval->max_nodes--;
				bit_set(topo_eval->node_map, i);
				if ((rem_nodes <= 0) && (rem_cpus <= 0) &&
				    gres_sched_test(job_ptr->gres_list_req,
						    job_ptr->job_id)) {
					error_code = SLURM_SUCCESS;
					all_done = true;
					break;
				}
				if (topo_eval->max_nodes == 0) {
					all_done = true;
					break;
				}
			}
		}
	}
	list_iterator_destroy(iter);

	if (error_code == SLURM_SUCCESS) {
		/* Already succeeded */
	} else if ((rem_cpus > 0) || (min_rem_nodes > 0) ||
		   !gres_sched_test(job_ptr->gres_list_req, job_ptr->job_id)) {
		error_code = SLURM_ERROR;
		topo_eval->eval_action = EVAL_ACTION_RETRY_DEFAULT;
	} else {
		error_code = SLURM_SUCCESS;
	}

fini:
	if (error_code == SLURM_SUCCESS)
		eval_nodes_clip_socket_cores(topo_eval);
	FREE_NULL_LIST(node_weight_list);
	FREE_NULL_BITMAP(orig_node_map);
	return error_code;
}

static int _eval_nodes_consec(topology_eval_t *topo_eval)
{
	int i, error_code = SLURM_ERROR;
	int *consec_cpus;	/* how many CPUs we can add from this
				 * consecutive set of nodes */
	list_t **consec_gres;	/* how many GRES we can add from this
				 * consecutive set of nodes */
	int *consec_nodes;	/* how many nodes we can add from this
				 * consecutive set of nodes */
	int *consec_start;	/* where this consecutive set starts (index) */
	int *consec_end;	/* where this consecutive set ends (index) */
	int *consec_req;	/* are nodes from this set required
				 * (in req_bitmap) */
	uint64_t *consec_weight; /* node scheduling weight */
	node_record_t *node_ptr = NULL;
	int consec_index, consec_size, sufficient;
	int rem_cpus, rem_nodes; /* remaining resources desired */
	int min_rem_nodes;	/* remaining resources desired */
	int best_fit_nodes, best_fit_cpus, best_fit_req;
	int best_fit_sufficient, best_fit_index = 0;
	bool new_best;
	uint64_t best_weight = 0;
	int64_t rem_max_cpus;
	int total_cpus = 0;	/* #CPUs allocated to job */
	bool required_node;
	avail_res_t **avail_res_array = topo_eval->avail_res_array;
	job_record_t *job_ptr = topo_eval->job_ptr;
	job_details_t *details_ptr = job_ptr->details;
	bitstr_t *req_map = details_ptr->req_node_bitmap;
	uint32_t min_nodes = topo_eval->min_nodes;
	uint32_t req_nodes = topo_eval->req_nodes;
	uint16_t *avail_cpu_per_node = NULL;
	uint64_t maxtasks;

	topo_eval->avail_cpus = 0;

	/* make allocation for 50 sets of consecutive nodes, expand as needed */
	consec_size = 50;
	consec_cpus   = xcalloc(consec_size, sizeof(int));
	consec_nodes  = xcalloc(consec_size, sizeof(int));
	consec_start  = xcalloc(consec_size, sizeof(int));
	consec_end    = xcalloc(consec_size, sizeof(int));
	consec_req    = xcalloc(consec_size, sizeof(int));
	consec_weight = xcalloc(consec_size, sizeof(uint64_t));

	/* Build table with information about sets of consecutive nodes */
	consec_index = 0;
	consec_req[consec_index] = -1;	/* no required nodes here by default */
	consec_weight[consec_index] = NO_VAL64;

	avail_cpu_per_node = xcalloc(node_record_count, sizeof(uint16_t));
	rem_cpus = details_ptr->min_cpus;
	min_rem_nodes = min_nodes;
	if ((topo_eval->gres_per_job =
	     gres_sched_init(job_ptr->gres_list_req))) {
		rem_nodes = MIN(min_nodes, req_nodes);
		consec_gres = xcalloc(consec_size, sizeof(list_t *));
	} else
		rem_nodes = MAX(min_nodes, req_nodes);
	rem_max_cpus = eval_nodes_get_rem_max_cpus(details_ptr, rem_nodes);
	maxtasks = eval_nodes_set_max_tasks(job_ptr, rem_max_cpus,
					    topo_eval->max_nodes);

	/*
	 * If there are required nodes, first determine the resources they
	 * provide, then select additional resources as needed in next loop
	 */
	if (req_map) {
		int count = 0;
		uint16_t *arbitrary_tpn = job_ptr->details->arbitrary_tpn;
		for (i = 0;
		     ((node_ptr = next_node_bitmap(req_map, &i)) &&
		      (topo_eval->max_nodes > 0));
		     i++) {
			eval_nodes_select_cores(topo_eval, i, min_rem_nodes);
			if (arbitrary_tpn) {
				int req_cpus = arbitrary_tpn[count++];
				hres_select_t *hres_select =
					topo_eval->job_ptr->hres_select;
				if ((details_ptr->cpus_per_task != NO_VAL16) &&
				    (details_ptr->cpus_per_task != 0))
					req_cpus *= details_ptr->cpus_per_task;

				req_cpus = MAX(req_cpus,
					       (int) details_ptr->pn_min_cpus);
				req_cpus = MAX(req_cpus,
					       details_ptr->min_gres_cpu);

				if (topo_eval->avail_cpus < req_cpus) {
					debug("%pJ required node %s needed %d cpus but only has %d",
					      job_ptr, node_ptr->name, req_cpus,
					      topo_eval->avail_cpus);
					topo_eval->eval_action =
						EVAL_ACTION_BREAK;
					goto fini;
				}
				topo_eval->avail_cpus = req_cpus;

				avail_res_array[i]->avail_cpus =
					topo_eval->avail_cpus;
				if (hres_select &&
				    !hres_select_check(
					    hres_select,
					    avail_res_array[i]
						    ->hres_leaf_idx)) {
					topo_eval->avail_cpus = 0;
				}
				if (topo_eval->gres_per_job) {
					eval_nodes_gres(topo_eval, &maxtasks,
							job_ptr, node_ptr,
							min_rem_nodes, i,
							(count - 1));
				}
			} else
				(void) eval_nodes_cpus_to_use(topo_eval, i,
							      rem_max_cpus,
							      min_rem_nodes,
							      &maxtasks, true);

			if (topo_eval->avail_cpus == 0) {
				debug("%pJ required node %s lacks available resources",
				      job_ptr, node_ptr->name);
				topo_eval->eval_action = EVAL_ACTION_BREAK;
				goto fini;
			}
			avail_cpu_per_node[i] = topo_eval->avail_cpus;
			total_cpus += topo_eval->avail_cpus;
			rem_cpus -= topo_eval->avail_cpus;
			rem_max_cpus -= topo_eval->avail_cpus;
			rem_nodes--;
			min_rem_nodes--;
			topo_eval->max_nodes--;
		}

		if ((rem_nodes <= 0) && (rem_cpus <= 0) &&
		    gres_sched_test(job_ptr->gres_list_req, job_ptr->job_id)) {
			error_code = SLURM_SUCCESS;
			bit_and(topo_eval->node_map, req_map);
			goto fini;
		}
		if (topo_eval->max_nodes <= 0) {
			topo_eval->eval_action = EVAL_ACTION_BREAK;
			goto fini;
		}
	}

	/* Compute CPUs already allocated to required nodes */
	if ((details_ptr->max_cpus != NO_VAL) &&
	    (total_cpus > details_ptr->max_cpus)) {
		info("%pJ can't use required nodes due to max CPU limit",
		     job_ptr);
		topo_eval->eval_action = EVAL_ACTION_BREAK;
		goto fini;
	}

	for (i = 0; next_node(&i); i++) { /* For each node */
		if ((consec_index + 1) >= consec_size) {
			consec_size *= 2;
			xrecalloc(consec_cpus, consec_size, sizeof(int));
			xrecalloc(consec_nodes, consec_size, sizeof(int));
			xrecalloc(consec_start, consec_size, sizeof(int));
			xrecalloc(consec_end, consec_size, sizeof(int));
			xrecalloc(consec_req, consec_size, sizeof(int));
			xrecalloc(consec_weight, consec_size, sizeof(uint64_t));
			if (topo_eval->gres_per_job) {
				xrecalloc(consec_gres,
					  consec_size, sizeof(list_t *));
			}
		}
		if (req_map)
			required_node = bit_test(req_map, i);
		else
			required_node = false;
		if (!bit_test(topo_eval->node_map, i)) {
			node_ptr = NULL;    /* Use as flag, avoid second test */
		} else if (required_node) {
			node_ptr = node_record_table_ptr[i];
		} else {
			node_ptr = node_record_table_ptr[i];
			eval_nodes_select_cores(topo_eval, i, min_rem_nodes);
			if (topo_eval->avail_cpus == 0) {
				bit_clear(topo_eval->node_map, i);
				node_ptr = NULL;
			}
			avail_cpu_per_node[i] = topo_eval->avail_cpus;
		}
		/*
		 * If job requested contiguous nodes,
		 * do not worry about matching node weights
		 */
		if (node_ptr &&
		    !details_ptr->contiguous &&
		    (consec_weight[consec_index] != NO_VAL64) && /* Init value*/
		    (node_ptr->sched_weight != consec_weight[consec_index])) {
			/* End last consecutive set, setup start of next set */
			if (consec_nodes[consec_index] == 0) {
				/* Only required nodes, reuse consec record */
				consec_req[consec_index] = -1;
			} else {
				/* End last set, setup for start of next set */
				consec_end[consec_index]   = i - 1;
				consec_req[++consec_index] = -1;
			}
		}
		if (node_ptr) {
			if (consec_nodes[consec_index] == 0)
				consec_start[consec_index] = i;
			if (required_node) {
				/*
				 * Required node, resources counters updated
				 * in above loop, leave bitmap set
				 */
				if (consec_req[consec_index] == -1) {
					/* first required node in set */
					consec_req[consec_index] = i;
				}
				continue;
			}

			/* node not selected (yet) */
			bit_clear(topo_eval->node_map, i);
			consec_cpus[consec_index] += topo_eval->avail_cpus;
			consec_nodes[consec_index]++;
			if (topo_eval->gres_per_job) {
				gres_sched_consec(
					&consec_gres[consec_index],
					job_ptr->gres_list_req,
					avail_res_array[i]->sock_gres_list);
			}
			consec_weight[consec_index] = node_ptr->sched_weight;
		} else if (consec_nodes[consec_index] == 0) {
			/* Only required nodes, reuse consec record */
			consec_req[consec_index] = -1;
			consec_weight[consec_index] = NO_VAL64;
		} else {
			/* End last set, setup for start of next set */
			consec_end[consec_index]   = i - 1;
			consec_req[++consec_index] = -1;
			consec_weight[consec_index] = NO_VAL64;
		}
	}
	if (consec_nodes[consec_index] != 0)
		consec_end[consec_index++] = i - 1;

	if (slurm_conf.debug_flags & DEBUG_FLAG_SELECT_TYPE) {
		if (consec_index == 0) {
			info("consec_index is zero");
		}
		for (i = 0; i < consec_index; i++) {
			char *gres_str = NULL, *gres_print = "";
			bitstr_t *host_bitmap;
			char *host_list;
			if (topo_eval->gres_per_job) {
				gres_str = gres_sched_str(consec_gres[i]);
				if (gres_str) {
					xstrcat(gres_str, " ");
					gres_print = gres_str;
				}
			}

			host_bitmap = bit_alloc(node_record_count);
			bit_nset(host_bitmap, consec_start[i], consec_end[i]);
			host_list = bitmap2node_name(host_bitmap);
			info("set:%d consec CPUs:%d nodes:%d:%s %sbegin:%d end:%d required:%d weight:%"PRIu64,
			     i, consec_cpus[i], consec_nodes[i],
			     host_list, gres_print, consec_start[i],
			     consec_end[i], consec_req[i], consec_weight[i]);
			FREE_NULL_BITMAP(host_bitmap);
			xfree(gres_str);
			xfree(host_list);
		}
	}

	/*
	 * accumulate nodes from these sets of consecutive nodes until
	 * sufficient resources have been accumulated
	 */
	while (consec_index && (topo_eval->max_nodes > 0)) {
		best_fit_cpus = best_fit_nodes = best_fit_sufficient = 0;
		best_fit_req = -1;	/* first required node, -1 if none */
		for (i = 0; i < consec_index; i++) {
			if (consec_nodes[i] == 0)
				continue;	/* no usable nodes here */

			if (details_ptr->contiguous &&
			    details_ptr->req_node_bitmap &&
			    (consec_req[i] == -1))
				continue;  /* not required nodes */
			sufficient = (consec_cpus[i] >= rem_cpus) &&
				     eval_nodes_enough_nodes(
					     consec_nodes[i], rem_nodes,
					     min_nodes, req_nodes);
			if (sufficient && topo_eval->gres_per_job) {
				sufficient = gres_sched_sufficient(
					job_ptr->gres_list_req, consec_gres[i]);
			}

			/*
			 * if first possibility OR
			 * contains required nodes OR
			 * lowest node weight
			 */
			if ((best_fit_nodes == 0) ||
			    ((best_fit_req == -1) && (consec_req[i] != -1)) ||
			    (consec_weight[i] < best_weight))
				new_best = true;
			else
				new_best = false;
			/*
			 * If equal node weight
			 * first set large enough for request OR
			 * tightest fit (less resource/CPU waste) OR
			 * nothing yet large enough, but this is biggest
			 */
			if (!new_best && (consec_weight[i] == best_weight) &&
			    ((sufficient && (best_fit_sufficient == 0)) ||
			     (sufficient && (consec_cpus[i] < best_fit_cpus)) ||
			     (!sufficient &&
			      (consec_cpus[i] > best_fit_cpus))))
				new_best = true;
			/*
			 * if first continuous node set large enough
			 */
			if (!new_best && !best_fit_sufficient &&
			    details_ptr->contiguous && sufficient)
				new_best = true;
			if (new_best) {
				best_fit_cpus = consec_cpus[i];
				best_fit_nodes = consec_nodes[i];
				best_fit_index = i;
				best_fit_req = consec_req[i];
				best_fit_sufficient = sufficient;
				best_weight = consec_weight[i];
			}

			if (details_ptr->contiguous &&
			    details_ptr->req_node_bitmap) {
				/*
				 * Must wait for all required nodes to be
				 * in a single consecutive block
				 */
				int j, other_blocks = 0;
				for (j = (i+1); j < consec_index; j++) {
					if (consec_req[j] != -1) {
						other_blocks = 1;
						break;
					}
				}
				if (other_blocks) {
					best_fit_nodes = 0;
					break;
				}
			}
		}
		if (best_fit_nodes == 0)
			break;

		if (details_ptr->contiguous && !best_fit_sufficient)
			break;	/* no hole large enough */
		if (best_fit_req != -1) {
			/*
			 * This collection of nodes includes required ones
			 * select nodes from this set, first working up
			 * then down from the required nodes
			 */
			for (i = best_fit_req;
			     i <= consec_end[best_fit_index]; i++) {
				if ((topo_eval->max_nodes == 0) ||
				    ((rem_nodes <= 0) && (rem_cpus <= 0) &&
				     (!topo_eval->gres_per_job ||
				      gres_sched_test(job_ptr->gres_list_req,
						      job_ptr->job_id))))
					break;
				if (bit_test(topo_eval->node_map, i)) {
					/* required node already in set */
					continue;
				}
				if (avail_cpu_per_node[i] == 0)
					continue;
				topo_eval->avail_cpus = avail_cpu_per_node[i];

				/*
				 * This could result in 0, but if the user
				 * requested nodes here we will still give
				 * them and then the step layout will sort
				 * things out. But if the gres's cpu requirement
				 * can not be satisfied due to HRes or gres
				 * layout try next node.
				 */
				if (!eval_nodes_cpus_to_use(topo_eval, i,
							    rem_max_cpus,
							    min_rem_nodes,
							    &maxtasks, true))
					continue;

				total_cpus += topo_eval->avail_cpus;
				bit_set(topo_eval->node_map, i);
				rem_nodes--;
				min_rem_nodes--;
				topo_eval->max_nodes--;
				rem_cpus -= topo_eval->avail_cpus;
				rem_max_cpus -= topo_eval->avail_cpus;
			}
			for (i = (best_fit_req - 1);
			     i >= consec_start[best_fit_index]; i--) {
				if ((topo_eval->max_nodes == 0) ||
				    ((rem_nodes <= 0) && (rem_cpus <= 0) &&
				     (!topo_eval->gres_per_job ||
				      gres_sched_test(job_ptr->gres_list_req,
						      job_ptr->job_id))))
					break;
				if (bit_test(topo_eval->node_map, i))
					continue;
				if (avail_cpu_per_node[i] == 0)
					continue;
				topo_eval->avail_cpus = avail_cpu_per_node[i];

				/*
				 * This could result in 0, but if the user
				 * requested nodes here we will still give
				 * them and then the step layout will sort
				 * things out. But if the gres's cpu requirement
				 * can not be satisfied due to HRes or gres
				 * layout try next node.
				 */
				if (!eval_nodes_cpus_to_use(topo_eval, i,
							    rem_max_cpus,
							    min_rem_nodes,
							    &maxtasks, true))
					continue;

				total_cpus += topo_eval->avail_cpus;
				rem_cpus -= topo_eval->avail_cpus;
				rem_max_cpus -= topo_eval->avail_cpus;
				bit_set(topo_eval->node_map, i);
				rem_nodes--;
				min_rem_nodes--;
				topo_eval->max_nodes--;
			}
		} else {
			/* No required nodes, try best fit single node */
			int best_fit = -1, best_size = 0;
			int first = consec_start[best_fit_index];
			int last  = consec_end[best_fit_index];
			if (rem_nodes <= 1) {
				for (i = first; i <= last; i++) {
					if (bit_test(topo_eval->node_map, i) ||
					    !avail_res_array[i])
						continue;
					if (avail_cpu_per_node[i] < rem_cpus)
						continue;
					if (topo_eval->gres_per_job &&
					    !gres_sched_sufficient(
						    job_ptr->gres_list_req,
						    avail_res_array[i]->
						    sock_gres_list)) {
						continue;
					}
					if ((best_fit == -1) ||
					    (avail_cpu_per_node[i] <best_size)){
						best_fit = i;
						best_size =
							avail_cpu_per_node[i];
						if (best_size == rem_cpus)
							break;
					}
				}
				/*
				 * If we found a single node to use,
				 * clear CPU counts for all other nodes
				 */
				if (best_fit != -1) {
					for (i = first; i <= last; i++) {
						if (i == best_fit)
							continue;
						avail_cpu_per_node[i] = 0;
					}
				}
			}

			for (i = first; i <= last; i++) {
				if ((topo_eval->max_nodes == 0) ||
				    ((rem_nodes <= 0) && (rem_cpus <= 0) &&
				     (!topo_eval->gres_per_job ||
				      gres_sched_test(job_ptr->gres_list_req,
						      job_ptr->job_id))))
					break;
				if (bit_test(topo_eval->node_map, i) ||
				    !avail_res_array[i])
					continue;

				topo_eval->avail_cpus = avail_cpu_per_node[i];
				if (topo_eval->avail_cpus <= 0)
					continue;

				if ((topo_eval->max_nodes == 1) &&
				    (topo_eval->avail_cpus < rem_cpus)) {
					/*
					 * Job can only take one more node and
					 * this one has insufficient CPU
					 */
					continue;
				}

				/*
				 * This could result in 0, but if the user
				 * requested nodes here we will still give
				 * them and then the step layout will sort
				 * things out. But if the gres's cpu requirement
				 * can not be satisfied due to HRes or
				 * gres layout try next node.
				 */
				if (!eval_nodes_cpus_to_use(topo_eval, i,
							    rem_max_cpus,
							    min_rem_nodes,
							    &maxtasks, true))
					continue;

				total_cpus += topo_eval->avail_cpus;
				rem_cpus -= topo_eval->avail_cpus;
				rem_max_cpus -= topo_eval->avail_cpus;
				bit_set(topo_eval->node_map, i);
				rem_nodes--;
				min_rem_nodes--;
				topo_eval->max_nodes--;
			}
		}

		if ((rem_nodes <= 0) && (rem_cpus <= 0) &&
		    gres_sched_test(job_ptr->gres_list_req, job_ptr->job_id)) {
			error_code = SLURM_SUCCESS;
			break;
		}
		consec_cpus[best_fit_index] = 0;
		consec_nodes[best_fit_index] = 0;
	}

	if (error_code && (rem_cpus <= 0) &&
	    gres_sched_test(job_ptr->gres_list_req, job_ptr->job_id) &&
	    eval_nodes_enough_nodes(0, rem_nodes, min_nodes, req_nodes))
		error_code = SLURM_SUCCESS;
	else
		topo_eval->eval_action = EVAL_ACTION_RETRY_DEFAULT;

fini:
	if (error_code == SLURM_SUCCESS)
		eval_nodes_clip_socket_cores(topo_eval);
	xfree(avail_cpu_per_node);
	xfree(consec_cpus);
	xfree(consec_nodes);
	xfree(consec_start);
	xfree(consec_end);
	xfree(consec_req);
	xfree(consec_weight);
	if (topo_eval->gres_per_job) {
		for (i = 0; i < consec_size; i++)
			FREE_NULL_LIST(consec_gres[i]);
		xfree(consec_gres);
	}

	return error_code;
}

static int _eval_nodes_lln(topology_eval_t *topo_eval)
{
	int i, i_start, i_end, error_code = SLURM_ERROR;
	int rem_cpus, rem_nodes; /* remaining resources desired */
	int min_rem_nodes;	/* remaining resources desired */
	int total_cpus = 0;	/* #CPUs allocated to job */
	int64_t rem_max_cpus;
	job_record_t *job_ptr = topo_eval->job_ptr;
	job_details_t *details_ptr = job_ptr->details;
	bitstr_t *req_map = details_ptr->req_node_bitmap;
	bitstr_t *orig_node_map = bit_copy(topo_eval->node_map);
	bool all_done = false;
	node_record_t *node_ptr;
	list_t *node_weight_list = NULL;
	node_weight_type *nwt;
	list_itr_t *iter;
	avail_res_t **avail_res_array = topo_eval->avail_res_array;
	uint32_t min_nodes = topo_eval->min_nodes;
	uint32_t req_nodes = topo_eval->req_nodes;
	uint64_t maxtasks;

	if (details_ptr->arbitrary_tpn) {
		info("does not support arbitrary tasks distribution");
		error_code = ESLURM_NOT_SUPPORTED;
		goto fini;
	}

	topo_eval->avail_cpus = 0;

	rem_cpus = details_ptr->min_cpus;
	min_rem_nodes = min_nodes;
	if ((details_ptr->num_tasks != NO_VAL) &&
	    (details_ptr->num_tasks != 0))
		topo_eval->max_nodes = MIN(topo_eval->max_nodes,
					   details_ptr->num_tasks);
	if ((topo_eval->gres_per_job = gres_sched_init(job_ptr->gres_list_req)))
		rem_nodes = MIN(min_nodes, req_nodes);
	else
		rem_nodes = MAX(min_nodes, req_nodes);
	rem_max_cpus = eval_nodes_get_rem_max_cpus(details_ptr, rem_nodes);
	maxtasks = eval_nodes_set_max_tasks(job_ptr, rem_max_cpus,
					    topo_eval->max_nodes);

	i_start = bit_ffs(topo_eval->node_map);
	if (i_start >= 0)
		i_end = bit_fls(topo_eval->node_map);
	else
		i_end = i_start - 1;
	if (req_map) {
		for (i = i_start; i <= i_end; i++) {
			if (!bit_test(req_map, i)) {
				bit_clear(topo_eval->node_map, i);
				continue;
			}
			node_ptr = node_record_table_ptr[i];
			if (!avail_res_array[i] ||
			    !avail_res_array[i]->avail_cpus) {
				debug("%pJ required node %s lacks available resources",
				      job_ptr, node_ptr->name);
				topo_eval->eval_action = EVAL_ACTION_BREAK;
				goto fini;
			}
			if (topo_eval->max_nodes <= 0) {
				log_flag(SELECT_TYPE, "%pJ requires nodes exceed maximum node limit",
					 job_ptr);
				topo_eval->eval_action = EVAL_ACTION_BREAK;
				goto fini;
			}
			eval_nodes_select_cores(topo_eval, i, min_rem_nodes);
			(void) eval_nodes_cpus_to_use(topo_eval, i,
						      rem_max_cpus,
						      min_rem_nodes,
						      &maxtasks, true);
			if (topo_eval->avail_cpus <= 0) {
				debug("%pJ required node %s not available",
				      job_ptr, node_ptr->name);
				topo_eval->eval_action = EVAL_ACTION_BREAK;
				goto fini;
			}
			total_cpus += topo_eval->avail_cpus;
			rem_cpus   -= topo_eval->avail_cpus;
			rem_max_cpus -= topo_eval->avail_cpus;
			rem_nodes--;
			min_rem_nodes--;
			/* leaving bitmap set, decr max limit */
			topo_eval->max_nodes--;
		}
		if ((rem_nodes <= 0) && (rem_cpus <= 0) &&
		    gres_sched_test(job_ptr->gres_list_req, job_ptr->job_id)) {
			error_code = SLURM_SUCCESS;
			bit_and(topo_eval->node_map, req_map);
			goto fini;
		}
		if (topo_eval->max_nodes <= 0) {
			topo_eval->eval_action = EVAL_ACTION_BREAK;
			goto fini;
		}
		bit_and_not(orig_node_map, topo_eval->node_map);
	} else {
		bit_clear_all(topo_eval->node_map);
	}

	/* Compute CPUs already allocated to required nodes */
	if ((details_ptr->max_cpus != NO_VAL) &&
	    (total_cpus > details_ptr->max_cpus)) {
		topo_eval->eval_action = EVAL_ACTION_BREAK;
		info("%pJ can't use required nodes due to max CPU limit",
		     job_ptr);
		goto fini;
	}

	/*
	 * Accumulate nodes from those with highest available CPU count.
	 * Logic is optimized for small node/CPU count allocations.
	 * For larger allocation, use list_sort().
	 */
	if (topo_eval->max_nodes == 0)
		all_done = true;
	node_weight_list = _build_node_weight_list(orig_node_map);
	iter = list_iterator_create(node_weight_list);
	while (!all_done && (nwt = list_next(iter))) {
		int last_max_cpu_cnt = -1;
		while (!all_done) {
			int max_cpu_idx = -1;
			for (i = i_start; i <= i_end; i++) {
				/* Node not available or already selected */
				if (!bit_test(nwt->node_bitmap, i) ||
				    bit_test(topo_eval->node_map, i))
					continue;
				if (!avail_res_array[i] ||
				    !avail_res_array[i]->avail_cpus)
					continue;
				eval_nodes_select_cores(topo_eval, i,
							min_rem_nodes);
				if (topo_eval->avail_cpus == 0)
					continue;
				/*
				 * Find the "least-loaded" node at the current
				 * node-weight level. This is defined as the
				 * node with the greatest ratio of available to
				 * total cpus. (But shift the divisors around
				 * to avoid any floating-point math.)
				 */
				if ((max_cpu_idx == -1) ||
				    ((avail_res_array[max_cpu_idx]->max_cpus *
				      node_record_table_ptr[i]->cpus) <
				     (avail_res_array[i]->max_cpus *
				      node_record_table_ptr[max_cpu_idx]->
				      cpus))) {
					max_cpu_idx = i;
					if (avail_res_array[max_cpu_idx]->
					    max_cpus == last_max_cpu_cnt)
						break;
				}
			}
			if (max_cpu_idx == -1) {
				/* No more usable nodes left, get next weight */
				break;
			}
			i = max_cpu_idx;

			(void) eval_nodes_cpus_to_use(topo_eval, i,
						      rem_max_cpus,
						      min_rem_nodes, &maxtasks,
						      true);
			if (topo_eval->avail_cpus == 0) {
				bit_clear(nwt->node_bitmap, i);
				continue;
			}

			last_max_cpu_cnt = avail_res_array[i]->max_cpus;
			total_cpus += topo_eval->avail_cpus;
			rem_cpus -= topo_eval->avail_cpus;
			rem_max_cpus -= topo_eval->avail_cpus;
			rem_nodes--;
			min_rem_nodes--;
			topo_eval->max_nodes--;
			bit_set(topo_eval->node_map, i);
			if ((rem_nodes <= 0) && (rem_cpus <= 0) &&
			    gres_sched_test(job_ptr->gres_list_req,
					    job_ptr->job_id)) {
				error_code = SLURM_SUCCESS;
				all_done = true;
				break;
			}
			if (topo_eval->max_nodes == 0) {
				all_done = true;
				break;
			}
		}
	}
	list_iterator_destroy(iter);

	if (error_code == SLURM_SUCCESS) {
		/* Already succeeded */
	} else if ((rem_cpus > 0) || (min_rem_nodes > 0) ||
		   !gres_sched_test(job_ptr->gres_list_req, job_ptr->job_id)) {
		error_code = SLURM_ERROR;
		topo_eval->eval_action = EVAL_ACTION_RETRY_DEFAULT;
	} else {
		error_code = SLURM_SUCCESS;
	}

fini:
	if (error_code == SLURM_SUCCESS)
		eval_nodes_clip_socket_cores(topo_eval);
	FREE_NULL_LIST(node_weight_list);
	FREE_NULL_BITMAP(orig_node_map);
	return error_code;
}

/*
 * A variation of _eval_nodes() to select resources at the end of the node
 * list to reduce fragmentation
 */
static int _eval_nodes_serial(topology_eval_t *topo_eval)
{
	int i, i_start, i_end, error_code = SLURM_ERROR;
	int rem_cpus, rem_nodes; /* remaining resources desired */
	int min_rem_nodes;	/* remaining resources desired */
	int total_cpus = 0;	/* #CPUs allocated to job */
	int64_t rem_max_cpus;
	job_record_t *job_ptr = topo_eval->job_ptr;
	job_details_t *details_ptr = job_ptr->details;
	bitstr_t *req_map = details_ptr->req_node_bitmap;
	bitstr_t *orig_node_map = bit_copy(topo_eval->node_map);
	avail_res_t **avail_res_array = topo_eval->avail_res_array;
	uint32_t min_nodes = topo_eval->min_nodes;
	uint32_t req_nodes = topo_eval->req_nodes;
	bool all_done = false;
	node_record_t *node_ptr;
	list_t *node_weight_list = NULL;
	node_weight_type *nwt;
	list_itr_t *iter;
	uint64_t maxtasks;

	if (details_ptr->arbitrary_tpn) {
		info("does not support arbitrary tasks distribution");
		error_code = ESLURM_NOT_SUPPORTED;
		goto fini;
	}

	topo_eval->avail_cpus = 0;

	rem_cpus = details_ptr->min_cpus;
	min_rem_nodes = min_nodes;
	if ((details_ptr->num_tasks != NO_VAL) &&
	    (details_ptr->num_tasks != 0))
		topo_eval->max_nodes = MIN(topo_eval->max_nodes,
					   details_ptr->num_tasks);
	if ((topo_eval->gres_per_job = gres_sched_init(job_ptr->gres_list_req)))
		rem_nodes = MIN(min_nodes, req_nodes);
	else
		rem_nodes = MAX(min_nodes, req_nodes);
	rem_max_cpus = eval_nodes_get_rem_max_cpus(details_ptr, rem_nodes);
	maxtasks = eval_nodes_set_max_tasks(job_ptr, rem_max_cpus,
					    topo_eval->max_nodes);

	i_start = bit_ffs(topo_eval->node_map);
	if (i_start >= 0)
		i_end = bit_fls(topo_eval->node_map);
	else
		i_end = i_start - 1;
	if (req_map) {
		for (i = i_start; i <= i_end; i++) {
			if (!bit_test(req_map, i)) {
				bit_clear(topo_eval->node_map, i);
				continue;
			}
			node_ptr = node_record_table_ptr[i];
			if (!avail_res_array[i] ||
			    !avail_res_array[i]->avail_cpus) {
				debug("%pJ required node %s lacks available resources",
				      job_ptr, node_ptr->name);
				topo_eval->eval_action = EVAL_ACTION_BREAK;
				goto fini;
			}
			if (topo_eval->max_nodes <= 0) {
				log_flag(SELECT_TYPE, "%pJ requires nodes exceed maximum node limit",
					 job_ptr);
				topo_eval->eval_action = EVAL_ACTION_BREAK;
				goto fini;
			}
			eval_nodes_select_cores(topo_eval, i, min_rem_nodes);
			(void) eval_nodes_cpus_to_use(topo_eval, i,
						      rem_max_cpus, min_rem_nodes,
						      &maxtasks, true);
			if (topo_eval->avail_cpus <= 0) {
				debug("%pJ required node %s lacks available resources",
				      job_ptr, node_ptr->name);
				topo_eval->eval_action = EVAL_ACTION_BREAK;
				goto fini;
			}
			total_cpus += topo_eval->avail_cpus;
			rem_cpus   -= topo_eval->avail_cpus;
			rem_max_cpus -= topo_eval->avail_cpus;
			rem_nodes--;
			min_rem_nodes--;
			/* leaving bitmap set, decr max limit */
			topo_eval->max_nodes--;
		}
		if ((rem_nodes <= 0) && (rem_cpus <= 0) &&
		    gres_sched_test(job_ptr->gres_list_req, job_ptr->job_id)) {
			error_code = SLURM_SUCCESS;
			bit_and(topo_eval->node_map, req_map);
			goto fini;
		}
		if (topo_eval->max_nodes <= 0) {
			topo_eval->eval_action = EVAL_ACTION_BREAK;
			goto fini;
		}
		bit_and_not(orig_node_map, topo_eval->node_map);
	} else {
		bit_clear_all(topo_eval->node_map);
	}

	/* Compute CPUs already allocated to required nodes */
	if ((details_ptr->max_cpus != NO_VAL) &&
	    (total_cpus > details_ptr->max_cpus)) {
		info("%pJ can't use required nodes due to max CPU limit",
		     job_ptr);
		topo_eval->eval_action = EVAL_ACTION_BREAK;
		goto fini;
	}

	if (topo_eval->max_nodes == 0)
		all_done = true;
	node_weight_list = _build_node_weight_list(orig_node_map);
	iter = list_iterator_create(node_weight_list);
	while (!all_done && (nwt = list_next(iter))) {
		for (i = i_end;
		     ((i >= i_start) && (topo_eval->max_nodes > 0));
		     i--) {
			if (!avail_res_array[i] ||
			    !avail_res_array[i]->avail_cpus)
				continue;
			/* Node not available or already selected */
			if (!bit_test(nwt->node_bitmap, i) ||
			    bit_test(topo_eval->node_map, i))
				continue;
			eval_nodes_select_cores(topo_eval, i, min_rem_nodes);
			(void) eval_nodes_cpus_to_use(topo_eval, i,
						      rem_max_cpus, min_rem_nodes,
						      &maxtasks, true);
			if (topo_eval->avail_cpus == 0)
				continue;
			total_cpus += topo_eval->avail_cpus;
			rem_cpus -= topo_eval->avail_cpus;
			rem_max_cpus -= topo_eval->avail_cpus;
			rem_nodes--;
			min_rem_nodes--;
			topo_eval->max_nodes--;
			bit_set(topo_eval->node_map, i);
			if ((rem_nodes <= 0) && (rem_cpus <= 0) &&
			    gres_sched_test(job_ptr->gres_list_req,
					    job_ptr->job_id)) {
				error_code = SLURM_SUCCESS;
				all_done = true;
				break;
			}
			if (topo_eval->max_nodes == 0) {
				all_done = true;
				break;
			}
		}
	}
	list_iterator_destroy(iter);

	if (error_code == SLURM_SUCCESS) {
		/* Already succeeded */
	} else if ((rem_cpus > 0) || (min_rem_nodes > 0) ||
		   !gres_sched_test(job_ptr->gres_list_req, job_ptr->job_id)) {
		error_code = SLURM_ERROR;
		topo_eval->eval_action = EVAL_ACTION_RETRY_DEFAULT;
	} else {
		error_code = SLURM_SUCCESS;
	}

fini:
	if (error_code == SLURM_SUCCESS)
		eval_nodes_clip_socket_cores(topo_eval);
	FREE_NULL_LIST(node_weight_list);
	FREE_NULL_BITMAP(orig_node_map);
	return error_code;
}

static int _add_nodes_by_weight_spread(void *x, void *arg)
{
	foreach_add_nodes_lln_t *args = arg;
	node_weight_type *nwt = x;

	for (int i = args->i_start; i <= args->i_end; i++) {
		if (!args->avail_res_array[i] ||
		    !args->avail_res_array[i]->avail_cpus)
			continue;
		/* Node not available or already selected */
		if (!bit_test(nwt->node_bitmap, i) ||
		    bit_test(args->node_map, i))
			continue;
		if (!args->avail_cpu_per_node[i])
			continue;

		bit_set(args->node_map, i);
		args->used_cpu_per_node[i] = args->cpus_per_task;

		(*args->rem_nodes)--;
		(*args->min_rem_nodes)--;
		(*args->max_nodes)--;
		*args->rem_max_cpus -= args->cpus_per_task;
		*args->rem_cpus -= args->cpus_per_task;
		if ((*args->max_nodes <= 0) || (*args->rem_nodes <= 0))
			return 1;
	}
	return 0;
}
/*
 * A variation of _eval_nodes() to select resources using as many nodes as
 * possible.
 */
static int _eval_nodes_spread(topology_eval_t *topo_eval)
{
	int i, i_start, i_end, error_code = SLURM_ERROR;
	int rem_cpus, rem_nodes; /* remaining resources desired */
	int min_rem_nodes, orig_min_rem_nodes; /* remaining resources desired */
	int64_t rem_max_cpus, orig_rem_max_cpus;
	avail_res_t **avail_res_array = topo_eval->avail_res_array;
	job_record_t *job_ptr = topo_eval->job_ptr;
	job_details_t *details_ptr = job_ptr->details;
	bitstr_t *req_map = details_ptr->req_node_bitmap;
	bitstr_t *orig_node_map = bit_copy(topo_eval->node_map);
	uint32_t min_nodes = topo_eval->min_nodes;
	uint32_t req_nodes = topo_eval->req_nodes;
	uint32_t cpus_per_task = job_ptr->details->cpus_per_task;
	bool all_done = false;
	node_record_t *node_ptr;
	list_t *node_weight_list = NULL;
	uint64_t maxtasks;
	uint16_t *avail_cpu_per_node = NULL;
	uint16_t *used_cpu_per_node = NULL;
	uint32_t prev_max_nodes = topo_eval->max_nodes;
	foreach_add_nodes_lln_t args = { 0 };

	if (details_ptr->arbitrary_tpn) {
		info("does not support arbitrary tasks distribution");
		error_code = ESLURM_NOT_SUPPORTED;
		goto fini;
	}

	topo_eval->avail_cpus = 0;

	rem_cpus = details_ptr->min_cpus;
	min_rem_nodes = min_nodes;
	orig_min_rem_nodes = min_rem_nodes;
	if ((details_ptr->num_tasks != NO_VAL) &&
	    (details_ptr->num_tasks != 0))
		topo_eval->max_nodes =
			MIN(topo_eval->max_nodes, details_ptr->num_tasks);
	if ((topo_eval->gres_per_job = gres_sched_init(job_ptr->gres_list_req)))
		rem_nodes = MIN(min_nodes, req_nodes);
	else
		rem_nodes = MAX(min_nodes, req_nodes);
	rem_max_cpus = eval_nodes_get_rem_max_cpus(details_ptr, rem_nodes);
	orig_rem_max_cpus = rem_max_cpus;
	maxtasks = eval_nodes_set_max_tasks(job_ptr, rem_max_cpus,
					    topo_eval->max_nodes);

	avail_cpu_per_node = xcalloc(node_record_count, sizeof(uint16_t));
	used_cpu_per_node = xcalloc(node_record_count, sizeof(uint16_t));
	for (i = 0; (node_ptr = next_node_bitmap(orig_node_map, &i)); i++) {
		node_ptr = node_record_table_ptr[i];
		eval_nodes_select_cores(topo_eval, i, min_rem_nodes);
		if (topo_eval->avail_cpus == 0)
			bit_clear(topo_eval->node_map, i);
		avail_cpu_per_node[i] = topo_eval->avail_cpus;
	}

	i_start = bit_ffs(topo_eval->node_map);
	if (i_start >= 0)
		i_end = bit_fls(topo_eval->node_map);
	else
		i_end = i_start - 1;
	if (req_map) {
		for (i = i_start; i <= i_end; i++) {
			if (!bit_test(req_map, i)) {
				bit_clear(topo_eval->node_map, i);
				continue;
			}
			node_ptr = node_record_table_ptr[i];
			if (!avail_res_array[i] ||
			    !avail_res_array[i]->avail_cpus) {
				debug("%pJ required node %s lacks available resources",
				      job_ptr, node_ptr->name);
				topo_eval->eval_action = EVAL_ACTION_BREAK;
				goto fini;
			}
			if (topo_eval->max_nodes <= 0) {
				log_flag(SELECT_TYPE, "%pJ requires nodes exceed maximum node limit",
					 job_ptr);
				topo_eval->eval_action = EVAL_ACTION_BREAK;
				goto fini;
			}
			used_cpu_per_node[i] = cpus_per_task;

			rem_nodes--;
			min_rem_nodes--;
			topo_eval->max_nodes--;
			rem_max_cpus -= cpus_per_task;
			rem_cpus -= cpus_per_task;
		}
		bit_and_not(orig_node_map, topo_eval->node_map);
	} else {
		bit_clear_all(topo_eval->node_map);
	}

	if (topo_eval->max_nodes > 0) {
		node_weight_list = _build_node_weight_list(orig_node_map);

		args.avail_res_array = avail_res_array;
		args.node_map = topo_eval->node_map;
		args.avail_cpu_per_node = avail_cpu_per_node;
		args.used_cpu_per_node = used_cpu_per_node;
		args.rem_nodes = &rem_nodes;
		args.min_rem_nodes = &min_rem_nodes;
		args.max_nodes = &(topo_eval->max_nodes);
		args.rem_max_cpus = &rem_max_cpus;
		args.rem_cpus = &rem_cpus;
		args.i_start = i_start;
		args.i_end = i_end;
		args.cpus_per_task = cpus_per_task;
more_nodes:
		list_for_each(node_weight_list, _add_nodes_by_weight_spread,
			      &args);
	}

	if (rem_cpus <= 0)
		all_done = true;

	while (!all_done) {
		all_done = true;
		for (i = 0;
		     (node_ptr = next_node_bitmap(topo_eval->node_map, &i));
		     i++) {
			if (used_cpu_per_node[i] >= avail_cpu_per_node[i])
				continue;

			used_cpu_per_node[i] += cpus_per_task;
			rem_max_cpus -= cpus_per_task;
			rem_cpus -= cpus_per_task;

			if (rem_cpus <= 0) {
				all_done = true;
				break;
			} else {
				all_done = false;
			}
		}
	}
	if ((rem_cpus > 0 ||
	     !gres_sched_test(job_ptr->gres_list_req, job_ptr->job_id)) &&
	    (topo_eval->max_nodes > 0) &&
	    (prev_max_nodes != topo_eval->max_nodes)) {
		if (!rem_nodes)
			rem_nodes++;
		prev_max_nodes = topo_eval->max_nodes;
		all_done = false;
		goto more_nodes;
	}
	rem_max_cpus = orig_rem_max_cpus;
	rem_cpus = details_ptr->min_cpus;
	min_rem_nodes = orig_min_rem_nodes;
	for (i = 0; (node_ptr = next_node_bitmap(topo_eval->node_map, &i));
	     i++) {
		topo_eval->avail_cpus =
			MAX(used_cpu_per_node[i], details_ptr->pn_min_cpus);
		if (!eval_nodes_cpus_to_use(topo_eval, i, rem_max_cpus,
					    min_rem_nodes, &maxtasks, true)) {
			bit_clear(topo_eval->node_map, i);
			continue;
		}
		rem_cpus -= topo_eval->avail_cpus;
		rem_max_cpus -= topo_eval->avail_cpus;
		min_rem_nodes--;
	}

	if ((rem_cpus <= 0) && (min_rem_nodes <= 0) &&
	    gres_sched_test(job_ptr->gres_list_req, job_ptr->job_id)) {
		error_code = SLURM_SUCCESS;
	}

fini:
	if (error_code == SLURM_SUCCESS)
		eval_nodes_clip_socket_cores(topo_eval);
	FREE_NULL_LIST(node_weight_list);
	FREE_NULL_BITMAP(orig_node_map);
	xfree(avail_cpu_per_node);
	xfree(used_cpu_per_node);
	return error_code;
}

extern int eval_nodes(topology_eval_t *topo_eval)
{
	job_details_t *details_ptr = topo_eval->job_ptr->details;
	hres_select_t *hres_select = topo_eval->job_ptr->hres_select;

	static bool pack_serial_at_end = false;

	static bool set = false;

	if (!set) {
		if (xstrcasestr(slurm_conf.sched_params, "pack_serial_at_end"))
			pack_serial_at_end = true;
		else
			pack_serial_at_end = false;
		set = true;
	}
	if (hres_select)
		memcpy(hres_select->avail_hres, hres_select->avail_hres_orig,
		       hres_select->layers_cnt *
			       sizeof(*hres_select->avail_hres));

	xassert(topo_eval->node_map);
	if (bit_set_count(topo_eval->node_map) < topo_eval->min_nodes) {
		topo_eval->eval_action = EVAL_ACTION_BREAK;
		return SLURM_ERROR;
	}

	if ((details_ptr->req_node_bitmap) &&
	    (!bit_super_set(details_ptr->req_node_bitmap,
			    topo_eval->node_map))) {
		topo_eval->eval_action = EVAL_ACTION_BREAK;
		return SLURM_ERROR;
	}

	/* trump_others will skip any other algorithms */
	if (topo_eval->trump_others && topo_eval->eval_nodes) {
		return topo_eval->eval_nodes(topo_eval);
	}

	if (topo_eval->job_ptr->bit_flags & SPREAD_JOB) {
		/* Spread the job out over many nodes */
		return _eval_nodes_spread(topo_eval);
	}

	if (topo_eval->prefer_alloc_nodes && !details_ptr->contiguous) {
		/*
		 * Select resource on busy nodes first in order to leave
		 * idle resources free for as long as possible so that longer
		 * running jobs can get more easily started by the backfill
		 * scheduler plugin
		 */
		return _eval_nodes_busy(topo_eval);
	}

	if ((topo_eval->cr_type & SELECT_LLN) ||
	    (topo_eval->job_ptr->part_ptr &&
	     (topo_eval->job_ptr->part_ptr->flags & PART_FLAG_LLN))) {
		/* Select resource on the Least Loaded Node */
		return _eval_nodes_lln(topo_eval);
	}

	if (pack_serial_at_end &&
	    (details_ptr->min_cpus == 1) && (topo_eval->req_nodes == 1)) {
		/*
		 * Put serial jobs at the end of the available node list
		 * rather than using a best-fit algorithm, which fragments
		 * resources.
		 */
		return _eval_nodes_serial(topo_eval);
	}

	if (topo_eval->eval_nodes) {
		/*
		 * If we are here (likely topology/tree) we do a best-effort,
		 * so don't break and continue.
		 */
		int rc = topo_eval->eval_nodes(topo_eval);
		if (rc != ESLURM_NOT_SUPPORTED)
			return rc;
	}

	return _eval_nodes_consec(topo_eval);
}

extern bool eval_nodes_cpus_to_use(topology_eval_t *topo_eval, int node_inx,
				   int64_t rem_max_cpus, int rem_nodes,
				   uint64_t *max_tasks, bool check_gres)
{
	job_record_t *job_ptr = topo_eval->job_ptr;
	job_details_t *details_ptr = job_ptr->details;
	hres_select_t *hres_select = job_ptr->hres_select;
	avail_res_t *avail_res = topo_eval->avail_res_array[node_inx];
	int resv_cpus;	/* CPUs to be allocated on other nodes */

	if (hres_select &&
	    !hres_select_check(hres_select, avail_res->hres_leaf_idx)) {
		topo_eval->avail_cpus = 0;
		return false;
	}
	/* Use all resources on node */
	if (details_ptr->whole_node & WHOLE_NODE_REQUIRED)
		goto check_gres_per_job;

	resv_cpus = MAX((rem_nodes - 1), 0);
	resv_cpus *= job_mgr_determine_cpus_per_core(details_ptr, node_inx);
	if (topo_eval->cr_type & SELECT_SOCKET)
		resv_cpus *= node_record_table_ptr[node_inx]->cores;
	rem_max_cpus -= resv_cpus;
	if (topo_eval->avail_cpus > rem_max_cpus) {
		topo_eval->avail_cpus = MAX(rem_max_cpus,
					    (int)details_ptr->pn_min_cpus);
		if (avail_res->gres_min_cpus)
			topo_eval->avail_cpus =
				MAX(topo_eval->avail_cpus,
				    avail_res->gres_min_cpus);
		else
			topo_eval->avail_cpus =
				MAX(topo_eval->avail_cpus,
				    details_ptr->min_gres_cpu);
		/* Round up CPU count to CPU in allocation unit (e.g. core) */
		avail_res->avail_cpus = topo_eval->avail_cpus;
	}
check_gres_per_job:
	if (check_gres && topo_eval->gres_per_job && topo_eval->avail_cpus) {
		node_record_t *node_ptr = node_record_table_ptr[node_inx];
		bool ret = eval_nodes_gres(topo_eval, max_tasks, job_ptr,
					   node_ptr, rem_nodes, node_inx, 0);
		if (!ret && hres_select)
			hres_select_return(hres_select,
					   avail_res->hres_leaf_idx);
		return ret;
	}

	return true;
}

extern void eval_nodes_select_cores(topology_eval_t *topo_eval,
				    int node_inx, int rem_nodes)
{
	bitstr_t **avail_core = topo_eval->avail_core;
	uint16_t *avail_cpus = &topo_eval->avail_cpus;
	avail_res_t **avail_res_array = topo_eval->avail_res_array;
	uint16_t cr_type = topo_eval->cr_type;
	bool enforce_binding = topo_eval->enforce_binding;
	bool first_pass = topo_eval->first_pass;
	job_record_t *job_ptr = topo_eval->job_ptr;
	gres_mc_data_t *mc_ptr = topo_eval->mc_ptr;

	uint32_t min_tasks_this_node = 0, max_tasks_this_node = 0;
	uint32_t min_cores_this_node = 0;
	job_details_t *details_ptr = job_ptr->details;
	node_record_t *node_ptr = node_record_table_ptr[node_inx];

	xassert(mc_ptr->cpus_per_task);

	rem_nodes = MAX(rem_nodes, 1);	/* If range of node counts */
	if (mc_ptr->ntasks_per_node) {
		min_tasks_this_node = mc_ptr->ntasks_per_node;
		max_tasks_this_node = mc_ptr->ntasks_per_node;
	} else if (mc_ptr->ntasks_per_board) {
		min_tasks_this_node = mc_ptr->ntasks_per_board;
		max_tasks_this_node = mc_ptr->ntasks_per_board *
				      node_ptr->boards;
	} else if (mc_ptr->ntasks_per_socket) {
		min_tasks_this_node = mc_ptr->ntasks_per_socket;
		max_tasks_this_node = mc_ptr->ntasks_per_socket *
				      node_ptr->tot_sockets;
	} else if (mc_ptr->ntasks_per_core) {
		min_tasks_this_node = mc_ptr->ntasks_per_core;
		max_tasks_this_node = mc_ptr->ntasks_per_core *
				      (node_ptr->tot_cores -
				       node_ptr->core_spec_cnt);
	} else if (details_ptr && details_ptr->ntasks_per_tres &&
		   (details_ptr->ntasks_per_tres != NO_VAL16)) {
		/* Node ranges not allowed with --ntasks-per-gpu */
		if ((details_ptr->min_nodes != NO_VAL) &&
		    (details_ptr->min_nodes != 0) &&
		    (details_ptr->min_nodes == details_ptr->max_nodes)) {
			min_tasks_this_node = details_ptr->num_tasks /
				details_ptr->min_nodes;
			max_tasks_this_node = min_tasks_this_node;
		} else {
			min_tasks_this_node = details_ptr->ntasks_per_tres;
			max_tasks_this_node = details_ptr->num_tasks;
		}
	} else if (details_ptr && (details_ptr->max_nodes == 1)) {
		if ((details_ptr->num_tasks == NO_VAL) ||
		    (details_ptr->num_tasks == 0)) {
			min_tasks_this_node = 1;
			max_tasks_this_node = NO_VAL;
		} else {
			min_tasks_this_node = details_ptr->num_tasks;
			max_tasks_this_node = details_ptr->num_tasks;
		}
	} else if (details_ptr &&
		   ((details_ptr->num_tasks == 1) ||
		    ((details_ptr->num_tasks == details_ptr->min_nodes) &&
		     (details_ptr->num_tasks == details_ptr->max_nodes)))) {
		min_tasks_this_node = 1;
		max_tasks_this_node = 1;
	} else {
		min_tasks_this_node = 1;
		max_tasks_this_node = NO_VAL;
	}
	/* Determine how many tasks can be started on this node */
	if ((!details_ptr || !details_ptr->overcommit)) {
		int alloc_tasks = avail_res_array[node_inx]->avail_cpus /
			      mc_ptr->cpus_per_task;
		if (alloc_tasks < min_tasks_this_node)
			max_tasks_this_node = 0;
		else if ((max_tasks_this_node == NO_VAL) ||
			 (alloc_tasks < max_tasks_this_node))
			max_tasks_this_node = alloc_tasks;
	}

	/* Ensure max_tasks doesn't exceed total job tasks. */
	if (details_ptr &&
	    (details_ptr->num_tasks != NO_VAL) &&
	    (details_ptr->num_tasks != 0) &&
	    ((max_tasks_this_node == NO_VAL) ||
	     (details_ptr->num_tasks < max_tasks_this_node))) {
		max_tasks_this_node = details_ptr->num_tasks;
	}

	*avail_cpus = avail_res_array[node_inx]->avail_cpus;
	/*
	 * _allocate_sc() filters available cpus and cores if the job does
	 * not request gres. If the job requests gres, _allocate_sc() defers
	 * filtering cpus and cores so that gres_select_filter_sock_core() can
	 * do it.
	 */
	if (job_ptr->gres_list_req) {
		foreach_gres_filter_sock_core_args_t args = {
			.job_ptr = job_ptr,
			.mc_ptr = mc_ptr,
			.sockets = avail_res_array[node_inx]->sock_cnt,
			.cores_per_socket = node_ptr->cores,
			.cpus_per_core = node_ptr->tpc,
			.avail_cpus = avail_cpus,
			.min_tasks_this_node = &min_tasks_this_node,
			.max_tasks_this_node = &max_tasks_this_node,
			.min_cores_this_node = &min_cores_this_node,
			.rem_nodes = rem_nodes,
			.enforce_binding = enforce_binding,
			.first_pass = first_pass,
			.avail_core = avail_core[node_inx],
			.node_name = node_record_table_ptr[node_inx]->name,
			.cr_type = cr_type,
			.res_cores_per_gpu = node_ptr->res_cores_per_gpu,
			.node_i = node_inx,
		};

		gres_filter_sock_core(
			avail_res_array[node_inx]->sock_gres_list,
			&avail_res_array[node_inx]->avail_cores_per_sock,
			&args);
	}
	if (max_tasks_this_node == 0) {
		*avail_cpus = 0;
	} else if ((slurm_conf.select_type_param & SELECT_ONE_TASK_PER_CORE) &&
		   ((mc_ptr->ntasks_per_core == INFINITE16) ||
		    (mc_ptr->ntasks_per_core == 0)) &&
		   details_ptr && (details_ptr->min_gres_cpu == 0)) {
		*avail_cpus = bit_set_count(avail_core[node_inx]);
	}
	avail_res_array[node_inx]->gres_min_cpus =
		job_mgr_determine_cpus_per_core(job_ptr->details, node_inx) *
		min_cores_this_node;
	avail_res_array[node_inx]->min_cpus =
		avail_res_array[node_inx]->gres_min_cpus;
	avail_res_array[node_inx]->gres_max_tasks = max_tasks_this_node;
}

extern int64_t eval_nodes_get_rem_max_cpus(
	job_details_t *details_ptr, int rem_nodes)
{
	int64_t rem_max_cpus = details_ptr->min_cpus;

	if (details_ptr->max_cpus != NO_VAL)
		rem_max_cpus = details_ptr->max_cpus;
	if (details_ptr->min_gres_cpu)
		rem_max_cpus = MAX(rem_max_cpus,
				   details_ptr->min_gres_cpu * rem_nodes);
	if (details_ptr->min_job_gres_cpu)
		rem_max_cpus = MAX(rem_max_cpus, details_ptr->min_job_gres_cpu);

	return rem_max_cpus;

}

extern int eval_nodes_topo_weight_find(void *x, void *key)
{
	topo_weight_info_t *nw = x;
	topo_weight_info_t *nw_key = key;
	if (nw->weight == nw_key->weight)
		return 1;
	return 0;
}

extern int eval_nodes_topo_node_find(void *x, void *key)
{
	topo_weight_info_t *nw = x;
	bitstr_t *nw_key = key;
	if (bit_overlap_any(nw->node_bitmap, nw_key))
		return 1;
	return 0;
}

extern void eval_nodes_topo_weight_free(void *x)
{
	topo_weight_info_t *nw = x;
	FREE_NULL_BITMAP(nw->node_bitmap);
	xfree(nw);
}

extern int eval_nodes_topo_weight_log(void *x, void *arg)
{
	topo_weight_info_t *nw = x;
	char *node_names = bitmap2node_name(nw->node_bitmap);
	info("Topo:%s weight:%"PRIu64, node_names, nw->weight);
	xfree(node_names);
	return 0;
}

extern int eval_nodes_topo_weight_sort(void *x, void *y)
{
	topo_weight_info_t *nwt1 = *(topo_weight_info_t **) x;
	topo_weight_info_t *nwt2 = *(topo_weight_info_t **) y;
	if (nwt1->weight < nwt2->weight)
		return -1;
	if (nwt1->weight > nwt2->weight)
		return 1;
	return 0;
}

extern bool eval_nodes_enough_nodes(int avail_nodes, int rem_nodes,
				    uint32_t min_nodes, uint32_t req_nodes)
{
	int needed_nodes;

	if (req_nodes > min_nodes)
		needed_nodes = rem_nodes + min_nodes - req_nodes;
	else
		needed_nodes = rem_nodes;

	return (avail_nodes >= needed_nodes);
}
