public
Description: This is where my memcached work lives before svn munges the changes.
Homepage: http://www.danga.com/memcached/
Clone URL: git://github.com/dustin/memcached.git
Merge multithreaded into trunk, commit #2 (first commit only did the
new files, not the modified ones.)


git-svn-id: http://code.sixapart.com/svn/memcached/trunk/server@509 
b0b603af-a30f-0410-a34e-baf09ae79d0b
sgrimm (author)
Mon Apr 16 08:34:03 -0700 2007
commit  111846a42f3a37af17486f4d94c27ace266b45bd
tree    6a3b5a81e147e26321f88ff679d345cb3cea025f
parent  6d10c112651a4b97a28a62ced6ed4b20ac4672c9
...
69
70
71
 
 
 
 
 
 
 
 
 
 
 
72
73
74
...
97
98
99
 
 
 
 
 
100
101
102
103
 
 
 
 
 
104
105
106
...
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
...
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
0
@@ -69,6 +69,17 @@
0
 
0
   * Explicitly compare against NULL or zero in many places.
0
 
0
+2007-03-05
0
+ * Steven Grimm <sgrimm@facebook.com>: Per-object-type stats collection
0
+ support. Specify the object type delimiter with the -D command line
0
+ option. Turn stats gathering on and off with "stats detail on" and
0
+ "stats detail off". Dump the per-object-type details with
0
+ "stats detail dump".
0
+
0
+2007-03-01
0
+ * Steven Grimm <sgrimm@facebook.com>: Fix an off-by-one error in the
0
+ multithreaded version's message passing code.
0
+
0
 2006-12-23
0
   * fix expirations of items set with absolute expiration times in
0
    the past, before the server's start time. bug was introduced in
0
@@ -97,10 +108,20 @@
0
   * Steve Peters <steve@fisharerojo.org>: OpenBSD has a malloc.h,
0
   but warns to use stdlib.h instead
0
 
0
+2006-11-22
0
+ * Steven Grimm <sgrimm@facebook.com>: Add support for multithreaded
0
+ execution. Run configure with "--enable-threads" to enable. See
0
+ doc/threads.txt for details.
0
+
0
 2006-11-13
0
   * Iain Wade <iwade@optusnet.com.au>: Fix for UDP responses on non-"get"
0
    commands.
0
 
0
+2006-10-15
0
+ * Steven Grimm <sgrimm@facebook.com>: Dynamic sizing of hashtable to
0
+ reduce collisions on very large caches and conserve memory on
0
+ small caches.
0
+
0
 2006-10-13
0
   * Steven Grimm <sgrimm@facebook.com>: New faster hash function.
0
 
...
1
2
3
 
4
5
6
...
1
2
 
3
4
5
6
0
@@ -1,6 +1,6 @@
0
 bin_PROGRAMS = memcached memcached-debug
0
 
0
-memcached_SOURCES = memcached.c slabs.c slabs.h items.c items.h assoc.c assoc.h memcached.h
0
+memcached_SOURCES = memcached.c slabs.c slabs.h items.c items.h assoc.c assoc.h memcached.h thread.c stats.c stats.h
0
 memcached_debug_SOURCES = $(memcached_SOURCES)
0
 memcached_CPPFLAGS = -DNDEBUG
0
 memcached_LDADD = @LIBOBJS@
...
12
13
14
15
16
 
 
17
18
19
20
21
...
24
25
26
27
28
29
30
31
32
33
34
35
36
...
142
143
144
145
 
146
147
148
...
323
324
325
326
 
327
328
329
...
541
542
543
544
545
 
 
546
547
548
549
 
550
551
552
 
553
554
555
556
557
 
558
559
560
561
562
563
 
564
565
566
567
568
 
569
570
571
572
573
574
575
576
 
 
 
 
 
 
 
 
 
577
578
579
...
12
13
14
 
 
15
16
17
 
18
19
20
...
23
24
25
 
26
 
27
28
 
 
29
30
31
...
137
138
139
 
140
141
142
143
...
318
319
320
 
321
322
323
324
...
536
537
538
 
 
539
540
541
542
543
 
544
545
546
 
547
548
549
550
551
 
552
553
554
555
556
557
 
558
559
560
561
562
 
563
564
 
 
 
 
 
 
 
565
566
567
568
569
570
571
572
573
574
575
576
0
@@ -12,10 +12,9 @@
0
  *
0
  * $Id$
0
  */
0
-#include "config.h"
0
-#include <sys/types.h>
0
+
0
+#include "memcached.h"
0
 #include <sys/stat.h>
0
-#include <sys/time.h>
0
 #include <sys/socket.h>
0
 #include <sys/signal.h>
0
 #include <sys/resource.h>
0
@@ -24,13 +23,9 @@
0
 #include <stdio.h>
0
 #include <string.h>
0
 #include <unistd.h>
0
-#include <netinet/in.h>
0
 #include <errno.h>
0
-#include <event.h>
0
 #include <assert.h>
0
 
0
-#include "memcached.h"
0
-
0
 /*
0
  * Since the hash function does bit manipulation, it needs to know
0
  * whether it's big or little-endian. ENDIAN_LITTLE and ENDIAN_BIG
0
@@ -142,7 +137,7 @@ and these came close:
0
 }
0
 
0
 #if HASH_LITTLE_ENDIAN == 1
0
-static uint32_t hash(
0
+uint32_t hash(
0
   const void *key, /* the key to hash */
0
   size_t length, /* length of the key */
0
   const uint32_t initval) /* initval */
0
@@ -323,7 +318,7 @@ static uint32_t hash(
0
  * from hashlittle() on all machines. hashbig() takes advantage of
0
  * big-endian byte ordering.
0
  */
0
-static uint32_t hash( const void *key, size_t length, const uint32_t initval)
0
+uint32_t hash( const void *key, size_t length, const uint32_t initval)
0
 {
0
   uint32_t a,b,c;
0
   union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */
0
@@ -541,39 +536,41 @@ static void assoc_expand(void) {
0
 
0
     primary_hashtable = calloc(hashsize(hashpower + 1), sizeof(void *));
0
     if (primary_hashtable) {
0
- if (settings.verbose > 1)
0
- fprintf(stderr, "Hash table expansion starting\n");
0
+ if (settings.verbose > 1)
0
+ fprintf(stderr, "Hash table expansion starting\n");
0
         hashpower++;
0
         expanding = 1;
0
         expand_bucket = 0;
0
- assoc_move_next_bucket();
0
+ do_assoc_move_next_bucket();
0
     } else {
0
         primary_hashtable = old_hashtable;
0
- /* Bad news, but we can keep running. */
0
+ /* Bad news, but we can keep running. */
0
     }
0
 }
0
 
0
 /* migrates the next bucket to the primary hashtable if we're expanding. */
0
-void assoc_move_next_bucket(void) {
0
+void do_assoc_move_next_bucket(void) {
0
     item *it, *next;
0
     int bucket;
0
 
0
     if (expanding) {
0
         for (it = old_hashtable[expand_bucket]; NULL != it; it = next) {
0
- next = it->h_next;
0
+ next = it->h_next;
0
 
0
             bucket = hash(ITEM_key(it), it->nkey, 0) & hashmask(hashpower);
0
             it->h_next = primary_hashtable[bucket];
0
             primary_hashtable[bucket] = it;
0
- }
0
+ }
0
 
0
- expand_bucket++;
0
- if (expand_bucket == hashsize(hashpower - 1)) {
0
- expanding = 0;
0
- free(old_hashtable);
0
- if (settings.verbose > 1)
0
- fprintf(stderr, "Hash table expansion done\n");
0
- }
0
+ old_hashtable[expand_bucket] = NULL;
0
+
0
+ expand_bucket++;
0
+ if (expand_bucket == hashsize(hashpower - 1)) {
0
+ expanding = 0;
0
+ free(old_hashtable);
0
+ if (settings.verbose > 1)
0
+ fprintf(stderr, "Hash table expansion done\n");
0
+ }
0
     }
0
 }
0
 
...
3
4
5
6
 
 
...
3
4
5
 
6
7
0
@@ -3,4 +3,5 @@ void assoc_init(void);
0
 item *assoc_find(const char *key, const size_t nkey);
0
 int assoc_insert(item *item);
0
 void assoc_delete(const char *key, const size_t nkey);
0
-void assoc_move_next_bucket(void);
0
+void do_assoc_move_next_bucket(void);
0
+uint32_t hash( const void *key, size_t length, const uint32_t initval);
...
94
95
96
 
97
98
99
...
156
157
158
 
 
 
 
 
 
 
 
 
159
160
161
...
94
95
96
97
98
99
100
...
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
0
@@ -94,6 +94,7 @@ dnl ----------------------------------------------------------------------------
0
 AC_SEARCH_LIBS(socket, socket)
0
 AC_SEARCH_LIBS(gethostbyname, nsl)
0
 AC_SEARCH_LIBS(mallinfo, malloc)
0
+AC_SEARCH_LIBS(pthread_create, pthread)
0
 
0
 AC_CHECK_FUNC(daemon,AC_DEFINE([HAVE_DAEMON],,[Define this if you have daemon()]),[AC_LIBOBJ(daemon)])
0
 
0
@@ -156,6 +157,15 @@ fi
0
 
0
 AC_C_ENDIAN
0
 
0
+dnl Check whether the user wants threads or not
0
+AC_ARG_ENABLE(threads,
0
+ [AS_HELP_STRING([--enable-threads],[support multithreaded execution])],
0
+ [if test "$ac_cv_search_pthread_create" != "no"; then
0
+ AC_DEFINE([USE_THREADS],,[Define this if you want to use pthreads])
0
+ else
0
+ AC_MSG_ERROR([Can't enable threads without the POSIX thread library.])
0
+ fi])
0
+
0
 AC_CHECK_FUNCS(mlockall)
0
 
0
 AC_CONFIG_FILES(Makefile doc/Makefile)
...
84
85
86
 
 
 
 
 
 
 
 
 
 
 
 
87
88
89
...
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
0
@@ -84,6 +84,18 @@ Print memcached and libevent licenses.
0
 .TP
0
 .B \-P <filename>
0
 Print pidfile to <filename>, only used under -d option.
0
+.TP
0
+.B \-t <threads>
0
+Number of threads to use to process incoming requests. This option is only
0
+meaningful if memcached was compiled with thread support enabled. It is
0
+typically not useful to set this higher than the number of CPU cores on the
0
+memcached server.
0
+.TP
0
+.B \-D <char>
0
+Use <char> as the delimiter between key prefixes and IDs. This is used for
0
+per-prefix stats reporting. The default is ":" (colon). If this option is
0
+specified, stats collection is turned on automatically; if not, then it may
0
+be turned on by sending the "stats detail on" command to the server.
0
 .br
0
 .SH LICENSE
0
 The memcached daemon is copyright Danga Interactive and is distributed under
...
1
2
3
 
4
5
6
7
8
...
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
44
45
46
 
 
 
 
 
 
 
 
 
 
 
47
48
49
...
65
66
67
68
 
69
70
71
...
98
99
100
101
 
 
102
103
 
 
 
104
105
106
...
115
116
117
118
 
 
119
120
121
...
136
137
138
 
139
140
141
...
192
193
194
195
 
196
197
198
199
200
201
 
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
...
337
338
339
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
340
341
 
342
343
344
...
353
354
355
356
 
357
358
359
...
1
2
 
3
4
 
5
6
7
...
10
11
12
 
13
14
 
15
16
 
 
17
18
19
...
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
...
71
72
73
 
74
75
76
77
...
104
105
106
 
107
108
109
 
110
111
112
113
114
115
...
124
125
126
 
127
128
129
130
131
...
146
147
148
149
150
151
152
...
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
...
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
 
412
413
414
415
...
424
425
426
 
427
428
429
430
0
@@ -1,8 +1,7 @@
0
 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
0
 /* $Id$ */
0
-#include <sys/types.h>
0
+#include "memcached.h"
0
 #include <sys/stat.h>
0
-#include <sys/time.h>
0
 #include <sys/socket.h>
0
 #include <sys/signal.h>
0
 #include <sys/resource.h>
0
@@ -11,14 +10,10 @@
0
 #include <stdio.h>
0
 #include <string.h>
0
 #include <unistd.h>
0
-#include <netinet/in.h>
0
 #include <errno.h>
0
 #include <time.h>
0
-#include <event.h>
0
 #include <assert.h>
0
 
0
-#include "memcached.h"
0
-
0
 /* Forward Declarations */
0
 static void item_link_q(item *it);
0
 static void item_unlink_q(item *it);
0
@@ -44,6 +39,17 @@ void item_init(void) {
0
     }
0
 }
0
 
0
+/* Enable this for reference-count debugging. */
0
+#if 0
0
+# define DEBUG_REFCNT(it,op) \
0
+ fprintf(stderr, "item %x refcnt(%c) %d %c%c%c\n", \
0
+ it, op, it->refcount, \
0
+ (it->it_flags & ITEM_LINKED) ? 'L' : ' ', \
0
+ (it->it_flags & ITEM_SLABBED) ? 'S' : ' ', \
0
+ (it->it_flags & ITEM_DELETED) ? 'D' : ' ')
0
+#else
0
+# define DEBUG_REFCNT(it,op) while(0)
0
+#endif
0
 
0
 /*
0
  * Generates the variable-sized part of the header for an object.
0
@@ -65,7 +71,7 @@ static size_t item_make_header(const uint8_t nkey, const int flags, const int nb
0
 }
0
 
0
 /*@null@*/
0
-item *item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t exptime, const int nbytes) {
0
+item *do_item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t exptime, const int nbytes) {
0
     uint8_t nsuffix;
0
     item *it;
0
     char suffix[40];
0
@@ -98,9 +104,12 @@ item *item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t
0
 
0
         for (search = tails[id]; tries > 0 && search != NULL; tries--, search=search->prev) {
0
             if (search->refcount == 0) {
0
- if (search->exptime > current_time)
0
+ if (search->exptime > current_time) {
0
+ STATS_LOCK();
0
                        stats.evictions++;
0
- item_unlink(search);
0
+ STATS_UNLOCK();
0
+ }
0
+ do_item_unlink(search);
0
                 break;
0
             }
0
         }
0
@@ -115,7 +124,8 @@ item *item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t
0
     assert(it != heads[it->slabs_clsid]);
0
 
0
     it->next = it->prev = it->h_next = 0;
0
- it->refcount = 0;
0
+ it->refcount = 1; /* the caller will have a reference */
0
+ DEBUG_REFCNT(it, '*');
0
     it->it_flags = 0;
0
     it->nkey = nkey;
0
     it->nbytes = nbytes;
0
@@ -136,6 +146,7 @@ void item_free(item *it) {
0
     /* so slab size changer can tell later if item is already free or not */
0
     it->slabs_clsid = 0;
0
     it->it_flags |= ITEM_SLABBED;
0
+ DEBUG_REFCNT(it, 'F');
0
     slabs_free(it, ntotal);
0
 }
0
 
0
@@ -192,57 +203,66 @@ static void item_unlink_q(item *it) {
0
     return;
0
 }
0
 
0
-int item_link(item *it) {
0
+int do_item_link(item *it) {
0
     assert((it->it_flags & (ITEM_LINKED|ITEM_SLABBED)) == 0);
0
     assert(it->nbytes < 1048576);
0
     it->it_flags |= ITEM_LINKED;
0
     it->time = current_time;
0
     assoc_insert(it);
0
 
0
+ STATS_LOCK();
0
     stats.curr_bytes += ITEM_ntotal(it);
0
     stats.curr_items += 1;
0
     stats.total_items += 1;
0
+ STATS_UNLOCK();
0
 
0
     item_link_q(it);
0
 
0
     return 1;
0
 }
0
 
0
-void item_unlink(item *it) {
0
+void do_item_unlink(item *it) {
0
     if ((it->it_flags & ITEM_LINKED) != 0) {
0
         it->it_flags &= ~ITEM_LINKED;
0
+ STATS_LOCK();
0
         stats.curr_bytes -= ITEM_ntotal(it);
0
         stats.curr_items -= 1;
0
+ STATS_UNLOCK();
0
         assoc_delete(ITEM_key(it), it->nkey);
0
         item_unlink_q(it);
0
+ if (it->refcount == 0) item_free(it);
0
     }
0
- if (it->refcount == 0) item_free(it);
0
 }
0
 
0
-void item_remove(item *it) {
0
+void do_item_remove(item *it) {
0
     assert((it->it_flags & ITEM_SLABBED) == 0);
0
- if (it->refcount != 0) it->refcount--;
0
+ if (it->refcount != 0) {
0
+ it->refcount--;
0
+ DEBUG_REFCNT(it, '-');
0
+ }
0
     assert((it->it_flags & ITEM_DELETED) == 0 || it->refcount != 0);
0
     if (it->refcount == 0 && (it->it_flags & ITEM_LINKED) == 0) {
0
         item_free(it);
0
     }
0
 }
0
 
0
-void item_update(item *it) {
0
+void do_item_update(item *it) {
0
     if (it->time < current_time - ITEM_UPDATE_INTERVAL) {
0
         assert((it->it_flags & ITEM_SLABBED) == 0);
0
 
0
- item_unlink_q(it);
0
- it->time = current_time;
0
- item_link_q(it);
0
+ if (it->it_flags & ITEM_LINKED) {
0
+ item_unlink_q(it);
0
+ it->time = current_time;
0
+ item_link_q(it);
0
+ }
0
     }
0
 }
0
 
0
-int item_replace(item *it, item *new_it) {
0
+int do_item_replace(item *it, item *new_it) {
0
     assert((it->it_flags & ITEM_SLABBED) == 0);
0
 
0
- item_unlink(it);
0
- return item_link(new_it);
0
+ do_item_unlink(it);
0
+ return do_item_link(new_it);
0
 }
0
 
0
 /*@null@*/
0
@@ -337,8 +357,59 @@ char* item_stats_sizes(int *bytes) {
0
     return buf;
0
 }
0
 
0
+/* returns true if a deleted item's delete-locked-time is over, and it
0
+ should be removed from the namespace */
0
+int item_delete_lock_over (item *it) {
0
+ assert(it->it_flags & ITEM_DELETED);
0
+ return (current_time >= it->exptime);
0
+}
0
+
0
+/* wrapper around assoc_find which does the lazy expiration/deletion logic */
0
+item *do_item_get_notedeleted(char *key, size_t nkey, int *delete_locked) {
0
+ item *it = assoc_find(key, nkey);
0
+ if (delete_locked) *delete_locked = 0;
0
+ if (it && (it->it_flags & ITEM_DELETED)) {
0
+ /* it's flagged as delete-locked. let's see if that condition
0
+ is past due, and the 5-second delete_timer just hasn't
0
+ gotten to it yet... */
0
+ if (! item_delete_lock_over(it)) {
0
+ if (delete_locked) *delete_locked = 1;
0
+ it = 0;
0
+ }
0
+ }
0
+ if (it && settings.oldest_live && settings.oldest_live <= current_time &&
0
+ it->time <= settings.oldest_live) {
0
+ do_item_unlink(it); // MTSAFE - cache_lock held
0
+ it = 0;
0
+ }
0
+ if (it && it->exptime && it->exptime <= current_time) {
0
+ do_item_unlink(it); // MTSAFE - cache_lock held
0
+ it = 0;
0
+ }
0
+
0
+ if (it) {
0
+ it->refcount++;
0
+ DEBUG_REFCNT(it, '+');
0
+ }
0
+ return it;
0
+}
0
+
0
+item *item_get(char *key, size_t nkey) {
0
+ return item_get_notedeleted(key, nkey, 0);
0
+}
0
+
0
+/* returns an item whether or not it's delete-locked or expired. */
0
+item *do_item_get_nocheck(char *key, size_t nkey) {
0
+ item *it = assoc_find(key, nkey);
0
+ if (it) {
0
+ it->refcount++;
0
+ DEBUG_REFCNT(it, '+');
0
+ }
0
+ return it;
0
+}
0
+
0
 /* expires items that are more recent than the oldest_live setting. */
0
-void item_flush_expired(void) {
0
+void do_item_flush_expired(void) {
0
     int i;
0
     item *iter, *next;
0
     if (settings.oldest_live == 0)
0
@@ -353,7 +424,7 @@ void item_flush_expired(void) {
0
             if (iter->time >= settings.oldest_live) {
0
                 next = iter->next;
0
                 if ((iter->it_flags & ITEM_SLABBED) == 0) {
0
- item_unlink(iter);
0
+ do_item_unlink(iter);
0
                 }
0
             } else {
0
                 /* We've hit the first old item. Continue to the next queue. */
...
1
2
3
4
 
5
6
7
8
9
10
11
12
 
 
 
 
 
13
14
15
...
17
18
19
20
 
 
 
 
 
...
1
2
3
 
4
5
6
7
 
 
 
 
 
8
9
10
11
12
13
14
15
...
17
18
19
 
20
21
22
23
24
0
@@ -1,15 +1,15 @@
0
 /* See items.c */
0
 void item_init(void);
0
 /*@null@*/
0
-item *item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t exptime, const int nbytes);
0
+item *do_item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t exptime, const int nbytes);
0
 void item_free(item *it);
0
 bool item_size_ok(const size_t nkey, const int flags, const int nbytes);
0
 
0
-int item_link(item *it); /* may fail if transgresses limits */
0
-void item_unlink(item *it);
0
-void item_remove(item *it);
0
-void item_update(item *it); /* update LRU time to current and reposition */
0
-int item_replace(item *it, item *new_it);
0
+int do_item_link(item *it); /* may fail if transgresses limits */
0
+void do_item_unlink(item *it);
0
+void do_item_remove(item *it);
0
+void do_item_update(item *it); /* update LRU time to current and reposition */
0
+int do_item_replace(item *it, item *new_it);
0
 
0
 /*@null@*/
0
 char *item_cachedump(const unsigned int slabs_clsid, const unsigned int limit, unsigned int *bytes);
0
@@ -17,4 +17,8 @@ void item_stats(char *buffer, const int buflen);
0
 
0
 /*@null@*/
0
 char *item_stats_sizes(int *bytes);
0
-void item_flush_expired(void);
0
+void do_item_flush_expired(void);
0
+item *item_get(char *key, size_t nkey);
0
+
0
+item *do_item_get_notedeleted(char *key, size_t nkey, int *delete_locked);
0
+item *do_item_get_nocheck(char *key, size_t nkey);
...
15
16
17
18
19
 
20
21
22
23
24
...
41
42
43
44
45
46
47
48
49
50
51
52
...
64
65
66
67
68
69
70
71
...
85
86
87
88
89
90
91
...
115
116
117
 
118
119
120
...
159
160
161
 
162
163
164
 
165
166
167
 
 
168
169
170
...
180
181
182
 
 
 
 
 
 
 
183
184
185
...
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
...
260
261
262
 
 
 
 
 
263
264
265
266
 
267
268
269
...
273
274
275
276
277
278
 
 
 
 
 
279
280
281
282
283
284
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
286
287
...
317
318
319
 
320
 
321
322
323
...
350
351
352
 
353
354
355
356
357
358
359
360
361
362
363
364
365
366
 
 
367
368
369
370
 
371
372
 
373
374
375
...
378
379
380
381
 
382
383
384
...
397
398
399
400
 
401
402
403
...
429
430
431
432
 
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
 
449
 
450
451
452
...
689
690
691
692
693
694
695
 
696
 
697
698
699
700
 
 
 
 
 
 
701
702
703
704
705
706
707
708
709
 
 
 
710
711
712
713
714
 
 
 
 
 
 
 
 
 
 
 
715
716
717
718
719
720
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
721
722
723
724
725
726
 
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
 
 
742
743
744
...
816
817
818
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
819
820
821
...
838
839
840
 
841
842
843
...
859
860
861
 
862
 
863
864
865
...
989
990
991
 
 
 
 
 
 
 
 
992
993
994
...
1042
1043
1044
 
1045
1046
 
 
 
 
 
1047
1048
1049
...
1069
1070
1071
 
 
1072
1073
 
1074
1075
1076
1077
1078
 
 
 
 
 
1079
1080
1081
...
1135
1136
1137
 
 
 
 
1138
1139
1140
...
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
...
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
...
1215
1216
1217
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1218
1219
1220
1221
1222
1223
1224
1225
 
1226
1227
1228
...
1231
1232
1233
1234
1235
 
 
1236
1237
1238
 
1239
1240
1241
 
1242
1243
 
1244
1245
 
 
1246
1247
 
1248
1249
1250
1251
 
 
1252
1253
1254
...
1289
1290
1291
1292
1293
1294
1295
 
 
1296
1297
1298
1299
1300
1301
 
 
 
 
 
 
 
 
 
 
 
 
1302
 
 
 
 
 
 
 
 
 
1303
1304
1305
...
1310
1311
1312
1313
1314
 
 
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
 
 
1325
1326
1327
...
1565
1566
1567
 
1568
 
1569
1570
1571
...
1632
1633
1634
 
1635
 
1636
1637
1638
...
1653
1654
1655
 
1656
1657
1658
1659
 
1660
1661
1662
...
1666
1667
1668
 
 
1669
1670
1671
...
1704
1705
1706
 
1707
 
1708
1709
1710
...
1762
1763
1764
 
1765
1766
1767
1768
1769
1770
 
1771
1772
 
1773
1774
1775
...
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
 
 
1790
1791
1792
...
1826
1827
1828
 
1829
 
1830
1831
1832
...
1870
1871
1872
 
1873
 
1874
1875
1876
...
1955
1956
1957
1958
1959
1960
1961
...
1977
1978
1979
1980
1981
1982
1983
...
2188
2189
2190
 
2191
2192
2193
...
2208
2209
2210
 
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2224
2225
2226
 
2227
2228
2229
...
2247
2248
2249
 
 
 
2250
2251
2252
...
2356
2357
2358
2359
2360
2361
2362
...
2377
2378
2379
2380
 
2381
2382
2383
...
2446
2447
2448
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2449
2450
2451
...
2559
2560
2561
 
 
2562
2563
2564
2565
2566