The stories and information posted here are artistic works of fiction and falsehood. Only a fool would take anything posted here as fact.
I present to you, the solution to part 1 of day 5 of 2020 aoc, written in x86_64 AT&T GAS assembly[…]
To build, do
It has 0 dependencies, and does I/O directly by invoking syscalls.
as -o 5.o 5.s
ld -o 5 5.o
./5
you should see something like
./5
Silver star answer: 0000000820
I should note the input filename is hardcoded to “input.txt”, and it must reside within the working directory from which the program ran.
[…]
The register epilogue/prologue code is just my own failure to have enough mental energy to actually adhere to a sane ABI - i simply save every single register that i use, no exclusions. makes it easier to reason about but at the cost of performance and code size.
[ I heard these new fangled “compilers” can do it automatically - as if i would ever trust some algorithm to do it for me! ]
The code is even well-commented, and thus probably the most readable code on this site, despite being in assembler.
I’m pretty sure this guy is the only Real Programmer in the AOC thread (unless there’s another assembly-writer I missed).
Very cool! I’m looking forward to anything else you send in, milkanon.
milkanon/assembly-day05-1.smilkanon/assembly-day05-1.s
.text .global \_start .type \_start, @function .set SYS\_READ, 0 /\* SYS\_OPEN: rdi: int fd rsi: char \*buffer rdx: size\_t count \*/ .set SYS\_OPEN, 2 /\* SYS\_OPEN: rdi: const char \*filename rsi: int flags rdx: int mode \*/ .set SYS\_EXIT, 60 /\* SYS\_EXIT: rdi: int exitcode \*/ .set O\_RDONLY, 0 \_start: movq %rsp, %rbp // Open input file movq $SYS\_OPEN, %rax movq $filename, %rdi movq $O\_RDONLY, %rsi movq $0600, %rdx syscall // We should have the fd in `rax`. Store it. movq %rax, fd .Lread\_seat\_entry: // Read 11 bytes. movq $SYS\_READ, %rax movq fd, %rdi movq $buffer, %rsi movq $BUFFER\_SIZE, %rdx syscall // `rax` returns the number of bytes read, or negative for // error. cmp $0, %rax je .Lread\_all\_ok // At this point, `buffer` looks like // (gdb) p \*((char [11] \*)$rsi) // $1 = "FFBBBFBLRL\n" // Get the row index of `buffer` string specifier call get\_row // Save it before it gets clobbered push %rax // Now get the row index call get\_col // Restore rax, use (rax, rbx) as (row, col) tuple mov %rax, %rbx pop %rax // Mark this seat as occupied in the `taken\_tab` call mark\_taken // Also calculate and store the id for this seat in `id\_tab` call store\_id jmp .Lread\_seat\_entry .Lread\_all\_ok: // Get the max id for Silver call get\_max\_id push %rax mov $silver\_str, %rax call print\_string pop %rax call print\_number // Exit program movq $SYS\_EXIT, %rax movq $0, %rdi syscall .size \_start, .-\_start .section .data /\* Utility symbols \*/ .set WIDTH, 8 .set HEIGHT, 128 /\* File descriptor \*/ fd: .quad 0 /\* Table whether or not seat is taken. \*/ .align 8 taken\_tab: .zero 8 \* (8 \* WIDTH \* HEIGHT) /\* Table of seat ids \*/ .align 8 id\_tab: .zero 8 \* (8 \* WIDTH \* HEIGHT) .set BUFFER\_SIZE, 11 buffer: // 7 bytes for the column string (e.g. "FFBFFBF") .byte 0 .byte 0 .byte 0 .byte 0 .byte 0 .byte 0 .byte 0 // 3 bytes for the row string (e.g. "LRL") .byte 0 .byte 0 .byte 0 // 1 byte for the newline .byte 0 .section .rodata silver\_str: .asciz "Silver star answer: " filename: .asciz "input.txt" /\* Lookup table for the "BFBBBFB"-to-binary conversion \*/ bintab\_row: .quad 64 .quad 32 .quad 16 .quad 8 .quad 4 .quad 2 .quad 1 /\* Lookup table for the "LRL"-to-binary conversion \*/ bintab\_col: /\* Due to the way the lookups happen, we need to offset these abit forward to account for how the fact that the row specifier appears at the end of the string. This just makes indexing more easier elsewhere in the code. \*/ .quad 0 .quad 0 .quad 0 .quad 0 .quad 0 .quad 0 .quad 0 .quad 4 .quad 2 .quad 1 /\* Helper routines \*/ .section .text /\* size\_t get\_row(void): Get row of string spec in `buffer`. \*/ .global get\_row .type get\_row, @function /\* size\_t get\_col(void): Get col of string spec in `buffer`. \*/ .global get\_col .type get\_col, @function /\* void mark\_taken(size\_t row, size\_t col): Set corresponding seat location to &\#39;1&\#39; in `taken\_tab`. rax - row rbx - col \*/ .global mark\_taken .type mark\_taken, @function /\* void store\_id(size\_t row, size\_t col): Set corresponding seat location to the seat id in `id\_tab`. rax - row rbx - col \*/ .global store\_id .type store\_id, @function .global get\_max\_id .type get\_max\_id, @function .global print\_silver .type print\_silver, @function .global print\_string .type print\_string, @function .global strlen .type strlen, @function strlen: push %rbx push %rsi mov %rax, %rsi mov $0, %rbx .Lstrlen\_loop: mov (%rsi, %rbx, 1), %al cmp $0, %al je .Lstrlen\_done inc %rbx jmp .Lstrlen\_loop .Lstrlen\_done: mov %rbx, %rax pop %rsi pop %rbx ret print\_string: push %rax push %rcx push %rsi push %rdi call strlen mov %rax, %rdx movq $1, %rax movq $1, %rdi movq 24(%rsp), %rsi syscall pop %rdi pop %rsi pop %rcx pop %rax ret .size print\_string, .-print\_string print\_number: // I need a bit of scratch memory space cos i need // to give sys\_write an address to a buffer push %rbp movq %rsp, %rbp // number to print push %rax // index push %rbx push %rcx push %rdx // We need to zero terminate it cos `print\_string` // will be scanning for it until we get the zero // so $10 (MAXINT="42945967295") + $2 ("\n\0") = $12 sub $12, %rsp movb $&\#39;\n&\#39;, 10(%rsp) movb $0, 11(%rsp) movq $9, %rbx movq $10, %rcx .Lprint\_number\_loop: cltd idiv %ecx add $0x30, %dl mov %dl, (%rsp, %rbx, 1) cmp $0, %rbx je .Lprint\_number\_done dec %rbx jmp .Lprint\_number\_loop .Lprint\_number\_done: mov %rsp, %rax call print\_string add $12, %rsp pop %rdx pop %rcx pop %rbx pop %rax pop %rbp ret print\_silver: .size print\_silver, .-print\_silver get\_max\_id: push %rbx push %rdx push %rsi push %rdi /\* Pointer to `taken\_tab` table \*/ movq $taken\_tab, %rsi /\* Pointer to `id\_tab` table \*/ movq $id\_tab, %rdi /\* Index \*/ movq $0, %rdx /\* Running max \*/ movq $0, %rbx .Lget\_max\_id\_loop: /\* load the taken variable for this seat \*/ movq (%rsi, %rdx, 8), %rax /\* if its 0, it is not taken, and therefore we skip it. \*/ cmp $0, %rax je .Lget\_max\_id\_skip /\* load the id for this seat \*/ movq (%rdi, %rdx, 8), %rax cmp %rax, %rbx cmovc %rax, %rbx .Lget\_max\_id\_skip: inc %rdx cmp $(128 \* 8), %rdx jl .Lget\_max\_id\_loop mov %rbx, %rax pop %rdi pop %rsi pop %rdx pop %rbx ret .size get\_max\_id, .-get\_max\_id store\_id: push %rax push %rbx push %rcx push %rdi // calculate id imul $8, %rax add %rbx, %rax // store it in rcx mov %rax, %rcx // restore rax mov 24(%rsp), %rax // calculate row memory location movq $id\_tab, %rdi imul $(8 \* 8), %rax add %rax, %rdi // add column offset imul $8, %rbx add %rbx, %rdi // store id in rcx to slot in id\_tab movq %rcx, (%rdi) pop %rdi pop %rcx pop %rbx pop %rax ret .size store\_id, .-store\_id mark\_taken: push %rax push %rbx push %rcx push %rdi // calculate id imul $8, %rax add %rbx, %rax // store it in rcx mov %rax, %rcx // restore rax mov 24(%rsp), %rax // calculate row memory location movq $taken\_tab, %rdi imul $(8 \* 8), %rax add %rax, %rdi // add column offset imul $8, %rbx add %rbx, %rdi // store presence in rcx to slot in id\_tab movq $1, (%rdi) pop %rdi pop %rcx pop %rbx pop %rax ret .size mark\_taken, .-mark\_taken get\_row: push %rbx push %rcx push %rdx push %rsi push %rdi push %r8 /\* zero-register for cmov below \*/ movq $0, %rbx /\* input buffer, e.g. "FFBBFFBLRL\n" \*/ movq $buffer, %rsi /\* lookup table for (2 \* %ecx) \*/ movq $bintab\_row, %rdi /\* index \*/ movq $0, %rcx /\* r8 will contain the row number \*/ movq $0, %r8 .Lget\_row\_loop: /\* Load in this seat (&\#39;F&\#39; or &\#39;B&\#39; into eax) \*/ mov (%rsi, %rcx, 1), %eax /\* Load in the value of this bit \*/ movq (%rdi, %rcx, 8), %rdx /\* I use a trick here: Notice that &\#39;F&\#39; = 0x46, and &\#39;B&\#39; = 0x42. Hence, if we subtract &\#39;B&\#39;, we get either zero or non-zero. Then, we can use `cmov` on the zero flag to move in the addend. \*/ cmp $0x42, %al cmovnz %rbx, %rdx add %rdx, %r8 inc %rcx cmp $7, %rcx jl .Lget\_row\_loop .Lget\_row\_done: mov %r8, %rax pop %r8 pop %rdi pop %rsi pop %rdx pop %rcx pop %rbx ret .size get\_row, .-get\_row get\_col: push %rbx push %rcx push %rdx push %rsi push %rdi push %r8 /\* zero-register for cmov below \*/ movq $0, %rbx /\* input buffer, e.g. "FFBBFFBLRL\n" \*/ movq $buffer, %rsi /\* lookup table for (2 \* %ecx) \*/ movq $bintab\_col, %rdi /\* index (NOTE! We start at 7) \*/ movq $7, %rcx /\* r8 will contain the col number \*/ movq $0, %r8 .Lget\_col\_loop: /\* Load in this seat (&\#39;F&\#39; or &\#39;B&\#39; into eax) \*/ mov (%rsi, %rcx, 1), %eax /\* Load in the value of this bit \*/ movq (%rdi, %rcx, 8), %rdx /\* I use a trick here: Notice that &\#39;L&\#39; = 0x4C, and &\#39;R&\#39; = 0x52. Hence, if we subtract &\#39;L&\#39;, we get either zero or non-zero. Then, we can use `cmov` on the zero flag to move in the addend. \*/ cmp $0x4C, %al cmovz %rbx, %rdx add %rdx, %r8 inc %rcx cmp $10, %rcx jl .Lget\_col\_loop .Lget\_col\_done: mov %r8, %rax pop %r8 pop %rdi pop %rsi pop %rdx pop %rcx pop %rbx ret .size get\_col, .-get\_col
milkanon/assembly-day05-2.smilkanon/assembly-day05-2.s
.text .global \_start .type \_start, @function .set SYS\_READ, 0 /\* SYS\_OPEN: rdi: int fd rsi: char \*buffer rdx: size\_t count \*/ .set SYS\_OPEN, 2 /\* SYS\_OPEN: rdi: const char \*filename rsi: int flags rdx: int mode \*/ .set SYS\_EXIT, 60 /\* SYS\_EXIT: rdi: int exitcode \*/ .set O\_RDONLY, 0 \_start: movq %rsp, %rbp // Open input file movq $SYS\_OPEN, %rax movq $filename, %rdi movq $O\_RDONLY, %rsi movq $0600, %rdx syscall // We should have the fd in `rax`. Store it. movq %rax, fd .Lread\_seat\_entry: // Read 11 bytes. movq $SYS\_READ, %rax movq fd, %rdi movq $buffer, %rsi movq $BUFFER\_SIZE, %rdx syscall // `rax` returns the number of bytes read, or negative for // error. cmp $0, %rax je .Lread\_all\_ok // At this point, `buffer` looks like // (gdb) p \*((char [11] \*)$rsi) // $1 = "FFBBBFBLRL\n" // Get the row index of `buffer` string specifier call get\_row // Save it before it gets clobbered push %rax // Now get the row index call get\_col // Restore rax, use (rax, rbx) as (row, col) tuple mov %rax, %rbx pop %rax // Mark this seat as occupied in the `taken\_tab` call mark\_taken // Also calculate and store the id for this seat in `id\_tab` call store\_id jmp .Lread\_seat\_entry .Lread\_all\_ok: // Get the max id for Silver call get\_max\_id push %rax mov $silver\_str, %rax call print\_string pop %rax call print\_number // Get our seat id for Gold call find\_empty\_seat\_id push %rax mov $gold\_str, %rax call print\_string pop %rax call print\_number // Exit program movq $SYS\_EXIT, %rax movq $0, %rdi syscall .size \_start, .-\_start .section .data /\* Utility symbols \*/ .set WIDTH, 8 .set HEIGHT, 128 /\* File descriptor \*/ fd: .quad 0 /\* Table whether or not seat is taken. \*/ .align 8 taken\_tab: .zero 8 \* (8 \* WIDTH \* HEIGHT) /\* Table of seat ids \*/ .align 8 id\_tab: .zero 8 \* (8 \* WIDTH \* HEIGHT) .set BUFFER\_SIZE, 11 buffer: // 7 bytes for the column string (e.g. "FFBFFBF") .byte 0 .byte 0 .byte 0 .byte 0 .byte 0 .byte 0 .byte 0 // 3 bytes for the row string (e.g. "LRL") .byte 0 .byte 0 .byte 0 // 1 byte for the newline .byte 0 .section .rodata silver\_str: .asciz "Silver star answer: " gold\_str: .asciz "Gold star answer: " filename: .asciz "input.txt" /\* Lookup table for the "BFBBBFB"-to-binary conversion \*/ bintab\_row: .quad 64 .quad 32 .quad 16 .quad 8 .quad 4 .quad 2 .quad 1 /\* Lookup table for the "LRL"-to-binary conversion \*/ bintab\_col: /\* Due to the way the lookups happen, we need to offset these abit forward to account for how the fact that the row specifier appears at the end of the string. This just makes indexing more easier elsewhere in the code. \*/ .quad 0 .quad 0 .quad 0 .quad 0 .quad 0 .quad 0 .quad 0 .quad 4 .quad 2 .quad 1 /\* Helper routines \*/ .section .text /\* size\_t get\_row(void): Get row of string spec in `buffer`. \*/ .global get\_row .type get\_row, @function /\* size\_t get\_col(void): Get col of string spec in `buffer`. \*/ .global get\_col .type get\_col, @function /\* void mark\_taken(size\_t row, size\_t col): Set corresponding seat location to &\#39;1&\#39; in `taken\_tab`. rax - row rbx - col \*/ .global mark\_taken .type mark\_taken, @function /\* void store\_id(size\_t row, size\_t col): Set corresponding seat location to the seat id in `id\_tab`. rax - row rbx - col \*/ .global store\_id .type store\_id, @function .global get\_max\_id .type get\_max\_id, @function .global print\_silver .type print\_silver, @function .global print\_string .type print\_string, @function .global strlen .type strlen, @function .global find\_emtpy\_seat\_id .type find\_empty\_seat\_id, @function find\_empty\_seat\_id: // Ok, so we have x and y to walk the entire plane. // We know the seat infront must not be empty, // and the seat behind must not be empty. // So we iterate over [1, 126] of the [0, 127] rows // (because it obviously cant be the front most or back most row) // Once we find an empty seat, look at the seat infront, and the // seat behind. if theyre both populated, return id in `rax`. // linear index of current seat push %rsi // linear index of alternative seat push %rdi // `taken\_tab` or `id\_tab` pointer push %rdx // `taken\_tab` or `id\_tab` slot // We don&\#39;t save it because we clobber it on return anyway. // push %rax // Load `taken\_tab` into %rsi movq $taken\_tab, %rdx // Remember, it can&\#39;t be the first or last row // so start on second row movq $8, %rsi .Lfind\_empty\_seat\_id.loop: // Load our seat movq (%rdx, %rsi, 8), %rax // If its empty, go check the front seat. cmp $0, %rax je .Lfind\_empty\_seat\_id.check\_front inc %rsi jmp .Lfind\_empty\_seat\_id.loop .Lfind\_empty\_seat\_id.check\_front: // Now check if the seat infront is taken // Calculate the index of seat infront movq %rsi, %rdi subq $8, %rdi // Lookup in `taken\_tab` movq (%rdx, %rdi, 8), %rax // If its taken, go check the back seat. cmp $0, %rax jne .Lfind\_empty\_seat\_id.check\_back inc %rsi jmp .Lfind\_empty\_seat\_id.loop .Lfind\_empty\_seat\_id.check\_back: // Now check if the seat behind is also taken // Calculate the index of seat behind movq %rsi, %rdi addq $8, %rdi // Lookup in `taken\_tab` movq (%rdx, %rdi, 8), %rax // If its taken, that means we&\#39;ve found our seat cmp $0, %rax jne .Lfind\_empty\_seat\_id.found inc %rdi jmp .Lfind\_empty\_seat\_id.loop .Lfind\_empty\_seat\_id.found: // Ok, we found an empty seat with both the front and // back seats taken. %rsi already has the id, return it. movq %rsi, %rax pop %rdx pop %rdi pop %rsi ret .size find\_empty\_seat\_id, .-find\_empty\_seat\_id strlen: push %rbx push %rsi mov %rax, %rsi mov $0, %rbx .Lstrlen\_loop: mov (%rsi, %rbx, 1), %al cmp $0, %al je .Lstrlen\_done inc %rbx jmp .Lstrlen\_loop .Lstrlen\_done: mov %rbx, %rax pop %rsi pop %rbx ret print\_string: push %rax push %rcx push %rsi push %rdi call strlen mov %rax, %rdx movq $1, %rax movq $1, %rdi movq 24(%rsp), %rsi syscall pop %rdi pop %rsi pop %rcx pop %rax ret .size print\_string, .-print\_string print\_number: // I need a bit of scratch memory space cos i need // to give sys\_write an address to a buffer push %rbp movq %rsp, %rbp // number to print push %rax // index push %rbx push %rcx push %rdx // We need to zero terminate it cos `print\_string` // will be scanning for it until we get the zero // so $10 (MAXINT="42945967295") + $2 ("\n\0") = $12 sub $12, %rsp movb $&\#39;\n&\#39;, 10(%rsp) movb $0, 11(%rsp) movq $9, %rbx movq $10, %rcx .Lprint\_number\_loop: cltd idiv %ecx add $0x30, %dl mov %dl, (%rsp, %rbx, 1) cmp $0, %rbx je .Lprint\_number\_done dec %rbx jmp .Lprint\_number\_loop .Lprint\_number\_done: mov %rsp, %rax call print\_string add $12, %rsp pop %rdx pop %rcx pop %rbx pop %rax pop %rbp ret print\_silver: .size print\_silver, .-print\_silver get\_max\_id: push %rbx push %rdx push %rsi push %rdi /\* Pointer to `taken\_tab` table \*/ movq $taken\_tab, %rsi /\* Pointer to `id\_tab` table \*/ movq $id\_tab, %rdi /\* Index \*/ movq $0, %rdx /\* Running max \*/ movq $0, %rbx .Lget\_max\_id\_loop: /\* load the taken variable for this seat \*/ movq (%rsi, %rdx, 8), %rax /\* if its 0, it is not taken, and therefore we skip it. \*/ cmp $0, %rax je .Lget\_max\_id\_skip /\* load the id for this seat \*/ movq (%rdi, %rdx, 8), %rax cmp %rax, %rbx cmovc %rax, %rbx .Lget\_max\_id\_skip: inc %rdx cmp $(128 \* 8), %rdx jl .Lget\_max\_id\_loop mov %rbx, %rax pop %rdi pop %rsi pop %rdx pop %rbx ret .size get\_max\_id, .-get\_max\_id store\_id: push %rax push %rbx push %rcx push %rdi // calculate id imul $8, %rax add %rbx, %rax // store it in rcx mov %rax, %rcx // restore rax mov 24(%rsp), %rax // calculate row memory location movq $id\_tab, %rdi imul $(8 \* 8), %rax add %rax, %rdi // add column offset imul $8, %rbx add %rbx, %rdi // store id in rcx to slot in id\_tab movq %rcx, (%rdi) pop %rdi pop %rcx pop %rbx pop %rax ret .size store\_id, .-store\_id mark\_taken: push %rax push %rbx push %rcx push %rdi // calculate id imul $8, %rax add %rbx, %rax // store it in rcx mov %rax, %rcx // restore rax mov 24(%rsp), %rax // calculate row memory location movq $taken\_tab, %rdi imul $(8 \* 8), %rax add %rax, %rdi // add column offset imul $8, %rbx add %rbx, %rdi // store presence in rcx to slot in id\_tab movq $1, (%rdi) pop %rdi pop %rcx pop %rbx pop %rax ret .size mark\_taken, .-mark\_taken get\_row: push %rbx push %rcx push %rdx push %rsi push %rdi push %r8 /\* zero-register for cmov below \*/ movq $0, %rbx /\* input buffer, e.g. "FFBBFFBLRL\n" \*/ movq $buffer, %rsi /\* lookup table for (2 \* %ecx) \*/ movq $bintab\_row, %rdi /\* index \*/ movq $0, %rcx /\* r8 will contain the row number \*/ movq $0, %r8 .Lget\_row\_loop: /\* Load in this seat (&\#39;F&\#39; or &\#39;B&\#39; into eax) \*/ mov (%rsi, %rcx, 1), %eax /\* Load in the value of this bit \*/ movq (%rdi, %rcx, 8), %rdx /\* I use a trick here: Notice that &\#39;F&\#39; = 0x46, and &\#39;B&\#39; = 0x42. Hence, if we subtract &\#39;B&\#39;, we get either zero or non-zero. Then, we can use `cmov` on the zero flag to move in the addend. \*/ cmp $0x42, %al cmovnz %rbx, %rdx add %rdx, %r8 inc %rcx cmp $7, %rcx jl .Lget\_row\_loop .Lget\_row\_done: mov %r8, %rax pop %r8 pop %rdi pop %rsi pop %rdx pop %rcx pop %rbx ret .size get\_row, .-get\_row get\_col: push %rbx push %rcx push %rdx push %rsi push %rdi push %r8 /\* zero-register for cmov below \*/ movq $0, %rbx /\* input buffer, e.g. "FFBBFFBLRL\n" \*/ movq $buffer, %rsi /\* lookup table for (2 \* %ecx) \*/ movq $bintab\_col, %rdi /\* index (NOTE! We start at 7) \*/ movq $7, %rcx /\* r8 will contain the col number \*/ movq $0, %r8 .Lget\_col\_loop: /\* Load in this seat (&\#39;F&\#39; or &\#39;B&\#39; into eax) \*/ mov (%rsi, %rcx, 1), %eax /\* Load in the value of this bit \*/ movq (%rdi, %rcx, 8), %rdx /\* I use a trick here: Notice that &\#39;L&\#39; = 0x4C, and &\#39;R&\#39; = 0x52. Hence, if we subtract &\#39;L&\#39;, we get either zero or non-zero. Then, we can use `cmov` on the zero flag to move in the addend. \*/ cmp $0x4C, %al cmovz %rbx, %rdx add %rdx, %r8 inc %rcx cmp $10, %rcx jl .Lget\_col\_loop .Lget\_col\_done: mov %r8, %rax pop %r8 pop %rdi pop %rsi pop %rdx pop %rcx pop %rbx ret .size get\_col, .-get\_col
milkanon/input.txtmilkanon/input.txt
FFBBBFBLRL BFFFBFBRRR BFFFBFBLRL BFFBFBBLRR BBFFBFFRLL BFFFBFBRLR FFFFBBBRLR BBFFFBBRRR BBFBFBBRRR BFFBBBFLRR FFBBFBBRLR BBFFBFFLLL BFFFBFBLLR FBBFFBFLRR FBBFBBFRRL BFFBBBBRRR BFBBBBFLLL BFFBFBFLRR FBBFFBFRRR FFBFBFBLRL BFFFFBFLRR FBBFFFFLRR BFFFBBBLLL BFBFFFBRLL FFBBBBBRLL FFBBFFBLLR FBFFBBFLRL FFBFFBBRRL BFBBBBFLLR BFFFFBFRRL BFFBFFFLLL FBFFFBFLRL FBBBFFFLLL FFBFBBFRLR FBBBFBFRRL BFBBBFBLLL FBFFFBFLRR FBFBFFBLRR BFFFFBFLRL FBFBFFFRRL FBBFBFBLRL FBBFFBFLLL FBFBBBBRRR FBBBBBFLRR FBBBFBBLLR FFFBFBFRLL FFFBBFFLRR BFBBFFBRRL FFBFBFBRRL FBFBBBBLRR BFFBBFFLLL FFFBFBBRRL BFBBFFFLRR FBFBFBFLRL BFBFFBFLLL FBBBFBFRLR FBBBFBFLLL BFBFFFBLRR BFFBFBFLRL BFBBFFFRRR FFFBFFFRLR FFFBBBBRLL FFBBBBBLLL BFBBBFBRLL FFFBBBFLLR BBFFFFFRRL FBFBBFFRLL BFFBFBFRLR FBFBBFFRRL FBFBBFFLLL FBFBBBBRLL FBBFFBFLRL BFFBFBBRRL FBBFBFBRRL FFFBBFBLRR BFFBBBFRLL FBFFFFFRRR BFFFFBBLRR FBBFFFBRRL FBBBBBBRRR FFFBBFFLLR BBFBFFFRRL BBFFFFBRRL FBBBFBFLRL FFBFBBFRRR FFBBBFFLRL BFFBFFFLRL BFBFBFBRRR BFBBFFBRLL BFFFFBBLLR FBBBFBBLRR BFFBFFFLRR BFFFBFBLLL FBBBBBFRRR BFFBFBFLLL FBFFBFFRRL BFBBFBBRRR FBFFFBFRRL BFBBBFBLLR BFBBBBBRRR FFFBFBFRRL BFFFBFFLLL BFFFFFFLRR FFBBFFFRLL BFFBFFFRRR BBFFBBFLRL FBFBBBBLLR FFFBFFBLRL FFBBBBBRLR FBFFBFFRRR BFBBFBBRLR FBFBBBBLRL BFBBFFFLLR FBBBFBBLRL BFFBBBFLLR FFFBBBBRRR BBFBFFFLRL FBFBBFFLLR BBFFFFBLRR FFBBFFBRRR FFBBBFBRLL BFFFFBFLLL BBFFBFBRLR FBFBBFBRRL FFFBBBFRRR FBBBFBBRRL FFFBFBBRRR FBFFBBBRLL FFBFBBBRLR BFFFBBBRLL FBBFBBBRLL FBBBBBBLLR FBFBBFBLRR BFBFBFBLRL FFFBFFBRLR BFFFFFFRLL BFFBFBBLRL FBFFBFFLRR FBFBBBBRLR BBFFFBBLRL FBBBFBBRRR BBFBFBFRRR FBFBFFFLRR BBFBFFBLLL FBBBFFFLLR BFBFFBFRRL BFBBBBFRLL FFBFBFFLRL FBBBBFBLRR FBBBBFFLLR FFBBFFBLLL FBFFFBBRRR FFBBBBFLLR BFBFBFFRRL FFBBBBBLRR BFFFFFFRRL BBFBFFFLRR BFFBFBFRRL BBFFBBBRRR BBFFBFFLRL FFFBFFBRRR BFBFBBFRLR BBFBFBBLRR BFBBBBBRLL FBBBFFBRRL FBBBBBBRLL FBBFBBFRLR FBBFFFFRRL BFBFFBFRRR BFBBFFFLLL FBFFBFFLLR BFBBBFBLRR BFFBBBBRLL FBBBBBBLRR FFBFFBFRRL BBFFFBBRLL FBFFFBBRLL FFBBFFFLRL BFBBBBFRRL FFBFFBBRRR BFFBBFBRLL FFBFFFBLLR FFBFFFFLLL FBBBBBFLLR FBFBFBBLLR FBFBBFBRRR BFBFFFFLRR BFBBFFBRRR FBFBFFFRRR FFBFBBBRLL BFBBBFBRRL BFFBBBFLRL BBFBFFFRLR BBFBBFFLRR FBBFFBBRRL FBFBBBFLRL FFBFBBFRRL BFBFFBFLRL BFFFBFFRLR BBFBBFFRLR FFBFBFBLLL BFBFFBBRRR BBFFBBBRRL FBBBFBBLLL FFFBFFFLRL BBFFBFBLLL BFBBFFFRLR FBBBBFBLRL BFBFBBBLRL FFFBFBFRLR FBFFFFBRRL BFFBBFFLLR FBFFFFFRRL FBBFFBFRLR FBBBFBBRLL FBFFFFFLLR BFBFBBBLLR FFBBBBBRRR BFFFBBFLLR FBFBBFBLLL FBBFFFFLLR FBFFFFBLRL FBBBFBBRLR FBBFBBFLRR FBBBBBBRRL BBFBFBFLRL FFBBBBFRRL BFBBFBFLRL BFFFFFFRRR BBFBFBBLRL BFBFFFFRLL BBFFBFBRRR BFBFFBBLLR FBFBFFBRRL FFBBFBFLLL BFBFBBBRLR FFBBFBFLRL BBFFFFFRRR FBFFFFFLRL FFBFBFBRLL BBFFFFFLRR BBFFFBBRLR FBFBBFBRLR FBBBBBFLRL FBBFFFBLRR FFFBBFBLLL BBFBFFBRLL FFBFBFBLLR BFBBFBBRRL BFFBFFBLLL BBFFBFFRLR BBFFBBFLLR BFFFFFBLRL FBBFFFBLLL FBFFFFBLLR FBBFBFFRRL BFBBBFFRRL BBFFFBFLRL FBFBFBFLLR FBFFFBBRLR BBFFBFBRLL BBFFBFFRRL BBFFBBFRLR FBFFBFBRLL BFBFBBFRRR BFBBFBBLRL FFBBFFBRLL FBFFBFBRLR BBFBFBBLLR BFBBBFFRLL FFBFFBFLLL BFBFFBFRLR BFFFBBFLRL BFFFBBBLRL BFBBBFFLLR FBBFBBFRRR BFFBFFBRRL BFBFFBBRRL BFBBFBFRLL FBBFFBFRLL FFBBBBFRLL FBBBFFBRLL BBFFBFBLRL BBFFFFFLLL BBFFFFBRRR FBBBBFBRLR BFFBFFBRRR FFBFBFFRLL FFFFBBFRRR BBFBBFFLRL FFFBBBFLRR BFFBFBBLLL BFBBFFFLRL BFFBFFFRLL BFBFFBBRLL FFBBBBFLLL BFFBFBBRRR BFBBBBFRRR FFBFFFFRLR BFBFBFFRRR BFBFBBBLRR FBBBFFFLRR FFBFFFBRRL FBBFFFBRLR FFFBFFBRLL BFBBFFBLLR FFBBFBBLRL FFBFFFBRLR BFBBFFFRLL FBBFBBBLLL FBBFFFFLLL FFBFBBBRRR FFFBBBFLRL BBFFBBFLRR FFFBFFFRLL FFBBFBFRRR FBFFBFBRRR BFFBBFBLRL FFFBBBFRLL FBBBFFBRRR FFBBBBFLRL FFBFBBBRRL FBFBFFBRRR FBFFFBBLLL FBBFFFFLRL BFFFFBBRLR BFBBBBBRLR BBFFFBBRRL BBFBFBFRLR FBFFFFFLLL FBFBBBBRRL BFBFFFBLRL FFFFBBBLLR FFBBBFBRLR FBBFFFFRRR BBFBFFBLRL BBFFFFBLRL BFBFFBFLRR FFFBBBBLRR FBBBBBBLRL FBBFFBFRRL FBFFBBBRRL FBFFBBFRRL FFFBFBFLLR BBFFFFFRLL FFFBBBBLLR BBFFBBBLRR FBFBFFBLLR BFBBFBBRLL BFFFFFBRLR FFFBFBBLRR BFFBBBBLLL BFFBBFBLLR FBBBBBFRLR BBFBFFBRLR FBFFBFFLLL FBBFBBBLLR FFFFBBBRRL FBBFBBFRLL BFBFFFFRLR FFFFBBBRLL BFBBBFFLLL BFBFBFFRLR FBFBFFBRLL FBBBFFFRLL FBFBFFBRLR FBBBBFFRLR BBFBFFFRLL BFFBBBBRLR BFBFFFFRRL FBBBFFFRRR FFBFFBBRLR FFBBBBFLRR BBFBBFBRRL BFFBFFBLLR BBFBBFBRLL FBFFBFBLLR BBFBFBFLLR FBFBFBBRLR BBFBBFFRRL BFBFFFBLLR BFBFFBBLLL FBFFBFBLRR BFBBBBBLLL BFFFBFFRRL BFBBBFBRLR BBFFFBFRLL BFFBFFBRLL BBFBBFFLLL BFBBBBBLRR FFFBFFBLLL BBFFBBBRLL FFFBBBBLRL FFBBBFBLLR FFBBFFBLRL FFBBFFBLRR FBBBBBBRLR BFFFBBBLLR FBBFBFBLRR FBFBFBFRLR FFBFBBBLRR FFFFBBBLRL FFFBBFFRLL FFFBFFFLRR BBFFFFBLLR BFBFBFFLLL FFBBBFFRLR FBBFBFBRLL FFBBBFBLLL FBBFFFBLLR BBFBFFBLRR BFFFFBBLLL FBBBFBFLLR FFBBBFFLRR BFFBBBFRRR FFBFFFBLRR FBBFFBBRLR BFBBFBFLRR FFFBBFFRRR FFBFFFFRLL FFFBFBFRRR FFBFFFFRRR FBBFBBBLRL FFFBFBBRLR FBBFFFBRLL FFBFBFFRLR FBBBBFFRRR BFBFFBBLRR FBFFBBBLRL FBBFBBFLLR BBFFFFFLRL BFBBFBFRLR BFFBFFBLRR FFBFFFBRLL FBFBFFBLLL BFFFFFBLLL FBFFBBFLLL BFBFFBBRLR FBFBBFBRLL FFBBFBBRLL BFFFBBBRLR BFFFFFFLRL BBFFFBBLLR BFBFBBFLRL FFBBBFFRRL BBFFFBFLLL BBFFBBBLLL BBFFBFBLLR FBFBBBFRLL FFBBBBBRRL FFBBBFFLLR FFBFBBBLRL BFFBFFBLRL BFFFBFFRRR BFBFFFBRRR FFFBBBFRLR FFBBFBBLLR BFFFBBFLRR FFBBBBFRRR BFBFBBFLRR FFFBBBBRRL BFBFFBFRLL FBFFBFFRLR FBBFFFFRLR BFBBBBBLRL FFBBFBBLLL FFBFBBFLLR BFFBBBBLRL BFBFFFFLRL BBFFFBFRRL FFBFFBBLRR FBFBFBFRRR BBFFFBFRLR BFBBBFFLRR BBFBFFBLLR BBFBBFBLLR FBFFFBFRLL BFBFBBBLLL FBBFBFFLRL BFFFBBBLRR BBFBFFBRRR FBBFFFFRLL BFFFBBBRRL FBBFBBBRRR FBBFFBFLLR BFFFFFBRRL FBFFFFBRLR FBFBFBBRRR FBFBFBFRLL BFFBFFFRRL FFFBBFFRRL BFBFFFBRLR FBBFBFFLRR BFFFBFBRRL FBFBBBFLLR FBFBBBFRRR FBFFBFBLRL FFFFBBBRRR FFBBBFFRRR FFFBFBFLLL BFBBFFBLRR FBBFBFBRRR BBFFBBFRRL BBFFBFFRRR BBFFBFFLRR FBBFFBBLRR BFBFFFFRRR BFFBFBBRLR BFFFBBFRLL BFBFBBBRLL BBFFFBBLRR FFBBFBFLRR FBFBBBFLLL BBFFBBBLLR FFBBFFFLRR FBFFFBFRRR BFBBFBBLLR FFBBFBFRLR BFBFFFFLLR FBBBFBFRRR BFBBFBFRRR BFFBFBFRRR FFBFBBBLLR FBFBFFFRLR FBBFBFBLLL BFFBBFFRLL FBBBBFBLLR FFFBBBFLLL BFBFBBFRLL FFFBFBFLRR FBBBBFBLLL FFBBFBFRLL BBFFBBFRRR FFBFFBFRLL FBFBFBBLLL FBFFBBBLLR FFFBFBBLRL FFFBBFBRRR FBFFBFFLRL FBBFBFBLLR BFBBBBFRLR FBBFBFBRLR FFFBFFBRRL FBFBBFBLLR BFFBFFFRLR FFBFBFBLRR BFBFBBBRRL FBFFFFFLRR FFFBBFFLLL BFFFBBFRLR FFBFFBFRLR BBFFFFBRLR BFFBFFBRLR BFBFBFBRLR FFBFFBBRLL FBBBFFBLLL BFBBBBBLLR BFFFFBFRLL FBBFFFBRRR FFFFBBBLLL FBFBFBBRRL FBFFFFFRLR FBFFBBFRRR BFFBBFFLRL BFBFBBBRRR FBBFBFFRLR BFFBBFBRRL BFBBFBFRRL FBFFFBBRRL FBBFFBBLLL FBFBFBBRLL BFFFFBFRRR FBBBFFBLLR FBBFBBBLRR FFBFBFFLLR FBFFBFFRLL FBBBFFBRLR FBFFBBBLLL FBFBFBFRRL BBFBBFBLLL FFBFBFFRRL FBBBFFFRRL FBFFBBFLRR FBFBFFFLLR BFFFFBBLRL BFBFBFFLRL FBFFFFBLLL FBBBFFFLRL BFFBBBBLLR BFFBBBFLLL BFFBBFBRRR FBBBFBFRLL BBFBFFFRRR BFBBBFFRLR FFBFBFBRRR FBFFBBFRLL FFFBBBBLLL FBFFBFBRRL BFFFFFBLLR BFBFFFBLLL FBFBBBBLLL BFFBBFFRRL BBFFBFBRRL FBBFBFFRRR BFBBFFBLLL BFBFFBBLRL BBFFFFFLLR FFBFBBFRLL FFBFFBFLLR FBBFFBBLRL BBFFFFBLLL BFFFBFFLLR FBFFFBFLLL BBFBFBFLLL FFBFFBBLRL FBFFBBBRLR BFFFFBBRRL FFBBBBBLLR FFBBFBFRRL FFBFBBFLRL FFBFBFFLRR FFFBFFBLLR BBFBFBBRLL BFBBBBBRRL FBFBBBFRLR FBFBFFFRLL BFFFFFFLLR FBBBBFFLLL FBBBFFBLRR BFFFBFBLRR FBBBFFFRLR BFFFFFBRLL FBBBBFBRRL BFBBBFBLRL FFBBFBBLRR BFFBBFBLLL BFBBFFFRRL FBBBBFFRRL BFFFBBFRRR FFFBFFFRRR FBFBBFBLRL BFBBFFBLRL BBFBBFBRLR BBFBBFBLRL FFBFBFFRRR BBFFBBFLLL FBBBFFBLRL FBBFFBBRRR BFFBFBFLLR FBFBFBBLRR BFFFBBBRRR FBBFBFFLLL FBFBBFFLRR FFBBFFBRLR FFBBBBBLRL FFBBBFBRRL BFBFBFBLRR FBFFFFFRLL BFBFBBFLLL BBFBFBBRRL FBFBFBFLLL FFBFFBBLLR FFFBFFFLLR BFFFBFBRLL FBBFFBBRLL FBBFBFFLLR BFFFFBFLLR BFFFFFBLRR BBFFFBFLRR FBFBBFFRLR BFBBFBFLLL BFFBBBFRLR BBFBFBBRLR FFBBFBFLLR BBFBFBFRRL FBFBFBBLRL FBFFFBBLRR FBFBBFFRRR BFFFFBFRLR FBFFFBFLLR FFBBBFFRLL FBFBFFFLRL BFFFBBFLLL FFFBBFBRLL FFBBBFFLLL BFBBFBFLLR FFBFBBFLLL BFFBBBBRRL BBFFBBBLRL FBFFFFBLRR BFBBFBBLRR BFBBBFBRRR BBFBFFFLLL BFFBBFFRLR BFFFBFFLRL FBBBBFFRLL FBFFBBFLLR FBFBFFFLLL BFFBBFFLRR FFBBFBBRRL BFBFBBFLLR FFFBBFBLRL FFBBFFFRRR BBFFFBFRRR FBBBBFFLRL FFFBFBBLLL FFFBBFFRLR FBFFFBBLRL BBFBFBFRLL BBFFBBBRLR FFFBBFBRLR BBFBBFFRLL BBFFFFFRLR BFBFBFBLLL BBFBFFFLLR FBFFBFBLLL BBFFFBBLLL FBFFFFBRLL BBFFFFBRLL FFBFFFBRRR BFFFFFFRLR BBFFBBFRLL BFBFFFBRRL FFBBBBFRLR FFBFFBFRRR BBFBFBFLRR FFBFFBFLRL BFBFBFFLLR FBBBBBFLLL FFBFFFBLLL FFBBBFBLRR FFFFBBFRRL FBFBBBFLRR BFFBBFFRRR FBFBBBFRRL BFFFFFBRRR BFBFBBFRRL BFBFBFBRLL BFBFFFFLLL FBBBBBFRRL BFBBBBFLRR BBFBBFFRRR BFFBFFFLLR BFBBFBBLLL FBFFFFBRRR FBFFFBFRLR BFFBFBBRLL FFBBBFBRRR FFBBFFFLLR BBFBBFFLLR BFBBFFBRLR FFFBBFFLRL BFFFFFFLLL FFBBFFFRLR BFFBFBFRLL FFBFFBBLLL BFFFBFFLRR FFBFFFFLRR FBBBBFBRLL BFBFFBFLLR FFBBFFBRRL BFFBBFBLRR FFBFFBFLRR BFFBFBBLLR FBBFBBFLLL FBFFBBBLRR BBFFBFFLLR BFBBBFFRRR FFFBFFFRRL FBBFBBBRRL FFFBFBBLLR BFFFBFFRLL BBFBFFBRRL FFBFFFFRRL FFBFBFBRLR FBBFBFFRLL FFFBBFBLLR FFFBBBFRRL FBBBBBFRLL BFFFBBFRRL BFBFBFBRRL FBBBBFFLRR FFFBBFBRRL BFFBBFBRLR BBFBFBBLLL FFBFFFFLLR FFBFFFFLRL FFFFBBBLRR BBFBBFBLRR FBBBFBFLRR FFFBFBBRLL FBFFBBFRLR FFBFBBFLRR FFFBFBFLRL FFFBBBBRLR FBBFBBBRLR FBFBFFBLRL BFBBBBFLRL BFFFFBBRRR BFFBBBFRRL BFFFFBBRLL BFBFBFFLRR BBFFBFBLRR FBBFBBFLRL FFFBFFFLLL FBBBBFBRRR FBFFFBBLLR FBBFFBBLLR FFBBFFFLLL FFBFBFFLLL FBFBFBFLRR BBFFFBFLLR BFFBBBBLRR FBBFFFBLRL FFFBFFBLRR BFBFBFBLLR FBFBBFFLRL BFBFBFFRLL FFBFBBBLLL FBFFBBBRRR FFBBFFFRRL FFBBFBBRRR FFBFFFBLRL BFBBBFFLRL