function Resolver(problem_sub, sub_frozen, problems, users) {
	this.problem_sub = problem_sub;
	this.sub_frozen = sub_frozen;
	this.problems = problems;
	this.users = users;
	this.frozen_seconds = 200;
	this.operations = [];
	this.frozen_op = 0;
	this.isshow = [];
	this.total_points = {};
	this.delay = false;

	for (var problem in this.problems) {
		this.total_points[problem] = 0;
		for (var i in this.problems[problem]) {
			this.total_points[problem] += this.problems[problem][i];
		}
		this.total_points[problem] = round2(this.total_points[problem]);
	}
}

function round2(num) {
	return Math.round(num * 100) / 100;
}

Resolver.prototype.status = function (problem) {
	if (problem.old_verdict == 'NA' && problem.new_verdict == 'NA') {
		return 'untouched';
	} else if (problem.old_verdict == 'AC') {
		return 'ac';
	} else if (problem.old_verdict == 'PA' && problem.new_verdict == 'NA') {
		return 'partially';
	} else if (problem.new_verdict == 'NA' && problem.old_verdict == 'WA') {
		return 'failed';
	} else {
		return "frozen";
	}
}

Resolver.prototype.substatus = function (problem, subproblem) {
	if (problem.old_verdict == 'NA' && problem.new_verdict == 'NA') {
		return 'untouched';
	} else if (problem[subproblem].old_verdict == 'AC') {
		return 'ac';
	} else if (problem[subproblem].old_verdict == 'PA' && problem.new_verdict == 'NA') {
		return 'partially';
	} else if (problem[subproblem].old_verdict == 'WA' && problem.new_verdict == 'NA') {
		return 'failed';
	}
	else {
		return 'frozen';
	}
}

Resolver.prototype.pointstatus = function (point, problem, sub) {
	if (sub == undefined) {
		if (point == this.total_points[problem]) return 'AC';
		if (point == 0) return 'WA';
		return 'PA';
	}
	if (point == this.problems[problem][sub]) return 'AC';
	if (point == 0) return 'WA';
	return 'PA';
}

Resolver.prototype.calcOperations = function () {
	this.rank = {};
	this.users_cnt = Object.keys(this.users).length;
	for (let id = 1; id <= this.users_cnt; id++) {
		this.rank[id] = {
			'user_id': id,
			'score': 0,
			'rank_show': -1,
			'last_submission': this.users[id].last_submission,
		};
		this.rank[id].problem = {}
		for (let i = 1; i <= this.problem_sub.length; i++) {
			this.rank[id].problem[i] = {
				'old_point': 0,
				'new_point': 0,
				'old_verdict': 'NA',
				'new_verdict': 'NA',
			}
			for (let j = 1; j <= this.problem_sub[i - 1]; j++) {
				this.rank[id].problem[i][j] = {
					'old_point': 0,
					'new_point': 0,
					'old_verdict': 'NA',
					'new_verdict': 'NA'
				};
			}
		}
		for (let problemid in this.users[id].problems) {
			for (let j = 1; j <= this.problem_sub[problemid - 1]; j++) {
				if (j < this.sub_frozen[problemid - 1]) {
					this.rank[id].problem[problemid][j].old_point = this.users[id].problems[problemid].frozen_points[j];
					this.rank[id].problem[problemid].old_point += this.rank[id].problem[problemid][j].old_point;
					this.rank[id].problem[problemid][j].old_verdict = this.pointstatus(this.users[id].problems[problemid].frozen_points[j], problemid, j);
					this.rank[id].problem[problemid].old_point = round2(this.rank[id].problem[problemid].old_point)
					if (this.users[id].problems[problemid].points[j] != -1) {
						this.rank[id].problem[problemid][j].new_point = this.users[id].problems[problemid].points[j];
						this.rank[id].problem[problemid].new_point += this.rank[id].problem[problemid][j].new_point;
						this.rank[id].problem[problemid].new_point = round2(this.rank[id].problem[problemid].new_point)
						this.rank[id].problem[problemid][j].new_verdict = this.pointstatus(this.users[id].problems[problemid].points[j], problemid, j);
					}
				}
			}
			this.rank[id].problem[problemid].old_verdict = this.pointstatus(this.rank[id].problem[problemid].old_point, problemid);
			this.rank[id].score += this.rank[id].problem[problemid].old_point;
			this.rank[id].score = round2(this.rank[id].score)
			if (this.users[id].problems[problemid].points[1] != -1) {
				this.rank[id].problem[problemid].new_verdict = this.pointstatus(this.rank[id].problem[problemid].new_point, problemid);
			}
		}
	}
	this.rank_frozen = $.extend(true, [], this.rank);
	var uids = Object.keys(this.rank);
	this.rankarr = [];
	for (let key in uids) {
		this.rankarr.push(this.rank[uids[key]]);
	}
	this.rankarr.sort(function (a, b) {
		if (a.score != b.score) {
			return (b.score - a.score);
		} else {
			return (a.last_submission - b.last_submission);
		}
	});

	for (let i = 0; i < this.rankarr.length; i++) {
		this.rankarr[i].rank_show = i + 1;
		this.rank[this.rankarr[i].user_id].rank_show = i + 1;
		this.rank_frozen[this.rankarr[i].user_id].rank_show = i + 1;
	}
	console.log(this.rank_frozen);
	for (let i = this.rankarr.length - 1; i >= 0; i--) {
		var flag = true;
		while (flag) {
			flag = false;
			for (let j = 1; j <= this.problem_sub.length; j++) {
				if (this.status(this.rankarr[i].problem[j]) == 'frozen') {
					frozen_op = true;
					flag = true;
					for (let sub = 1; sub < this.sub_frozen[j - 1]; sub++) {
						if (this.rankarr[i].problem[j][sub].old_verdict == 'AC') continue;
						var op = {
							id: this.operations.length,
							type: 'sub',
							frozen: 'no',
							user_id: this.rankarr[i].user_id,
							problem_index: j,
							problem_sub: sub,
							old_point: this.rankarr[i].problem[j][sub].old_point,
							new_point: this.rankarr[i].problem[j][sub].new_point,
							old_verdict: this.rankarr[i].problem[j][sub].old_verdict,
							new_verdict: this.rankarr[i].problem[j][sub].new_verdict,
						};
						var tmp = this.rankarr[i];
						tmp.problem[j][sub].old_point = tmp.problem[j][sub].new_point;
						tmp.problem[j][sub].new_point = 0;
						tmp.problem[j][sub].old_verdict = tmp.problem[j][sub].new_verdict;
						tmp.problem[j][sub].new_verdict = 'NA';
						this.operations.push(op);
					}
					var op = {
						id: this.operations.length,
						type: 'problem',
						frozen: 'no',
						user_id: this.rankarr[i].user_id,
						problem_index: j,
						old_point: this.rankarr[i].problem[j].old_point,
						new_point: this.rankarr[i].problem[j].new_point,
						old_verdict: this.rankarr[i].problem[j].old_verdict,
						new_verdict: this.rankarr[i].problem[j].new_verdict,
						old_rank: i + 1,
						new_rank: -1,
					};
					var tmp = this.rankarr[i];
					if (tmp.problem[j].new_point > tmp.problem[j].old_point) {
						tmp.score += tmp.problem[j].new_point - tmp.problem[j].old_point;
						tmp.score = round2(tmp.score);
					}
					tmp.problem[j].old_point = tmp.problem[j].new_point;
					tmp.problem[j].new_point = 0;
					tmp.problem[j].old_verdict = tmp.problem[j].new_verdict;
					tmp.problem[j].new_verdict = 'NA';
					var k = i - 1;
					while (k >= 0 && this.rankarr[k].score < tmp.score) {
						tmp.rank_show--;
						this.rankarr[k].rank_show++;
						this.rankarr[k + 1] = this.rankarr[k];
						k--;
					}
					this.rankarr[k + 1] = tmp;
					op.new_rank = k + 2;
					this.operations.push(op);
					break;
				}
			}
		}
	}
	this.check = [];
	for (let i = 1; i <= this.users_cnt; i++) {
		var usercheck = [];
		for (let j = 1; j <= this.problem_sub.length; j++) {
			var cc = [];
			for (let k = 1; k <= this.sub_frozen[j - 1] - 1; k++) {
				cc.push(k);
			}
			usercheck.push(cc);
		}
		this.check.push(usercheck);
	}
}

Resolver.prototype.showrank = function () {
	for (let rankid = this.rankarr.length - 1; rankid >= 0; rankid--) {
		if (this.isshow.indexOf(this.rankarr[rankid].user_id) != -1) continue;
		var ok = true;
		for (let problemid in this.users[this.rankarr[rankid].user_id].problems) {
			for (let sub = 1; sub <= this.problem_sub[problemid - 1]; sub++) {
				if (this.check[this.rankarr[rankid].user_id - 1][problemid - 1].indexOf(sub) == -1) {
					ok = false;
				}
			}
		}
		if (ok) {
			var op = {
				id: this.operations.length,
				type: 'show',
				user_id: this.rankarr[rankid].user_id,
			};
			this.delay = true;
			this.isshow.push(this.rankarr[rankid].user_id);
			this.operations.push(op);
			return true;
		} else {
			return false;
		}
	}
	return false;
}

Resolver.prototype.next_operation = function () {
	if (this.delay) {
		var op = {
			id: this.operations.length,
			type: 'delay',
		};
		this.delay = false;
		this.operations.push(op);
		return true;
	}
	var isshowrank = this.showrank();
	if (isshowrank == true) return true;
	for (let i = this.rankarr.length - 1; i >= 0; i--) {
		for (let problemid = 1; problemid <= this.problem_sub.length; problemid++) {
			let ok = false;
			var id = this.rankarr[i].user_id;
			for (let cc in this.users[id].problems) {
				if (cc == problemid) ok = true;
			}
			if (ok == false) {
				continue;
			}
			for (let sub = this.sub_frozen[problemid - 1]; sub <= this.problem_sub[problemid - 1]; sub++) {
				if (this.check[this.rankarr[i].user_id - 1][problemid - 1].indexOf(sub) == -1) {
					this.operation(i, problemid, sub);
					return true;
				}
			}
		}
	}
	return false;
}

Resolver.prototype.operation = function (rankid, problemid, sub) {
	var id = this.rankarr[rankid].user_id;
	if (this.check[this.rankarr[rankid].user_id - 1][problemid - 1].indexOf(sub) != -1) return false;
	this.check[this.rankarr[rankid].user_id - 1][problemid - 1].push(sub);
	this.rankarr[rankid].problem[problemid][sub].new_point = this.users[id].problems[problemid].points[sub];
	this.rankarr[rankid].problem[problemid][sub].new_verdict = this.pointstatus(this.rankarr[rankid].problem[problemid][sub].new_point, problemid, sub);
	this.rankarr[rankid].problem[problemid].new_point =
		this.rankarr[rankid].problem[problemid].old_point + this.rankarr[rankid].problem[problemid][sub].new_point - this.rankarr[rankid].problem[problemid][sub].old_point;
	this.rankarr[rankid].problem[problemid].new_verdict = this.pointstatus(this.rankarr[rankid].problem[problemid].new_point, problemid);
	this.rankarr[rankid].problem[problemid].new_point = round2(this.rankarr[rankid].problem[problemid].new_point)
	var op = {
		id: this.operations.length,
		type: 'sub',
		frozen: 'ok',
		user_id: this.rankarr[rankid].user_id,
		problem_index: problemid,
		problem_sub: sub,
		old_point: this.rankarr[rankid].problem[problemid][sub].old_point,
		new_point: this.rankarr[rankid].problem[problemid][sub].new_point,
		old_verdict: this.rankarr[rankid].problem[problemid][sub].old_verdict,
		new_verdict: this.rankarr[rankid].problem[problemid][sub].new_verdict,
	};
	var tmp = this.rankarr[rankid];
	tmp.problem[problemid][sub].old_point = tmp.problem[problemid][sub].new_point;
	tmp.problem[problemid][sub].new_point = 0;
	tmp.problem[problemid][sub].old_verdict = tmp.problem[problemid][sub].new_verdict;
	tmp.problem[problemid][sub].new_verdict = 'NA';
	this.operations.push(op);
	var op1 = {
		id: this.operations.length,
		type: 'problem',
		frozen: 'ok',
		user_id: this.rankarr[rankid].user_id,
		problem_index: problemid,
		old_point: this.rankarr[rankid].problem[problemid].old_point,
		new_point: this.rankarr[rankid].problem[problemid].new_point,
		old_verdict: this.rankarr[rankid].problem[problemid].old_verdict,
		new_verdict: this.rankarr[rankid].problem[problemid].new_verdict,
		old_rank: rankid + 1,
		new_rank: -1,
	};
	if (tmp.problem[problemid].new_point > tmp.problem[problemid].old_point) {
		tmp.score += tmp.problem[problemid].new_point - tmp.problem[problemid].old_point;
		tmp.score = round2(tmp.score)
	}
	tmp.problem[problemid].old_point = tmp.problem[problemid].new_point;
	tmp.problem[problemid].new_point = 0;
	tmp.problem[problemid].old_verdict = tmp.problem[problemid].new_verdict;
	tmp.problem[problemid].new_verdict = 'NA';
	var k = rankid - 1;
	while (k >= 0 && (this.rankarr[k].score < tmp.score || (this.rankarr[k].score == tmp.score && this.rankarr[k].last_submission > tmp.last_submission))) {
		tmp.rank_show--;
		this.rankarr[k].rank_show++;
		this.rankarr[k + 1] = this.rankarr[k];
		k--;
	}
	this.rankarr[k + 1] = tmp;
	op1.new_rank = k + 2;
	this.operations.push(op1);
	return true;
}