sparkforensics-cli 0.1.0
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/bin/sparkforensics-analyze.mjs +288 -0
- package/package.json +29 -0
- package/vendor-core/analyzer.js +167 -0
- package/vendor-core/assert-never.js +3 -0
- package/vendor-core/cli/budgets.js +203 -0
- package/vendor-core/cli/collect-run.js +107 -0
- package/vendor-core/core-count.js +68 -0
- package/vendor-core/core-locality-ratio.js +54 -0
- package/vendor-core/core-time-series.js +92 -0
- package/vendor-core/core-usage-locality.js +70 -0
- package/vendor-core/detectors.js +1989 -0
- package/vendor-core/docs-config.js +72 -0
- package/vendor-core/docs-site-config.js +23 -0
- package/vendor-core/efficiency-model.js +62 -0
- package/vendor-core/etl-phases.js +28 -0
- package/vendor-core/event-handlers.js +906 -0
- package/vendor-core/event-schemas.js +405 -0
- package/vendor-core/evidence-availability.js +121 -0
- package/vendor-core/evidence-report.js +459 -0
- package/vendor-core/finding-action-label.js +97 -0
- package/vendor-core/finding-filter-predicate.js +38 -0
- package/vendor-core/format-utils.js +167 -0
- package/vendor-core/impact-band.js +50 -0
- package/vendor-core/impact-estimator.js +428 -0
- package/vendor-core/ingest.js +139 -0
- package/vendor-core/job-groups.js +30 -0
- package/vendor-core/load-vendored.js +24 -0
- package/vendor-core/lz4-block.js +135 -0
- package/vendor-core/mcp-error.js +3 -0
- package/vendor-core/mcp-server-factory.js +115 -0
- package/vendor-core/mcp-tools.js +331 -0
- package/vendor-core/model-assembler.js +76 -0
- package/vendor-core/occupancy.js +202 -0
- package/vendor-core/parser-worker.js +249 -0
- package/vendor-core/plan-dot.js +25 -0
- package/vendor-core/plan-duration-attribution.js +185 -0
- package/vendor-core/plan-graph-model.js +171 -0
- package/vendor-core/plan-node-detail.js +159 -0
- package/vendor-core/plan-summary.js +233 -0
- package/vendor-core/plan-tree-walk.js +29 -0
- package/vendor-core/proxy.js +157 -0
- package/vendor-core/recommendation-rollup.js +197 -0
- package/vendor-core/redact.js +175 -0
- package/vendor-core/rolling-log-reassembly.js +52 -0
- package/vendor-core/run-aggregates.js +44 -0
- package/vendor-core/run-comparison.js +458 -0
- package/vendor-core/scaling-sim.js +73 -0
- package/vendor-core/session-snapshot.js +79 -0
- package/vendor-core/shs-fetch.js +196 -0
- package/vendor-core/shs-load.js +121 -0
- package/vendor-core/shs-request.js +101 -0
- package/vendor-core/shs-schemas.js +13 -0
- package/vendor-core/snappy-block.js +140 -0
- package/vendor-core/stage-quantiles.js +199 -0
- package/vendor-core/threshold-summary.js +35 -0
- package/vendor-core/types.js +286 -0
- package/vendor-core/vendor/fflate.js +2695 -0
- package/vendor-core/vendor/fzstd.js +768 -0
- package/vendor-core/wall-clock.js +36 -0
- package/vendor-core/wasted-core-hours.js +68 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// Single source of truth for the packed per-task numeric array: FIELDS (offset
|
|
2
|
+
// constants), TASK_FIELD_NAMES (display labels), and the finalizeStage hot-loop
|
|
3
|
+
// push order are all derived from this list. `prop` is the task record's real
|
|
4
|
+
// property name, kept separate from `name` since MEM_SPILLED's display label
|
|
5
|
+
// ('memorySpilled') differs from it (memSpilled).
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
const TASK_FIELD_DESCRIPTORS = [
|
|
13
|
+
{ key: 'DURATION', name: 'duration', prop: 'duration' },
|
|
14
|
+
{ key: 'GC_TIME', name: 'gcTime', prop: 'gcTime' },
|
|
15
|
+
{ key: 'MEM_SPILLED', name: 'memorySpilled', prop: 'memSpilled' },
|
|
16
|
+
{ key: 'DISK_SPILLED', name: 'diskSpilled', prop: 'diskSpilled' },
|
|
17
|
+
{ key: 'SHUFFLE_READ', name: 'shuffleRead', prop: 'shuffleRead' },
|
|
18
|
+
{ key: 'SHUFFLE_WRITE', name: 'shuffleWrite', prop: 'shuffleWrite' },
|
|
19
|
+
{ key: 'LAUNCH_TIME', name: 'launchTime', prop: 'launchTime' },
|
|
20
|
+
{ key: 'FINISH_TIME', name: 'finishTime', prop: 'finishTime' },
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const STRIDE = TASK_FIELD_DESCRIPTORS.length;
|
|
24
|
+
|
|
25
|
+
export const FIELDS = Object.freeze({
|
|
26
|
+
...Object.fromEntries(TASK_FIELD_DESCRIPTORS.map((d, i) => [d.key, i])),
|
|
27
|
+
STRIDE,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
;
|
|
32
|
+
|
|
33
|
+
export const TASK_FIELD_NAMES = TASK_FIELD_DESCRIPTORS.map((d) => d.name);
|
|
34
|
+
|
|
35
|
+
const TASK_FIELD_PROPS = TASK_FIELD_DESCRIPTORS.map((d) => d.prop);
|
|
36
|
+
|
|
37
|
+
// Numeric accumulator fields on `stage` that this function reads/increments.
|
|
38
|
+
// `stage`'s public parameter type stays the loose Record shape per this
|
|
39
|
+
// module's Produces contract; this narrow view is only for the arithmetic
|
|
40
|
+
// below, never a restatement of the object's real shape.
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
export function finalizeStage(
|
|
55
|
+
stageId ,
|
|
56
|
+
stage ,
|
|
57
|
+
state
|
|
58
|
+
) {
|
|
59
|
+
// Already finalized (e.g. a duplicate/replayed StageCompleted event):
|
|
60
|
+
// degrade gracefully instead of crashing on stage.taskAttempts.values().
|
|
61
|
+
if (stage.taskAttempts === null) return null;
|
|
62
|
+
|
|
63
|
+
const acc = stage ;
|
|
64
|
+
|
|
65
|
+
const buf = [];
|
|
66
|
+
let taskCount = 0, failedTasks = 0, speculativeTasks = 0;
|
|
67
|
+
const hostStats = new Map();
|
|
68
|
+
const executorStats = new Map();
|
|
69
|
+
const failureReasons = new Map();
|
|
70
|
+
const localityStats = new Map();
|
|
71
|
+
let peakExecutionMemoryMax = 0;
|
|
72
|
+
|
|
73
|
+
for (const t of stage.taskAttempts.values()) {
|
|
74
|
+
taskCount++;
|
|
75
|
+
if (t.failed) {
|
|
76
|
+
failedTasks++;
|
|
77
|
+
if (t.reason) failureReasons.set(t.reason, (failureReasons.get(t.reason) ?? 0) + 1);
|
|
78
|
+
}
|
|
79
|
+
if (t.speculative) speculativeTasks++;
|
|
80
|
+
if (t.host) {
|
|
81
|
+
let hs = hostStats.get(t.host);
|
|
82
|
+
if (!hs) { hs = { taskCount: 0, totalDuration: 0 }; hostStats.set(t.host, hs); }
|
|
83
|
+
hs.taskCount++;
|
|
84
|
+
hs.totalDuration += t.duration;
|
|
85
|
+
}
|
|
86
|
+
if (t.executorId) {
|
|
87
|
+
let es = executorStats.get(t.executorId);
|
|
88
|
+
if (!es) { es = { taskCount: 0, totalDuration: 0, inputBytes: 0, shuffleReadBytes: 0, shuffleWriteBytes: 0 }; executorStats.set(t.executorId, es); }
|
|
89
|
+
es.taskCount++;
|
|
90
|
+
es.totalDuration += t.duration;
|
|
91
|
+
es.inputBytes += t.inputBytes;
|
|
92
|
+
es.shuffleReadBytes += t.shuffleRead;
|
|
93
|
+
es.shuffleWriteBytes += t.shuffleWrite;
|
|
94
|
+
}
|
|
95
|
+
if (t.locality) {
|
|
96
|
+
localityStats.set(t.locality, (localityStats.get(t.locality) ?? 0) + 1);
|
|
97
|
+
}
|
|
98
|
+
if (t.peakExecMem > peakExecutionMemoryMax) peakExecutionMemoryMax = t.peakExecMem;
|
|
99
|
+
acc.shuffleReadBytes += t.shuffleRead;
|
|
100
|
+
acc.shuffleWriteBytes += t.shuffleWrite;
|
|
101
|
+
acc.fetchWaitTime += t.fetchWaitTime;
|
|
102
|
+
acc.memoryBytesSpilled += t.memSpilled;
|
|
103
|
+
acc.diskBytesSpilled += t.diskSpilled;
|
|
104
|
+
acc.jvmGCTime += t.gcTime;
|
|
105
|
+
acc.executorRunTime += t.executorRunTime;
|
|
106
|
+
acc.executorCpuTime += t.executorCpuTime;
|
|
107
|
+
acc.inputBytes += t.inputBytes;
|
|
108
|
+
acc.outputBytes += t.outputBytes;
|
|
109
|
+
for (let i = 0; i < TASK_FIELD_PROPS.length; i++) buf.push(t[TASK_FIELD_PROPS[i]]);
|
|
110
|
+
}
|
|
111
|
+
stage.taskCount = taskCount;
|
|
112
|
+
stage.failedTasks = failedTasks;
|
|
113
|
+
stage.speculativeTasks = speculativeTasks;
|
|
114
|
+
stage.taskAttempts = null; // no longer needed after finalize, freeing memory
|
|
115
|
+
|
|
116
|
+
const arr = new Float64Array(buf);
|
|
117
|
+
state.taskStore.set(stageId, arr);
|
|
118
|
+
|
|
119
|
+
const { p50, p95, max } = computeDurationQuantiles(arr);
|
|
120
|
+
const spillClass = acc.memoryBytesSpilled > 0 ? classifySpill(arr) : 'unclassified';
|
|
121
|
+
const { p50: shuffleReadP50, p95: shuffleReadP95, max: shuffleReadMax } = computeFieldQuantiles(arr, FIELDS.SHUFFLE_READ);
|
|
122
|
+
const { p50: spillMemP50, p95: spillMemP95, max: spillMemMax } = computeFieldQuantiles(arr, FIELDS.MEM_SPILLED);
|
|
123
|
+
const { p50: spillDiskP50, p95: spillDiskP95, max: spillDiskMax } = computeFieldQuantiles(arr, FIELDS.DISK_SPILLED);
|
|
124
|
+
|
|
125
|
+
// Straggler count: tasks with duration > 4 * P50.
|
|
126
|
+
const stragglerThreshold = 4 * p50;
|
|
127
|
+
let stragglerCount = 0;
|
|
128
|
+
const taskArrCount = arr.length / FIELDS.STRIDE;
|
|
129
|
+
if (p50 > 0) {
|
|
130
|
+
for (let i = 0; i < taskArrCount; i++) {
|
|
131
|
+
if (arr[i * FIELDS.STRIDE + FIELDS.DURATION] > stragglerThreshold) stragglerCount++;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const hostStatsArr = [...hostStats.entries()].map(
|
|
136
|
+
([host, s]) => ({ host, taskCount: s.taskCount, totalDuration: s.totalDuration })
|
|
137
|
+
);
|
|
138
|
+
const executorStatsArr = [...executorStats.entries()].map(
|
|
139
|
+
([executorId, s]) => ({ executorId, taskCount: s.taskCount, totalDuration: s.totalDuration, inputBytes: s.inputBytes, shuffleReadBytes: s.shuffleReadBytes, shuffleWriteBytes: s.shuffleWriteBytes })
|
|
140
|
+
);
|
|
141
|
+
const failureReasonsArr = [...failureReasons.entries()].map(
|
|
142
|
+
([reason, count]) => ({ reason, count })
|
|
143
|
+
);
|
|
144
|
+
const localityStatsArr = [...localityStats.entries()].map(
|
|
145
|
+
([locality, count]) => ({ locality, count })
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const data = {
|
|
149
|
+
...stage,
|
|
150
|
+
hostStats: hostStatsArr, executorStats: executorStatsArr, failureReasons: failureReasonsArr, localityStats: localityStatsArr, stragglerCount,
|
|
151
|
+
peakExecutionMemoryMax,
|
|
152
|
+
taskDurationP50: p50,
|
|
153
|
+
taskDurationP95: p95,
|
|
154
|
+
taskDurationMax: max,
|
|
155
|
+
shuffleReadP50, shuffleReadP95, shuffleReadMax,
|
|
156
|
+
spillMemP50, spillMemP95, spillMemMax,
|
|
157
|
+
spillDiskP50, spillDiskP95, spillDiskMax,
|
|
158
|
+
gcPct: acc.executorRunTime > 0 ? (acc.jvmGCTime / acc.executorRunTime) * 100 : 0,
|
|
159
|
+
spillClassification: spillClass,
|
|
160
|
+
stageType: acc.shuffleReadBytes > 0 ? 'REDUCE' : 'MAP',
|
|
161
|
+
};
|
|
162
|
+
delete data.taskAttempts; // internal-only field, already nulled above; never part of the public message
|
|
163
|
+
|
|
164
|
+
return { type: 'stage', data };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function computeFieldQuantiles(arr , fieldIndex ) {
|
|
168
|
+
const taskCount = arr.length / FIELDS.STRIDE;
|
|
169
|
+
if (taskCount === 0) return { p50: 0, p95: 0, max: 0 };
|
|
170
|
+
|
|
171
|
+
const values = new Float64Array(taskCount);
|
|
172
|
+
for (let i = 0; i < taskCount; i++) values[i] = arr[i * FIELDS.STRIDE + fieldIndex];
|
|
173
|
+
values.sort();
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
p50: values[Math.ceil(taskCount * 0.50) - 1],
|
|
177
|
+
p95: values[Math.ceil(taskCount * 0.95) - 1],
|
|
178
|
+
max: values[taskCount - 1],
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function computeDurationQuantiles(arr ) {
|
|
183
|
+
return computeFieldQuantiles(arr, FIELDS.DURATION);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function classifySpill(arr ) {
|
|
187
|
+
const taskCount = arr.length / FIELDS.STRIDE;
|
|
188
|
+
if (taskCount === 0) return 'unclassified';
|
|
189
|
+
|
|
190
|
+
let zeroCount = 0;
|
|
191
|
+
for (let i = 0; i < taskCount; i++) {
|
|
192
|
+
if (arr[i * FIELDS.STRIDE + FIELDS.MEM_SPILLED] === 0) zeroCount++;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const zeroFraction = zeroCount / taskCount;
|
|
196
|
+
if (zeroFraction >= 0.80) return 'skew';
|
|
197
|
+
if (zeroFraction < 0.20) return 'volume';
|
|
198
|
+
return 'unclassified';
|
|
199
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const THRESHOLD_SUMMARIES = {
|
|
2
|
+
spill: 'single-task disk spill above 1 GiB',
|
|
3
|
+
shuffle: 'shuffle read above the configured minimum byte threshold',
|
|
4
|
+
skew: 'task duration skew above the configured ratio',
|
|
5
|
+
gc: 'JVM GC time share above the configured ratio',
|
|
6
|
+
slowHost: 'a host running 2x+ slower than its peers by mean task duration (per-executor byte/time dimensions use a separate, narrower ratio ladder starting at 1.33x; only those can reach critical on ratio alone)',
|
|
7
|
+
stageSlowness: 'a stage running far longer than its peers, not attributable to a single slow host',
|
|
8
|
+
straggler: 'one or more tasks finishing far after the rest of their stage',
|
|
9
|
+
speculationWaste: 'speculative task attempts that completed after the original',
|
|
10
|
+
tinyTask: 'median task duration below the configured floor',
|
|
11
|
+
partitionSizing: 'partition byte size outside the configured target range',
|
|
12
|
+
stageShape: 'low parallelism, data explosion, or task-count skew relative to core count',
|
|
13
|
+
stageFailed: 'a stage that failed outright',
|
|
14
|
+
failures: 'task failures above the configured rate',
|
|
15
|
+
retryWaste: 'retried task attempts consuming executor time',
|
|
16
|
+
coldStart: 'executor startup time above the configured floor',
|
|
17
|
+
incompleteRun: 'an event log missing its terminal ApplicationEnd/job-completion event',
|
|
18
|
+
utilization: 'core occupancy below the configured floor across the run',
|
|
19
|
+
memoryUtilization: 'executor heap usage outside the configured band',
|
|
20
|
+
cacheUtilization: 'cached partitions evicted or spilled to disk',
|
|
21
|
+
coreLocality: 'task placement missing data-local core assignment',
|
|
22
|
+
cachingOpportunity: 'a dataset re-read from source multiple times with no cache/persist',
|
|
23
|
+
jobFailureRate: 'job failure rate above the configured threshold',
|
|
24
|
+
autoscalingChurn: 'executor add/remove churn above the configured rate',
|
|
25
|
+
configAudit: 'a Spark conf value outside the recommended range',
|
|
26
|
+
duplicatePlanSubtree: 'the same physical plan subtree executed more than once',
|
|
27
|
+
smallFiles: 'output files below the configured target size',
|
|
28
|
+
overBroadcast: 'a broadcast join above the configured size ceiling',
|
|
29
|
+
underBroadcast: 'a join below the configured size floor that skipped broadcast',
|
|
30
|
+
broadcastSizing: 'a broadcast join outside the configured size range in either direction',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export function getThresholdSummary(type ) {
|
|
34
|
+
return THRESHOLD_SUMMARIES[type] ?? 'criteria not met';
|
|
35
|
+
}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
// Contract-pinned evidence `eventType`: the same fixed set as the parser's
|
|
42
|
+
// `evidenceInputs` counter keys, so provenance stays addressable against the
|
|
43
|
+
// documented `{ eventType, count }` shape (review: values must not diverge
|
|
44
|
+
// from the counters they summarize).
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
/** Static, hand-authored tag for how much work a finding's fix requires:
|
|
201
|
+
* a conf/spark-submit flag change, a Spark job edit, or a pipeline restructure. */
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
|