@xcpcio/core 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@xcpcio/core",
3
+ "description": "XCPCIO Core",
4
+ "version": "0.2.1",
5
+ "license": "MIT",
6
+ "author": "Dup4 <lyuzhi.pan@gmail.com>",
7
+ "keywords": [
8
+ "ICPC",
9
+ "CCPC"
10
+ ],
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/xcpcio/xcpcio.git"
14
+ },
15
+ "homepage": "https://github.com/xcpcio/xcpcio",
16
+ "bugs": {
17
+ "url": "https://github.com/xcpcio/xcpcio/issues"
18
+ },
19
+ "main": "./dist/index.mjs",
20
+ "module": "./dist/index.mjs",
21
+ "types": "./dist/index.d.ts",
22
+ "exports": {
23
+ ".": {
24
+ "require": "./dist/index.cjs",
25
+ "import": "./dist/index.mjs",
26
+ "types": "./dist/index.d.ts"
27
+ }
28
+ },
29
+ "typesVersions": {
30
+ "*": {
31
+ "*": [
32
+ "./dist/*",
33
+ "./dist/index.d.ts"
34
+ ]
35
+ }
36
+ },
37
+ "files": [
38
+ "src",
39
+ "dist"
40
+ ],
41
+ "sideEffects": false,
42
+ "devDependencies": {
43
+ "@babel/types": "^7.22.4",
44
+ "@types/lodash": "^4.14.195",
45
+ "@types/node": "^17.0.45",
46
+ "@typescript-eslint/eslint-plugin": "^5.59.9",
47
+ "@typescript-eslint/parser": "^5.59.9",
48
+ "bumpp": "^7.2.0",
49
+ "eslint": "^8.42.0",
50
+ "esmo": "^0.14.1",
51
+ "npm-run-all": "^4.1.5",
52
+ "pnpm": "^7.33.0",
53
+ "prettier": "^2.8.8",
54
+ "taze": "^0.10.2",
55
+ "typescript": "^4.9.5",
56
+ "unbuild": "^0.7.6",
57
+ "vite": "^4.3.9",
58
+ "vitest": "^0.32.0"
59
+ },
60
+ "dependencies": {
61
+ "dayjs": "^1.11.8",
62
+ "lodash": "^4.17.21",
63
+ "@xcpcio/types": "0.2.1"
64
+ },
65
+ "scripts": {
66
+ "build": "unbuild",
67
+ "dev": "unbuild --stub",
68
+ "start": "esmo src/index.ts",
69
+ "test": "vitest",
70
+ "lint": "run-p lint:*",
71
+ "lint:build": "# tsc --noEmit"
72
+ }
73
+ }
package/src/contest.ts ADDED
@@ -0,0 +1,191 @@
1
+ import { Contest as IContest, ContestState, Image, VERSION, StatusTimeDisplay } from "@xcpcio/types";
2
+
3
+ import { Problem, Problems, createProblems, createProblemsByProblemIds } from "./problem";
4
+ import { dayjs, createDayJS, getTimeDiff } from "./utils";
5
+
6
+ export class Contest {
7
+ name = "";
8
+
9
+ startTime: dayjs.Dayjs;
10
+ endTime: dayjs.Dayjs;
11
+ freezeTime: dayjs.Dayjs;
12
+
13
+ totalDurationTimestamp: number;
14
+ freezeDurationTimestamp: number;
15
+ unFreezeDurationTimestamp: number;
16
+
17
+ penalty: number;
18
+
19
+ problems: Problems;
20
+ problemsMap: Map<string, Problem>;
21
+
22
+ statusTimeDisplay: StatusTimeDisplay;
23
+
24
+ badge?: string;
25
+ medal?: Record<string, Record<string, number>>;
26
+ organization?: string;
27
+
28
+ group?: Record<string, string>;
29
+ tag?: Record<string, string>;
30
+
31
+ logo?: Image;
32
+ banner?: Image;
33
+ boardLink?: string;
34
+
35
+ version = VERSION;
36
+
37
+ constructor() {
38
+ this.startTime = createDayJS();
39
+ this.endTime = createDayJS();
40
+ this.freezeTime = createDayJS();
41
+
42
+ this.totalDurationTimestamp = 0;
43
+ this.freezeDurationTimestamp = 0;
44
+ this.unFreezeDurationTimestamp = 0;
45
+
46
+ // 20 mins
47
+ this.penalty = 20 * 60;
48
+
49
+ this.problems = [];
50
+ this.problemsMap = new Map<string, Problem>();
51
+
52
+ this.statusTimeDisplay = {
53
+ correct: true,
54
+ incorrect: true,
55
+ pending: true,
56
+ };
57
+ }
58
+
59
+ getContestDuration(timeFormat = "HH:mm:ss"): string {
60
+ return dayjs.duration(this.endTime.diff(this.startTime)).format(timeFormat);
61
+ }
62
+
63
+ getContestState(): ContestState {
64
+ const now = createDayJS();
65
+
66
+ if (now.isBefore(this.startTime)) {
67
+ return ContestState.PENDING;
68
+ }
69
+
70
+ if (now.isSameOrAfter(this.endTime)) {
71
+ return ContestState.FINISHED;
72
+ }
73
+
74
+ if (now.isSameOrAfter(this.freezeTime)) {
75
+ return ContestState.FROZEN;
76
+ }
77
+
78
+ return ContestState.RUNNING;
79
+ }
80
+
81
+ getContestPendingTime(): string {
82
+ let baseTime = createDayJS();
83
+ if (baseTime.isAfter(this.startTime)) {
84
+ baseTime = this.startTime;
85
+ }
86
+
87
+ return getTimeDiff(Math.floor(dayjs.duration(this.startTime.diff(baseTime)).asSeconds()));
88
+ }
89
+
90
+ getContestRemainingTime(endTime: dayjs.Dayjs): string {
91
+ let baseTime = dayjs();
92
+ if (baseTime.isAfter(endTime)) {
93
+ baseTime = endTime;
94
+ }
95
+
96
+ return getTimeDiff(Math.floor(dayjs.duration(endTime.diff(baseTime)).asSeconds()));
97
+ }
98
+
99
+ getContestElapsedTime(): string {
100
+ let baseTime = dayjs();
101
+ if (baseTime.isAfter(this.endTime)) {
102
+ baseTime = this.endTime;
103
+ }
104
+
105
+ return getTimeDiff(Math.floor(dayjs.duration(baseTime.diff(this.startTime)).asSeconds()));
106
+ }
107
+
108
+ getContestProgressRatio(): number {
109
+ const baseTime = dayjs();
110
+
111
+ if (this.startTime.isSameOrAfter(baseTime)) {
112
+ return 0;
113
+ }
114
+
115
+ if (this.endTime.isSameOrBefore(baseTime)) {
116
+ return 100;
117
+ }
118
+
119
+ const total = this.endTime.diff(this.startTime, "s");
120
+ const pass = baseTime.diff(this.startTime, "s");
121
+
122
+ return Math.round((pass * 100) / total);
123
+ }
124
+ }
125
+
126
+ export function createContest(contestJSON: IContest): Contest {
127
+ const c = new Contest();
128
+
129
+ c.name = contestJSON.contest_name;
130
+
131
+ c.startTime = createDayJS(contestJSON.start_time);
132
+ c.endTime = createDayJS(contestJSON.end_time);
133
+
134
+ c.totalDurationTimestamp = c.endTime.unix() - c.startTime.unix();
135
+
136
+ {
137
+ // default value
138
+ c.freezeTime = c.endTime;
139
+ c.freezeDurationTimestamp = 0;
140
+
141
+ if (contestJSON.frozen_time !== undefined && contestJSON.frozen_time != null) {
142
+ const frozenTime = Number(contestJSON.frozen_time);
143
+
144
+ c.freezeTime = createDayJS(c.endTime.unix() - frozenTime);
145
+ c.freezeDurationTimestamp = frozenTime;
146
+ }
147
+
148
+ if (contestJSON.freeze_time !== undefined && contestJSON.freeze_time !== null) {
149
+ c.freezeTime = createDayJS(contestJSON.freeze_time);
150
+ c.freezeDurationTimestamp = c.endTime.unix() - c.freezeTime.unix();
151
+ }
152
+
153
+ c.unFreezeDurationTimestamp = c.totalDurationTimestamp - c.freezeDurationTimestamp;
154
+ }
155
+
156
+ c.penalty = contestJSON.penalty;
157
+
158
+ {
159
+ if (contestJSON.problem_id !== undefined && contestJSON.problem_id !== null) {
160
+ c.problems = createProblemsByProblemIds(contestJSON.problem_id, contestJSON.balloon_color);
161
+ }
162
+
163
+ if (contestJSON.problems !== undefined && contestJSON.problems !== null) {
164
+ c.problems = createProblems(contestJSON.problems);
165
+ }
166
+
167
+ c.problemsMap = new Map(c.problems.map((p) => [p.id, p]));
168
+ }
169
+
170
+ if (contestJSON.status_time_display !== undefined && contestJSON.status_time_display !== null) {
171
+ c.statusTimeDisplay = {
172
+ correct: Boolean(contestJSON.status_time_display.correct ?? false),
173
+ incorrect: Boolean(contestJSON.status_time_display.incorrect ?? false),
174
+ pending: Boolean(contestJSON.status_time_display.pending ?? false),
175
+ };
176
+ }
177
+
178
+ c.badge = contestJSON.badge;
179
+ c.medal = contestJSON.medal;
180
+ c.organization = contestJSON.organization;
181
+
182
+ c.group = contestJSON.group;
183
+ c.tag = contestJSON.tag;
184
+
185
+ c.banner = contestJSON.banner;
186
+
187
+ c.logo = contestJSON.logo;
188
+ c.boardLink = contestJSON.board_link;
189
+
190
+ return c;
191
+ }
package/src/image.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { Image } from "@xcpcio/types";
2
+
3
+ export function getImageSource(image: Image): string {
4
+ if (image?.url) {
5
+ return image.url;
6
+ }
7
+
8
+ if (image?.base64) {
9
+ return `data:image/${image.type};base64,${image.base64}`;
10
+ }
11
+
12
+ return "";
13
+ }
package/src/index.ts ADDED
@@ -0,0 +1,9 @@
1
+ export * from "./utils";
2
+ export * from "./contest";
3
+ export * from "./image";
4
+ export * from "./problem";
5
+ export * from "./rank";
6
+ export * from "./resolver";
7
+ export * from "./submission-status";
8
+ export * from "./submission";
9
+ export * from "./team";
package/src/problem.ts ADDED
@@ -0,0 +1,157 @@
1
+ import { Problem as IProblem, Problems as IProblems, BalloonColor } from "@xcpcio/types";
2
+
3
+ import { Submissions } from "./submission";
4
+
5
+ export interface ProblemStatistics {
6
+ acceptedNum: number;
7
+ rejectedNum: number;
8
+ pendingNum: number;
9
+
10
+ submittedNum: number;
11
+ }
12
+
13
+ export class Problem {
14
+ id: string;
15
+ label: string;
16
+
17
+ name: string;
18
+
19
+ timeLimit?: string;
20
+ memoryLimit?: string;
21
+
22
+ balloonColor?: BalloonColor;
23
+
24
+ statistics: ProblemStatistics;
25
+
26
+ constructor() {
27
+ this.id = "";
28
+ this.label = "";
29
+
30
+ this.name = "";
31
+
32
+ this.statistics = {
33
+ acceptedNum: 0,
34
+ rejectedNum: 0,
35
+ pendingNum: 0,
36
+ submittedNum: 0,
37
+ };
38
+ }
39
+ }
40
+
41
+ export type Problems = Array<Problem>;
42
+
43
+ export function createProblem(problemJSON: IProblem): Problem {
44
+ const p = new Problem();
45
+
46
+ p.id = String(problemJSON.id);
47
+ p.label = problemJSON.label;
48
+
49
+ p.name = problemJSON.name ?? "";
50
+
51
+ p.timeLimit = problemJSON.time_limit;
52
+ p.memoryLimit = problemJSON.memory_limit;
53
+
54
+ p.balloonColor = problemJSON.balloon_color;
55
+
56
+ return p;
57
+ }
58
+
59
+ export function createProblems(problemsJSON: IProblems): Problems {
60
+ return problemsJSON.map((pJSON) => {
61
+ const p = new Problem();
62
+
63
+ p.id = pJSON.id;
64
+ p.label = pJSON.label;
65
+
66
+ p.name = pJSON.name ?? "";
67
+
68
+ p.timeLimit = pJSON.time_limit;
69
+ p.memoryLimit = pJSON.memory_limit;
70
+
71
+ p.balloonColor = pJSON.balloon_color;
72
+
73
+ return p;
74
+ });
75
+ }
76
+
77
+ export function createProblemsByProblemIds(problemIds: string[], balloonColors?: BalloonColor[]): Problems {
78
+ const problems = problemIds.map((label: string, index: number) => {
79
+ const p = new Problem();
80
+ p.id = String(index);
81
+ p.label = label;
82
+
83
+ return p;
84
+ });
85
+
86
+ if (balloonColors !== undefined && balloonColors !== null) {
87
+ for (const index in balloonColors) {
88
+ problems[index].balloonColor = balloonColors[index];
89
+ }
90
+ }
91
+
92
+ return problems;
93
+ }
94
+
95
+ export class TeamProblemStatistics {
96
+ isFirstSolved: boolean;
97
+
98
+ isSolved: boolean;
99
+ solvedTimestamp: number;
100
+
101
+ isSubmitted: boolean;
102
+ lastSubmitTimestamp: number;
103
+
104
+ failedCount: number;
105
+ pendingCount: number;
106
+ ignoreCount: number;
107
+ totalCount: number;
108
+
109
+ submissions: Submissions;
110
+ problem: Problem;
111
+
112
+ contestPenalty: number;
113
+
114
+ constructor(options?: { teamProblemStatistics?: TeamProblemStatistics }) {
115
+ this.isFirstSolved = options?.teamProblemStatistics?.isFirstSolved ?? false;
116
+
117
+ this.isSolved = options?.teamProblemStatistics?.isSolved ?? false;
118
+ this.solvedTimestamp = options?.teamProblemStatistics?.solvedTimestamp ?? 0;
119
+
120
+ this.isSubmitted = options?.teamProblemStatistics?.isSubmitted ?? false;
121
+ this.lastSubmitTimestamp = options?.teamProblemStatistics?.lastSubmitTimestamp ?? 0;
122
+
123
+ this.failedCount = options?.teamProblemStatistics?.failedCount ?? 0;
124
+ this.pendingCount = options?.teamProblemStatistics?.pendingCount ?? 0;
125
+ this.ignoreCount = options?.teamProblemStatistics?.ignoreCount ?? 0;
126
+ this.totalCount = options?.teamProblemStatistics?.totalCount ?? 0;
127
+
128
+ this.submissions = options?.teamProblemStatistics?.submissions ?? [];
129
+ this.problem = options?.teamProblemStatistics?.problem ?? new Problem();
130
+
131
+ this.contestPenalty = options?.teamProblemStatistics?.contestPenalty ?? 20 * 60;
132
+ }
133
+
134
+ get isAccepted() {
135
+ return this.isSolved;
136
+ }
137
+
138
+ get isWrongAnswer() {
139
+ return !this.isSolved && this.pendingCount === 0 && this.failedCount > 0;
140
+ }
141
+
142
+ get isPending() {
143
+ return !this.isSolved && this.pendingCount > 0;
144
+ }
145
+
146
+ get isUnSubmitted() {
147
+ return this.totalCount === 0;
148
+ }
149
+
150
+ get penalty() {
151
+ if (this.isSolved === false) {
152
+ return 0;
153
+ }
154
+
155
+ return Math.floor(this.solvedTimestamp / 60) * 60 + this.failedCount * this.contestPenalty;
156
+ }
157
+ }
package/src/rank.ts ADDED
@@ -0,0 +1,122 @@
1
+ import _ from "lodash";
2
+
3
+ import { Contest } from "./contest";
4
+ import { Team, Teams } from "./team";
5
+ import { Submission, Submissions } from "./submission";
6
+ import { TeamProblemStatistics } from "./problem";
7
+
8
+ export class Rank {
9
+ readonly contest: Contest;
10
+
11
+ teams: Teams;
12
+ teamsMap: Map<string, Team>;
13
+
14
+ submissions: Submissions;
15
+ submissionsMap: Map<string, Submission>;
16
+
17
+ firstSolvedSubmissions: Map<string, Submissions>;
18
+
19
+ constructor(contest: Contest, teams: Teams, submissions: Submissions) {
20
+ this.contest = contest;
21
+
22
+ this.teams = _.cloneDeep(teams);
23
+ this.teamsMap = new Map(this.teams.map((t) => [t.id, t]));
24
+
25
+ this.submissions = _.cloneDeep(submissions).sort(Submission.compare);
26
+ this.submissionsMap = new Map(this.submissions.map((s) => [s.id, s]));
27
+
28
+ this.firstSolvedSubmissions = new Map(this.contest.problems.map((p) => [p.id, []]));
29
+ }
30
+
31
+ buildRank(options?: { timestamp?: number }) {
32
+ (() => {
33
+ for (const t of this.teams) {
34
+ t.problemStatistics = this.contest.problems.map((p) => {
35
+ const ps = new TeamProblemStatistics();
36
+ ps.problem = p;
37
+ ps.contestPenalty = this.contest.penalty;
38
+
39
+ return ps;
40
+ });
41
+
42
+ t.problemStatisticsMap = new Map(t.problemStatistics.map((ps) => [ps.problem.id, ps]));
43
+ }
44
+
45
+ this.firstSolvedSubmissions = new Map(this.contest.problems.map((p) => [p.id, []]));
46
+
47
+ for (const s of this.submissions) {
48
+ const teamId = s.teamId;
49
+ const problemId = s.problemId;
50
+ const team = this.teamsMap.get(teamId);
51
+ const problem = this.contest.problemsMap.get(problemId);
52
+
53
+ if (team === undefined || problem === undefined) {
54
+ continue;
55
+ }
56
+
57
+ if (options?.timestamp !== undefined && options?.timestamp !== null) {
58
+ if (s.timestamp > options.timestamp) {
59
+ break;
60
+ }
61
+ }
62
+
63
+ const problemStatistics = team.problemStatisticsMap.get(problemId) as TeamProblemStatistics;
64
+ const submissions = problemStatistics.submissions;
65
+ const firstSolvedSubmissions = this.firstSolvedSubmissions.get(problemId) as Array<Submission>;
66
+
67
+ submissions.push(s);
68
+ problem.statistics.submittedNum++;
69
+
70
+ if (problemStatistics.isSolved) {
71
+ continue;
72
+ }
73
+
74
+ if (s.isIgnore || s.isNotCalculatedPenaltyStatus()) {
75
+ problemStatistics.ignoreCount++;
76
+ continue;
77
+ }
78
+
79
+ problemStatistics.isSubmitted = true;
80
+ problemStatistics.lastSubmitTimestamp = s.timestamp;
81
+ problemStatistics.totalCount++;
82
+
83
+ if (s.isAccepted()) {
84
+ problemStatistics.isSolved = true;
85
+ problemStatistics.solvedTimestamp = s.timestamp;
86
+
87
+ problem.statistics.acceptedNum++;
88
+
89
+ if (
90
+ firstSolvedSubmissions.length === 0 ||
91
+ firstSolvedSubmissions[firstSolvedSubmissions.length - 1].timestamp === s.timestamp
92
+ ) {
93
+ problemStatistics.isFirstSolved = true;
94
+ firstSolvedSubmissions.push(s);
95
+ }
96
+ }
97
+
98
+ if (s.isRejected()) {
99
+ problemStatistics.failedCount++;
100
+ problem.statistics.rejectedNum++;
101
+ }
102
+
103
+ if (s.isPending()) {
104
+ problemStatistics.pendingCount++;
105
+ problem.statistics.pendingNum++;
106
+ }
107
+ }
108
+
109
+ this.teams.forEach((t) => t.calcSolvedData());
110
+ this.teams.sort(Team.compare);
111
+
112
+ {
113
+ let rank = 1;
114
+ for (const t of this.teams) {
115
+ t.rank = rank++;
116
+ }
117
+ }
118
+ })();
119
+
120
+ return this;
121
+ }
122
+ }
@@ -0,0 +1,22 @@
1
+ import { TeamProblemStatistics } from "./problem";
2
+ import { Team } from "./team";
3
+
4
+ export class ResolverOperation {
5
+ id: number;
6
+
7
+ team: Team;
8
+ problemIx: number;
9
+
10
+ beforeTeamProblemStatistics: TeamProblemStatistics;
11
+ afterTeamProblemStatistics: TeamProblemStatistics;
12
+
13
+ constructor() {
14
+ this.id = 0;
15
+
16
+ this.team = new Team();
17
+ this.problemIx = 0;
18
+
19
+ this.beforeTeamProblemStatistics = new TeamProblemStatistics();
20
+ this.afterTeamProblemStatistics = new TeamProblemStatistics();
21
+ }
22
+ }
@@ -0,0 +1,120 @@
1
+ import _ from "lodash";
2
+
3
+ import { Rank } from "./rank";
4
+ import { Contest } from "./contest";
5
+ import { Team, Teams } from "./team";
6
+ import { Submission, Submissions } from "./submission";
7
+ import { TeamProblemStatistics } from "./problem";
8
+ import { ResolverOperation } from "./resolver-operation";
9
+
10
+ export class Resolver extends Rank {
11
+ finalRank: Rank;
12
+ operations: Array<ResolverOperation>;
13
+
14
+ beforeFreezeSubmissions: Submissions;
15
+ afterFreezeSubmissions: Submissions;
16
+
17
+ constructor(contest: Contest, teams: Teams, submissions: Submissions) {
18
+ submissions.sort(Submission.compare);
19
+
20
+ let beforeFreezeSubmissions = submissions;
21
+ let afterFreezeSubmissions = submissions;
22
+
23
+ {
24
+ const ix = _.sortedIndex(
25
+ submissions.map((s) => s.timestamp),
26
+ contest.unFreezeDurationTimestamp,
27
+ );
28
+
29
+ beforeFreezeSubmissions = submissions.slice(0, ix + 1);
30
+ afterFreezeSubmissions = submissions.slice(ix, -1);
31
+ }
32
+
33
+ super(contest, teams, beforeFreezeSubmissions);
34
+
35
+ this.finalRank = new Rank(contest, teams, submissions);
36
+ this.operations = [];
37
+
38
+ this.beforeFreezeSubmissions = beforeFreezeSubmissions;
39
+ this.afterFreezeSubmissions = afterFreezeSubmissions;
40
+ }
41
+
42
+ buildResolver() {
43
+ this.buildRank();
44
+ this.finalRank.buildRank();
45
+
46
+ for (const s of this.afterFreezeSubmissions) {
47
+ const teamId = s.teamId;
48
+ const problemId = s.problemId;
49
+ const team = this.teamsMap.get(teamId);
50
+ const problem = this.contest.problemsMap.get(problemId);
51
+
52
+ if (team === undefined || problem === undefined) {
53
+ continue;
54
+ }
55
+
56
+ const problemStatistics = team.problemStatisticsMap.get(problemId) as TeamProblemStatistics;
57
+
58
+ problemStatistics.pendingCount++;
59
+ problemStatistics.totalCount++;
60
+ if (!problemStatistics.isAccepted) {
61
+ problemStatistics.lastSubmitTimestamp = s.timestamp;
62
+ }
63
+ }
64
+
65
+ {
66
+ const teams_ = _.cloneDeep(this.teams);
67
+
68
+ for (let i = this.teams.length - 1; i >= 0; ) {
69
+ const team = teams_[i];
70
+ const teamId = team.id;
71
+
72
+ let handleCnt = 0;
73
+ let problemIx = -1;
74
+ for (const p of team.problemStatistics) {
75
+ problemIx++;
76
+
77
+ if (!p.isPending) {
78
+ continue;
79
+ }
80
+
81
+ handleCnt++;
82
+
83
+ const beforeTeamProblemStatistics = this.teamsMap.get(teamId)?.problemStatistics[
84
+ problemIx
85
+ ] as TeamProblemStatistics;
86
+ const afterTeamProblemStatistics = this.finalRank.teamsMap.get(teamId)?.problemStatistics[
87
+ problemIx
88
+ ] as TeamProblemStatistics;
89
+
90
+ const op = new ResolverOperation();
91
+ op.id = this.operations.length;
92
+ op.team = this.teamsMap.get(teamId) as Team;
93
+ op.problemIx = problemIx;
94
+
95
+ op.beforeTeamProblemStatistics = beforeTeamProblemStatistics;
96
+ op.afterTeamProblemStatistics = afterTeamProblemStatistics;
97
+
98
+ this.operations.push(op);
99
+
100
+ team.problemStatistics[problemIx] = afterTeamProblemStatistics;
101
+ team.calcSolvedData();
102
+
103
+ break;
104
+ }
105
+
106
+ {
107
+ let j = i;
108
+ while (j > 0 && Team.compare(teams_[j], teams_[j - 1]) < 0) {
109
+ [teams_[j], teams_[j - 1]] = [teams_[j - 1], teams_[j]];
110
+ j--;
111
+ }
112
+ }
113
+
114
+ if (handleCnt === 0) {
115
+ i--;
116
+ }
117
+ }
118
+ }
119
+ }
120
+ }