1 /*
2 Copyright 2011 Google Inc. All Rights Reserved.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15
16 Author: lode.vandevenne@gmail.com (Lode Vandevenne)
17 Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala)
18 */
19
20 #include "squeeze.h"
21
22 #include <assert.h>
23 #include <math.h>
24 #include <stdio.h>
25
26 #include "blocksplitter.h"
27 #include "deflate.h"
28 #include "symbols.h"
29 #include "tree.h"
30 #include "util.h"
31
32 typedef struct SymbolStats {
33 /* The literal and length symbols. */
34 size_t litlens[ZOPFLI_NUM_LL];
35 /* The 32 unique dist symbols, not the 32768 possible dists. */
36 size_t dists[ZOPFLI_NUM_D];
37
38 /* Length of each lit/len symbol in bits. */
39 double ll_symbols[ZOPFLI_NUM_LL];
40 /* Length of each dist symbol in bits. */
41 double d_symbols[ZOPFLI_NUM_D];
42 } SymbolStats;
43
44 /* Sets everything to 0. */
InitStats(SymbolStats * stats)45 static void InitStats(SymbolStats* stats) {
46 memset(stats->litlens, 0, ZOPFLI_NUM_LL * sizeof(stats->litlens[0]));
47 memset(stats->dists, 0, ZOPFLI_NUM_D * sizeof(stats->dists[0]));
48
49 memset(stats->ll_symbols, 0, ZOPFLI_NUM_LL * sizeof(stats->ll_symbols[0]));
50 memset(stats->d_symbols, 0, ZOPFLI_NUM_D * sizeof(stats->d_symbols[0]));
51 }
52
CopyStats(SymbolStats * source,SymbolStats * dest)53 static void CopyStats(SymbolStats* source, SymbolStats* dest) {
54 memcpy(dest->litlens, source->litlens,
55 ZOPFLI_NUM_LL * sizeof(dest->litlens[0]));
56 memcpy(dest->dists, source->dists, ZOPFLI_NUM_D * sizeof(dest->dists[0]));
57
58 memcpy(dest->ll_symbols, source->ll_symbols,
59 ZOPFLI_NUM_LL * sizeof(dest->ll_symbols[0]));
60 memcpy(dest->d_symbols, source->d_symbols,
61 ZOPFLI_NUM_D * sizeof(dest->d_symbols[0]));
62 }
63
64 /* Adds the bit lengths. */
AddWeighedStatFreqs(const SymbolStats * stats1,double w1,const SymbolStats * stats2,double w2,SymbolStats * result)65 static void AddWeighedStatFreqs(const SymbolStats* stats1, double w1,
66 const SymbolStats* stats2, double w2,
67 SymbolStats* result) {
68 size_t i;
69 for (i = 0; i < ZOPFLI_NUM_LL; i++) {
70 result->litlens[i] =
71 (size_t) (stats1->litlens[i] * w1 + stats2->litlens[i] * w2);
72 }
73 for (i = 0; i < ZOPFLI_NUM_D; i++) {
74 result->dists[i] =
75 (size_t) (stats1->dists[i] * w1 + stats2->dists[i] * w2);
76 }
77 result->litlens[256] = 1; /* End symbol. */
78 }
79
80 typedef struct RanState {
81 unsigned int m_w, m_z;
82 } RanState;
83
InitRanState(RanState * state)84 static void InitRanState(RanState* state) {
85 state->m_w = 1;
86 state->m_z = 2;
87 }
88
89 /* Get random number: "Multiply-With-Carry" generator of G. Marsaglia */
Ran(RanState * state)90 static unsigned int Ran(RanState* state) {
91 state->m_z = 36969 * (state->m_z & 65535) + (state->m_z >> 16);
92 state->m_w = 18000 * (state->m_w & 65535) + (state->m_w >> 16);
93 return (state->m_z << 16) + state->m_w; /* 32-bit result. */
94 }
95
RandomizeFreqs(RanState * state,size_t * freqs,int n)96 static void RandomizeFreqs(RanState* state, size_t* freqs, int n) {
97 int i;
98 for (i = 0; i < n; i++) {
99 if ((Ran(state) >> 4) % 3 == 0) freqs[i] = freqs[Ran(state) % n];
100 }
101 }
102
RandomizeStatFreqs(RanState * state,SymbolStats * stats)103 static void RandomizeStatFreqs(RanState* state, SymbolStats* stats) {
104 RandomizeFreqs(state, stats->litlens, ZOPFLI_NUM_LL);
105 RandomizeFreqs(state, stats->dists, ZOPFLI_NUM_D);
106 stats->litlens[256] = 1; /* End symbol. */
107 }
108
ClearStatFreqs(SymbolStats * stats)109 static void ClearStatFreqs(SymbolStats* stats) {
110 size_t i;
111 for (i = 0; i < ZOPFLI_NUM_LL; i++) stats->litlens[i] = 0;
112 for (i = 0; i < ZOPFLI_NUM_D; i++) stats->dists[i] = 0;
113 }
114
115 /*
116 Function that calculates a cost based on a model for the given LZ77 symbol.
117 litlen: means literal symbol if dist is 0, length otherwise.
118 */
119 typedef double CostModelFun(unsigned litlen, unsigned dist, void* context);
120
121 /*
122 Cost model which should exactly match fixed tree.
123 type: CostModelFun
124 */
GetCostFixed(unsigned litlen,unsigned dist,void * unused)125 static double GetCostFixed(unsigned litlen, unsigned dist, void* unused) {
126 (void)unused;
127 if (dist == 0) {
128 if (litlen <= 143) return 8;
129 else return 9;
130 } else {
131 int dbits = ZopfliGetDistExtraBits(dist);
132 int lbits = ZopfliGetLengthExtraBits(litlen);
133 int lsym = ZopfliGetLengthSymbol(litlen);
134 int cost = 0;
135 if (lsym <= 279) cost += 7;
136 else cost += 8;
137 cost += 5; /* Every dist symbol has length 5. */
138 return cost + dbits + lbits;
139 }
140 }
141
142 /*
143 Cost model based on symbol statistics.
144 type: CostModelFun
145 */
GetCostStat(unsigned litlen,unsigned dist,void * context)146 static double GetCostStat(unsigned litlen, unsigned dist, void* context) {
147 SymbolStats* stats = (SymbolStats*)context;
148 if (dist == 0) {
149 return stats->ll_symbols[litlen];
150 } else {
151 int lsym = ZopfliGetLengthSymbol(litlen);
152 int lbits = ZopfliGetLengthExtraBits(litlen);
153 int dsym = ZopfliGetDistSymbol(dist);
154 int dbits = ZopfliGetDistExtraBits(dist);
155 return lbits + dbits + stats->ll_symbols[lsym] + stats->d_symbols[dsym];
156 }
157 }
158
159 /*
160 Finds the minimum possible cost this cost model can return for valid length and
161 distance symbols.
162 */
GetCostModelMinCost(CostModelFun * costmodel,void * costcontext)163 static double GetCostModelMinCost(CostModelFun* costmodel, void* costcontext) {
164 double mincost;
165 int bestlength = 0; /* length that has lowest cost in the cost model */
166 int bestdist = 0; /* distance that has lowest cost in the cost model */
167 int i;
168 /*
169 Table of distances that have a different distance symbol in the deflate
170 specification. Each value is the first distance that has a new symbol. Only
171 different symbols affect the cost model so only these need to be checked.
172 See RFC 1951 section 3.2.5. Compressed blocks (length and distance codes).
173 */
174 static const int dsymbols[30] = {
175 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513,
176 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
177 };
178
179 mincost = ZOPFLI_LARGE_FLOAT;
180 for (i = 3; i < 259; i++) {
181 double c = costmodel(i, 1, costcontext);
182 if (c < mincost) {
183 bestlength = i;
184 mincost = c;
185 }
186 }
187
188 mincost = ZOPFLI_LARGE_FLOAT;
189 for (i = 0; i < 30; i++) {
190 double c = costmodel(3, dsymbols[i], costcontext);
191 if (c < mincost) {
192 bestdist = dsymbols[i];
193 mincost = c;
194 }
195 }
196
197 return costmodel(bestlength, bestdist, costcontext);
198 }
199
zopfli_min(size_t a,size_t b)200 static size_t zopfli_min(size_t a, size_t b) {
201 return a < b ? a : b;
202 }
203
204 /*
205 Performs the forward pass for "squeeze". Gets the most optimal length to reach
206 every byte from a previous byte, using cost calculations.
207 s: the ZopfliBlockState
208 in: the input data array
209 instart: where to start
210 inend: where to stop (not inclusive)
211 costmodel: function to calculate the cost of some lit/len/dist pair.
212 costcontext: abstract context for the costmodel function
213 length_array: output array of size (inend - instart) which will receive the best
214 length to reach this byte from a previous byte.
215 returns the cost that was, according to the costmodel, needed to get to the end.
216 */
GetBestLengths(ZopfliBlockState * s,const unsigned char * in,size_t instart,size_t inend,CostModelFun * costmodel,void * costcontext,unsigned short * length_array,ZopfliHash * h,float * costs)217 static double GetBestLengths(ZopfliBlockState *s,
218 const unsigned char* in,
219 size_t instart, size_t inend,
220 CostModelFun* costmodel, void* costcontext,
221 unsigned short* length_array,
222 ZopfliHash* h, float* costs) {
223 /* Best cost to get here so far. */
224 size_t blocksize = inend - instart;
225 size_t i = 0, k, kend;
226 unsigned short leng;
227 unsigned short dist;
228 unsigned short sublen[259];
229 size_t windowstart = instart > ZOPFLI_WINDOW_SIZE
230 ? instart - ZOPFLI_WINDOW_SIZE : 0;
231 double result;
232 double mincost = GetCostModelMinCost(costmodel, costcontext);
233 double mincostaddcostj;
234
235 if (instart == inend) return 0;
236
237 ZopfliResetHash(ZOPFLI_WINDOW_SIZE, h);
238 ZopfliWarmupHash(in, windowstart, inend, h);
239 for (i = windowstart; i < instart; i++) {
240 ZopfliUpdateHash(in, i, inend, h);
241 }
242
243 for (i = 1; i < blocksize + 1; i++) costs[i] = ZOPFLI_LARGE_FLOAT;
244 costs[0] = 0; /* Because it's the start. */
245 length_array[0] = 0;
246
247 for (i = instart; i < inend; i++) {
248 size_t j = i - instart; /* Index in the costs array and length_array. */
249 ZopfliUpdateHash(in, i, inend, h);
250
251 #ifdef ZOPFLI_SHORTCUT_LONG_REPETITIONS
252 /* If we're in a long repetition of the same character and have more than
253 ZOPFLI_MAX_MATCH characters before and after our position. */
254 if (h->same[i & ZOPFLI_WINDOW_MASK] > ZOPFLI_MAX_MATCH * 2
255 && i > instart + ZOPFLI_MAX_MATCH + 1
256 && i + ZOPFLI_MAX_MATCH * 2 + 1 < inend
257 && h->same[(i - ZOPFLI_MAX_MATCH) & ZOPFLI_WINDOW_MASK]
258 > ZOPFLI_MAX_MATCH) {
259 double symbolcost = costmodel(ZOPFLI_MAX_MATCH, 1, costcontext);
260 /* Set the length to reach each one to ZOPFLI_MAX_MATCH, and the cost to
261 the cost corresponding to that length. Doing this, we skip
262 ZOPFLI_MAX_MATCH values to avoid calling ZopfliFindLongestMatch. */
263 for (k = 0; k < ZOPFLI_MAX_MATCH; k++) {
264 costs[j + ZOPFLI_MAX_MATCH] = costs[j] + symbolcost;
265 length_array[j + ZOPFLI_MAX_MATCH] = ZOPFLI_MAX_MATCH;
266 i++;
267 j++;
268 ZopfliUpdateHash(in, i, inend, h);
269 }
270 }
271 #endif
272
273 ZopfliFindLongestMatch(s, h, in, i, inend, ZOPFLI_MAX_MATCH, sublen,
274 &dist, &leng);
275
276 /* Literal. */
277 if (i + 1 <= inend) {
278 double newCost = costmodel(in[i], 0, costcontext) + costs[j];
279 assert(newCost >= 0);
280 if (newCost < costs[j + 1]) {
281 costs[j + 1] = newCost;
282 length_array[j + 1] = 1;
283 }
284 }
285 /* Lengths. */
286 kend = zopfli_min(leng, inend-i);
287 mincostaddcostj = mincost + costs[j];
288 for (k = 3; k <= kend; k++) {
289 double newCost;
290
291 /* Calling the cost model is expensive, avoid this if we are already at
292 the minimum possible cost that it can return. */
293 if (costs[j + k] <= mincostaddcostj) continue;
294
295 newCost = costmodel(k, sublen[k], costcontext) + costs[j];
296 assert(newCost >= 0);
297 if (newCost < costs[j + k]) {
298 assert(k <= ZOPFLI_MAX_MATCH);
299 costs[j + k] = newCost;
300 length_array[j + k] = k;
301 }
302 }
303 }
304
305 assert(costs[blocksize] >= 0);
306 result = costs[blocksize];
307
308 return result;
309 }
310
311 /*
312 Calculates the optimal path of lz77 lengths to use, from the calculated
313 length_array. The length_array must contain the optimal length to reach that
314 byte. The path will be filled with the lengths to use, so its data size will be
315 the amount of lz77 symbols.
316 */
TraceBackwards(size_t size,const unsigned short * length_array,unsigned short ** path,size_t * pathsize)317 static void TraceBackwards(size_t size, const unsigned short* length_array,
318 unsigned short** path, size_t* pathsize) {
319 size_t index = size;
320 if (size == 0) return;
321 for (;;) {
322 ZOPFLI_APPEND_DATA(length_array[index], path, pathsize);
323 assert(length_array[index] <= index);
324 assert(length_array[index] <= ZOPFLI_MAX_MATCH);
325 assert(length_array[index] != 0);
326 index -= length_array[index];
327 if (index == 0) break;
328 }
329
330 /* Mirror result. */
331 for (index = 0; index < *pathsize / 2; index++) {
332 unsigned short temp = (*path)[index];
333 (*path)[index] = (*path)[*pathsize - index - 1];
334 (*path)[*pathsize - index - 1] = temp;
335 }
336 }
337
FollowPath(ZopfliBlockState * s,const unsigned char * in,size_t instart,size_t inend,unsigned short * path,size_t pathsize,ZopfliLZ77Store * store,ZopfliHash * h)338 static void FollowPath(ZopfliBlockState* s,
339 const unsigned char* in, size_t instart, size_t inend,
340 unsigned short* path, size_t pathsize,
341 ZopfliLZ77Store* store, ZopfliHash *h) {
342 size_t i, j, pos = 0;
343 size_t windowstart = instart > ZOPFLI_WINDOW_SIZE
344 ? instart - ZOPFLI_WINDOW_SIZE : 0;
345
346 size_t total_length_test = 0;
347
348 if (instart == inend) return;
349
350 ZopfliResetHash(ZOPFLI_WINDOW_SIZE, h);
351 ZopfliWarmupHash(in, windowstart, inend, h);
352 for (i = windowstart; i < instart; i++) {
353 ZopfliUpdateHash(in, i, inend, h);
354 }
355
356 pos = instart;
357 for (i = 0; i < pathsize; i++) {
358 unsigned short length = path[i];
359 unsigned short dummy_length;
360 unsigned short dist;
361 assert(pos < inend);
362
363 ZopfliUpdateHash(in, pos, inend, h);
364
365 /* Add to output. */
366 if (length >= ZOPFLI_MIN_MATCH) {
367 /* Get the distance by recalculating longest match. The found length
368 should match the length from the path. */
369 ZopfliFindLongestMatch(s, h, in, pos, inend, length, 0,
370 &dist, &dummy_length);
371 assert(!(dummy_length != length && length > 2 && dummy_length > 2));
372 ZopfliVerifyLenDist(in, inend, pos, dist, length);
373 ZopfliStoreLitLenDist(length, dist, pos, store);
374 total_length_test += length;
375 } else {
376 length = 1;
377 ZopfliStoreLitLenDist(in[pos], 0, pos, store);
378 total_length_test++;
379 }
380
381
382 assert(pos + length <= inend);
383 for (j = 1; j < length; j++) {
384 ZopfliUpdateHash(in, pos + j, inend, h);
385 }
386
387 pos += length;
388 }
389 }
390
391 /* Calculates the entropy of the statistics */
CalculateStatistics(SymbolStats * stats)392 static void CalculateStatistics(SymbolStats* stats) {
393 ZopfliCalculateEntropy(stats->litlens, ZOPFLI_NUM_LL, stats->ll_symbols);
394 ZopfliCalculateEntropy(stats->dists, ZOPFLI_NUM_D, stats->d_symbols);
395 }
396
397 /* Appends the symbol statistics from the store. */
GetStatistics(const ZopfliLZ77Store * store,SymbolStats * stats)398 static void GetStatistics(const ZopfliLZ77Store* store, SymbolStats* stats) {
399 size_t i;
400 for (i = 0; i < store->size; i++) {
401 if (store->dists[i] == 0) {
402 stats->litlens[store->litlens[i]]++;
403 } else {
404 stats->litlens[ZopfliGetLengthSymbol(store->litlens[i])]++;
405 stats->dists[ZopfliGetDistSymbol(store->dists[i])]++;
406 }
407 }
408 stats->litlens[256] = 1; /* End symbol. */
409
410 CalculateStatistics(stats);
411 }
412
413 /*
414 Does a single run for ZopfliLZ77Optimal. For good compression, repeated runs
415 with updated statistics should be performed.
416 s: the block state
417 in: the input data array
418 instart: where to start
419 inend: where to stop (not inclusive)
420 path: pointer to dynamically allocated memory to store the path
421 pathsize: pointer to the size of the dynamic path array
422 length_array: array of size (inend - instart) used to store lengths
423 costmodel: function to use as the cost model for this squeeze run
424 costcontext: abstract context for the costmodel function
425 store: place to output the LZ77 data
426 returns the cost that was, according to the costmodel, needed to get to the end.
427 This is not the actual cost.
428 */
LZ77OptimalRun(ZopfliBlockState * s,const unsigned char * in,size_t instart,size_t inend,unsigned short ** path,size_t * pathsize,unsigned short * length_array,CostModelFun * costmodel,void * costcontext,ZopfliLZ77Store * store,ZopfliHash * h,float * costs)429 static double LZ77OptimalRun(ZopfliBlockState* s,
430 const unsigned char* in, size_t instart, size_t inend,
431 unsigned short** path, size_t* pathsize,
432 unsigned short* length_array, CostModelFun* costmodel,
433 void* costcontext, ZopfliLZ77Store* store,
434 ZopfliHash* h, float* costs) {
435 double cost = GetBestLengths(s, in, instart, inend, costmodel,
436 costcontext, length_array, h, costs);
437 free(*path);
438 *path = 0;
439 *pathsize = 0;
440 TraceBackwards(inend - instart, length_array, path, pathsize);
441 FollowPath(s, in, instart, inend, *path, *pathsize, store, h);
442 assert(cost < ZOPFLI_LARGE_FLOAT);
443 return cost;
444 }
445
ZopfliLZ77Optimal(ZopfliBlockState * s,const unsigned char * in,size_t instart,size_t inend,int numiterations,ZopfliLZ77Store * store)446 void ZopfliLZ77Optimal(ZopfliBlockState *s,
447 const unsigned char* in, size_t instart, size_t inend,
448 int numiterations,
449 ZopfliLZ77Store* store) {
450 /* Dist to get to here with smallest cost. */
451 size_t blocksize = inend - instart;
452 unsigned short* length_array =
453 (unsigned short*)malloc(sizeof(unsigned short) * (blocksize + 1));
454 unsigned short* path = 0;
455 size_t pathsize = 0;
456 ZopfliLZ77Store currentstore;
457 ZopfliHash hash;
458 ZopfliHash* h = &hash;
459 SymbolStats stats, beststats, laststats;
460 int i;
461 float* costs = (float*)malloc(sizeof(float) * (blocksize + 1));
462 double cost;
463 double bestcost = ZOPFLI_LARGE_FLOAT;
464 double lastcost = 0;
465 /* Try randomizing the costs a bit once the size stabilizes. */
466 RanState ran_state;
467 int lastrandomstep = -1;
468
469 if (!costs) exit(-1); /* Allocation failed. */
470 if (!length_array) exit(-1); /* Allocation failed. */
471
472 InitRanState(&ran_state);
473 InitStats(&stats);
474 ZopfliInitLZ77Store(in, ¤tstore);
475 ZopfliAllocHash(ZOPFLI_WINDOW_SIZE, h);
476
477 /* Do regular deflate, then loop multiple shortest path runs, each time using
478 the statistics of the previous run. */
479
480 /* Initial run. */
481 ZopfliLZ77Greedy(s, in, instart, inend, ¤tstore, h);
482 GetStatistics(¤tstore, &stats);
483
484 /* Repeat statistics with each time the cost model from the previous stat
485 run. */
486 for (i = 0; i < numiterations; i++) {
487 ZopfliCleanLZ77Store(¤tstore);
488 ZopfliInitLZ77Store(in, ¤tstore);
489 LZ77OptimalRun(s, in, instart, inend, &path, &pathsize,
490 length_array, GetCostStat, (void*)&stats,
491 ¤tstore, h, costs);
492 cost = ZopfliCalculateBlockSize(¤tstore, 0, currentstore.size, 2);
493 if (s->options->verbose_more || (s->options->verbose && cost < bestcost)) {
494 fprintf(stderr, "Iteration %d: %d bit\n", i, (int) cost);
495 }
496 if (cost < bestcost) {
497 /* Copy to the output store. */
498 ZopfliCopyLZ77Store(¤tstore, store);
499 CopyStats(&stats, &beststats);
500 bestcost = cost;
501 }
502 CopyStats(&stats, &laststats);
503 ClearStatFreqs(&stats);
504 GetStatistics(¤tstore, &stats);
505 if (lastrandomstep != -1) {
506 /* This makes it converge slower but better. Do it only once the
507 randomness kicks in so that if the user does few iterations, it gives a
508 better result sooner. */
509 AddWeighedStatFreqs(&stats, 1.0, &laststats, 0.5, &stats);
510 CalculateStatistics(&stats);
511 }
512 if (i > 5 && cost == lastcost) {
513 CopyStats(&beststats, &stats);
514 RandomizeStatFreqs(&ran_state, &stats);
515 CalculateStatistics(&stats);
516 lastrandomstep = i;
517 }
518 lastcost = cost;
519 }
520
521 free(length_array);
522 free(path);
523 free(costs);
524 ZopfliCleanLZ77Store(¤tstore);
525 ZopfliCleanHash(h);
526 }
527
ZopfliLZ77OptimalFixed(ZopfliBlockState * s,const unsigned char * in,size_t instart,size_t inend,ZopfliLZ77Store * store)528 void ZopfliLZ77OptimalFixed(ZopfliBlockState *s,
529 const unsigned char* in,
530 size_t instart, size_t inend,
531 ZopfliLZ77Store* store)
532 {
533 /* Dist to get to here with smallest cost. */
534 size_t blocksize = inend - instart;
535 unsigned short* length_array =
536 (unsigned short*)malloc(sizeof(unsigned short) * (blocksize + 1));
537 unsigned short* path = 0;
538 size_t pathsize = 0;
539 ZopfliHash hash;
540 ZopfliHash* h = &hash;
541 float* costs = (float*)malloc(sizeof(float) * (blocksize + 1));
542
543 if (!costs) exit(-1); /* Allocation failed. */
544 if (!length_array) exit(-1); /* Allocation failed. */
545
546 ZopfliAllocHash(ZOPFLI_WINDOW_SIZE, h);
547
548 s->blockstart = instart;
549 s->blockend = inend;
550
551 /* Shortest path for fixed tree This one should give the shortest possible
552 result for fixed tree, no repeated runs are needed since the tree is known. */
553 LZ77OptimalRun(s, in, instart, inend, &path, &pathsize,
554 length_array, GetCostFixed, 0, store, h, costs);
555
556 free(length_array);
557 free(path);
558 free(costs);
559 ZopfliCleanHash(h);
560 }
561