diff --git a/AffineMatrixColPEBox.hpp b/AffineMatrixColPEBox.hpp new file mode 100644 index 0000000..a38d93e --- /dev/null +++ b/AffineMatrixColPEBox.hpp @@ -0,0 +1,888 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#ifndef AFFINEMATRIXCOLPEBOX_H__ +#define AFFINEMATRIXCOLPEBOX_H__ + +#define AFFINE_MATRIX_COL_PE_BOX4_N 840 + +const ALIGNED_TYPE_(uint8_t, 16) AffineMatrixColPEBox4[AFFINE_MATRIX_COL_PE_BOX4_N][16] = +{ + {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15}, + {0, 9, 8, 1, 4, 13, 12, 5, 2, 11, 10, 3, 6, 15, 14, 7}, + {0, 10, 8, 2, 4, 14, 12, 6, 1, 11, 9, 3, 5, 15, 13, 7}, + {0, 12, 8, 4, 2, 14, 10, 6, 1, 13, 9, 5, 3, 15, 11, 7}, + {0, 8, 5, 13, 4, 12, 1, 9, 2, 10, 7, 15, 6, 14, 3, 11}, + {0, 8, 6, 14, 4, 12, 2, 10, 1, 9, 7, 15, 5, 13, 3, 11}, + {0, 12, 4, 8, 2, 14, 6, 10, 1, 13, 5, 9, 3, 15, 7, 11}, + {0, 8, 4, 12, 3, 11, 7, 15, 2, 10, 6, 14, 1, 9, 5, 13}, + {0, 8, 6, 14, 2, 10, 4, 12, 1, 9, 7, 15, 3, 11, 5, 13}, + {0, 10, 4, 14, 2, 8, 6, 12, 1, 11, 5, 15, 3, 9, 7, 13}, + {0, 8, 4, 12, 3, 11, 7, 15, 1, 9, 5, 13, 2, 10, 6, 14}, + {0, 8, 5, 13, 2, 10, 7, 15, 1, 9, 4, 12, 3, 11, 6, 14}, + {0, 9, 4, 13, 2, 11, 6, 15, 1, 8, 5, 12, 3, 10, 7, 14}, + {0, 10, 9, 3, 8, 2, 1, 11, 4, 14, 13, 7, 12, 6, 5, 15}, + {0, 12, 9, 5, 8, 4, 1, 13, 2, 14, 11, 7, 10, 6, 3, 15}, + {0, 12, 10, 6, 8, 4, 2, 14, 1, 13, 11, 7, 9, 5, 3, 15}, + {0, 9, 8, 1, 6, 15, 14, 7, 4, 13, 12, 5, 2, 11, 10, 3}, + {0, 10, 8, 2, 5, 15, 13, 7, 4, 14, 12, 6, 1, 11, 9, 3}, + {0, 12, 8, 4, 5, 9, 13, 1, 2, 14, 10, 6, 7, 11, 15, 3}, + {0, 12, 9, 5, 4, 8, 13, 1, 2, 14, 11, 7, 6, 10, 15, 3}, + {0, 13, 8, 5, 4, 9, 12, 1, 2, 15, 10, 7, 6, 11, 14, 3}, + {0, 12, 8, 4, 6, 10, 14, 2, 1, 13, 9, 5, 7, 11, 15, 3}, + {0, 12, 10, 6, 4, 8, 14, 2, 1, 13, 11, 7, 5, 9, 15, 3}, + {0, 14, 8, 6, 4, 10, 12, 2, 1, 15, 9, 7, 5, 11, 13, 3}, + {0, 9, 8, 1, 6, 15, 14, 7, 2, 11, 10, 3, 4, 13, 12, 5}, + {0, 10, 8, 2, 4, 14, 12, 6, 3, 9, 11, 1, 7, 13, 15, 5}, + {0, 10, 9, 3, 4, 14, 13, 7, 2, 8, 11, 1, 6, 12, 15, 5}, + {0, 11, 8, 3, 4, 15, 12, 7, 2, 9, 10, 1, 6, 13, 14, 5}, + {0, 9, 8, 1, 4, 13, 12, 5, 3, 10, 11, 2, 7, 14, 15, 6}, + {0, 9, 8, 1, 5, 12, 13, 4, 2, 11, 10, 3, 7, 14, 15, 6}, + {0, 10, 8, 2, 6, 12, 14, 4, 1, 11, 9, 3, 7, 13, 15, 5}, + {0, 10, 8, 2, 5, 15, 13, 7, 1, 11, 9, 3, 4, 14, 12, 6}, + {0, 10, 9, 3, 4, 14, 13, 7, 1, 11, 8, 2, 5, 15, 12, 6}, + {0, 11, 8, 3, 4, 15, 12, 7, 1, 10, 9, 2, 5, 14, 13, 6}, + {0, 12, 8, 4, 3, 15, 11, 7, 2, 14, 10, 6, 1, 13, 9, 5}, + {0, 12, 10, 6, 2, 14, 8, 4, 1, 13, 11, 7, 3, 15, 9, 5}, + {0, 14, 8, 6, 2, 12, 10, 4, 1, 15, 9, 7, 3, 13, 11, 5}, + {0, 12, 8, 4, 3, 15, 11, 7, 1, 13, 9, 5, 2, 14, 10, 6}, + {0, 12, 9, 5, 2, 14, 11, 7, 1, 13, 8, 4, 3, 15, 10, 6}, + {0, 13, 8, 5, 2, 15, 10, 7, 1, 12, 9, 4, 3, 14, 11, 6}, + {0, 8, 6, 14, 5, 13, 3, 11, 4, 12, 2, 10, 1, 9, 7, 15}, + {0, 12, 5, 9, 4, 8, 1, 13, 2, 14, 7, 11, 6, 10, 3, 15}, + {0, 12, 6, 10, 4, 8, 2, 14, 1, 13, 7, 11, 5, 9, 3, 15}, + {0, 8, 6, 14, 4, 12, 2, 10, 3, 11, 5, 13, 7, 15, 1, 9}, + {0, 8, 6, 14, 5, 13, 3, 11, 2, 10, 4, 12, 7, 15, 1, 9}, + {0, 8, 7, 15, 4, 12, 3, 11, 2, 10, 5, 13, 6, 14, 1, 9}, + {0, 10, 5, 15, 4, 14, 1, 11, 2, 8, 7, 13, 6, 12, 3, 9}, + {0, 8, 5, 13, 4, 12, 1, 9, 3, 11, 6, 14, 7, 15, 2, 10}, + {0, 9, 5, 12, 4, 13, 1, 8, 2, 11, 7, 14, 6, 15, 3, 10}, + {0, 10, 6, 12, 4, 14, 2, 8, 1, 11, 7, 13, 5, 15, 3, 9}, + {0, 8, 6, 14, 5, 13, 3, 11, 1, 9, 7, 15, 4, 12, 2, 10}, + {0, 8, 7, 15, 4, 12, 3, 11, 1, 9, 6, 14, 5, 13, 2, 10}, + {0, 9, 6, 15, 4, 13, 2, 11, 1, 8, 7, 14, 5, 12, 3, 10}, + {0, 12, 4, 8, 3, 15, 7, 11, 2, 14, 6, 10, 1, 13, 5, 9}, + {0, 12, 6, 10, 2, 14, 4, 8, 1, 13, 7, 11, 3, 15, 5, 9}, + {0, 14, 4, 10, 2, 12, 6, 8, 1, 15, 5, 11, 3, 13, 7, 9}, + {0, 12, 4, 8, 3, 15, 7, 11, 1, 13, 5, 9, 2, 14, 6, 10}, + {0, 12, 5, 9, 2, 14, 7, 11, 1, 13, 4, 8, 3, 15, 6, 10}, + {0, 13, 4, 9, 2, 15, 6, 11, 1, 12, 5, 8, 3, 14, 7, 10}, + {0, 8, 6, 14, 3, 11, 5, 13, 2, 10, 4, 12, 1, 9, 7, 15}, + {0, 10, 4, 14, 3, 9, 7, 13, 2, 8, 6, 12, 1, 11, 5, 15}, + {0, 8, 5, 13, 3, 11, 6, 14, 2, 10, 7, 15, 1, 9, 4, 12}, + {0, 9, 4, 13, 3, 10, 7, 14, 2, 11, 6, 15, 1, 8, 5, 12}, + {0, 10, 6, 12, 2, 8, 4, 14, 1, 11, 7, 13, 3, 9, 5, 15}, + {0, 8, 6, 14, 3, 11, 5, 13, 1, 9, 7, 15, 2, 10, 4, 12}, + {0, 8, 7, 15, 2, 10, 5, 13, 1, 9, 6, 14, 3, 11, 4, 12}, + {0, 9, 6, 15, 2, 11, 4, 13, 1, 8, 7, 14, 3, 10, 5, 12}, + {0, 10, 4, 14, 3, 9, 7, 13, 1, 11, 5, 15, 2, 8, 6, 12}, + {0, 10, 5, 15, 2, 8, 7, 13, 1, 11, 4, 14, 3, 9, 6, 12}, + {0, 11, 4, 15, 2, 9, 6, 13, 1, 10, 5, 14, 3, 8, 7, 12}, + {0, 8, 5, 13, 3, 11, 6, 14, 1, 9, 4, 12, 2, 10, 7, 15}, + {0, 9, 4, 13, 3, 10, 7, 14, 1, 8, 5, 12, 2, 11, 6, 15}, + {0, 9, 5, 12, 2, 11, 7, 14, 1, 8, 4, 13, 3, 10, 6, 15}, + {0, 12, 10, 6, 9, 5, 3, 15, 8, 4, 2, 14, 1, 13, 11, 7}, + {0, 12, 9, 5, 8, 4, 1, 13, 6, 10, 15, 3, 14, 2, 7, 11}, + {0, 12, 10, 6, 8, 4, 2, 14, 5, 9, 15, 3, 13, 1, 7, 11}, + {0, 12, 10, 6, 9, 5, 3, 15, 4, 8, 14, 2, 13, 1, 7, 11}, + {0, 13, 10, 7, 8, 5, 2, 15, 4, 9, 14, 3, 12, 1, 6, 11}, + {0, 14, 9, 7, 8, 6, 1, 15, 4, 10, 13, 3, 12, 2, 5, 11}, + {0, 10, 9, 3, 8, 2, 1, 11, 6, 12, 15, 5, 14, 4, 7, 13}, + {0, 11, 10, 1, 8, 3, 2, 9, 4, 15, 14, 5, 12, 7, 6, 13}, + {0, 10, 9, 3, 8, 2, 1, 11, 5, 15, 12, 6, 13, 7, 4, 14}, + {0, 11, 9, 2, 8, 3, 1, 10, 4, 15, 13, 6, 12, 7, 5, 14}, + {0, 13, 12, 1, 8, 5, 4, 9, 2, 15, 14, 3, 10, 7, 6, 11}, + {0, 14, 12, 2, 8, 6, 4, 10, 1, 15, 13, 3, 9, 7, 5, 11}, + {0, 12, 10, 6, 8, 4, 2, 14, 3, 15, 9, 5, 11, 7, 1, 13}, + {0, 12, 10, 6, 9, 5, 3, 15, 2, 14, 8, 4, 11, 7, 1, 13}, + {0, 12, 11, 7, 8, 4, 3, 15, 2, 14, 9, 5, 10, 6, 1, 13}, + {0, 14, 9, 7, 8, 6, 1, 15, 2, 12, 11, 5, 10, 4, 3, 13}, + {0, 12, 9, 5, 8, 4, 1, 13, 3, 15, 10, 6, 11, 7, 2, 14}, + {0, 13, 9, 4, 8, 5, 1, 12, 2, 15, 11, 6, 10, 7, 3, 14}, + {0, 14, 10, 4, 8, 6, 2, 12, 1, 15, 11, 5, 9, 7, 3, 13}, + {0, 12, 10, 6, 9, 5, 3, 15, 1, 13, 11, 7, 8, 4, 2, 14}, + {0, 12, 11, 7, 8, 4, 3, 15, 1, 13, 10, 6, 9, 5, 2, 14}, + {0, 13, 10, 7, 8, 5, 2, 15, 1, 12, 11, 6, 9, 4, 3, 14}, + {0, 12, 8, 4, 6, 10, 14, 2, 5, 9, 13, 1, 3, 15, 11, 7}, + {0, 12, 9, 5, 6, 10, 15, 3, 4, 8, 13, 1, 2, 14, 11, 7}, + {0, 13, 8, 5, 6, 11, 14, 3, 4, 9, 12, 1, 2, 15, 10, 7}, + {0, 12, 10, 6, 5, 9, 15, 3, 4, 8, 14, 2, 1, 13, 11, 7}, + {0, 14, 8, 6, 5, 11, 13, 3, 4, 10, 12, 2, 1, 15, 9, 7}, + {0, 10, 8, 2, 6, 12, 14, 4, 5, 15, 13, 7, 3, 9, 11, 1}, + {0, 10, 8, 2, 7, 13, 15, 5, 4, 14, 12, 6, 3, 9, 11, 1}, + {0, 10, 9, 3, 6, 12, 15, 5, 4, 14, 13, 7, 2, 8, 11, 1}, + {0, 11, 8, 3, 6, 13, 14, 5, 4, 15, 12, 7, 2, 9, 10, 1}, + {0, 9, 8, 1, 6, 15, 14, 7, 5, 12, 13, 4, 3, 10, 11, 2}, + {0, 9, 8, 1, 7, 14, 15, 6, 4, 13, 12, 5, 3, 10, 11, 2}, + {0, 10, 9, 3, 5, 15, 12, 6, 4, 14, 13, 7, 1, 11, 8, 2}, + {0, 11, 8, 3, 5, 14, 13, 6, 4, 15, 12, 7, 1, 10, 9, 2}, + {0, 13, 12, 1, 4, 9, 8, 5, 2, 15, 14, 3, 6, 11, 10, 7}, + {0, 14, 12, 2, 4, 10, 8, 6, 1, 15, 13, 3, 5, 11, 9, 7}, + {0, 12, 8, 4, 6, 10, 14, 2, 3, 15, 11, 7, 5, 9, 13, 1}, + {0, 12, 8, 4, 7, 11, 15, 3, 2, 14, 10, 6, 5, 9, 13, 1}, + {0, 12, 9, 5, 6, 10, 15, 3, 2, 14, 11, 7, 4, 8, 13, 1}, + {0, 13, 8, 5, 6, 11, 14, 3, 2, 15, 10, 7, 4, 9, 12, 1}, + {0, 12, 10, 6, 4, 8, 14, 2, 3, 15, 9, 5, 7, 11, 13, 1}, + {0, 12, 10, 6, 5, 9, 15, 3, 2, 14, 8, 4, 7, 11, 13, 1}, + {0, 12, 11, 7, 4, 8, 15, 3, 2, 14, 9, 5, 6, 10, 13, 1}, + {0, 13, 10, 7, 4, 9, 14, 3, 2, 15, 8, 5, 6, 11, 12, 1}, + {0, 14, 8, 6, 4, 10, 12, 2, 3, 13, 11, 5, 7, 9, 15, 1}, + {0, 14, 8, 6, 5, 11, 13, 3, 2, 12, 10, 4, 7, 9, 15, 1}, + {0, 14, 9, 7, 4, 10, 13, 3, 2, 12, 11, 5, 6, 8, 15, 1}, + {0, 15, 8, 7, 4, 11, 12, 3, 2, 13, 10, 5, 6, 9, 14, 1}, + {0, 12, 8, 4, 5, 9, 13, 1, 3, 15, 11, 7, 6, 10, 14, 2}, + {0, 12, 9, 5, 4, 8, 13, 1, 3, 15, 10, 6, 7, 11, 14, 2}, + {0, 13, 8, 5, 4, 9, 12, 1, 3, 14, 11, 6, 7, 10, 15, 2}, + {0, 12, 8, 4, 7, 11, 15, 3, 1, 13, 9, 5, 6, 10, 14, 2}, + {0, 12, 9, 5, 6, 10, 15, 3, 1, 13, 8, 4, 7, 11, 14, 2}, + {0, 13, 8, 5, 6, 11, 14, 3, 1, 12, 9, 4, 7, 10, 15, 2}, + {0, 12, 10, 6, 5, 9, 15, 3, 1, 13, 11, 7, 4, 8, 14, 2}, + {0, 12, 11, 7, 4, 8, 15, 3, 1, 13, 10, 6, 5, 9, 14, 2}, + {0, 13, 10, 7, 4, 9, 14, 3, 1, 12, 11, 6, 5, 8, 15, 2}, + {0, 14, 8, 6, 5, 11, 13, 3, 1, 15, 9, 7, 4, 10, 12, 2}, + {0, 14, 9, 7, 4, 10, 13, 3, 1, 15, 8, 6, 5, 11, 12, 2}, + {0, 15, 8, 7, 4, 11, 12, 3, 1, 14, 9, 6, 5, 10, 13, 2}, + {0, 10, 8, 2, 6, 12, 14, 4, 3, 9, 11, 1, 5, 15, 13, 7}, + {0, 10, 9, 3, 6, 12, 15, 5, 2, 8, 11, 1, 4, 14, 13, 7}, + {0, 11, 8, 3, 6, 13, 14, 5, 2, 9, 10, 1, 4, 15, 12, 7}, + {0, 9, 8, 1, 6, 15, 14, 7, 3, 10, 11, 2, 5, 12, 13, 4}, + {0, 9, 8, 1, 7, 14, 15, 6, 2, 11, 10, 3, 5, 12, 13, 4}, + {0, 11, 10, 1, 4, 15, 14, 5, 2, 9, 8, 3, 6, 13, 12, 7}, + {0, 10, 8, 2, 5, 15, 13, 7, 3, 9, 11, 1, 6, 12, 14, 4}, + {0, 10, 9, 3, 5, 15, 12, 6, 2, 8, 11, 1, 7, 13, 14, 4}, + {0, 11, 8, 3, 5, 14, 13, 6, 2, 9, 10, 1, 7, 12, 15, 4}, + {0, 9, 8, 1, 5, 12, 13, 4, 3, 10, 11, 2, 6, 15, 14, 7}, + {0, 10, 8, 2, 7, 13, 15, 5, 1, 11, 9, 3, 6, 12, 14, 4}, + {0, 10, 9, 3, 6, 12, 15, 5, 1, 11, 8, 2, 7, 13, 14, 4}, + {0, 11, 8, 3, 6, 13, 14, 5, 1, 10, 9, 2, 7, 12, 15, 4}, + {0, 10, 9, 3, 5, 15, 12, 6, 1, 11, 8, 2, 4, 14, 13, 7}, + {0, 11, 8, 3, 5, 14, 13, 6, 1, 10, 9, 2, 4, 15, 12, 7}, + {0, 11, 9, 2, 4, 15, 13, 6, 1, 10, 8, 3, 5, 14, 12, 7}, + {0, 12, 10, 6, 3, 15, 9, 5, 2, 14, 8, 4, 1, 13, 11, 7}, + {0, 14, 8, 6, 3, 13, 11, 5, 2, 12, 10, 4, 1, 15, 9, 7}, + {0, 12, 9, 5, 3, 15, 10, 6, 2, 14, 11, 7, 1, 13, 8, 4}, + {0, 13, 8, 5, 3, 14, 11, 6, 2, 15, 10, 7, 1, 12, 9, 4}, + {0, 14, 10, 4, 2, 12, 8, 6, 1, 15, 11, 5, 3, 13, 9, 7}, + {0, 12, 10, 6, 3, 15, 9, 5, 1, 13, 11, 7, 2, 14, 8, 4}, + {0, 12, 11, 7, 2, 14, 9, 5, 1, 13, 10, 6, 3, 15, 8, 4}, + {0, 13, 10, 7, 2, 15, 8, 5, 1, 12, 11, 6, 3, 14, 9, 4}, + {0, 14, 8, 6, 3, 13, 11, 5, 1, 15, 9, 7, 2, 12, 10, 4}, + {0, 14, 9, 7, 2, 12, 11, 5, 1, 15, 8, 6, 3, 13, 10, 4}, + {0, 15, 8, 7, 2, 13, 10, 5, 1, 14, 9, 6, 3, 12, 11, 4}, + {0, 12, 9, 5, 3, 15, 10, 6, 1, 13, 8, 4, 2, 14, 11, 7}, + {0, 13, 8, 5, 3, 14, 11, 6, 1, 12, 9, 4, 2, 15, 10, 7}, + {0, 13, 9, 4, 2, 15, 11, 6, 1, 12, 8, 5, 3, 14, 10, 7}, + {0, 12, 6, 10, 5, 9, 3, 15, 4, 8, 2, 14, 1, 13, 7, 11}, + {0, 8, 7, 15, 6, 14, 1, 9, 4, 12, 3, 11, 2, 10, 5, 13}, + {0, 10, 6, 12, 5, 15, 3, 9, 4, 14, 2, 8, 1, 11, 7, 13}, + {0, 8, 7, 15, 5, 13, 2, 10, 4, 12, 3, 11, 1, 9, 6, 14}, + {0, 9, 6, 15, 5, 12, 3, 10, 4, 13, 2, 11, 1, 8, 7, 14}, + {0, 12, 6, 10, 4, 8, 2, 14, 3, 15, 5, 9, 7, 11, 1, 13}, + {0, 12, 6, 10, 5, 9, 3, 15, 2, 14, 4, 8, 7, 11, 1, 13}, + {0, 12, 7, 11, 4, 8, 3, 15, 2, 14, 5, 9, 6, 10, 1, 13}, + {0, 14, 5, 11, 4, 10, 1, 15, 2, 12, 7, 9, 6, 8, 3, 13}, + {0, 12, 5, 9, 4, 8, 1, 13, 3, 15, 6, 10, 7, 11, 2, 14}, + {0, 13, 5, 8, 4, 9, 1, 12, 2, 15, 7, 10, 6, 11, 3, 14}, + {0, 14, 6, 8, 4, 10, 2, 12, 1, 15, 7, 9, 5, 11, 3, 13}, + {0, 12, 6, 10, 5, 9, 3, 15, 1, 13, 7, 11, 4, 8, 2, 14}, + {0, 12, 7, 11, 4, 8, 3, 15, 1, 13, 6, 10, 5, 9, 2, 14}, + {0, 13, 6, 11, 4, 9, 2, 15, 1, 12, 7, 10, 5, 8, 3, 14}, + {0, 8, 7, 15, 6, 14, 1, 9, 2, 10, 5, 13, 4, 12, 3, 11}, + {0, 10, 6, 12, 4, 14, 2, 8, 3, 9, 5, 15, 7, 13, 1, 11}, + {0, 10, 6, 12, 5, 15, 3, 9, 2, 8, 4, 14, 7, 13, 1, 11}, + {0, 10, 7, 13, 4, 14, 3, 9, 2, 8, 5, 15, 6, 12, 1, 11}, + {0, 9, 6, 15, 4, 13, 2, 11, 3, 10, 5, 12, 7, 14, 1, 8}, + {0, 9, 6, 15, 5, 12, 3, 10, 2, 11, 4, 13, 7, 14, 1, 8}, + {0, 9, 7, 14, 4, 13, 3, 10, 2, 11, 5, 12, 6, 15, 1, 8}, + {0, 10, 5, 15, 4, 14, 1, 11, 3, 9, 6, 12, 7, 13, 2, 8}, + {0, 11, 5, 14, 4, 15, 1, 10, 2, 9, 7, 12, 6, 13, 3, 8}, + {0, 9, 5, 12, 4, 13, 1, 8, 3, 10, 6, 15, 7, 14, 2, 11}, + {0, 10, 6, 12, 5, 15, 3, 9, 1, 11, 7, 13, 4, 14, 2, 8}, + {0, 10, 7, 13, 4, 14, 3, 9, 1, 11, 6, 12, 5, 15, 2, 8}, + {0, 11, 6, 13, 4, 15, 2, 9, 1, 10, 7, 12, 5, 14, 3, 8}, + {0, 8, 7, 15, 5, 13, 2, 10, 1, 9, 6, 14, 4, 12, 3, 11}, + {0, 9, 6, 15, 5, 12, 3, 10, 1, 8, 7, 14, 4, 13, 2, 11}, + {0, 9, 7, 14, 4, 13, 3, 10, 1, 8, 6, 15, 5, 12, 2, 11}, + {0, 12, 6, 10, 3, 15, 5, 9, 2, 14, 4, 8, 1, 13, 7, 11}, + {0, 14, 4, 10, 3, 13, 7, 9, 2, 12, 6, 8, 1, 15, 5, 11}, + {0, 12, 5, 9, 3, 15, 6, 10, 2, 14, 7, 11, 1, 13, 4, 8}, + {0, 13, 4, 9, 3, 14, 7, 10, 2, 15, 6, 11, 1, 12, 5, 8}, + {0, 14, 6, 8, 2, 12, 4, 10, 1, 15, 7, 9, 3, 13, 5, 11}, + {0, 12, 6, 10, 3, 15, 5, 9, 1, 13, 7, 11, 2, 14, 4, 8}, + {0, 12, 7, 11, 2, 14, 5, 9, 1, 13, 6, 10, 3, 15, 4, 8}, + {0, 13, 6, 11, 2, 15, 4, 9, 1, 12, 7, 10, 3, 14, 5, 8}, + {0, 14, 4, 10, 3, 13, 7, 9, 1, 15, 5, 11, 2, 12, 6, 8}, + {0, 14, 5, 11, 2, 12, 7, 9, 1, 15, 4, 10, 3, 13, 6, 8}, + {0, 15, 4, 11, 2, 13, 6, 9, 1, 14, 5, 10, 3, 12, 7, 8}, + {0, 12, 5, 9, 3, 15, 6, 10, 1, 13, 4, 8, 2, 14, 7, 11}, + {0, 13, 4, 9, 3, 14, 7, 10, 1, 12, 5, 8, 2, 15, 6, 11}, + {0, 13, 5, 8, 2, 15, 7, 10, 1, 12, 4, 9, 3, 14, 6, 11}, + {0, 10, 6, 12, 3, 9, 5, 15, 2, 8, 4, 14, 1, 11, 7, 13}, + {0, 8, 7, 15, 3, 11, 4, 12, 2, 10, 5, 13, 1, 9, 6, 14}, + {0, 9, 6, 15, 3, 10, 5, 12, 2, 11, 4, 13, 1, 8, 7, 14}, + {0, 10, 5, 15, 3, 9, 6, 12, 2, 8, 7, 13, 1, 11, 4, 14}, + {0, 11, 4, 15, 3, 8, 7, 12, 2, 9, 6, 13, 1, 10, 5, 14}, + {0, 9, 5, 12, 3, 10, 6, 15, 2, 11, 7, 14, 1, 8, 4, 13}, + {0, 10, 6, 12, 3, 9, 5, 15, 1, 11, 7, 13, 2, 8, 4, 14}, + {0, 10, 7, 13, 2, 8, 5, 15, 1, 11, 6, 12, 3, 9, 4, 14}, + {0, 11, 6, 13, 2, 9, 4, 15, 1, 10, 7, 12, 3, 8, 5, 14}, + {0, 8, 7, 15, 3, 11, 4, 12, 1, 9, 6, 14, 2, 10, 5, 13}, + {0, 9, 6, 15, 3, 10, 5, 12, 1, 8, 7, 14, 2, 11, 4, 13}, + {0, 9, 7, 14, 2, 11, 5, 12, 1, 8, 6, 15, 3, 10, 4, 13}, + {0, 10, 5, 15, 3, 9, 6, 12, 1, 11, 4, 14, 2, 8, 7, 13}, + {0, 11, 4, 15, 3, 8, 7, 12, 1, 10, 5, 14, 2, 9, 6, 13}, + {0, 11, 5, 14, 2, 9, 7, 12, 1, 10, 4, 15, 3, 8, 6, 13}, + {0, 9, 5, 12, 3, 10, 6, 15, 1, 8, 4, 13, 2, 11, 7, 14}, + {0, 13, 12, 1, 10, 7, 6, 11, 8, 5, 4, 9, 2, 15, 14, 3}, + {0, 14, 12, 2, 9, 7, 5, 11, 8, 6, 4, 10, 1, 15, 13, 3}, + {0, 12, 11, 7, 10, 6, 1, 13, 8, 4, 3, 15, 2, 14, 9, 5}, + {0, 14, 10, 4, 9, 7, 3, 13, 8, 6, 2, 12, 1, 15, 11, 5}, + {0, 12, 11, 7, 9, 5, 2, 14, 8, 4, 3, 15, 1, 13, 10, 6}, + {0, 13, 10, 7, 9, 4, 3, 14, 8, 5, 2, 15, 1, 12, 11, 6}, + {0, 13, 12, 1, 8, 5, 4, 9, 6, 11, 10, 7, 14, 3, 2, 15}, + {0, 13, 12, 1, 10, 7, 6, 11, 4, 9, 8, 5, 14, 3, 2, 15}, + {0, 14, 12, 2, 8, 6, 4, 10, 5, 11, 9, 7, 13, 3, 1, 15}, + {0, 14, 12, 2, 9, 7, 5, 11, 4, 10, 8, 6, 13, 3, 1, 15}, + {0, 14, 13, 3, 8, 6, 5, 11, 4, 10, 9, 7, 12, 2, 1, 15}, + {0, 12, 10, 6, 8, 4, 2, 14, 7, 11, 13, 1, 15, 3, 5, 9}, + {0, 12, 11, 7, 8, 4, 3, 15, 6, 10, 13, 1, 14, 2, 5, 9}, + {0, 13, 10, 7, 8, 5, 2, 15, 6, 11, 12, 1, 14, 3, 4, 9}, + {0, 12, 9, 5, 8, 4, 1, 13, 7, 11, 14, 2, 15, 3, 6, 10}, + {0, 13, 9, 4, 8, 5, 1, 12, 6, 11, 15, 2, 14, 3, 7, 10}, + {0, 12, 11, 7, 10, 6, 1, 13, 4, 8, 15, 3, 14, 2, 5, 9}, + {0, 14, 10, 4, 8, 6, 2, 12, 5, 11, 15, 1, 13, 3, 7, 9}, + {0, 14, 11, 5, 8, 6, 3, 13, 4, 10, 15, 1, 12, 2, 7, 9}, + {0, 15, 10, 5, 8, 7, 2, 13, 4, 11, 14, 1, 12, 3, 6, 9}, + {0, 12, 11, 7, 8, 4, 3, 15, 5, 9, 14, 2, 13, 1, 6, 10}, + {0, 12, 11, 7, 9, 5, 2, 14, 4, 8, 15, 3, 13, 1, 6, 10}, + {0, 13, 11, 6, 8, 5, 3, 14, 4, 9, 15, 2, 12, 1, 7, 10}, + {0, 14, 9, 7, 8, 6, 1, 15, 5, 11, 12, 2, 13, 3, 4, 10}, + {0, 15, 9, 6, 8, 7, 1, 14, 4, 11, 13, 2, 12, 3, 5, 10}, + {0, 11, 10, 1, 8, 3, 2, 9, 6, 13, 12, 7, 14, 5, 4, 15}, + {0, 10, 9, 3, 8, 2, 1, 11, 7, 13, 14, 4, 15, 5, 6, 12}, + {0, 11, 9, 2, 8, 3, 1, 10, 6, 13, 15, 4, 14, 5, 7, 12}, + {0, 11, 10, 1, 8, 3, 2, 9, 5, 14, 15, 4, 13, 6, 7, 12}, + {0, 11, 10, 1, 9, 2, 3, 8, 4, 15, 14, 5, 13, 6, 7, 12}, + {0, 11, 9, 2, 8, 3, 1, 10, 5, 14, 12, 7, 13, 6, 4, 15}, + {0, 13, 12, 1, 10, 7, 6, 11, 2, 15, 14, 3, 8, 5, 4, 9}, + {0, 14, 12, 2, 8, 6, 4, 10, 3, 13, 15, 1, 11, 5, 7, 9}, + {0, 14, 13, 3, 8, 6, 5, 11, 2, 12, 15, 1, 10, 4, 7, 9}, + {0, 15, 12, 3, 8, 7, 4, 11, 2, 13, 14, 1, 10, 5, 6, 9}, + {0, 13, 12, 1, 8, 5, 4, 9, 3, 14, 15, 2, 11, 6, 7, 10}, + {0, 13, 12, 1, 9, 4, 5, 8, 2, 15, 14, 3, 11, 6, 7, 10}, + {0, 14, 12, 2, 10, 4, 6, 8, 1, 15, 13, 3, 11, 5, 7, 9}, + {0, 14, 12, 2, 9, 7, 5, 11, 1, 15, 13, 3, 8, 6, 4, 10}, + {0, 14, 13, 3, 8, 6, 5, 11, 1, 15, 12, 2, 9, 7, 4, 10}, + {0, 15, 12, 3, 8, 7, 4, 11, 1, 14, 13, 2, 9, 6, 5, 10}, + {0, 12, 11, 7, 10, 6, 1, 13, 2, 14, 9, 5, 8, 4, 3, 15}, + {0, 14, 10, 4, 8, 6, 2, 12, 3, 13, 9, 7, 11, 5, 1, 15}, + {0, 14, 10, 4, 9, 7, 3, 13, 2, 12, 8, 6, 11, 5, 1, 15}, + {0, 14, 11, 5, 8, 6, 3, 13, 2, 12, 9, 7, 10, 4, 1, 15}, + {0, 13, 10, 7, 8, 5, 2, 15, 3, 14, 9, 4, 11, 6, 1, 12}, + {0, 13, 10, 7, 9, 4, 3, 14, 2, 15, 8, 5, 11, 6, 1, 12}, + {0, 13, 11, 6, 8, 5, 3, 14, 2, 15, 9, 4, 10, 7, 1, 12}, + {0, 14, 9, 7, 8, 6, 1, 15, 3, 13, 10, 4, 11, 5, 2, 12}, + {0, 15, 9, 6, 8, 7, 1, 14, 2, 13, 11, 4, 10, 5, 3, 12}, + {0, 13, 9, 4, 8, 5, 1, 12, 3, 14, 10, 7, 11, 6, 2, 15}, + {0, 14, 10, 4, 9, 7, 3, 13, 1, 15, 11, 5, 8, 6, 2, 12}, + {0, 14, 11, 5, 8, 6, 3, 13, 1, 15, 10, 4, 9, 7, 2, 12}, + {0, 15, 10, 5, 8, 7, 2, 13, 1, 14, 11, 4, 9, 6, 3, 12}, + {0, 12, 11, 7, 9, 5, 2, 14, 1, 13, 10, 6, 8, 4, 3, 15}, + {0, 13, 10, 7, 9, 4, 3, 14, 1, 12, 11, 6, 8, 5, 2, 15}, + {0, 13, 11, 6, 8, 5, 3, 14, 1, 12, 10, 7, 9, 4, 2, 15}, + {0, 13, 12, 1, 6, 11, 10, 7, 4, 9, 8, 5, 2, 15, 14, 3}, + {0, 14, 12, 2, 5, 11, 9, 7, 4, 10, 8, 6, 1, 15, 13, 3}, + {0, 12, 8, 4, 7, 11, 15, 3, 6, 10, 14, 2, 1, 13, 9, 5}, + {0, 12, 10, 6, 7, 11, 13, 1, 4, 8, 14, 2, 3, 15, 9, 5}, + {0, 12, 11, 7, 6, 10, 13, 1, 4, 8, 15, 3, 2, 14, 9, 5}, + {0, 13, 10, 7, 6, 11, 12, 1, 4, 9, 14, 3, 2, 15, 8, 5}, + {0, 14, 8, 6, 7, 9, 15, 1, 4, 10, 12, 2, 3, 13, 11, 5}, + {0, 14, 9, 7, 6, 8, 15, 1, 4, 10, 13, 3, 2, 12, 11, 5}, + {0, 15, 8, 7, 6, 9, 14, 1, 4, 11, 12, 3, 2, 13, 10, 5}, + {0, 12, 8, 4, 7, 11, 15, 3, 5, 9, 13, 1, 2, 14, 10, 6}, + {0, 12, 9, 5, 7, 11, 14, 2, 4, 8, 13, 1, 3, 15, 10, 6}, + {0, 13, 8, 5, 7, 10, 15, 2, 4, 9, 12, 1, 3, 14, 11, 6}, + {0, 12, 11, 7, 5, 9, 14, 2, 4, 8, 15, 3, 1, 13, 10, 6}, + {0, 13, 10, 7, 5, 8, 15, 2, 4, 9, 14, 3, 1, 12, 11, 6}, + {0, 14, 9, 7, 5, 11, 12, 2, 4, 10, 13, 3, 1, 15, 8, 6}, + {0, 15, 8, 7, 5, 10, 13, 2, 4, 11, 12, 3, 1, 14, 9, 6}, + {0, 10, 8, 2, 7, 13, 15, 5, 6, 12, 14, 4, 1, 11, 9, 3}, + {0, 11, 10, 1, 6, 13, 12, 7, 4, 15, 14, 5, 2, 9, 8, 3}, + {0, 9, 8, 1, 7, 14, 15, 6, 5, 12, 13, 4, 2, 11, 10, 3}, + {0, 11, 9, 2, 5, 14, 12, 7, 4, 15, 13, 6, 1, 10, 8, 3}, + {0, 13, 12, 1, 6, 11, 10, 7, 2, 15, 14, 3, 4, 9, 8, 5}, + {0, 14, 12, 2, 4, 10, 8, 6, 3, 13, 15, 1, 7, 9, 11, 5}, + {0, 14, 13, 3, 4, 10, 9, 7, 2, 12, 15, 1, 6, 8, 11, 5}, + {0, 15, 12, 3, 4, 11, 8, 7, 2, 13, 14, 1, 6, 9, 10, 5}, + {0, 13, 12, 1, 4, 9, 8, 5, 3, 14, 15, 2, 7, 10, 11, 6}, + {0, 13, 12, 1, 5, 8, 9, 4, 2, 15, 14, 3, 7, 10, 11, 6}, + {0, 14, 12, 2, 6, 8, 10, 4, 1, 15, 13, 3, 7, 9, 11, 5}, + {0, 14, 12, 2, 5, 11, 9, 7, 1, 15, 13, 3, 4, 10, 8, 6}, + {0, 14, 13, 3, 4, 10, 9, 7, 1, 15, 12, 2, 5, 11, 8, 6}, + {0, 15, 12, 3, 4, 11, 8, 7, 1, 14, 13, 2, 5, 10, 9, 6}, + {0, 12, 10, 6, 7, 11, 13, 1, 2, 14, 8, 4, 5, 9, 15, 3}, + {0, 12, 11, 7, 6, 10, 13, 1, 2, 14, 9, 5, 4, 8, 15, 3}, + {0, 13, 10, 7, 6, 11, 12, 1, 2, 15, 8, 5, 4, 9, 14, 3}, + {0, 14, 8, 6, 7, 9, 15, 1, 2, 12, 10, 4, 5, 11, 13, 3}, + {0, 14, 9, 7, 6, 8, 15, 1, 2, 12, 11, 5, 4, 10, 13, 3}, + {0, 15, 8, 7, 6, 9, 14, 1, 2, 13, 10, 5, 4, 11, 12, 3}, + {0, 14, 10, 4, 5, 11, 15, 1, 2, 12, 8, 6, 7, 9, 13, 3}, + {0, 14, 11, 5, 4, 10, 15, 1, 2, 12, 9, 7, 6, 8, 13, 3}, + {0, 15, 10, 5, 4, 11, 14, 1, 2, 13, 8, 7, 6, 9, 12, 3}, + {0, 13, 9, 4, 5, 8, 12, 1, 2, 15, 11, 6, 7, 10, 14, 3}, + {0, 14, 10, 4, 6, 8, 12, 2, 1, 15, 11, 5, 7, 9, 13, 3}, + {0, 12, 9, 5, 7, 11, 14, 2, 1, 13, 8, 4, 6, 10, 15, 3}, + {0, 13, 8, 5, 7, 10, 15, 2, 1, 12, 9, 4, 6, 11, 14, 3}, + {0, 13, 9, 4, 6, 11, 15, 2, 1, 12, 8, 5, 7, 10, 14, 3}, + {0, 12, 11, 7, 5, 9, 14, 2, 1, 13, 10, 6, 4, 8, 15, 3}, + {0, 13, 10, 7, 5, 8, 15, 2, 1, 12, 11, 6, 4, 9, 14, 3}, + {0, 13, 11, 6, 4, 9, 15, 2, 1, 12, 10, 7, 5, 8, 14, 3}, + {0, 14, 9, 7, 5, 11, 12, 2, 1, 15, 8, 6, 4, 10, 13, 3}, + {0, 15, 8, 7, 5, 10, 13, 2, 1, 14, 9, 6, 4, 11, 12, 3}, + {0, 15, 9, 6, 4, 11, 13, 2, 1, 14, 8, 7, 5, 10, 12, 3}, + {0, 11, 10, 1, 6, 13, 12, 7, 2, 9, 8, 3, 4, 15, 14, 5}, + {0, 10, 8, 2, 7, 13, 15, 5, 3, 9, 11, 1, 4, 14, 12, 6}, + {0, 10, 9, 3, 7, 13, 14, 4, 2, 8, 11, 1, 5, 15, 12, 6}, + {0, 11, 8, 3, 7, 12, 15, 4, 2, 9, 10, 1, 5, 14, 13, 6}, + {0, 9, 8, 1, 7, 14, 15, 6, 3, 10, 11, 2, 4, 13, 12, 5}, + {0, 11, 10, 1, 4, 15, 14, 5, 3, 8, 9, 2, 7, 12, 13, 6}, + {0, 11, 10, 1, 5, 14, 15, 4, 2, 9, 8, 3, 7, 12, 13, 6}, + {0, 11, 9, 2, 4, 15, 13, 6, 3, 8, 10, 1, 7, 12, 14, 5}, + {0, 10, 9, 3, 7, 13, 14, 4, 1, 11, 8, 2, 6, 12, 15, 5}, + {0, 11, 8, 3, 7, 12, 15, 4, 1, 10, 9, 2, 6, 13, 14, 5}, + {0, 11, 9, 2, 6, 13, 15, 4, 1, 10, 8, 3, 7, 12, 14, 5}, + {0, 11, 9, 2, 5, 14, 12, 7, 1, 10, 8, 3, 4, 15, 13, 6}, + {0, 14, 10, 4, 3, 13, 9, 7, 2, 12, 8, 6, 1, 15, 11, 5}, + {0, 12, 11, 7, 3, 15, 8, 4, 2, 14, 9, 5, 1, 13, 10, 6}, + {0, 13, 10, 7, 3, 14, 9, 4, 2, 15, 8, 5, 1, 12, 11, 6}, + {0, 14, 9, 7, 3, 13, 10, 4, 2, 12, 11, 5, 1, 15, 8, 6}, + {0, 15, 8, 7, 3, 12, 11, 4, 2, 13, 10, 5, 1, 14, 9, 6}, + {0, 13, 9, 4, 3, 14, 10, 7, 2, 15, 11, 6, 1, 12, 8, 5}, + {0, 14, 10, 4, 3, 13, 9, 7, 1, 15, 11, 5, 2, 12, 8, 6}, + {0, 14, 11, 5, 2, 12, 9, 7, 1, 15, 10, 4, 3, 13, 8, 6}, + {0, 15, 10, 5, 2, 13, 8, 7, 1, 14, 11, 4, 3, 12, 9, 6}, + {0, 12, 11, 7, 3, 15, 8, 4, 1, 13, 10, 6, 2, 14, 9, 5}, + {0, 13, 10, 7, 3, 14, 9, 4, 1, 12, 11, 6, 2, 15, 8, 5}, + {0, 13, 11, 6, 2, 15, 9, 4, 1, 12, 10, 7, 3, 14, 8, 5}, + {0, 14, 9, 7, 3, 13, 10, 4, 1, 15, 8, 6, 2, 12, 11, 5}, + {0, 15, 8, 7, 3, 12, 11, 4, 1, 14, 9, 6, 2, 13, 10, 5}, + {0, 15, 9, 6, 2, 13, 11, 4, 1, 14, 8, 7, 3, 12, 10, 5}, + {0, 13, 9, 4, 3, 14, 10, 7, 1, 12, 8, 5, 2, 15, 11, 6}, + {0, 12, 7, 11, 6, 10, 1, 13, 4, 8, 3, 15, 2, 14, 5, 9}, + {0, 14, 6, 8, 5, 11, 3, 13, 4, 10, 2, 12, 1, 15, 7, 9}, + {0, 12, 7, 11, 5, 9, 2, 14, 4, 8, 3, 15, 1, 13, 6, 10}, + {0, 13, 6, 11, 5, 8, 3, 14, 4, 9, 2, 15, 1, 12, 7, 10}, + {0, 10, 7, 13, 6, 12, 1, 11, 4, 14, 3, 9, 2, 8, 5, 15}, + {0, 8, 7, 15, 6, 14, 1, 9, 5, 13, 2, 10, 3, 11, 4, 12}, + {0, 9, 7, 14, 6, 15, 1, 8, 4, 13, 3, 10, 2, 11, 5, 12}, + {0, 10, 7, 13, 5, 15, 2, 8, 4, 14, 3, 9, 1, 11, 6, 12}, + {0, 11, 6, 13, 5, 14, 3, 8, 4, 15, 2, 9, 1, 10, 7, 12}, + {0, 9, 7, 14, 5, 12, 2, 11, 4, 13, 3, 10, 1, 8, 6, 15}, + {0, 12, 7, 11, 6, 10, 1, 13, 2, 14, 5, 9, 4, 8, 3, 15}, + {0, 14, 6, 8, 4, 10, 2, 12, 3, 13, 5, 11, 7, 9, 1, 15}, + {0, 14, 6, 8, 5, 11, 3, 13, 2, 12, 4, 10, 7, 9, 1, 15}, + {0, 14, 7, 9, 4, 10, 3, 13, 2, 12, 5, 11, 6, 8, 1, 15}, + {0, 13, 6, 11, 4, 9, 2, 15, 3, 14, 5, 8, 7, 10, 1, 12}, + {0, 13, 6, 11, 5, 8, 3, 14, 2, 15, 4, 9, 7, 10, 1, 12}, + {0, 13, 7, 10, 4, 9, 3, 14, 2, 15, 5, 8, 6, 11, 1, 12}, + {0, 14, 5, 11, 4, 10, 1, 15, 3, 13, 6, 8, 7, 9, 2, 12}, + {0, 15, 5, 10, 4, 11, 1, 14, 2, 13, 7, 8, 6, 9, 3, 12}, + {0, 13, 5, 8, 4, 9, 1, 12, 3, 14, 6, 11, 7, 10, 2, 15}, + {0, 14, 6, 8, 5, 11, 3, 13, 1, 15, 7, 9, 4, 10, 2, 12}, + {0, 14, 7, 9, 4, 10, 3, 13, 1, 15, 6, 8, 5, 11, 2, 12}, + {0, 15, 6, 9, 4, 11, 2, 13, 1, 14, 7, 8, 5, 10, 3, 12}, + {0, 12, 7, 11, 5, 9, 2, 14, 1, 13, 6, 10, 4, 8, 3, 15}, + {0, 13, 6, 11, 5, 8, 3, 14, 1, 12, 7, 10, 4, 9, 2, 15}, + {0, 13, 7, 10, 4, 9, 3, 14, 1, 12, 6, 11, 5, 8, 2, 15}, + {0, 10, 7, 13, 6, 12, 1, 11, 2, 8, 5, 15, 4, 14, 3, 9}, + {0, 8, 7, 15, 6, 14, 1, 9, 3, 11, 4, 12, 5, 13, 2, 10}, + {0, 9, 7, 14, 6, 15, 1, 8, 2, 11, 5, 12, 4, 13, 3, 10}, + {0, 11, 6, 13, 4, 15, 2, 9, 3, 8, 5, 14, 7, 12, 1, 10}, + {0, 11, 6, 13, 5, 14, 3, 8, 2, 9, 4, 15, 7, 12, 1, 10}, + {0, 11, 7, 12, 4, 15, 3, 8, 2, 9, 5, 14, 6, 13, 1, 10}, + {0, 8, 7, 15, 5, 13, 2, 10, 3, 11, 4, 12, 6, 14, 1, 9}, + {0, 11, 5, 14, 4, 15, 1, 10, 3, 8, 6, 13, 7, 12, 2, 9}, + {0, 10, 7, 13, 5, 15, 2, 8, 1, 11, 6, 12, 4, 14, 3, 9}, + {0, 11, 6, 13, 5, 14, 3, 8, 1, 10, 7, 12, 4, 15, 2, 9}, + {0, 11, 7, 12, 4, 15, 3, 8, 1, 10, 6, 13, 5, 14, 2, 9}, + {0, 9, 7, 14, 5, 12, 2, 11, 1, 8, 6, 15, 4, 13, 3, 10}, + {0, 14, 6, 8, 3, 13, 5, 11, 2, 12, 4, 10, 1, 15, 7, 9}, + {0, 12, 7, 11, 3, 15, 4, 8, 2, 14, 5, 9, 1, 13, 6, 10}, + {0, 13, 6, 11, 3, 14, 5, 8, 2, 15, 4, 9, 1, 12, 7, 10}, + {0, 14, 5, 11, 3, 13, 6, 8, 2, 12, 7, 9, 1, 15, 4, 10}, + {0, 15, 4, 11, 3, 12, 7, 8, 2, 13, 6, 9, 1, 14, 5, 10}, + {0, 13, 5, 8, 3, 14, 6, 11, 2, 15, 7, 10, 1, 12, 4, 9}, + {0, 14, 6, 8, 3, 13, 5, 11, 1, 15, 7, 9, 2, 12, 4, 10}, + {0, 14, 7, 9, 2, 12, 5, 11, 1, 15, 6, 8, 3, 13, 4, 10}, + {0, 15, 6, 9, 2, 13, 4, 11, 1, 14, 7, 8, 3, 12, 5, 10}, + {0, 12, 7, 11, 3, 15, 4, 8, 1, 13, 6, 10, 2, 14, 5, 9}, + {0, 13, 6, 11, 3, 14, 5, 8, 1, 12, 7, 10, 2, 15, 4, 9}, + {0, 13, 7, 10, 2, 15, 5, 8, 1, 12, 6, 11, 3, 14, 4, 9}, + {0, 14, 5, 11, 3, 13, 6, 8, 1, 15, 4, 10, 2, 12, 7, 9}, + {0, 15, 4, 11, 3, 12, 7, 8, 1, 14, 5, 10, 2, 13, 6, 9}, + {0, 15, 5, 10, 2, 13, 7, 8, 1, 14, 4, 11, 3, 12, 6, 9}, + {0, 13, 5, 8, 3, 14, 6, 11, 1, 12, 4, 9, 2, 15, 7, 10}, + {0, 10, 7, 13, 3, 9, 4, 14, 2, 8, 5, 15, 1, 11, 6, 12}, + {0, 11, 6, 13, 3, 8, 5, 14, 2, 9, 4, 15, 1, 10, 7, 12}, + {0, 9, 7, 14, 3, 10, 4, 13, 2, 11, 5, 12, 1, 8, 6, 15}, + {0, 11, 5, 14, 3, 8, 6, 13, 2, 9, 7, 12, 1, 10, 4, 15}, + {0, 10, 7, 13, 3, 9, 4, 14, 1, 11, 6, 12, 2, 8, 5, 15}, + {0, 11, 6, 13, 3, 8, 5, 14, 1, 10, 7, 12, 2, 9, 4, 15}, + {0, 11, 7, 12, 2, 9, 5, 14, 1, 10, 6, 13, 3, 8, 4, 15}, + {0, 9, 7, 14, 3, 10, 4, 13, 1, 8, 6, 15, 2, 11, 5, 12}, + {0, 11, 5, 14, 3, 8, 6, 13, 1, 10, 4, 15, 2, 9, 7, 12}, + {0, 14, 13, 3, 12, 2, 1, 15, 8, 6, 5, 11, 4, 10, 9, 7}, + {0, 14, 12, 2, 10, 4, 6, 8, 9, 7, 5, 11, 3, 13, 15, 1}, + {0, 14, 12, 2, 11, 5, 7, 9, 8, 6, 4, 10, 3, 13, 15, 1}, + {0, 14, 13, 3, 10, 4, 7, 9, 8, 6, 5, 11, 2, 12, 15, 1}, + {0, 15, 12, 3, 10, 5, 6, 9, 8, 7, 4, 11, 2, 13, 14, 1}, + {0, 13, 12, 1, 10, 7, 6, 11, 9, 4, 5, 8, 3, 14, 15, 2}, + {0, 13, 12, 1, 11, 6, 7, 10, 8, 5, 4, 9, 3, 14, 15, 2}, + {0, 14, 13, 3, 9, 7, 4, 10, 8, 6, 5, 11, 1, 15, 12, 2}, + {0, 15, 12, 3, 9, 6, 5, 10, 8, 7, 4, 11, 1, 14, 13, 2}, + {0, 14, 11, 5, 10, 4, 1, 15, 8, 6, 3, 13, 2, 12, 9, 7}, + {0, 12, 11, 7, 10, 6, 1, 13, 9, 5, 2, 14, 3, 15, 8, 4}, + {0, 13, 11, 6, 10, 7, 1, 12, 8, 5, 3, 14, 2, 15, 9, 4}, + {0, 14, 11, 5, 9, 7, 2, 12, 8, 6, 3, 13, 1, 15, 10, 4}, + {0, 15, 10, 5, 9, 6, 3, 12, 8, 7, 2, 13, 1, 14, 11, 4}, + {0, 13, 11, 6, 9, 4, 2, 15, 8, 5, 3, 14, 1, 12, 10, 7}, + {0, 14, 13, 3, 12, 2, 1, 15, 4, 10, 9, 7, 8, 6, 5, 11}, + {0, 14, 12, 2, 8, 6, 4, 10, 7, 9, 11, 5, 15, 1, 3, 13}, + {0, 14, 12, 2, 9, 7, 5, 11, 6, 8, 10, 4, 15, 1, 3, 13}, + {0, 15, 12, 3, 8, 7, 4, 11, 6, 9, 10, 5, 14, 1, 2, 13}, + {0, 13, 12, 1, 8, 5, 4, 9, 7, 10, 11, 6, 15, 2, 3, 14}, + {0, 13, 12, 1, 9, 4, 5, 8, 6, 11, 10, 7, 15, 2, 3, 14}, + {0, 14, 12, 2, 10, 4, 6, 8, 5, 11, 9, 7, 15, 1, 3, 13}, + {0, 14, 12, 2, 11, 5, 7, 9, 4, 10, 8, 6, 15, 1, 3, 13}, + {0, 15, 12, 3, 10, 5, 6, 9, 4, 11, 8, 7, 14, 1, 2, 13}, + {0, 13, 12, 1, 10, 7, 6, 11, 5, 8, 9, 4, 15, 2, 3, 14}, + {0, 13, 12, 1, 11, 6, 7, 10, 4, 9, 8, 5, 15, 2, 3, 14}, + {0, 15, 14, 1, 8, 7, 6, 9, 4, 11, 10, 5, 12, 3, 2, 13}, + {0, 15, 12, 3, 8, 7, 4, 11, 5, 10, 9, 6, 13, 2, 1, 14}, + {0, 15, 12, 3, 9, 6, 5, 10, 4, 11, 8, 7, 13, 2, 1, 14}, + {0, 15, 13, 2, 8, 7, 5, 10, 4, 11, 9, 6, 12, 3, 1, 14}, + {0, 14, 10, 4, 8, 6, 2, 12, 7, 9, 13, 3, 15, 1, 5, 11}, + {0, 14, 10, 4, 9, 7, 3, 13, 6, 8, 12, 2, 15, 1, 5, 11}, + {0, 15, 10, 5, 8, 7, 2, 13, 6, 9, 12, 3, 14, 1, 4, 11}, + {0, 12, 10, 6, 9, 5, 3, 15, 7, 11, 13, 1, 14, 2, 4, 8}, + {0, 12, 11, 7, 9, 5, 2, 14, 6, 10, 13, 1, 15, 3, 4, 8}, + {0, 13, 10, 7, 9, 4, 3, 14, 6, 11, 12, 1, 15, 2, 5, 8}, + {0, 13, 9, 4, 8, 5, 1, 12, 7, 10, 14, 3, 15, 2, 6, 11}, + {0, 12, 11, 7, 10, 6, 1, 13, 5, 9, 14, 2, 15, 3, 4, 8}, + {0, 13, 11, 6, 10, 7, 1, 12, 4, 9, 15, 2, 14, 3, 5, 8}, + {0, 14, 10, 4, 9, 7, 3, 13, 5, 11, 15, 1, 12, 2, 6, 8}, + {0, 14, 11, 5, 9, 7, 2, 12, 4, 10, 15, 1, 13, 3, 6, 8}, + {0, 15, 10, 5, 9, 6, 3, 12, 4, 11, 14, 1, 13, 2, 7, 8}, + {0, 13, 10, 7, 9, 4, 3, 14, 5, 8, 15, 2, 12, 1, 6, 11}, + {0, 15, 9, 6, 8, 7, 1, 14, 5, 10, 12, 3, 13, 2, 4, 11}, + {0, 11, 10, 1, 8, 3, 2, 9, 7, 12, 13, 6, 15, 4, 5, 14}, + {0, 11, 10, 1, 9, 2, 3, 8, 6, 13, 12, 7, 15, 4, 5, 14}, + {0, 11, 9, 2, 8, 3, 1, 10, 7, 12, 14, 5, 15, 4, 6, 13}, + {0, 11, 10, 1, 9, 2, 3, 8, 5, 14, 15, 4, 12, 7, 6, 13}, + {0, 14, 12, 2, 10, 4, 6, 8, 3, 13, 15, 1, 9, 7, 5, 11}, + {0, 14, 13, 3, 10, 4, 7, 9, 2, 12, 15, 1, 8, 6, 5, 11}, + {0, 15, 12, 3, 10, 5, 6, 9, 2, 13, 14, 1, 8, 7, 4, 11}, + {0, 13, 12, 1, 10, 7, 6, 11, 3, 14, 15, 2, 9, 4, 5, 8}, + {0, 13, 12, 1, 11, 6, 7, 10, 2, 15, 14, 3, 9, 4, 5, 8}, + {0, 15, 14, 1, 8, 7, 6, 9, 2, 13, 12, 3, 10, 5, 4, 11}, + {0, 14, 12, 2, 9, 7, 5, 11, 3, 13, 15, 1, 10, 4, 6, 8}, + {0, 14, 13, 3, 9, 7, 4, 10, 2, 12, 15, 1, 11, 5, 6, 8}, + {0, 15, 12, 3, 9, 6, 5, 10, 2, 13, 14, 1, 11, 4, 7, 8}, + {0, 13, 12, 1, 9, 4, 5, 8, 3, 14, 15, 2, 10, 7, 6, 11}, + {0, 14, 12, 2, 11, 5, 7, 9, 1, 15, 13, 3, 10, 4, 6, 8}, + {0, 14, 13, 3, 10, 4, 7, 9, 1, 15, 12, 2, 11, 5, 6, 8}, + {0, 15, 12, 3, 10, 5, 6, 9, 1, 14, 13, 2, 11, 4, 7, 8}, + {0, 14, 13, 3, 9, 7, 4, 10, 1, 15, 12, 2, 8, 6, 5, 11}, + {0, 15, 12, 3, 9, 6, 5, 10, 1, 14, 13, 2, 8, 7, 4, 11}, + {0, 15, 13, 2, 8, 7, 5, 10, 1, 14, 12, 3, 9, 6, 4, 11}, + {0, 14, 11, 5, 10, 4, 1, 15, 2, 12, 9, 7, 8, 6, 3, 13}, + {0, 12, 11, 7, 10, 6, 1, 13, 3, 15, 8, 4, 9, 5, 2, 14}, + {0, 13, 11, 6, 10, 7, 1, 12, 2, 15, 9, 4, 8, 5, 3, 14}, + {0, 15, 10, 5, 8, 7, 2, 13, 3, 12, 9, 6, 11, 4, 1, 14}, + {0, 15, 10, 5, 9, 6, 3, 12, 2, 13, 8, 7, 11, 4, 1, 14}, + {0, 15, 11, 4, 8, 7, 3, 12, 2, 13, 9, 6, 10, 5, 1, 14}, + {0, 12, 11, 7, 9, 5, 2, 14, 3, 15, 8, 4, 10, 6, 1, 13}, + {0, 15, 9, 6, 8, 7, 1, 14, 3, 12, 10, 5, 11, 4, 2, 13}, + {0, 14, 11, 5, 9, 7, 2, 12, 1, 15, 10, 4, 8, 6, 3, 13}, + {0, 15, 10, 5, 9, 6, 3, 12, 1, 14, 11, 4, 8, 7, 2, 13}, + {0, 15, 11, 4, 8, 7, 3, 12, 1, 14, 10, 5, 9, 6, 2, 13}, + {0, 13, 11, 6, 9, 4, 2, 15, 1, 12, 10, 7, 8, 5, 3, 14}, + {0, 14, 12, 2, 6, 8, 10, 4, 5, 11, 9, 7, 3, 13, 15, 1}, + {0, 14, 12, 2, 7, 9, 11, 5, 4, 10, 8, 6, 3, 13, 15, 1}, + {0, 14, 13, 3, 6, 8, 11, 5, 4, 10, 9, 7, 2, 12, 15, 1}, + {0, 15, 12, 3, 6, 9, 10, 5, 4, 11, 8, 7, 2, 13, 14, 1}, + {0, 13, 12, 1, 6, 11, 10, 7, 5, 8, 9, 4, 3, 14, 15, 2}, + {0, 13, 12, 1, 7, 10, 11, 6, 4, 9, 8, 5, 3, 14, 15, 2}, + {0, 14, 13, 3, 5, 11, 8, 6, 4, 10, 9, 7, 1, 15, 12, 2}, + {0, 15, 12, 3, 5, 10, 9, 6, 4, 11, 8, 7, 1, 14, 13, 2}, + {0, 12, 9, 5, 7, 11, 14, 2, 6, 10, 15, 3, 1, 13, 8, 4}, + {0, 13, 8, 5, 7, 10, 15, 2, 6, 11, 14, 3, 1, 12, 9, 4}, + {0, 14, 10, 4, 6, 8, 12, 2, 5, 11, 15, 1, 3, 13, 9, 7}, + {0, 14, 11, 5, 6, 8, 13, 3, 4, 10, 15, 1, 2, 12, 9, 7}, + {0, 15, 10, 5, 6, 9, 12, 3, 4, 11, 14, 1, 2, 13, 8, 7}, + {0, 12, 10, 6, 7, 11, 13, 1, 5, 9, 15, 3, 2, 14, 8, 4}, + {0, 12, 11, 7, 6, 10, 13, 1, 5, 9, 14, 2, 3, 15, 8, 4}, + {0, 13, 10, 7, 6, 11, 12, 1, 5, 8, 15, 2, 3, 14, 9, 4}, + {0, 14, 8, 6, 7, 9, 15, 1, 5, 11, 13, 3, 2, 12, 10, 4}, + {0, 14, 9, 7, 6, 8, 15, 1, 5, 11, 12, 2, 3, 13, 10, 4}, + {0, 15, 8, 7, 6, 9, 14, 1, 5, 10, 13, 2, 3, 12, 11, 4}, + {0, 13, 9, 4, 6, 11, 15, 2, 5, 8, 12, 1, 3, 14, 10, 7}, + {0, 13, 11, 6, 5, 8, 14, 3, 4, 9, 15, 2, 1, 12, 10, 7}, + {0, 15, 9, 6, 5, 10, 12, 3, 4, 11, 13, 2, 1, 14, 8, 7}, + {0, 10, 9, 3, 7, 13, 14, 4, 6, 12, 15, 5, 1, 11, 8, 2}, + {0, 11, 8, 3, 7, 12, 15, 4, 6, 13, 14, 5, 1, 10, 9, 2}, + {0, 11, 10, 1, 6, 13, 12, 7, 5, 14, 15, 4, 3, 8, 9, 2}, + {0, 11, 10, 1, 7, 12, 13, 6, 4, 15, 14, 5, 3, 8, 9, 2}, + {0, 10, 9, 3, 7, 13, 14, 4, 5, 15, 12, 6, 2, 8, 11, 1}, + {0, 11, 8, 3, 7, 12, 15, 4, 5, 14, 13, 6, 2, 9, 10, 1}, + {0, 11, 9, 2, 6, 13, 15, 4, 5, 14, 12, 7, 3, 8, 10, 1}, + {0, 11, 9, 2, 7, 12, 14, 5, 4, 15, 13, 6, 3, 8, 10, 1}, + {0, 14, 12, 2, 6, 8, 10, 4, 3, 13, 15, 1, 5, 11, 9, 7}, + {0, 14, 13, 3, 6, 8, 11, 5, 2, 12, 15, 1, 4, 10, 9, 7}, + {0, 15, 12, 3, 6, 9, 10, 5, 2, 13, 14, 1, 4, 11, 8, 7}, + {0, 13, 12, 1, 6, 11, 10, 7, 3, 14, 15, 2, 5, 8, 9, 4}, + {0, 13, 12, 1, 7, 10, 11, 6, 2, 15, 14, 3, 5, 8, 9, 4}, + {0, 15, 14, 1, 4, 11, 10, 5, 2, 13, 12, 3, 6, 9, 8, 7}, + {0, 14, 12, 2, 5, 11, 9, 7, 3, 13, 15, 1, 6, 8, 10, 4}, + {0, 14, 13, 3, 5, 11, 8, 6, 2, 12, 15, 1, 7, 9, 10, 4}, + {0, 15, 12, 3, 5, 10, 9, 6, 2, 13, 14, 1, 7, 8, 11, 4}, + {0, 13, 12, 1, 5, 8, 9, 4, 3, 14, 15, 2, 6, 11, 10, 7}, + {0, 14, 12, 2, 7, 9, 11, 5, 1, 15, 13, 3, 6, 8, 10, 4}, + {0, 14, 13, 3, 6, 8, 11, 5, 1, 15, 12, 2, 7, 9, 10, 4}, + {0, 15, 12, 3, 6, 9, 10, 5, 1, 14, 13, 2, 7, 8, 11, 4}, + {0, 14, 13, 3, 5, 11, 8, 6, 1, 15, 12, 2, 4, 10, 9, 7}, + {0, 15, 12, 3, 5, 10, 9, 6, 1, 14, 13, 2, 4, 11, 8, 7}, + {0, 15, 13, 2, 4, 11, 9, 6, 1, 14, 12, 3, 5, 10, 8, 7}, + {0, 14, 10, 4, 6, 8, 12, 2, 3, 13, 9, 7, 5, 11, 15, 1}, + {0, 14, 10, 4, 7, 9, 13, 3, 2, 12, 8, 6, 5, 11, 15, 1}, + {0, 14, 11, 5, 6, 8, 13, 3, 2, 12, 9, 7, 4, 10, 15, 1}, + {0, 15, 10, 5, 6, 9, 12, 3, 2, 13, 8, 7, 4, 11, 14, 1}, + {0, 12, 10, 6, 7, 11, 13, 1, 3, 15, 9, 5, 4, 8, 14, 2}, + {0, 12, 11, 7, 6, 10, 13, 1, 3, 15, 8, 4, 5, 9, 14, 2}, + {0, 13, 10, 7, 6, 11, 12, 1, 3, 14, 9, 4, 5, 8, 15, 2}, + {0, 14, 8, 6, 7, 9, 15, 1, 3, 13, 11, 5, 4, 10, 12, 2}, + {0, 14, 9, 7, 6, 8, 15, 1, 3, 13, 10, 4, 5, 11, 12, 2}, + {0, 15, 8, 7, 6, 9, 14, 1, 3, 12, 11, 4, 5, 10, 13, 2}, + {0, 12, 9, 5, 7, 11, 14, 2, 3, 15, 10, 6, 4, 8, 13, 1}, + {0, 13, 8, 5, 7, 10, 15, 2, 3, 14, 11, 6, 4, 9, 12, 1}, + {0, 13, 9, 4, 6, 11, 15, 2, 3, 14, 10, 7, 5, 8, 12, 1}, + {0, 13, 9, 4, 7, 10, 14, 3, 2, 15, 11, 6, 5, 8, 12, 1}, + {0, 14, 10, 4, 5, 11, 15, 1, 3, 13, 9, 7, 6, 8, 12, 2}, + {0, 14, 11, 5, 4, 10, 15, 1, 3, 13, 8, 6, 7, 9, 12, 2}, + {0, 15, 10, 5, 4, 11, 14, 1, 3, 12, 9, 6, 7, 8, 13, 2}, + {0, 12, 11, 7, 5, 9, 14, 2, 3, 15, 8, 4, 6, 10, 13, 1}, + {0, 13, 10, 7, 5, 8, 15, 2, 3, 14, 9, 4, 6, 11, 12, 1}, + {0, 13, 11, 6, 4, 9, 15, 2, 3, 14, 8, 5, 7, 10, 12, 1}, + {0, 13, 11, 6, 5, 8, 14, 3, 2, 15, 9, 4, 7, 10, 12, 1}, + {0, 14, 9, 7, 5, 11, 12, 2, 3, 13, 10, 4, 6, 8, 15, 1}, + {0, 15, 8, 7, 5, 10, 13, 2, 3, 12, 11, 4, 6, 9, 14, 1}, + {0, 15, 9, 6, 4, 11, 13, 2, 3, 12, 10, 5, 7, 8, 14, 1}, + {0, 15, 9, 6, 5, 10, 12, 3, 2, 13, 11, 4, 7, 8, 14, 1}, + {0, 13, 9, 4, 5, 8, 12, 1, 3, 14, 10, 7, 6, 11, 15, 2}, + {0, 14, 10, 4, 7, 9, 13, 3, 1, 15, 11, 5, 6, 8, 12, 2}, + {0, 14, 11, 5, 6, 8, 13, 3, 1, 15, 10, 4, 7, 9, 12, 2}, + {0, 15, 10, 5, 6, 9, 12, 3, 1, 14, 11, 4, 7, 8, 13, 2}, + {0, 13, 9, 4, 7, 10, 14, 3, 1, 12, 8, 5, 6, 11, 15, 2}, + {0, 13, 11, 6, 5, 8, 14, 3, 1, 12, 10, 7, 4, 9, 15, 2}, + {0, 15, 9, 6, 5, 10, 12, 3, 1, 14, 8, 7, 4, 11, 13, 2}, + {0, 11, 10, 1, 6, 13, 12, 7, 3, 8, 9, 2, 5, 14, 15, 4}, + {0, 11, 10, 1, 7, 12, 13, 6, 2, 9, 8, 3, 5, 14, 15, 4}, + {0, 11, 9, 2, 6, 13, 15, 4, 3, 8, 10, 1, 5, 14, 12, 7}, + {0, 11, 10, 1, 5, 14, 15, 4, 3, 8, 9, 2, 6, 13, 12, 7}, + {0, 11, 9, 2, 5, 14, 12, 7, 3, 8, 10, 1, 6, 13, 15, 4}, + {0, 11, 9, 2, 7, 12, 14, 5, 1, 10, 8, 3, 6, 13, 15, 4}, + {0, 14, 11, 5, 3, 13, 8, 6, 2, 12, 9, 7, 1, 15, 10, 4}, + {0, 15, 10, 5, 3, 12, 9, 6, 2, 13, 8, 7, 1, 14, 11, 4}, + {0, 13, 11, 6, 3, 14, 8, 5, 2, 15, 9, 4, 1, 12, 10, 7}, + {0, 15, 9, 6, 3, 12, 10, 5, 2, 13, 11, 4, 1, 14, 8, 7}, + {0, 14, 11, 5, 3, 13, 8, 6, 1, 15, 10, 4, 2, 12, 9, 7}, + {0, 15, 10, 5, 3, 12, 9, 6, 1, 14, 11, 4, 2, 13, 8, 7}, + {0, 15, 11, 4, 2, 13, 9, 6, 1, 14, 10, 5, 3, 12, 8, 7}, + {0, 13, 11, 6, 3, 14, 8, 5, 1, 12, 10, 7, 2, 15, 9, 4}, + {0, 15, 9, 6, 3, 12, 10, 5, 1, 14, 8, 7, 2, 13, 11, 4}, + {0, 14, 7, 9, 6, 8, 1, 15, 4, 10, 3, 13, 2, 12, 5, 11}, + {0, 12, 7, 11, 6, 10, 1, 13, 5, 9, 2, 14, 3, 15, 4, 8}, + {0, 13, 7, 10, 6, 11, 1, 12, 4, 9, 3, 14, 2, 15, 5, 8}, + {0, 14, 7, 9, 5, 11, 2, 12, 4, 10, 3, 13, 1, 15, 6, 8}, + {0, 15, 6, 9, 5, 10, 3, 12, 4, 11, 2, 13, 1, 14, 7, 8}, + {0, 13, 7, 10, 5, 8, 2, 15, 4, 9, 3, 14, 1, 12, 6, 11}, + {0, 10, 7, 13, 6, 12, 1, 11, 5, 15, 2, 8, 3, 9, 4, 14}, + {0, 11, 7, 12, 6, 13, 1, 10, 4, 15, 3, 8, 2, 9, 5, 14}, + {0, 9, 7, 14, 6, 15, 1, 8, 5, 12, 2, 11, 3, 10, 4, 13}, + {0, 11, 7, 12, 5, 14, 2, 9, 4, 15, 3, 8, 1, 10, 6, 13}, + {0, 14, 7, 9, 6, 8, 1, 15, 2, 12, 5, 11, 4, 10, 3, 13}, + {0, 12, 7, 11, 6, 10, 1, 13, 3, 15, 4, 8, 5, 9, 2, 14}, + {0, 13, 7, 10, 6, 11, 1, 12, 2, 15, 5, 8, 4, 9, 3, 14}, + {0, 15, 6, 9, 4, 11, 2, 13, 3, 12, 5, 10, 7, 8, 1, 14}, + {0, 15, 6, 9, 5, 10, 3, 12, 2, 13, 4, 11, 7, 8, 1, 14}, + {0, 15, 7, 8, 4, 11, 3, 12, 2, 13, 5, 10, 6, 9, 1, 14}, + {0, 12, 7, 11, 5, 9, 2, 14, 3, 15, 4, 8, 6, 10, 1, 13}, + {0, 15, 5, 10, 4, 11, 1, 14, 3, 12, 6, 9, 7, 8, 2, 13}, + {0, 14, 7, 9, 5, 11, 2, 12, 1, 15, 6, 8, 4, 10, 3, 13}, + {0, 15, 6, 9, 5, 10, 3, 12, 1, 14, 7, 8, 4, 11, 2, 13}, + {0, 15, 7, 8, 4, 11, 3, 12, 1, 14, 6, 9, 5, 10, 2, 13}, + {0, 13, 7, 10, 5, 8, 2, 15, 1, 12, 6, 11, 4, 9, 3, 14}, + {0, 10, 7, 13, 6, 12, 1, 11, 3, 9, 4, 14, 5, 15, 2, 8}, + {0, 11, 7, 12, 6, 13, 1, 10, 2, 9, 5, 14, 4, 15, 3, 8}, + {0, 9, 7, 14, 6, 15, 1, 8, 3, 10, 4, 13, 5, 12, 2, 11}, + {0, 10, 7, 13, 5, 15, 2, 8, 3, 9, 4, 14, 6, 12, 1, 11}, + {0, 9, 7, 14, 5, 12, 2, 11, 3, 10, 4, 13, 6, 15, 1, 8}, + {0, 11, 7, 12, 5, 14, 2, 9, 1, 10, 6, 13, 4, 15, 3, 8}, + {0, 14, 7, 9, 3, 13, 4, 10, 2, 12, 5, 11, 1, 15, 6, 8}, + {0, 15, 6, 9, 3, 12, 5, 10, 2, 13, 4, 11, 1, 14, 7, 8}, + {0, 13, 7, 10, 3, 14, 4, 9, 2, 15, 5, 8, 1, 12, 6, 11}, + {0, 15, 5, 10, 3, 12, 6, 9, 2, 13, 7, 8, 1, 14, 4, 11}, + {0, 14, 7, 9, 3, 13, 4, 10, 1, 15, 6, 8, 2, 12, 5, 11}, + {0, 15, 6, 9, 3, 12, 5, 10, 1, 14, 7, 8, 2, 13, 4, 11}, + {0, 15, 7, 8, 2, 13, 5, 10, 1, 14, 6, 9, 3, 12, 4, 11}, + {0, 13, 7, 10, 3, 14, 4, 9, 1, 12, 6, 11, 2, 15, 5, 8}, + {0, 15, 5, 10, 3, 12, 6, 9, 1, 14, 4, 11, 2, 13, 7, 8}, + {0, 11, 7, 12, 3, 8, 4, 15, 2, 9, 5, 14, 1, 10, 6, 13}, + {0, 11, 7, 12, 3, 8, 4, 15, 1, 10, 6, 13, 2, 9, 5, 14}, + {0, 14, 13, 3, 12, 2, 1, 15, 10, 4, 7, 9, 6, 8, 11, 5}, + {0, 15, 14, 1, 12, 3, 2, 13, 8, 7, 6, 9, 4, 11, 10, 5}, + {0, 14, 13, 3, 12, 2, 1, 15, 9, 7, 4, 10, 5, 11, 8, 6}, + {0, 15, 13, 2, 12, 3, 1, 14, 8, 7, 5, 10, 4, 11, 9, 6}, + {0, 14, 12, 2, 11, 5, 7, 9, 10, 4, 6, 8, 1, 15, 13, 3}, + {0, 15, 14, 1, 10, 5, 4, 11, 8, 7, 6, 9, 2, 13, 12, 3}, + {0, 13, 12, 1, 11, 6, 7, 10, 9, 4, 5, 8, 2, 15, 14, 3}, + {0, 15, 13, 2, 9, 6, 4, 11, 8, 7, 5, 10, 1, 14, 12, 3}, + {0, 14, 11, 5, 10, 4, 1, 15, 9, 7, 2, 12, 3, 13, 8, 6}, + {0, 15, 11, 4, 10, 5, 1, 14, 8, 7, 3, 12, 2, 13, 9, 6}, + {0, 13, 11, 6, 10, 7, 1, 12, 9, 4, 2, 15, 3, 14, 8, 5}, + {0, 15, 11, 4, 9, 6, 2, 13, 8, 7, 3, 12, 1, 14, 10, 5}, + {0, 14, 13, 3, 12, 2, 1, 15, 6, 8, 11, 5, 10, 4, 7, 9}, + {0, 15, 14, 1, 12, 3, 2, 13, 4, 11, 10, 5, 8, 7, 6, 9}, + {0, 14, 13, 3, 12, 2, 1, 15, 5, 11, 8, 6, 9, 7, 4, 10}, + {0, 15, 13, 2, 12, 3, 1, 14, 4, 11, 9, 6, 8, 7, 5, 10}, + {0, 14, 12, 2, 10, 4, 6, 8, 7, 9, 11, 5, 13, 3, 1, 15}, + {0, 14, 12, 2, 11, 5, 7, 9, 6, 8, 10, 4, 13, 3, 1, 15}, + {0, 14, 13, 3, 10, 4, 7, 9, 6, 8, 11, 5, 12, 2, 1, 15}, + {0, 14, 13, 3, 8, 6, 5, 11, 7, 9, 10, 4, 15, 1, 2, 12}, + {0, 14, 13, 3, 9, 7, 4, 10, 6, 8, 11, 5, 15, 1, 2, 12}, + {0, 15, 13, 2, 8, 7, 5, 10, 6, 9, 11, 4, 14, 1, 3, 12}, + {0, 13, 12, 1, 9, 4, 5, 8, 7, 10, 11, 6, 14, 3, 2, 15}, + {0, 14, 13, 3, 10, 4, 7, 9, 5, 11, 8, 6, 15, 1, 2, 12}, + {0, 14, 13, 3, 11, 5, 6, 8, 4, 10, 9, 7, 15, 1, 2, 12}, + {0, 15, 13, 2, 10, 5, 7, 8, 4, 11, 9, 6, 14, 1, 3, 12}, + {0, 13, 12, 1, 11, 6, 7, 10, 5, 8, 9, 4, 14, 3, 2, 15}, + {0, 15, 14, 1, 8, 7, 6, 9, 5, 10, 11, 4, 13, 2, 3, 12}, + {0, 15, 14, 1, 9, 6, 7, 8, 4, 11, 10, 5, 13, 2, 3, 12}, + {0, 14, 13, 3, 9, 7, 4, 10, 5, 11, 8, 6, 12, 2, 1, 15}, + {0, 14, 11, 5, 10, 4, 1, 15, 6, 8, 13, 3, 12, 2, 7, 9}, + {0, 14, 11, 5, 8, 6, 3, 13, 7, 9, 12, 2, 15, 1, 4, 10}, + {0, 14, 11, 5, 9, 7, 2, 12, 6, 8, 13, 3, 15, 1, 4, 10}, + {0, 15, 11, 4, 8, 7, 3, 12, 6, 9, 13, 2, 14, 1, 5, 10}, + {0, 13, 11, 6, 8, 5, 3, 14, 7, 10, 12, 1, 15, 2, 4, 9}, + {0, 13, 11, 6, 10, 7, 1, 12, 5, 8, 14, 3, 15, 2, 4, 9}, + {0, 15, 11, 4, 8, 7, 3, 12, 5, 10, 14, 1, 13, 2, 6, 9}, + {0, 13, 11, 6, 9, 4, 2, 15, 5, 8, 14, 3, 12, 1, 7, 10}, + {0, 11, 10, 1, 9, 2, 3, 8, 7, 12, 13, 6, 14, 5, 4, 15}, + {0, 15, 14, 1, 10, 5, 4, 11, 2, 13, 12, 3, 8, 7, 6, 9}, + {0, 14, 12, 2, 11, 5, 7, 9, 3, 13, 15, 1, 8, 6, 4, 10}, + {0, 14, 13, 3, 11, 5, 6, 8, 2, 12, 15, 1, 9, 7, 4, 10}, + {0, 15, 12, 3, 11, 4, 7, 8, 2, 13, 14, 1, 9, 6, 5, 10}, + {0, 13, 12, 1, 11, 6, 7, 10, 3, 14, 15, 2, 8, 5, 4, 9}, + {0, 15, 14, 1, 8, 7, 6, 9, 3, 12, 13, 2, 11, 4, 5, 10}, + {0, 15, 14, 1, 9, 6, 7, 8, 2, 13, 12, 3, 11, 4, 5, 10}, + {0, 15, 13, 2, 8, 7, 5, 10, 3, 12, 14, 1, 11, 4, 6, 9}, + {0, 14, 13, 3, 11, 5, 6, 8, 1, 15, 12, 2, 10, 4, 7, 9}, + {0, 15, 12, 3, 11, 4, 7, 8, 1, 14, 13, 2, 10, 5, 6, 9}, + {0, 15, 13, 2, 10, 5, 7, 8, 1, 14, 12, 3, 11, 4, 6, 9}, + {0, 15, 13, 2, 9, 6, 4, 11, 1, 14, 12, 3, 8, 7, 5, 10}, + {0, 14, 11, 5, 10, 4, 1, 15, 3, 13, 8, 6, 9, 7, 2, 12}, + {0, 15, 11, 4, 10, 5, 1, 14, 2, 13, 9, 6, 8, 7, 3, 12}, + {0, 13, 11, 6, 10, 7, 1, 12, 3, 14, 8, 5, 9, 4, 2, 15}, + {0, 14, 11, 5, 9, 7, 2, 12, 3, 13, 8, 6, 10, 4, 1, 15}, + {0, 13, 11, 6, 9, 4, 2, 15, 3, 14, 8, 5, 10, 7, 1, 12}, + {0, 15, 11, 4, 9, 6, 2, 13, 1, 14, 10, 5, 8, 7, 3, 12}, + {0, 14, 12, 2, 7, 9, 11, 5, 6, 8, 10, 4, 1, 15, 13, 3}, + {0, 15, 14, 1, 6, 9, 8, 7, 4, 11, 10, 5, 2, 13, 12, 3}, + {0, 13, 12, 1, 7, 10, 11, 6, 5, 8, 9, 4, 2, 15, 14, 3}, + {0, 15, 13, 2, 5, 10, 8, 7, 4, 11, 9, 6, 1, 14, 12, 3}, + {0, 14, 10, 4, 7, 9, 13, 3, 6, 8, 12, 2, 1, 15, 11, 5}, + {0, 13, 9, 4, 7, 10, 14, 3, 6, 11, 15, 2, 1, 12, 8, 5}, + {0, 14, 10, 4, 7, 9, 13, 3, 5, 11, 15, 1, 2, 12, 8, 6}, + {0, 14, 11, 5, 7, 9, 12, 2, 4, 10, 15, 1, 3, 13, 8, 6}, + {0, 15, 10, 5, 7, 8, 13, 2, 4, 11, 14, 1, 3, 12, 9, 6}, + {0, 13, 11, 6, 7, 10, 12, 1, 4, 9, 15, 2, 3, 14, 8, 5}, + {0, 15, 9, 6, 7, 8, 14, 1, 4, 11, 13, 2, 3, 12, 10, 5}, + {0, 13, 9, 4, 7, 10, 14, 3, 5, 8, 12, 1, 2, 15, 11, 6}, + {0, 11, 9, 2, 7, 12, 14, 5, 6, 13, 15, 4, 1, 10, 8, 3}, + {0, 11, 10, 1, 7, 12, 13, 6, 5, 14, 15, 4, 2, 9, 8, 3}, + {0, 15, 14, 1, 6, 9, 8, 7, 2, 13, 12, 3, 4, 11, 10, 5}, + {0, 14, 12, 2, 7, 9, 11, 5, 3, 13, 15, 1, 4, 10, 8, 6}, + {0, 14, 13, 3, 7, 9, 10, 4, 2, 12, 15, 1, 5, 11, 8, 6}, + {0, 15, 12, 3, 7, 8, 11, 4, 2, 13, 14, 1, 5, 10, 9, 6}, + {0, 13, 12, 1, 7, 10, 11, 6, 3, 14, 15, 2, 4, 9, 8, 5}, + {0, 15, 14, 1, 4, 11, 10, 5, 3, 12, 13, 2, 7, 8, 9, 6}, + {0, 15, 14, 1, 5, 10, 11, 4, 2, 13, 12, 3, 7, 8, 9, 6}, + {0, 15, 13, 2, 4, 11, 9, 6, 3, 12, 14, 1, 7, 8, 10, 5}, + {0, 14, 13, 3, 7, 9, 10, 4, 1, 15, 12, 2, 6, 8, 11, 5}, + {0, 15, 12, 3, 7, 8, 11, 4, 1, 14, 13, 2, 6, 9, 10, 5}, + {0, 15, 13, 2, 6, 9, 11, 4, 1, 14, 12, 3, 7, 8, 10, 5}, + {0, 15, 13, 2, 5, 10, 8, 7, 1, 14, 12, 3, 4, 11, 9, 6}, + {0, 13, 11, 6, 7, 10, 12, 1, 2, 15, 9, 4, 5, 8, 14, 3}, + {0, 15, 9, 6, 7, 8, 14, 1, 2, 13, 11, 4, 5, 10, 12, 3}, + {0, 15, 11, 4, 5, 10, 14, 1, 2, 13, 9, 6, 7, 8, 12, 3}, + {0, 14, 11, 5, 7, 9, 12, 2, 1, 15, 10, 4, 6, 8, 13, 3}, + {0, 15, 10, 5, 7, 8, 13, 2, 1, 14, 11, 4, 6, 9, 12, 3}, + {0, 15, 11, 4, 6, 9, 13, 2, 1, 14, 10, 5, 7, 8, 12, 3}, + {0, 11, 10, 1, 7, 12, 13, 6, 3, 8, 9, 2, 4, 15, 14, 5}, + {0, 11, 9, 2, 7, 12, 14, 5, 3, 8, 10, 1, 4, 15, 13, 6}, + {0, 15, 11, 4, 3, 12, 8, 7, 2, 13, 9, 6, 1, 14, 10, 5}, + {0, 15, 11, 4, 3, 12, 8, 7, 1, 14, 10, 5, 2, 13, 9, 6}, + {0, 14, 7, 9, 6, 8, 1, 15, 5, 11, 2, 12, 3, 13, 4, 10}, + {0, 15, 7, 8, 6, 9, 1, 14, 4, 11, 3, 12, 2, 13, 5, 10}, + {0, 13, 7, 10, 6, 11, 1, 12, 5, 8, 2, 15, 3, 14, 4, 9}, + {0, 15, 7, 8, 5, 10, 2, 13, 4, 11, 3, 12, 1, 14, 6, 9}, + {0, 11, 7, 12, 6, 13, 1, 10, 5, 14, 2, 9, 3, 8, 4, 15}, + {0, 14, 7, 9, 6, 8, 1, 15, 3, 13, 4, 10, 5, 11, 2, 12}, + {0, 15, 7, 8, 6, 9, 1, 14, 2, 13, 5, 10, 4, 11, 3, 12}, + {0, 13, 7, 10, 6, 11, 1, 12, 3, 14, 4, 9, 5, 8, 2, 15}, + {0, 14, 7, 9, 5, 11, 2, 12, 3, 13, 4, 10, 6, 8, 1, 15}, + {0, 13, 7, 10, 5, 8, 2, 15, 3, 14, 4, 9, 6, 11, 1, 12}, + {0, 15, 7, 8, 5, 10, 2, 13, 1, 14, 6, 9, 4, 11, 3, 12}, + {0, 11, 7, 12, 6, 13, 1, 10, 3, 8, 4, 15, 5, 14, 2, 9}, + {0, 11, 7, 12, 5, 14, 2, 9, 3, 8, 4, 15, 6, 13, 1, 10}, + {0, 15, 7, 8, 3, 12, 4, 11, 2, 13, 5, 10, 1, 14, 6, 9}, + {0, 15, 7, 8, 3, 12, 4, 11, 1, 14, 6, 9, 2, 13, 5, 10}, + {0, 15, 14, 1, 12, 3, 2, 13, 10, 5, 4, 11, 6, 9, 8, 7}, + {0, 14, 13, 3, 12, 2, 1, 15, 11, 5, 6, 8, 7, 9, 10, 4}, + {0, 15, 13, 2, 12, 3, 1, 14, 10, 5, 7, 8, 6, 9, 11, 4}, + {0, 15, 14, 1, 12, 3, 2, 13, 9, 6, 7, 8, 5, 10, 11, 4}, + {0, 15, 14, 1, 13, 2, 3, 12, 8, 7, 6, 9, 5, 10, 11, 4}, + {0, 15, 13, 2, 12, 3, 1, 14, 9, 6, 4, 11, 5, 10, 8, 7}, + {0, 14, 13, 3, 11, 5, 6, 8, 10, 4, 7, 9, 1, 15, 12, 2}, + {0, 15, 12, 3, 11, 4, 7, 8, 10, 5, 6, 9, 1, 14, 13, 2}, + {0, 15, 14, 1, 10, 5, 4, 11, 9, 6, 7, 8, 3, 12, 13, 2}, + {0, 15, 14, 1, 11, 4, 5, 10, 8, 7, 6, 9, 3, 12, 13, 2}, + {0, 14, 13, 3, 11, 5, 6, 8, 9, 7, 4, 10, 2, 12, 15, 1}, + {0, 15, 12, 3, 11, 4, 7, 8, 9, 6, 5, 10, 2, 13, 14, 1}, + {0, 15, 13, 2, 10, 5, 7, 8, 9, 6, 4, 11, 3, 12, 14, 1}, + {0, 15, 13, 2, 11, 4, 6, 9, 8, 7, 5, 10, 3, 12, 14, 1}, + {0, 15, 11, 4, 10, 5, 1, 14, 9, 6, 2, 13, 3, 12, 8, 7}, + {0, 15, 14, 1, 12, 3, 2, 13, 6, 9, 8, 7, 10, 5, 4, 11}, + {0, 14, 13, 3, 12, 2, 1, 15, 7, 9, 10, 4, 11, 5, 6, 8}, + {0, 15, 13, 2, 12, 3, 1, 14, 6, 9, 11, 4, 10, 5, 7, 8}, + {0, 15, 14, 1, 12, 3, 2, 13, 5, 10, 11, 4, 9, 6, 7, 8}, + {0, 15, 14, 1, 13, 2, 3, 12, 4, 11, 10, 5, 9, 6, 7, 8}, + {0, 15, 13, 2, 12, 3, 1, 14, 5, 10, 8, 7, 9, 6, 4, 11}, + {0, 15, 14, 1, 10, 5, 4, 11, 6, 9, 8, 7, 12, 3, 2, 13}, + {0, 15, 12, 3, 10, 5, 6, 9, 7, 8, 11, 4, 13, 2, 1, 14}, + {0, 15, 12, 3, 11, 4, 7, 8, 6, 9, 10, 5, 13, 2, 1, 14}, + {0, 15, 13, 2, 10, 5, 7, 8, 6, 9, 11, 4, 12, 3, 1, 14}, + {0, 15, 12, 3, 9, 6, 5, 10, 7, 8, 11, 4, 14, 1, 2, 13}, + {0, 15, 12, 3, 11, 4, 7, 8, 5, 10, 9, 6, 14, 1, 2, 13}, + {0, 15, 14, 1, 9, 6, 7, 8, 5, 10, 11, 4, 12, 3, 2, 13}, + {0, 15, 13, 2, 9, 6, 4, 11, 5, 10, 8, 7, 12, 3, 1, 14}, + {0, 14, 11, 5, 10, 4, 1, 15, 7, 9, 12, 2, 13, 3, 6, 8}, + {0, 15, 11, 4, 10, 5, 1, 14, 6, 9, 13, 2, 12, 3, 7, 8}, + {0, 15, 10, 5, 9, 6, 3, 12, 7, 8, 13, 2, 14, 1, 4, 11}, + {0, 13, 11, 6, 9, 4, 2, 15, 7, 10, 12, 1, 14, 3, 5, 8}, + {0, 15, 11, 4, 9, 6, 2, 13, 5, 10, 14, 1, 12, 3, 7, 8}, + {0, 15, 14, 1, 10, 5, 4, 11, 3, 12, 13, 2, 9, 6, 7, 8}, + {0, 15, 14, 1, 11, 4, 5, 10, 2, 13, 12, 3, 9, 6, 7, 8}, + {0, 15, 13, 2, 10, 5, 7, 8, 3, 12, 14, 1, 9, 6, 4, 11}, + {0, 15, 14, 1, 9, 6, 7, 8, 3, 12, 13, 2, 10, 5, 4, 11}, + {0, 15, 13, 2, 9, 6, 4, 11, 3, 12, 14, 1, 10, 5, 7, 8}, + {0, 15, 13, 2, 11, 4, 6, 9, 1, 14, 12, 3, 10, 5, 7, 8}, + {0, 15, 11, 4, 10, 5, 1, 14, 3, 12, 8, 7, 9, 6, 2, 13}, + {0, 15, 11, 4, 9, 6, 2, 13, 3, 12, 8, 7, 10, 5, 1, 14}, + {0, 14, 13, 3, 7, 9, 10, 4, 6, 8, 11, 5, 1, 15, 12, 2}, + {0, 15, 12, 3, 7, 8, 11, 4, 6, 9, 10, 5, 1, 14, 13, 2}, + {0, 15, 14, 1, 6, 9, 8, 7, 5, 10, 11, 4, 3, 12, 13, 2}, + {0, 15, 14, 1, 7, 8, 9, 6, 4, 11, 10, 5, 3, 12, 13, 2}, + {0, 14, 13, 3, 7, 9, 10, 4, 5, 11, 8, 6, 2, 12, 15, 1}, + {0, 15, 12, 3, 7, 8, 11, 4, 5, 10, 9, 6, 2, 13, 14, 1}, + {0, 15, 13, 2, 6, 9, 11, 4, 5, 10, 8, 7, 3, 12, 14, 1}, + {0, 15, 13, 2, 7, 8, 10, 5, 4, 11, 9, 6, 3, 12, 14, 1}, + {0, 14, 11, 5, 7, 9, 12, 2, 6, 8, 13, 3, 1, 15, 10, 4}, + {0, 15, 10, 5, 7, 8, 13, 2, 6, 9, 12, 3, 1, 14, 11, 4}, + {0, 15, 11, 4, 6, 9, 13, 2, 5, 10, 14, 1, 3, 12, 8, 7}, + {0, 13, 11, 6, 7, 10, 12, 1, 5, 8, 14, 3, 2, 15, 9, 4}, + {0, 15, 9, 6, 7, 8, 14, 1, 5, 10, 12, 3, 2, 13, 11, 4}, + {0, 15, 14, 1, 6, 9, 8, 7, 3, 12, 13, 2, 5, 10, 11, 4}, + {0, 15, 14, 1, 7, 8, 9, 6, 2, 13, 12, 3, 5, 10, 11, 4}, + {0, 15, 13, 2, 6, 9, 11, 4, 3, 12, 14, 1, 5, 10, 8, 7}, + {0, 15, 14, 1, 5, 10, 11, 4, 3, 12, 13, 2, 6, 9, 8, 7}, + {0, 15, 13, 2, 5, 10, 8, 7, 3, 12, 14, 1, 6, 9, 11, 4}, + {0, 15, 13, 2, 7, 8, 10, 5, 1, 14, 12, 3, 6, 9, 11, 4}, + {0, 14, 11, 5, 7, 9, 12, 2, 3, 13, 8, 6, 4, 10, 15, 1}, + {0, 15, 10, 5, 7, 8, 13, 2, 3, 12, 9, 6, 4, 11, 14, 1}, + {0, 15, 11, 4, 6, 9, 13, 2, 3, 12, 8, 7, 5, 10, 14, 1}, + {0, 15, 11, 4, 7, 8, 12, 3, 2, 13, 9, 6, 5, 10, 14, 1}, + {0, 13, 11, 6, 7, 10, 12, 1, 3, 14, 8, 5, 4, 9, 15, 2}, + {0, 15, 9, 6, 7, 8, 14, 1, 3, 12, 10, 5, 4, 11, 13, 2}, + {0, 15, 11, 4, 5, 10, 14, 1, 3, 12, 8, 7, 6, 9, 13, 2}, + {0, 15, 11, 4, 7, 8, 12, 3, 1, 14, 10, 5, 6, 9, 13, 2}, + {0, 15, 7, 8, 6, 9, 1, 14, 5, 10, 2, 13, 3, 12, 4, 11}, + {0, 15, 7, 8, 6, 9, 1, 14, 3, 12, 4, 11, 5, 10, 2, 13}, + {0, 15, 7, 8, 5, 10, 2, 13, 3, 12, 4, 11, 6, 9, 1, 14}, + {0, 15, 14, 1, 12, 3, 2, 13, 11, 4, 5, 10, 7, 8, 9, 6}, + {0, 15, 14, 1, 13, 2, 3, 12, 10, 5, 4, 11, 7, 8, 9, 6}, + {0, 15, 13, 2, 12, 3, 1, 14, 11, 4, 6, 9, 7, 8, 10, 5}, + {0, 15, 14, 1, 13, 2, 3, 12, 9, 6, 7, 8, 4, 11, 10, 5}, + {0, 15, 13, 2, 11, 4, 6, 9, 10, 5, 7, 8, 1, 14, 12, 3}, + {0, 15, 14, 1, 11, 4, 5, 10, 9, 6, 7, 8, 2, 13, 12, 3}, + {0, 15, 14, 1, 12, 3, 2, 13, 7, 8, 9, 6, 11, 4, 5, 10}, + {0, 15, 14, 1, 13, 2, 3, 12, 6, 9, 8, 7, 11, 4, 5, 10}, + {0, 15, 13, 2, 12, 3, 1, 14, 7, 8, 10, 5, 11, 4, 6, 9}, + {0, 15, 14, 1, 13, 2, 3, 12, 5, 10, 11, 4, 8, 7, 6, 9}, + {0, 15, 14, 1, 10, 5, 4, 11, 7, 8, 9, 6, 13, 2, 3, 12}, + {0, 15, 14, 1, 11, 4, 5, 10, 6, 9, 8, 7, 13, 2, 3, 12}, + {0, 14, 13, 3, 11, 5, 6, 8, 7, 9, 10, 4, 12, 2, 1, 15}, + {0, 15, 13, 2, 9, 6, 4, 11, 7, 8, 10, 5, 14, 1, 3, 12}, + {0, 15, 13, 2, 11, 4, 6, 9, 5, 10, 8, 7, 14, 1, 3, 12}, + {0, 15, 11, 4, 10, 5, 1, 14, 7, 8, 12, 3, 13, 2, 6, 9}, + {0, 15, 11, 4, 9, 6, 2, 13, 7, 8, 12, 3, 14, 1, 5, 10}, + {0, 15, 14, 1, 11, 4, 5, 10, 3, 12, 13, 2, 8, 7, 6, 9}, + {0, 15, 13, 2, 11, 4, 6, 9, 3, 12, 14, 1, 8, 7, 5, 10}, + {0, 15, 13, 2, 7, 8, 10, 5, 6, 9, 11, 4, 1, 14, 12, 3}, + {0, 15, 14, 1, 7, 8, 9, 6, 5, 10, 11, 4, 2, 13, 12, 3}, + {0, 15, 11, 4, 7, 8, 12, 3, 6, 9, 13, 2, 1, 14, 10, 5}, + {0, 15, 11, 4, 7, 8, 12, 3, 5, 10, 14, 1, 2, 13, 9, 6}, + {0, 15, 14, 1, 7, 8, 9, 6, 3, 12, 13, 2, 4, 11, 10, 5}, + {0, 15, 13, 2, 7, 8, 10, 5, 3, 12, 14, 1, 4, 11, 9, 6}, + {0, 15, 14, 1, 13, 2, 3, 12, 11, 4, 5, 10, 6, 9, 8, 7}, + {0, 15, 14, 1, 13, 2, 3, 12, 7, 8, 9, 6, 10, 5, 4, 11}, + {0, 15, 14, 1, 11, 4, 5, 10, 7, 8, 9, 6, 12, 3, 2, 13}, + {0, 15, 13, 2, 11, 4, 6, 9, 7, 8, 10, 5, 12, 3, 1, 14}, + +}; + +#endif //AFFINEMATRIXROWPEBOX_H__ \ No newline at end of file diff --git a/AffineMatrixRowPEBox.hpp b/AffineMatrixRowPEBox.hpp new file mode 100644 index 0000000..3f7eaf9 --- /dev/null +++ b/AffineMatrixRowPEBox.hpp @@ -0,0 +1,887 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#ifndef AFFINEMATRIXROWPEBOX_H__ +#define AFFINEMATRIXROWPEBOX_H__ + +#define AFFINE_MATRIX_ROW_PE_BOX4_N 840 + +const ALIGNED_TYPE_(uint8_t, 16) AffineMatrixRowPEBox4[AFFINE_MATRIX_ROW_PE_BOX4_N][16] = +{ +{0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15}, +{0, 1, 8, 9, 4, 5, 12, 13, 3, 2, 11, 10, 7, 6, 15, 14}, +{0, 2, 8, 10, 6, 4, 14, 12, 1, 3, 9, 11, 7, 5, 15, 13}, +{0, 4, 12, 8, 2, 6, 14, 10, 1, 5, 13, 9, 3, 7, 15, 11}, +{0, 8, 1, 9, 4, 12, 5, 13, 3, 11, 2, 10, 7, 15, 6, 14}, +{0, 8, 2, 10, 6, 14, 4, 12, 1, 9, 3, 11, 7, 15, 5, 13}, +{0, 12, 4, 8, 2, 14, 6, 10, 1, 13, 5, 9, 3, 15, 7, 11}, +{0, 8, 4, 12, 1, 9, 5, 13, 3, 11, 7, 15, 2, 10, 6, 14}, +{0, 8, 6, 14, 2, 10, 4, 12, 1, 9, 7, 15, 3, 11, 5, 13}, +{0, 10, 4, 14, 2, 8, 6, 12, 1, 11, 5, 15, 3, 9, 7, 13}, +{0, 8, 4, 12, 3, 11, 7, 15, 1, 9, 5, 13, 2, 10, 6, 14}, +{0, 8, 5, 13, 2, 10, 7, 15, 1, 9, 4, 12, 3, 11, 6, 14}, +{0, 9, 4, 13, 2, 11, 6, 15, 1, 8, 5, 12, 3, 10, 7, 14}, +{0, 2, 1, 3, 8, 10, 9, 11, 7, 5, 6, 4, 15, 13, 14, 12}, +{0, 1, 4, 5, 12, 13, 8, 9, 3, 2, 7, 6, 15, 14, 11, 10}, +{0, 4, 1, 5, 12, 8, 13, 9, 3, 7, 2, 6, 15, 11, 14, 10}, +{0, 4, 2, 6, 14, 10, 12, 8, 1, 5, 3, 7, 15, 11, 13, 9}, +{0, 8, 9, 1, 4, 12, 13, 5, 3, 11, 10, 2, 7, 15, 14, 6}, +{0, 9, 8, 1, 4, 13, 12, 5, 3, 10, 11, 2, 7, 14, 15, 6}, +{0, 8, 10, 2, 6, 14, 12, 4, 1, 9, 11, 3, 7, 15, 13, 5}, +{0, 10, 8, 2, 6, 12, 14, 4, 1, 11, 9, 3, 7, 13, 15, 5}, +{0, 2, 8, 10, 1, 3, 9, 11, 7, 5, 15, 13, 6, 4, 14, 12}, +{0, 1, 12, 13, 4, 5, 8, 9, 3, 2, 15, 14, 7, 6, 11, 10}, +{0, 4, 8, 12, 5, 1, 13, 9, 3, 7, 11, 15, 6, 2, 14, 10}, +{0, 5, 8, 13, 4, 1, 12, 9, 3, 6, 11, 14, 7, 2, 15, 10}, +{0, 4, 10, 14, 6, 2, 12, 8, 1, 5, 11, 15, 7, 3, 13, 9}, +{0, 2, 8, 10, 5, 7, 13, 15, 3, 1, 11, 9, 6, 4, 14, 12}, +{0, 2, 9, 11, 4, 6, 13, 15, 3, 1, 10, 8, 7, 5, 14, 12}, +{0, 1, 8, 9, 5, 4, 13, 12, 3, 2, 11, 10, 6, 7, 14, 15}, +{0, 1, 9, 8, 4, 5, 13, 12, 3, 2, 10, 11, 7, 6, 14, 15}, +{0, 2, 10, 8, 6, 4, 12, 14, 1, 3, 11, 9, 7, 5, 13, 15}, +{0, 2, 8, 10, 7, 5, 15, 13, 1, 3, 9, 11, 6, 4, 14, 12}, +{0, 2, 9, 11, 6, 4, 15, 13, 1, 3, 8, 10, 7, 5, 14, 12}, +{0, 3, 8, 11, 6, 5, 14, 13, 1, 2, 9, 10, 7, 4, 15, 12}, +{0, 4, 12, 8, 1, 5, 13, 9, 3, 7, 15, 11, 2, 6, 14, 10}, +{0, 4, 14, 10, 2, 6, 12, 8, 1, 5, 15, 11, 3, 7, 13, 9}, +{0, 6, 12, 10, 2, 4, 14, 8, 1, 7, 13, 11, 3, 5, 15, 9}, +{0, 4, 12, 8, 3, 7, 15, 11, 1, 5, 13, 9, 2, 6, 14, 10}, +{0, 4, 13, 9, 2, 6, 15, 11, 1, 5, 12, 8, 3, 7, 14, 10}, +{0, 5, 12, 9, 2, 7, 14, 11, 1, 4, 13, 8, 3, 6, 15, 10}, +{0, 8, 2, 10, 1, 9, 3, 11, 7, 15, 5, 13, 6, 14, 4, 12}, +{0, 8, 4, 12, 5, 13, 1, 9, 3, 11, 7, 15, 6, 14, 2, 10}, +{0, 8, 5, 13, 4, 12, 1, 9, 3, 11, 6, 14, 7, 15, 2, 10}, +{0, 12, 1, 13, 4, 8, 5, 9, 3, 15, 2, 14, 7, 11, 6, 10}, +{0, 12, 2, 14, 6, 10, 4, 8, 1, 13, 3, 15, 7, 11, 5, 9}, +{0, 8, 2, 10, 5, 13, 7, 15, 3, 11, 1, 9, 6, 14, 4, 12}, +{0, 10, 1, 11, 4, 14, 5, 15, 3, 9, 2, 8, 7, 13, 6, 12}, +{0, 8, 1, 9, 5, 13, 4, 12, 3, 11, 2, 10, 6, 14, 7, 15}, +{0, 9, 1, 8, 4, 13, 5, 12, 3, 10, 2, 11, 7, 14, 6, 15}, +{0, 10, 2, 8, 6, 12, 4, 14, 1, 11, 3, 9, 7, 13, 5, 15}, +{0, 8, 2, 10, 7, 15, 5, 13, 1, 9, 3, 11, 6, 14, 4, 12}, +{0, 8, 3, 11, 6, 14, 5, 13, 1, 9, 2, 10, 7, 15, 4, 12}, +{0, 9, 2, 11, 6, 15, 4, 13, 1, 8, 3, 10, 7, 14, 5, 12}, +{0, 12, 4, 8, 1, 13, 5, 9, 3, 15, 7, 11, 2, 14, 6, 10}, +{0, 12, 6, 10, 2, 14, 4, 8, 1, 13, 7, 11, 3, 15, 5, 9}, +{0, 14, 4, 10, 2, 12, 6, 8, 1, 15, 5, 11, 3, 13, 7, 9}, +{0, 12, 4, 8, 3, 15, 7, 11, 1, 13, 5, 9, 2, 14, 6, 10}, +{0, 12, 5, 9, 2, 14, 7, 11, 1, 13, 4, 8, 3, 15, 6, 10}, +{0, 13, 4, 9, 2, 15, 6, 11, 1, 12, 5, 8, 3, 14, 7, 10}, +{0, 8, 6, 14, 1, 9, 7, 15, 3, 11, 5, 13, 2, 10, 4, 12}, +{0, 10, 4, 14, 1, 11, 5, 15, 3, 9, 7, 13, 2, 8, 6, 12}, +{0, 8, 5, 13, 1, 9, 4, 12, 3, 11, 6, 14, 2, 10, 7, 15}, +{0, 9, 4, 13, 1, 8, 5, 12, 3, 10, 7, 14, 2, 11, 6, 15}, +{0, 10, 6, 12, 2, 8, 4, 14, 1, 11, 7, 13, 3, 9, 5, 15}, +{0, 8, 6, 14, 3, 11, 5, 13, 1, 9, 7, 15, 2, 10, 4, 12}, +{0, 8, 7, 15, 2, 10, 5, 13, 1, 9, 6, 14, 3, 11, 4, 12}, +{0, 9, 6, 15, 2, 11, 4, 13, 1, 8, 7, 14, 3, 10, 5, 12}, +{0, 10, 4, 14, 3, 9, 7, 13, 1, 11, 5, 15, 2, 8, 6, 12}, +{0, 10, 5, 15, 2, 8, 7, 13, 1, 11, 4, 14, 3, 9, 6, 12}, +{0, 11, 4, 15, 2, 9, 6, 13, 1, 10, 5, 14, 3, 8, 7, 12}, +{0, 8, 5, 13, 3, 11, 6, 14, 1, 9, 4, 12, 2, 10, 7, 15}, +{0, 9, 4, 13, 3, 10, 7, 14, 1, 8, 5, 12, 2, 11, 6, 15}, +{0, 9, 5, 12, 2, 11, 7, 14, 1, 8, 4, 13, 3, 10, 6, 15}, +{0, 4, 2, 6, 1, 5, 3, 7, 15, 11, 13, 9, 14, 10, 12, 8}, +{0, 2, 8, 10, 9, 11, 1, 3, 7, 5, 15, 13, 14, 12, 6, 4}, +{0, 2, 9, 11, 8, 10, 1, 3, 7, 5, 14, 12, 15, 13, 6, 4}, +{0, 8, 2, 10, 9, 1, 11, 3, 7, 15, 5, 13, 14, 6, 12, 4}, +{0, 10, 1, 11, 8, 2, 9, 3, 7, 13, 6, 12, 15, 5, 14, 4}, +{0, 8, 4, 12, 13, 5, 9, 1, 3, 11, 7, 15, 14, 6, 10, 2}, +{0, 8, 5, 13, 12, 4, 9, 1, 3, 11, 6, 14, 15, 7, 10, 2}, +{0, 9, 4, 13, 12, 5, 8, 1, 3, 10, 7, 14, 15, 6, 11, 2}, +{0, 4, 2, 6, 9, 13, 11, 15, 7, 3, 5, 1, 14, 10, 12, 8}, +{0, 1, 2, 3, 9, 8, 11, 10, 7, 6, 5, 4, 14, 15, 12, 13}, +{0, 1, 3, 2, 8, 9, 11, 10, 7, 6, 4, 5, 15, 14, 12, 13}, +{0, 2, 1, 3, 9, 11, 8, 10, 7, 5, 6, 4, 14, 12, 15, 13}, +{0, 3, 1, 2, 8, 11, 9, 10, 7, 4, 6, 5, 15, 12, 14, 13}, +{0, 4, 5, 1, 12, 8, 9, 13, 3, 7, 6, 2, 15, 11, 10, 14}, +{0, 5, 4, 1, 12, 9, 8, 13, 3, 6, 7, 2, 15, 10, 11, 14}, +{0, 2, 4, 6, 13, 15, 9, 11, 3, 1, 7, 5, 14, 12, 10, 8}, +{0, 2, 5, 7, 12, 14, 9, 11, 3, 1, 6, 4, 15, 13, 10, 8}, +{0, 1, 4, 5, 13, 12, 9, 8, 3, 2, 7, 6, 14, 15, 10, 11}, +{0, 1, 5, 4, 12, 13, 9, 8, 3, 2, 6, 7, 15, 14, 10, 11}, +{0, 2, 6, 4, 14, 12, 8, 10, 1, 3, 7, 5, 15, 13, 9, 11}, +{0, 4, 2, 6, 13, 9, 15, 11, 3, 7, 1, 5, 14, 10, 12, 8}, +{0, 6, 1, 7, 12, 10, 13, 11, 3, 5, 2, 4, 15, 9, 14, 8}, +{0, 4, 1, 5, 13, 9, 12, 8, 3, 7, 2, 6, 14, 10, 15, 11}, +{0, 5, 1, 4, 12, 9, 13, 8, 3, 6, 2, 7, 15, 10, 14, 11}, +{0, 6, 2, 4, 14, 8, 12, 10, 1, 7, 3, 5, 15, 9, 13, 11}, +{0, 4, 2, 6, 15, 11, 13, 9, 1, 5, 3, 7, 14, 10, 12, 8}, +{0, 4, 3, 7, 14, 10, 13, 9, 1, 5, 2, 6, 15, 11, 12, 8}, +{0, 5, 2, 7, 14, 11, 12, 9, 1, 4, 3, 6, 15, 10, 13, 8}, +{0, 8, 10, 2, 1, 9, 11, 3, 7, 15, 13, 5, 6, 14, 12, 4}, +{0, 10, 8, 2, 1, 11, 9, 3, 7, 13, 15, 5, 6, 12, 14, 4}, +{0, 8, 12, 4, 5, 13, 9, 1, 3, 11, 15, 7, 6, 14, 10, 2}, +{0, 8, 13, 5, 4, 12, 9, 1, 3, 11, 14, 6, 7, 15, 10, 2}, +{0, 9, 12, 5, 4, 13, 8, 1, 3, 10, 15, 6, 7, 14, 11, 2}, +{0, 12, 8, 4, 5, 9, 13, 1, 3, 15, 11, 7, 6, 10, 14, 2}, +{0, 12, 9, 5, 4, 8, 13, 1, 3, 15, 10, 6, 7, 11, 14, 2}, +{0, 13, 8, 5, 4, 9, 12, 1, 3, 14, 11, 6, 7, 10, 15, 2}, +{0, 8, 10, 2, 5, 13, 15, 7, 3, 11, 9, 1, 6, 14, 12, 4}, +{0, 10, 8, 2, 5, 15, 13, 7, 3, 9, 11, 1, 6, 12, 14, 4}, +{0, 8, 9, 1, 5, 13, 12, 4, 3, 11, 10, 2, 6, 14, 15, 7}, +{0, 9, 8, 1, 5, 12, 13, 4, 3, 10, 11, 2, 6, 15, 14, 7}, +{0, 8, 10, 2, 7, 15, 13, 5, 1, 9, 11, 3, 6, 14, 12, 4}, +{0, 8, 11, 3, 6, 14, 13, 5, 1, 9, 10, 2, 7, 15, 12, 4}, +{0, 9, 10, 3, 6, 15, 12, 5, 1, 8, 11, 2, 7, 14, 13, 4}, +{0, 10, 8, 2, 7, 13, 15, 5, 1, 11, 9, 3, 6, 12, 14, 4}, +{0, 10, 9, 3, 6, 12, 15, 5, 1, 11, 8, 2, 7, 13, 14, 4}, +{0, 11, 8, 3, 6, 13, 14, 5, 1, 10, 9, 2, 7, 12, 15, 4}, +{0, 4, 10, 14, 1, 5, 11, 15, 7, 3, 13, 9, 6, 2, 12, 8}, +{0, 1, 8, 9, 3, 2, 11, 10, 7, 6, 15, 14, 4, 5, 12, 13}, +{0, 2, 10, 8, 1, 3, 11, 9, 7, 5, 13, 15, 6, 4, 12, 14}, +{0, 2, 9, 11, 1, 3, 8, 10, 7, 5, 14, 12, 6, 4, 15, 13}, +{0, 3, 8, 11, 1, 2, 9, 10, 7, 4, 15, 12, 6, 5, 14, 13}, +{0, 4, 12, 8, 5, 1, 9, 13, 3, 7, 15, 11, 6, 2, 10, 14}, +{0, 5, 12, 9, 4, 1, 8, 13, 3, 6, 15, 10, 7, 2, 11, 14}, +{0, 2, 12, 14, 5, 7, 9, 11, 3, 1, 15, 13, 6, 4, 10, 8}, +{0, 2, 13, 15, 4, 6, 9, 11, 3, 1, 14, 12, 7, 5, 10, 8}, +{0, 1, 12, 13, 5, 4, 9, 8, 3, 2, 15, 14, 6, 7, 10, 11}, +{0, 1, 13, 12, 4, 5, 9, 8, 3, 2, 14, 15, 7, 6, 10, 11}, +{0, 2, 14, 12, 6, 4, 8, 10, 1, 3, 15, 13, 7, 5, 9, 11}, +{0, 4, 10, 14, 5, 1, 15, 11, 3, 7, 9, 13, 6, 2, 12, 8}, +{0, 6, 9, 15, 4, 2, 13, 11, 3, 5, 10, 12, 7, 1, 14, 8}, +{0, 4, 9, 13, 5, 1, 12, 8, 3, 7, 10, 14, 6, 2, 15, 11}, +{0, 5, 9, 12, 4, 1, 13, 8, 3, 6, 10, 15, 7, 2, 14, 11}, +{0, 4, 10, 14, 7, 3, 13, 9, 1, 5, 11, 15, 6, 2, 12, 8}, +{0, 4, 11, 15, 6, 2, 13, 9, 1, 5, 10, 14, 7, 3, 12, 8}, +{0, 5, 10, 15, 6, 3, 12, 9, 1, 4, 11, 14, 7, 2, 13, 8}, +{0, 1, 8, 9, 7, 6, 15, 14, 3, 2, 11, 10, 4, 5, 12, 13}, +{0, 2, 10, 8, 5, 7, 15, 13, 3, 1, 9, 11, 6, 4, 12, 14}, +{0, 1, 10, 11, 5, 4, 15, 14, 3, 2, 9, 8, 6, 7, 12, 13}, +{0, 1, 11, 10, 4, 5, 15, 14, 3, 2, 8, 9, 7, 6, 12, 13}, +{0, 2, 9, 11, 5, 7, 12, 14, 3, 1, 10, 8, 6, 4, 15, 13}, +{0, 1, 9, 8, 5, 4, 12, 13, 3, 2, 10, 11, 6, 7, 15, 14}, +{0, 2, 10, 8, 7, 5, 13, 15, 1, 3, 11, 9, 6, 4, 12, 14}, +{0, 2, 11, 9, 6, 4, 13, 15, 1, 3, 10, 8, 7, 5, 12, 14}, +{0, 3, 10, 9, 6, 5, 12, 15, 1, 2, 11, 8, 7, 4, 13, 14}, +{0, 2, 9, 11, 7, 5, 14, 12, 1, 3, 8, 10, 6, 4, 15, 13}, +{0, 3, 8, 11, 7, 4, 15, 12, 1, 2, 9, 10, 6, 5, 14, 13}, +{0, 3, 9, 10, 6, 5, 15, 12, 1, 2, 8, 11, 7, 4, 14, 13}, +{0, 4, 14, 10, 1, 5, 15, 11, 3, 7, 13, 9, 2, 6, 12, 8}, +{0, 6, 12, 10, 1, 7, 13, 11, 3, 5, 15, 9, 2, 4, 14, 8}, +{0, 4, 13, 9, 1, 5, 12, 8, 3, 7, 14, 10, 2, 6, 15, 11}, +{0, 5, 12, 9, 1, 4, 13, 8, 3, 6, 15, 10, 2, 7, 14, 11}, +{0, 6, 14, 8, 2, 4, 12, 10, 1, 7, 15, 9, 3, 5, 13, 11}, +{0, 4, 14, 10, 3, 7, 13, 9, 1, 5, 15, 11, 2, 6, 12, 8}, +{0, 4, 15, 11, 2, 6, 13, 9, 1, 5, 14, 10, 3, 7, 12, 8}, +{0, 5, 14, 11, 2, 7, 12, 9, 1, 4, 15, 10, 3, 6, 13, 8}, +{0, 6, 12, 10, 3, 5, 15, 9, 1, 7, 13, 11, 2, 4, 14, 8}, +{0, 6, 13, 11, 2, 4, 15, 9, 1, 7, 12, 10, 3, 5, 14, 8}, +{0, 7, 12, 11, 2, 5, 14, 9, 1, 6, 13, 10, 3, 4, 15, 8}, +{0, 4, 13, 9, 3, 7, 14, 10, 1, 5, 12, 8, 2, 6, 15, 11}, +{0, 5, 12, 9, 3, 6, 15, 10, 1, 4, 13, 8, 2, 7, 14, 11}, +{0, 5, 13, 8, 2, 7, 15, 10, 1, 4, 12, 9, 3, 6, 14, 11}, +{0, 12, 2, 14, 1, 13, 3, 15, 7, 11, 5, 9, 6, 10, 4, 8}, +{0, 8, 1, 9, 3, 11, 2, 10, 7, 15, 6, 14, 4, 12, 5, 13}, +{0, 10, 2, 8, 1, 11, 3, 9, 7, 13, 5, 15, 6, 12, 4, 14}, +{0, 8, 3, 11, 1, 9, 2, 10, 7, 15, 4, 12, 6, 14, 5, 13}, +{0, 9, 2, 11, 1, 8, 3, 10, 7, 14, 5, 12, 6, 15, 4, 13}, +{0, 12, 4, 8, 5, 9, 1, 13, 3, 15, 7, 11, 6, 10, 2, 14}, +{0, 12, 5, 9, 4, 8, 1, 13, 3, 15, 6, 10, 7, 11, 2, 14}, +{0, 10, 4, 14, 5, 15, 1, 11, 3, 9, 7, 13, 6, 12, 2, 8}, +{0, 10, 5, 15, 4, 14, 1, 11, 3, 9, 6, 12, 7, 13, 2, 8}, +{0, 9, 4, 13, 5, 12, 1, 8, 3, 10, 7, 14, 6, 15, 2, 11}, +{0, 9, 5, 12, 4, 13, 1, 8, 3, 10, 6, 15, 7, 14, 2, 11}, +{0, 12, 2, 14, 5, 9, 7, 11, 3, 15, 1, 13, 6, 10, 4, 8}, +{0, 14, 1, 15, 4, 10, 5, 11, 3, 13, 2, 12, 7, 9, 6, 8}, +{0, 12, 1, 13, 5, 9, 4, 8, 3, 15, 2, 14, 6, 10, 7, 11}, +{0, 13, 1, 12, 4, 9, 5, 8, 3, 14, 2, 15, 7, 10, 6, 11}, +{0, 14, 2, 12, 6, 8, 4, 10, 1, 15, 3, 13, 7, 9, 5, 11}, +{0, 12, 2, 14, 7, 11, 5, 9, 1, 13, 3, 15, 6, 10, 4, 8}, +{0, 12, 3, 15, 6, 10, 5, 9, 1, 13, 2, 14, 7, 11, 4, 8}, +{0, 13, 2, 15, 6, 11, 4, 9, 1, 12, 3, 14, 7, 10, 5, 8}, +{0, 8, 1, 9, 7, 15, 6, 14, 3, 11, 2, 10, 4, 12, 5, 13}, +{0, 10, 2, 8, 5, 15, 7, 13, 3, 9, 1, 11, 6, 12, 4, 14}, +{0, 9, 2, 11, 5, 12, 7, 14, 3, 10, 1, 8, 6, 15, 4, 13}, +{0, 10, 1, 11, 5, 15, 4, 14, 3, 9, 2, 8, 6, 12, 7, 13}, +{0, 11, 1, 10, 4, 15, 5, 14, 3, 8, 2, 9, 7, 12, 6, 13}, +{0, 9, 1, 8, 5, 12, 4, 13, 3, 10, 2, 11, 6, 15, 7, 14}, +{0, 10, 2, 8, 7, 13, 5, 15, 1, 11, 3, 9, 6, 12, 4, 14}, +{0, 10, 3, 9, 6, 12, 5, 15, 1, 11, 2, 8, 7, 13, 4, 14}, +{0, 11, 2, 9, 6, 13, 4, 15, 1, 10, 3, 8, 7, 12, 5, 14}, +{0, 8, 3, 11, 7, 15, 4, 12, 1, 9, 2, 10, 6, 14, 5, 13}, +{0, 9, 2, 11, 7, 14, 5, 12, 1, 8, 3, 10, 6, 15, 4, 13}, +{0, 9, 3, 10, 6, 15, 5, 12, 1, 8, 2, 11, 7, 14, 4, 13}, +{0, 12, 6, 10, 1, 13, 7, 11, 3, 15, 5, 9, 2, 14, 4, 8}, +{0, 14, 4, 10, 1, 15, 5, 11, 3, 13, 7, 9, 2, 12, 6, 8}, +{0, 12, 5, 9, 1, 13, 4, 8, 3, 15, 6, 10, 2, 14, 7, 11}, +{0, 13, 4, 9, 1, 12, 5, 8, 3, 14, 7, 10, 2, 15, 6, 11}, +{0, 14, 6, 8, 2, 12, 4, 10, 1, 15, 7, 9, 3, 13, 5, 11}, +{0, 12, 6, 10, 3, 15, 5, 9, 1, 13, 7, 11, 2, 14, 4, 8}, +{0, 12, 7, 11, 2, 14, 5, 9, 1, 13, 6, 10, 3, 15, 4, 8}, +{0, 13, 6, 11, 2, 15, 4, 9, 1, 12, 7, 10, 3, 14, 5, 8}, +{0, 14, 4, 10, 3, 13, 7, 9, 1, 15, 5, 11, 2, 12, 6, 8}, +{0, 14, 5, 11, 2, 12, 7, 9, 1, 15, 4, 10, 3, 13, 6, 8}, +{0, 15, 4, 11, 2, 13, 6, 9, 1, 14, 5, 10, 3, 12, 7, 8}, +{0, 12, 5, 9, 3, 15, 6, 10, 1, 13, 4, 8, 2, 14, 7, 11}, +{0, 13, 4, 9, 3, 14, 7, 10, 1, 12, 5, 8, 2, 15, 6, 11}, +{0, 13, 5, 8, 2, 15, 7, 10, 1, 12, 4, 9, 3, 14, 6, 11}, +{0, 10, 6, 12, 1, 11, 7, 13, 3, 9, 5, 15, 2, 8, 4, 14}, +{0, 8, 7, 15, 1, 9, 6, 14, 3, 11, 4, 12, 2, 10, 5, 13}, +{0, 9, 6, 15, 1, 8, 7, 14, 3, 10, 5, 12, 2, 11, 4, 13}, +{0, 10, 5, 15, 1, 11, 4, 14, 3, 9, 6, 12, 2, 8, 7, 13}, +{0, 11, 4, 15, 1, 10, 5, 14, 3, 8, 7, 12, 2, 9, 6, 13}, +{0, 9, 5, 12, 1, 8, 4, 13, 3, 10, 6, 15, 2, 11, 7, 14}, +{0, 10, 6, 12, 3, 9, 5, 15, 1, 11, 7, 13, 2, 8, 4, 14}, +{0, 10, 7, 13, 2, 8, 5, 15, 1, 11, 6, 12, 3, 9, 4, 14}, +{0, 11, 6, 13, 2, 9, 4, 15, 1, 10, 7, 12, 3, 8, 5, 14}, +{0, 8, 7, 15, 3, 11, 4, 12, 1, 9, 6, 14, 2, 10, 5, 13}, +{0, 9, 6, 15, 3, 10, 5, 12, 1, 8, 7, 14, 2, 11, 4, 13}, +{0, 9, 7, 14, 2, 11, 5, 12, 1, 8, 6, 15, 3, 10, 4, 13}, +{0, 10, 5, 15, 3, 9, 6, 12, 1, 11, 4, 14, 2, 8, 7, 13}, +{0, 11, 4, 15, 3, 8, 7, 12, 1, 10, 5, 14, 2, 9, 6, 13}, +{0, 11, 5, 14, 2, 9, 7, 12, 1, 10, 4, 15, 3, 8, 6, 13}, +{0, 9, 5, 12, 3, 10, 6, 15, 1, 8, 4, 13, 2, 11, 7, 14}, +{0, 1, 4, 5, 3, 2, 7, 6, 15, 14, 11, 10, 12, 13, 8, 9}, +{0, 2, 6, 4, 1, 3, 7, 5, 15, 13, 9, 11, 14, 12, 8, 10}, +{0, 4, 1, 5, 3, 7, 2, 6, 15, 11, 14, 10, 12, 8, 13, 9}, +{0, 6, 2, 4, 1, 7, 3, 5, 15, 9, 13, 11, 14, 8, 12, 10}, +{0, 4, 3, 7, 1, 5, 2, 6, 15, 11, 12, 8, 14, 10, 13, 9}, +{0, 5, 2, 7, 1, 4, 3, 6, 15, 10, 13, 8, 14, 11, 12, 9}, +{0, 8, 10, 2, 9, 1, 3, 11, 7, 15, 13, 5, 14, 6, 4, 12}, +{0, 10, 8, 2, 9, 3, 1, 11, 7, 13, 15, 5, 14, 4, 6, 12}, +{0, 10, 9, 3, 8, 2, 1, 11, 7, 13, 14, 4, 15, 5, 6, 12}, +{0, 1, 8, 9, 11, 10, 3, 2, 7, 6, 15, 14, 12, 13, 4, 5}, +{0, 2, 10, 8, 9, 11, 3, 1, 7, 5, 13, 15, 14, 12, 4, 6}, +{0, 1, 10, 11, 9, 8, 3, 2, 7, 6, 13, 12, 14, 15, 4, 5}, +{0, 1, 11, 10, 8, 9, 3, 2, 7, 6, 12, 13, 15, 14, 4, 5}, +{0, 3, 8, 11, 9, 10, 1, 2, 7, 4, 15, 12, 14, 13, 6, 5}, +{0, 3, 9, 10, 8, 11, 1, 2, 7, 4, 14, 13, 15, 12, 6, 5}, +{0, 4, 12, 8, 13, 9, 1, 5, 3, 7, 15, 11, 14, 10, 2, 6}, +{0, 4, 13, 9, 12, 8, 1, 5, 3, 7, 14, 10, 15, 11, 2, 6}, +{0, 8, 1, 9, 11, 3, 10, 2, 7, 15, 6, 14, 12, 4, 13, 5}, +{0, 10, 2, 8, 9, 3, 11, 1, 7, 13, 5, 15, 14, 4, 12, 6}, +{0, 8, 3, 11, 9, 1, 10, 2, 7, 15, 4, 12, 14, 6, 13, 5}, +{0, 9, 3, 10, 8, 1, 11, 2, 7, 14, 4, 13, 15, 6, 12, 5}, +{0, 10, 1, 11, 9, 3, 8, 2, 7, 13, 6, 12, 14, 4, 15, 5}, +{0, 11, 1, 10, 8, 3, 9, 2, 7, 12, 6, 13, 15, 4, 14, 5}, +{0, 12, 4, 8, 13, 1, 9, 5, 3, 15, 7, 11, 14, 2, 10, 6}, +{0, 13, 4, 9, 12, 1, 8, 5, 3, 14, 7, 10, 15, 2, 11, 6}, +{0, 10, 6, 12, 14, 4, 8, 2, 1, 11, 7, 13, 15, 5, 9, 3}, +{0, 1, 4, 5, 11, 10, 15, 14, 7, 6, 3, 2, 12, 13, 8, 9}, +{0, 2, 6, 4, 9, 11, 15, 13, 7, 5, 1, 3, 14, 12, 8, 10}, +{0, 4, 1, 5, 11, 15, 10, 14, 7, 3, 6, 2, 12, 8, 13, 9}, +{0, 6, 2, 4, 9, 15, 11, 13, 7, 1, 5, 3, 14, 8, 12, 10}, +{0, 5, 3, 6, 8, 13, 11, 14, 7, 2, 4, 1, 15, 10, 12, 9}, +{0, 2, 1, 3, 11, 9, 10, 8, 7, 5, 6, 4, 12, 14, 13, 15}, +{0, 2, 3, 1, 9, 11, 10, 8, 7, 5, 4, 6, 14, 12, 13, 15}, +{0, 3, 2, 1, 9, 10, 11, 8, 7, 4, 5, 6, 14, 13, 12, 15}, +{0, 1, 3, 2, 9, 8, 10, 11, 7, 6, 4, 5, 14, 15, 13, 12}, +{0, 3, 1, 2, 9, 10, 8, 11, 7, 4, 6, 5, 14, 13, 15, 12}, +{0, 4, 6, 2, 13, 9, 11, 15, 3, 7, 5, 1, 14, 10, 8, 12}, +{0, 6, 4, 2, 13, 11, 9, 15, 3, 5, 7, 1, 14, 8, 10, 12}, +{0, 4, 5, 1, 13, 9, 8, 12, 3, 7, 6, 2, 14, 10, 11, 15}, +{0, 5, 4, 1, 13, 8, 9, 12, 3, 6, 7, 2, 14, 11, 10, 15}, +{0, 1, 4, 5, 15, 14, 11, 10, 3, 2, 7, 6, 12, 13, 8, 9}, +{0, 2, 6, 4, 13, 15, 11, 9, 3, 1, 5, 7, 14, 12, 8, 10}, +{0, 1, 6, 7, 13, 12, 11, 10, 3, 2, 5, 4, 14, 15, 8, 9}, +{0, 1, 7, 6, 12, 13, 11, 10, 3, 2, 4, 5, 15, 14, 8, 9}, +{0, 2, 5, 7, 13, 15, 8, 10, 3, 1, 6, 4, 14, 12, 11, 9}, +{0, 1, 5, 4, 13, 12, 8, 9, 3, 2, 6, 7, 14, 15, 11, 10}, +{0, 2, 6, 4, 15, 13, 9, 11, 1, 3, 7, 5, 14, 12, 8, 10}, +{0, 2, 7, 5, 14, 12, 9, 11, 1, 3, 6, 4, 15, 13, 8, 10}, +{0, 3, 6, 5, 14, 13, 8, 11, 1, 2, 7, 4, 15, 12, 9, 10}, +{0, 4, 1, 5, 15, 11, 14, 10, 3, 7, 2, 6, 12, 8, 13, 9}, +{0, 6, 2, 4, 13, 11, 15, 9, 3, 5, 1, 7, 14, 8, 12, 10}, +{0, 5, 2, 7, 13, 8, 15, 10, 3, 6, 1, 4, 14, 11, 12, 9}, +{0, 6, 1, 7, 13, 11, 12, 10, 3, 5, 2, 4, 14, 8, 15, 9}, +{0, 7, 1, 6, 12, 11, 13, 10, 3, 4, 2, 5, 15, 8, 14, 9}, +{0, 5, 1, 4, 13, 8, 12, 9, 3, 6, 2, 7, 14, 11, 15, 10}, +{0, 6, 2, 4, 15, 9, 13, 11, 1, 7, 3, 5, 14, 8, 12, 10}, +{0, 6, 3, 5, 14, 8, 13, 11, 1, 7, 2, 4, 15, 9, 12, 10}, +{0, 7, 2, 5, 14, 9, 12, 11, 1, 6, 3, 4, 15, 8, 13, 10}, +{0, 4, 3, 7, 15, 11, 12, 8, 1, 5, 2, 6, 14, 10, 13, 9}, +{0, 5, 2, 7, 15, 10, 13, 8, 1, 4, 3, 6, 14, 11, 12, 9}, +{0, 5, 3, 6, 14, 11, 13, 8, 1, 4, 2, 7, 15, 10, 12, 9}, +{0, 8, 9, 1, 3, 11, 10, 2, 7, 15, 14, 6, 4, 12, 13, 5}, +{0, 9, 8, 1, 3, 10, 11, 2, 7, 14, 15, 6, 4, 13, 12, 5}, +{0, 8, 11, 3, 1, 9, 10, 2, 7, 15, 12, 4, 6, 14, 13, 5}, +{0, 9, 10, 3, 1, 8, 11, 2, 7, 14, 13, 4, 6, 15, 12, 5}, +{0, 10, 9, 3, 1, 11, 8, 2, 7, 13, 14, 4, 6, 12, 15, 5}, +{0, 11, 8, 3, 1, 10, 9, 2, 7, 12, 15, 4, 6, 13, 14, 5}, +{0, 12, 13, 1, 4, 8, 9, 5, 3, 15, 14, 2, 7, 11, 10, 6}, +{0, 13, 12, 1, 4, 9, 8, 5, 3, 14, 15, 2, 7, 10, 11, 6}, +{0, 10, 14, 4, 6, 12, 8, 2, 1, 11, 15, 5, 7, 13, 9, 3}, +{0, 14, 10, 4, 6, 8, 12, 2, 1, 15, 11, 5, 7, 9, 13, 3}, +{0, 8, 9, 1, 7, 15, 14, 6, 3, 11, 10, 2, 4, 12, 13, 5}, +{0, 9, 8, 1, 7, 14, 15, 6, 3, 10, 11, 2, 4, 13, 12, 5}, +{0, 9, 11, 2, 4, 13, 15, 6, 3, 10, 8, 1, 7, 14, 12, 5}, +{0, 11, 9, 2, 4, 15, 13, 6, 3, 8, 10, 1, 7, 12, 14, 5}, +{0, 8, 11, 3, 7, 15, 12, 4, 1, 9, 10, 2, 6, 14, 13, 5}, +{0, 9, 10, 3, 7, 14, 13, 4, 1, 8, 11, 2, 6, 15, 12, 5}, +{0, 9, 11, 2, 6, 15, 13, 4, 1, 8, 10, 3, 7, 14, 12, 5}, +{0, 10, 9, 3, 7, 13, 14, 4, 1, 11, 8, 2, 6, 12, 15, 5}, +{0, 11, 8, 3, 7, 12, 15, 4, 1, 10, 9, 2, 6, 13, 14, 5}, +{0, 11, 9, 2, 6, 13, 15, 4, 1, 10, 8, 3, 7, 12, 14, 5}, +{0, 1, 12, 13, 3, 2, 15, 14, 7, 6, 11, 10, 4, 5, 8, 9}, +{0, 2, 14, 12, 1, 3, 15, 13, 7, 5, 9, 11, 6, 4, 8, 10}, +{0, 5, 8, 13, 3, 6, 11, 14, 7, 2, 15, 10, 4, 1, 12, 9}, +{0, 4, 11, 15, 1, 5, 10, 14, 7, 3, 12, 8, 6, 2, 13, 9}, +{0, 5, 10, 15, 1, 4, 11, 14, 7, 2, 13, 8, 6, 3, 12, 9}, +{0, 2, 9, 11, 3, 1, 10, 8, 7, 5, 14, 12, 4, 6, 13, 15}, +{0, 1, 9, 8, 3, 2, 10, 11, 7, 6, 14, 15, 4, 5, 13, 12}, +{0, 2, 11, 9, 1, 3, 10, 8, 7, 5, 12, 14, 6, 4, 13, 15}, +{0, 3, 10, 9, 1, 2, 11, 8, 7, 4, 13, 14, 6, 5, 12, 15}, +{0, 3, 9, 10, 1, 2, 8, 11, 7, 4, 14, 13, 6, 5, 15, 12}, +{0, 4, 14, 10, 5, 1, 11, 15, 3, 7, 13, 9, 6, 2, 8, 12}, +{0, 6, 13, 11, 4, 2, 9, 15, 3, 5, 14, 8, 7, 1, 10, 12}, +{0, 4, 13, 9, 5, 1, 8, 12, 3, 7, 14, 10, 6, 2, 11, 15}, +{0, 5, 13, 8, 4, 1, 9, 12, 3, 6, 14, 11, 7, 2, 10, 15}, +{0, 1, 12, 13, 7, 6, 11, 10, 3, 2, 15, 14, 4, 5, 8, 9}, +{0, 2, 14, 12, 5, 7, 11, 9, 3, 1, 13, 15, 6, 4, 8, 10}, +{0, 1, 14, 15, 5, 4, 11, 10, 3, 2, 13, 12, 6, 7, 8, 9}, +{0, 1, 15, 14, 4, 5, 11, 10, 3, 2, 12, 13, 7, 6, 8, 9}, +{0, 2, 13, 15, 5, 7, 8, 10, 3, 1, 14, 12, 6, 4, 11, 9}, +{0, 1, 13, 12, 5, 4, 8, 9, 3, 2, 14, 15, 6, 7, 11, 10}, +{0, 2, 14, 12, 7, 5, 9, 11, 1, 3, 15, 13, 6, 4, 8, 10}, +{0, 2, 15, 13, 6, 4, 9, 11, 1, 3, 14, 12, 7, 5, 8, 10}, +{0, 3, 14, 13, 6, 5, 8, 11, 1, 2, 15, 12, 7, 4, 9, 10}, +{0, 5, 8, 13, 7, 2, 15, 10, 3, 6, 11, 14, 4, 1, 12, 9}, +{0, 4, 11, 15, 5, 1, 14, 10, 3, 7, 8, 12, 6, 2, 13, 9}, +{0, 5, 11, 14, 4, 1, 15, 10, 3, 6, 8, 13, 7, 2, 12, 9}, +{0, 7, 8, 15, 5, 2, 13, 10, 3, 4, 11, 12, 6, 1, 14, 9}, +{0, 4, 11, 15, 7, 3, 12, 8, 1, 5, 10, 14, 6, 2, 13, 9}, +{0, 5, 10, 15, 7, 2, 13, 8, 1, 4, 11, 14, 6, 3, 12, 9}, +{0, 5, 11, 14, 6, 3, 13, 8, 1, 4, 10, 15, 7, 2, 12, 9}, +{0, 2, 9, 11, 7, 5, 14, 12, 3, 1, 10, 8, 4, 6, 13, 15}, +{0, 1, 9, 8, 7, 6, 14, 15, 3, 2, 10, 11, 4, 5, 13, 12}, +{0, 2, 11, 9, 5, 7, 14, 12, 3, 1, 8, 10, 6, 4, 13, 15}, +{0, 1, 11, 10, 5, 4, 14, 15, 3, 2, 8, 9, 6, 7, 13, 12}, +{0, 2, 11, 9, 7, 5, 12, 14, 1, 3, 10, 8, 6, 4, 13, 15}, +{0, 3, 10, 9, 7, 4, 13, 14, 1, 2, 11, 8, 6, 5, 12, 15}, +{0, 3, 11, 8, 6, 5, 13, 14, 1, 2, 10, 9, 7, 4, 12, 15}, +{0, 3, 9, 10, 7, 4, 14, 13, 1, 2, 8, 11, 6, 5, 15, 12}, +{0, 6, 14, 8, 1, 7, 15, 9, 3, 5, 13, 11, 2, 4, 12, 10}, +{0, 4, 15, 11, 1, 5, 14, 10, 3, 7, 12, 8, 2, 6, 13, 9}, +{0, 5, 14, 11, 1, 4, 15, 10, 3, 6, 13, 8, 2, 7, 12, 9}, +{0, 6, 13, 11, 1, 7, 12, 10, 3, 5, 14, 8, 2, 4, 15, 9}, +{0, 7, 12, 11, 1, 6, 13, 10, 3, 4, 15, 8, 2, 5, 14, 9}, +{0, 5, 13, 8, 1, 4, 12, 9, 3, 6, 14, 11, 2, 7, 15, 10}, +{0, 6, 14, 8, 3, 5, 13, 11, 1, 7, 15, 9, 2, 4, 12, 10}, +{0, 6, 15, 9, 2, 4, 13, 11, 1, 7, 14, 8, 3, 5, 12, 10}, +{0, 7, 14, 9, 2, 5, 12, 11, 1, 6, 15, 8, 3, 4, 13, 10}, +{0, 4, 15, 11, 3, 7, 12, 8, 1, 5, 14, 10, 2, 6, 13, 9}, +{0, 5, 14, 11, 3, 6, 13, 8, 1, 4, 15, 10, 2, 7, 12, 9}, +{0, 5, 15, 10, 2, 7, 13, 8, 1, 4, 14, 11, 3, 6, 12, 9}, +{0, 6, 13, 11, 3, 5, 14, 8, 1, 7, 12, 10, 2, 4, 15, 9}, +{0, 7, 12, 11, 3, 4, 15, 8, 1, 6, 13, 10, 2, 5, 14, 9}, +{0, 7, 13, 10, 2, 5, 15, 8, 1, 6, 12, 11, 3, 4, 14, 9}, +{0, 5, 13, 8, 3, 6, 14, 11, 1, 4, 12, 9, 2, 7, 15, 10}, +{0, 8, 5, 13, 3, 11, 6, 14, 7, 15, 2, 10, 4, 12, 1, 9}, +{0, 12, 1, 13, 3, 15, 2, 14, 7, 11, 6, 10, 4, 8, 5, 9}, +{0, 14, 2, 12, 1, 15, 3, 13, 7, 9, 5, 11, 6, 8, 4, 10}, +{0, 12, 3, 15, 1, 13, 2, 14, 7, 11, 4, 8, 6, 10, 5, 9}, +{0, 13, 2, 15, 1, 12, 3, 14, 7, 10, 5, 8, 6, 11, 4, 9}, +{0, 10, 1, 11, 3, 9, 2, 8, 7, 13, 6, 12, 4, 14, 5, 15}, +{0, 9, 1, 8, 3, 10, 2, 11, 7, 14, 6, 15, 4, 13, 5, 12}, +{0, 10, 3, 9, 1, 11, 2, 8, 7, 13, 4, 14, 6, 12, 5, 15}, +{0, 11, 2, 9, 1, 10, 3, 8, 7, 12, 5, 14, 6, 13, 4, 15}, +{0, 9, 3, 10, 1, 8, 2, 11, 7, 14, 4, 13, 6, 15, 5, 12}, +{0, 14, 4, 10, 5, 11, 1, 15, 3, 13, 7, 9, 6, 8, 2, 12}, +{0, 14, 5, 11, 4, 10, 1, 15, 3, 13, 6, 8, 7, 9, 2, 12}, +{0, 13, 4, 9, 5, 8, 1, 12, 3, 14, 7, 10, 6, 11, 2, 15}, +{0, 13, 5, 8, 4, 9, 1, 12, 3, 14, 6, 11, 7, 10, 2, 15}, +{0, 8, 5, 13, 7, 15, 2, 10, 3, 11, 6, 14, 4, 12, 1, 9}, +{0, 8, 7, 15, 5, 13, 2, 10, 3, 11, 4, 12, 6, 14, 1, 9}, +{0, 11, 4, 15, 5, 14, 1, 10, 3, 8, 7, 12, 6, 13, 2, 9}, +{0, 11, 5, 14, 4, 15, 1, 10, 3, 8, 6, 13, 7, 12, 2, 9}, +{0, 12, 1, 13, 7, 11, 6, 10, 3, 15, 2, 14, 4, 8, 5, 9}, +{0, 14, 2, 12, 5, 11, 7, 9, 3, 13, 1, 15, 6, 8, 4, 10}, +{0, 13, 2, 15, 5, 8, 7, 10, 3, 14, 1, 12, 6, 11, 4, 9}, +{0, 14, 1, 15, 5, 11, 4, 10, 3, 13, 2, 12, 6, 8, 7, 9}, +{0, 15, 1, 14, 4, 11, 5, 10, 3, 12, 2, 13, 7, 8, 6, 9}, +{0, 13, 1, 12, 5, 8, 4, 9, 3, 14, 2, 15, 6, 11, 7, 10}, +{0, 14, 2, 12, 7, 9, 5, 11, 1, 15, 3, 13, 6, 8, 4, 10}, +{0, 14, 3, 13, 6, 8, 5, 11, 1, 15, 2, 12, 7, 9, 4, 10}, +{0, 15, 2, 13, 6, 9, 4, 11, 1, 14, 3, 12, 7, 8, 5, 10}, +{0, 12, 3, 15, 7, 11, 4, 8, 1, 13, 2, 14, 6, 10, 5, 9}, +{0, 13, 2, 15, 7, 10, 5, 8, 1, 12, 3, 14, 6, 11, 4, 9}, +{0, 13, 3, 14, 6, 11, 5, 8, 1, 12, 2, 15, 7, 10, 4, 9}, +{0, 10, 1, 11, 7, 13, 6, 12, 3, 9, 2, 8, 4, 14, 5, 15}, +{0, 9, 1, 8, 7, 14, 6, 15, 3, 10, 2, 11, 4, 13, 5, 12}, +{0, 11, 2, 9, 5, 14, 7, 12, 3, 8, 1, 10, 6, 13, 4, 15}, +{0, 11, 1, 10, 5, 14, 4, 15, 3, 8, 2, 9, 6, 13, 7, 12}, +{0, 10, 3, 9, 7, 13, 4, 14, 1, 11, 2, 8, 6, 12, 5, 15}, +{0, 11, 2, 9, 7, 12, 5, 14, 1, 10, 3, 8, 6, 13, 4, 15}, +{0, 11, 3, 8, 6, 13, 5, 14, 1, 10, 2, 9, 7, 12, 4, 15}, +{0, 9, 3, 10, 7, 14, 4, 13, 1, 8, 2, 11, 6, 15, 5, 12}, +{0, 14, 6, 8, 1, 15, 7, 9, 3, 13, 5, 11, 2, 12, 4, 10}, +{0, 12, 7, 11, 1, 13, 6, 10, 3, 15, 4, 8, 2, 14, 5, 9}, +{0, 13, 6, 11, 1, 12, 7, 10, 3, 14, 5, 8, 2, 15, 4, 9}, +{0, 14, 5, 11, 1, 15, 4, 10, 3, 13, 6, 8, 2, 12, 7, 9}, +{0, 15, 4, 11, 1, 14, 5, 10, 3, 12, 7, 8, 2, 13, 6, 9}, +{0, 13, 5, 8, 1, 12, 4, 9, 3, 14, 6, 11, 2, 15, 7, 10}, +{0, 14, 6, 8, 3, 13, 5, 11, 1, 15, 7, 9, 2, 12, 4, 10}, +{0, 14, 7, 9, 2, 12, 5, 11, 1, 15, 6, 8, 3, 13, 4, 10}, +{0, 15, 6, 9, 2, 13, 4, 11, 1, 14, 7, 8, 3, 12, 5, 10}, +{0, 12, 7, 11, 3, 15, 4, 8, 1, 13, 6, 10, 2, 14, 5, 9}, +{0, 13, 6, 11, 3, 14, 5, 8, 1, 12, 7, 10, 2, 15, 4, 9}, +{0, 13, 7, 10, 2, 15, 5, 8, 1, 12, 6, 11, 3, 14, 4, 9}, +{0, 14, 5, 11, 3, 13, 6, 8, 1, 15, 4, 10, 2, 12, 7, 9}, +{0, 15, 4, 11, 3, 12, 7, 8, 1, 14, 5, 10, 2, 13, 6, 9}, +{0, 15, 5, 10, 2, 13, 7, 8, 1, 14, 4, 11, 3, 12, 6, 9}, +{0, 13, 5, 8, 3, 14, 6, 11, 1, 12, 4, 9, 2, 15, 7, 10}, +{0, 10, 7, 13, 1, 11, 6, 12, 3, 9, 4, 14, 2, 8, 5, 15}, +{0, 11, 6, 13, 1, 10, 7, 12, 3, 8, 5, 14, 2, 9, 4, 15}, +{0, 9, 7, 14, 1, 8, 6, 15, 3, 10, 4, 13, 2, 11, 5, 12}, +{0, 11, 5, 14, 1, 10, 4, 15, 3, 8, 6, 13, 2, 9, 7, 12}, +{0, 10, 7, 13, 3, 9, 4, 14, 1, 11, 6, 12, 2, 8, 5, 15}, +{0, 11, 6, 13, 3, 8, 5, 14, 1, 10, 7, 12, 2, 9, 4, 15}, +{0, 11, 7, 12, 2, 9, 5, 14, 1, 10, 6, 13, 3, 8, 4, 15}, +{0, 9, 7, 14, 3, 10, 4, 13, 1, 8, 6, 15, 2, 11, 5, 12}, +{0, 11, 5, 14, 3, 8, 6, 13, 1, 10, 4, 15, 2, 9, 7, 12}, +{0, 8, 5, 13, 3, 11, 6, 14, 15, 7, 10, 2, 12, 4, 9, 1}, +{0, 9, 4, 13, 3, 10, 7, 14, 15, 6, 11, 2, 12, 5, 8, 1}, +{0, 10, 6, 12, 1, 11, 7, 13, 15, 5, 9, 3, 14, 4, 8, 2}, +{0, 2, 1, 3, 7, 5, 6, 4, 15, 13, 14, 12, 8, 10, 9, 11}, +{0, 4, 5, 1, 3, 7, 6, 2, 15, 11, 10, 14, 12, 8, 9, 13}, +{0, 5, 4, 1, 3, 6, 7, 2, 15, 10, 11, 14, 12, 9, 8, 13}, +{0, 2, 5, 7, 3, 1, 6, 4, 15, 13, 10, 8, 12, 14, 9, 11}, +{0, 1, 5, 4, 3, 2, 6, 7, 15, 14, 10, 11, 12, 13, 9, 8}, +{0, 2, 7, 5, 1, 3, 6, 4, 15, 13, 8, 10, 14, 12, 9, 11}, +{0, 3, 6, 5, 1, 2, 7, 4, 15, 12, 9, 10, 14, 13, 8, 11}, +{0, 6, 1, 7, 3, 5, 2, 4, 15, 9, 14, 8, 12, 10, 13, 11}, +{0, 5, 1, 4, 3, 6, 2, 7, 15, 10, 14, 11, 12, 9, 13, 8}, +{0, 6, 3, 5, 1, 7, 2, 4, 15, 9, 12, 10, 14, 8, 13, 11}, +{0, 7, 2, 5, 1, 6, 3, 4, 15, 8, 13, 10, 14, 9, 12, 11}, +{0, 5, 3, 6, 1, 4, 2, 7, 15, 10, 12, 9, 14, 11, 13, 8}, +{0, 12, 10, 6, 9, 5, 3, 15, 7, 11, 13, 1, 14, 2, 4, 8}, +{0, 8, 9, 1, 11, 3, 2, 10, 7, 15, 14, 6, 12, 4, 5, 13}, +{0, 9, 8, 1, 11, 2, 3, 10, 7, 14, 15, 6, 12, 5, 4, 13}, +{0, 8, 11, 3, 9, 1, 2, 10, 7, 15, 12, 4, 14, 6, 5, 13}, +{0, 9, 11, 2, 8, 1, 3, 10, 7, 14, 12, 5, 15, 6, 4, 13}, +{0, 11, 8, 3, 9, 2, 1, 10, 7, 12, 15, 4, 14, 5, 6, 13}, +{0, 11, 9, 2, 8, 3, 1, 10, 7, 12, 14, 5, 15, 4, 6, 13}, +{0, 4, 9, 13, 11, 15, 2, 6, 7, 3, 14, 10, 12, 8, 5, 1}, +{0, 5, 8, 13, 11, 14, 3, 6, 7, 2, 15, 10, 12, 9, 4, 1}, +{0, 6, 10, 12, 9, 15, 3, 5, 7, 1, 13, 11, 14, 8, 4, 2}, +{0, 4, 11, 15, 9, 13, 2, 6, 7, 3, 12, 8, 14, 10, 5, 1}, +{0, 5, 10, 15, 9, 12, 3, 6, 7, 2, 13, 8, 14, 11, 4, 1}, +{0, 5, 11, 14, 8, 13, 3, 6, 7, 2, 12, 9, 15, 10, 4, 1}, +{0, 1, 9, 8, 11, 10, 2, 3, 7, 6, 14, 15, 12, 13, 5, 4}, +{0, 1, 11, 10, 9, 8, 2, 3, 7, 6, 12, 13, 14, 15, 5, 4}, +{0, 6, 12, 10, 13, 11, 1, 7, 3, 5, 15, 9, 14, 8, 2, 4}, +{0, 6, 13, 11, 12, 10, 1, 7, 3, 5, 14, 8, 15, 9, 2, 4}, +{0, 5, 12, 9, 13, 8, 1, 4, 3, 6, 15, 10, 14, 11, 2, 7}, +{0, 5, 13, 8, 12, 9, 1, 4, 3, 6, 14, 11, 15, 10, 2, 7}, +{0, 8, 5, 13, 11, 3, 14, 6, 7, 15, 2, 10, 12, 4, 9, 1}, +{0, 9, 4, 13, 11, 2, 15, 6, 7, 14, 3, 10, 12, 5, 8, 1}, +{0, 10, 6, 12, 9, 3, 15, 5, 7, 13, 1, 11, 14, 4, 8, 2}, +{0, 12, 3, 15, 9, 5, 10, 6, 7, 11, 4, 8, 14, 2, 13, 1}, +{0, 13, 2, 15, 9, 4, 11, 6, 7, 10, 5, 8, 14, 3, 12, 1}, +{0, 13, 3, 14, 8, 5, 11, 6, 7, 10, 4, 9, 15, 2, 12, 1}, +{0, 9, 1, 8, 11, 2, 10, 3, 7, 14, 6, 15, 12, 5, 13, 4}, +{0, 11, 1, 10, 9, 2, 8, 3, 7, 12, 6, 13, 14, 5, 15, 4}, +{0, 12, 6, 10, 13, 1, 11, 7, 3, 15, 5, 9, 14, 2, 8, 4}, +{0, 14, 5, 11, 12, 2, 9, 7, 3, 13, 6, 8, 15, 1, 10, 4}, +{0, 12, 5, 9, 13, 1, 8, 4, 3, 15, 6, 10, 14, 2, 11, 7}, +{0, 13, 5, 8, 12, 1, 9, 4, 3, 14, 6, 11, 15, 2, 10, 7}, +{0, 8, 5, 13, 15, 7, 10, 2, 3, 11, 6, 14, 12, 4, 9, 1}, +{0, 9, 4, 13, 15, 6, 11, 2, 3, 10, 7, 14, 12, 5, 8, 1}, +{0, 10, 6, 12, 13, 7, 11, 1, 3, 9, 5, 15, 14, 4, 8, 2}, +{0, 8, 7, 15, 13, 5, 10, 2, 3, 11, 4, 12, 14, 6, 9, 1}, +{0, 9, 6, 15, 13, 4, 11, 2, 3, 10, 5, 12, 14, 7, 8, 1}, +{0, 9, 7, 14, 12, 5, 11, 2, 3, 10, 4, 13, 15, 6, 8, 1}, +{0, 10, 5, 15, 13, 7, 8, 2, 3, 9, 6, 12, 14, 4, 11, 1}, +{0, 11, 4, 15, 13, 6, 9, 2, 3, 8, 7, 12, 14, 5, 10, 1}, +{0, 11, 5, 14, 12, 7, 9, 2, 3, 8, 6, 13, 15, 4, 10, 1}, +{0, 9, 5, 12, 13, 4, 8, 1, 3, 10, 6, 15, 14, 7, 11, 2}, +{0, 10, 6, 12, 15, 5, 9, 3, 1, 11, 7, 13, 14, 4, 8, 2}, +{0, 10, 7, 13, 14, 4, 9, 3, 1, 11, 6, 12, 15, 5, 8, 2}, +{0, 11, 6, 13, 14, 5, 8, 3, 1, 10, 7, 12, 15, 4, 9, 2}, +{0, 2, 1, 3, 15, 13, 14, 12, 7, 5, 6, 4, 8, 10, 9, 11}, +{0, 4, 5, 1, 11, 15, 14, 10, 7, 3, 2, 6, 12, 8, 9, 13}, +{0, 5, 4, 1, 11, 14, 15, 10, 7, 2, 3, 6, 12, 9, 8, 13}, +{0, 1, 5, 4, 11, 10, 14, 15, 7, 6, 2, 3, 12, 13, 9, 8}, +{0, 3, 6, 5, 9, 10, 15, 12, 7, 4, 1, 2, 14, 13, 8, 11}, +{0, 5, 1, 4, 11, 14, 10, 15, 7, 2, 6, 3, 12, 9, 13, 8}, +{0, 6, 3, 5, 9, 15, 10, 12, 7, 1, 4, 2, 14, 8, 13, 11}, +{0, 5, 3, 6, 9, 12, 10, 15, 7, 2, 4, 1, 14, 11, 13, 8}, +{0, 1, 3, 2, 11, 10, 8, 9, 7, 6, 4, 5, 12, 13, 15, 14}, +{0, 3, 1, 2, 11, 8, 10, 9, 7, 4, 6, 5, 12, 15, 13, 14}, +{0, 4, 5, 1, 15, 11, 10, 14, 3, 7, 6, 2, 12, 8, 9, 13}, +{0, 5, 4, 1, 15, 10, 11, 14, 3, 6, 7, 2, 12, 9, 8, 13}, +{0, 5, 7, 2, 12, 9, 11, 14, 3, 6, 4, 1, 15, 10, 8, 13}, +{0, 7, 5, 2, 12, 11, 9, 14, 3, 4, 6, 1, 15, 8, 10, 13}, +{0, 2, 5, 7, 15, 13, 10, 8, 3, 1, 6, 4, 12, 14, 9, 11}, +{0, 1, 5, 4, 15, 14, 10, 11, 3, 2, 6, 7, 12, 13, 9, 8}, +{0, 2, 7, 5, 13, 15, 10, 8, 3, 1, 4, 6, 14, 12, 9, 11}, +{0, 1, 7, 6, 13, 12, 10, 11, 3, 2, 4, 5, 14, 15, 9, 8}, +{0, 2, 7, 5, 15, 13, 8, 10, 1, 3, 6, 4, 14, 12, 9, 11}, +{0, 3, 6, 5, 15, 12, 9, 10, 1, 2, 7, 4, 14, 13, 8, 11}, +{0, 3, 7, 4, 14, 13, 9, 10, 1, 2, 6, 5, 15, 12, 8, 11}, +{0, 6, 1, 7, 15, 9, 14, 8, 3, 5, 2, 4, 12, 10, 13, 11}, +{0, 5, 1, 4, 15, 10, 14, 11, 3, 6, 2, 7, 12, 9, 13, 8}, +{0, 7, 2, 5, 13, 10, 15, 8, 3, 4, 1, 6, 14, 9, 12, 11}, +{0, 7, 1, 6, 13, 10, 12, 11, 3, 4, 2, 5, 14, 9, 15, 8}, +{0, 6, 3, 5, 15, 9, 12, 10, 1, 7, 2, 4, 14, 8, 13, 11}, +{0, 7, 2, 5, 15, 8, 13, 10, 1, 6, 3, 4, 14, 9, 12, 11}, +{0, 7, 3, 4, 14, 9, 13, 10, 1, 6, 2, 5, 15, 8, 12, 11}, +{0, 5, 3, 6, 15, 10, 12, 9, 1, 4, 2, 7, 14, 11, 13, 8}, +{0, 8, 13, 5, 3, 11, 14, 6, 7, 15, 10, 2, 4, 12, 9, 1}, +{0, 9, 12, 5, 3, 10, 15, 6, 7, 14, 11, 2, 4, 13, 8, 1}, +{0, 10, 14, 4, 1, 11, 15, 5, 7, 13, 9, 3, 6, 12, 8, 2}, +{0, 12, 9, 5, 3, 15, 10, 6, 7, 11, 14, 2, 4, 8, 13, 1}, +{0, 13, 8, 5, 3, 14, 11, 6, 7, 10, 15, 2, 4, 9, 12, 1}, +{0, 14, 10, 4, 1, 15, 11, 5, 7, 9, 13, 3, 6, 8, 12, 2}, +{0, 9, 11, 2, 1, 8, 10, 3, 7, 14, 12, 5, 6, 15, 13, 4}, +{0, 11, 9, 2, 1, 10, 8, 3, 7, 12, 14, 5, 6, 13, 15, 4}, +{0, 12, 14, 2, 5, 9, 11, 7, 3, 15, 13, 1, 6, 10, 8, 4}, +{0, 14, 12, 2, 5, 11, 9, 7, 3, 13, 15, 1, 6, 8, 10, 4}, +{0, 12, 13, 1, 5, 9, 8, 4, 3, 15, 14, 2, 6, 10, 11, 7}, +{0, 13, 12, 1, 5, 8, 9, 4, 3, 14, 15, 2, 6, 11, 10, 7}, +{0, 8, 13, 5, 7, 15, 10, 2, 3, 11, 14, 6, 4, 12, 9, 1}, +{0, 9, 12, 5, 7, 14, 11, 2, 3, 10, 15, 6, 4, 13, 8, 1}, +{0, 10, 14, 4, 5, 15, 11, 1, 3, 9, 13, 7, 6, 12, 8, 2}, +{0, 8, 15, 7, 5, 13, 10, 2, 3, 11, 12, 4, 6, 14, 9, 1}, +{0, 9, 14, 7, 5, 12, 11, 2, 3, 10, 13, 4, 6, 15, 8, 1}, +{0, 9, 15, 6, 4, 13, 11, 2, 3, 10, 12, 5, 7, 14, 8, 1}, +{0, 10, 13, 7, 5, 15, 8, 2, 3, 9, 14, 4, 6, 12, 11, 1}, +{0, 11, 12, 7, 5, 14, 9, 2, 3, 8, 15, 4, 6, 13, 10, 1}, +{0, 11, 13, 6, 4, 15, 9, 2, 3, 8, 14, 5, 7, 12, 10, 1}, +{0, 9, 13, 4, 5, 12, 8, 1, 3, 10, 14, 7, 6, 15, 11, 2}, +{0, 10, 14, 4, 7, 13, 9, 3, 1, 11, 15, 5, 6, 12, 8, 2}, +{0, 10, 15, 5, 6, 12, 9, 3, 1, 11, 14, 4, 7, 13, 8, 2}, +{0, 11, 14, 5, 6, 13, 8, 3, 1, 10, 15, 4, 7, 12, 9, 2}, +{0, 12, 9, 5, 7, 11, 14, 2, 3, 15, 10, 6, 4, 8, 13, 1}, +{0, 13, 8, 5, 7, 10, 15, 2, 3, 14, 11, 6, 4, 9, 12, 1}, +{0, 14, 10, 4, 5, 11, 15, 1, 3, 13, 9, 7, 6, 8, 12, 2}, +{0, 12, 11, 7, 5, 9, 14, 2, 3, 15, 8, 4, 6, 10, 13, 1}, +{0, 13, 10, 7, 5, 8, 15, 2, 3, 14, 9, 4, 6, 11, 12, 1}, +{0, 13, 11, 6, 4, 9, 15, 2, 3, 14, 8, 5, 7, 10, 12, 1}, +{0, 14, 9, 7, 5, 11, 12, 2, 3, 13, 10, 4, 6, 8, 15, 1}, +{0, 15, 8, 7, 5, 10, 13, 2, 3, 12, 11, 4, 6, 9, 14, 1}, +{0, 15, 9, 6, 4, 11, 13, 2, 3, 12, 10, 5, 7, 8, 14, 1}, +{0, 13, 9, 4, 5, 8, 12, 1, 3, 14, 10, 7, 6, 11, 15, 2}, +{0, 14, 10, 4, 7, 9, 13, 3, 1, 15, 11, 5, 6, 8, 12, 2}, +{0, 14, 11, 5, 6, 8, 13, 3, 1, 15, 10, 4, 7, 9, 12, 2}, +{0, 15, 10, 5, 6, 9, 12, 3, 1, 14, 11, 4, 7, 8, 13, 2}, +{0, 10, 11, 1, 5, 15, 14, 4, 3, 9, 8, 2, 6, 12, 13, 7}, +{0, 11, 10, 1, 5, 14, 15, 4, 3, 8, 9, 2, 6, 13, 12, 7}, +{0, 9, 11, 2, 5, 12, 14, 7, 3, 10, 8, 1, 6, 15, 13, 4}, +{0, 11, 9, 2, 5, 14, 12, 7, 3, 8, 10, 1, 6, 13, 15, 4}, +{0, 9, 11, 2, 7, 14, 12, 5, 1, 8, 10, 3, 6, 15, 13, 4}, +{0, 11, 9, 2, 7, 12, 14, 5, 1, 10, 8, 3, 6, 13, 15, 4}, +{0, 5, 12, 9, 3, 6, 15, 10, 7, 2, 11, 14, 4, 1, 8, 13}, +{0, 2, 13, 15, 3, 1, 14, 12, 7, 5, 10, 8, 4, 6, 9, 11}, +{0, 1, 13, 12, 3, 2, 14, 15, 7, 6, 10, 11, 4, 5, 9, 8}, +{0, 2, 15, 13, 1, 3, 14, 12, 7, 5, 8, 10, 6, 4, 9, 11}, +{0, 3, 14, 13, 1, 2, 15, 12, 7, 4, 9, 10, 6, 5, 8, 11}, +{0, 6, 9, 15, 3, 5, 10, 12, 7, 1, 14, 8, 4, 2, 13, 11}, +{0, 5, 9, 12, 3, 6, 10, 15, 7, 2, 14, 11, 4, 1, 13, 8}, +{0, 5, 11, 14, 1, 4, 10, 15, 7, 2, 12, 9, 6, 3, 13, 8}, +{0, 1, 11, 10, 3, 2, 8, 9, 7, 6, 12, 13, 4, 5, 15, 14}, +{0, 3, 11, 8, 1, 2, 10, 9, 7, 4, 12, 15, 6, 5, 13, 14}, +{0, 5, 12, 9, 7, 2, 11, 14, 3, 6, 15, 10, 4, 1, 8, 13}, +{0, 4, 15, 11, 5, 1, 10, 14, 3, 7, 12, 8, 6, 2, 9, 13}, +{0, 5, 15, 10, 4, 1, 11, 14, 3, 6, 12, 9, 7, 2, 8, 13}, +{0, 7, 12, 11, 5, 2, 9, 14, 3, 4, 15, 8, 6, 1, 10, 13}, +{0, 2, 13, 15, 7, 5, 10, 8, 3, 1, 14, 12, 4, 6, 9, 11}, +{0, 1, 13, 12, 7, 6, 10, 11, 3, 2, 14, 15, 4, 5, 9, 8}, +{0, 2, 15, 13, 5, 7, 10, 8, 3, 1, 12, 14, 6, 4, 9, 11}, +{0, 1, 15, 14, 5, 4, 10, 11, 3, 2, 12, 13, 6, 7, 9, 8}, +{0, 2, 15, 13, 7, 5, 8, 10, 1, 3, 14, 12, 6, 4, 9, 11}, +{0, 3, 14, 13, 7, 4, 9, 10, 1, 2, 15, 12, 6, 5, 8, 11}, +{0, 3, 15, 12, 6, 5, 9, 10, 1, 2, 14, 13, 7, 4, 8, 11}, +{0, 6, 9, 15, 7, 1, 14, 8, 3, 5, 10, 12, 4, 2, 13, 11}, +{0, 5, 9, 12, 7, 2, 14, 11, 3, 6, 10, 15, 4, 1, 13, 8}, +{0, 7, 10, 13, 5, 2, 15, 8, 3, 4, 9, 14, 6, 1, 12, 11}, +{0, 7, 9, 14, 5, 2, 12, 11, 3, 4, 10, 13, 6, 1, 15, 8}, +{0, 5, 11, 14, 7, 2, 12, 9, 1, 4, 10, 15, 6, 3, 13, 8}, +{0, 1, 11, 10, 7, 6, 12, 13, 3, 2, 8, 9, 4, 5, 15, 14}, +{0, 3, 11, 8, 7, 4, 12, 15, 1, 2, 10, 9, 6, 5, 13, 14}, +{0, 6, 15, 9, 1, 7, 14, 8, 3, 5, 12, 10, 2, 4, 13, 11}, +{0, 7, 14, 9, 1, 6, 15, 8, 3, 4, 13, 10, 2, 5, 12, 11}, +{0, 5, 15, 10, 1, 4, 14, 11, 3, 6, 12, 9, 2, 7, 13, 8}, +{0, 7, 13, 10, 1, 6, 12, 11, 3, 4, 14, 9, 2, 5, 15, 8}, +{0, 6, 15, 9, 3, 5, 12, 10, 1, 7, 14, 8, 2, 4, 13, 11}, +{0, 7, 14, 9, 3, 4, 13, 10, 1, 6, 15, 8, 2, 5, 12, 11}, +{0, 7, 15, 8, 2, 5, 13, 10, 1, 6, 14, 9, 3, 4, 12, 11}, +{0, 5, 15, 10, 3, 6, 12, 9, 1, 4, 14, 11, 2, 7, 13, 8}, +{0, 7, 13, 10, 3, 4, 14, 9, 1, 6, 12, 11, 2, 5, 15, 8}, +{0, 12, 5, 9, 3, 15, 6, 10, 7, 11, 2, 14, 4, 8, 1, 13}, +{0, 10, 5, 15, 3, 9, 6, 12, 7, 13, 2, 8, 4, 14, 1, 11}, +{0, 9, 5, 12, 3, 10, 6, 15, 7, 14, 2, 11, 4, 13, 1, 8}, +{0, 14, 1, 15, 3, 13, 2, 12, 7, 9, 6, 8, 4, 10, 5, 11}, +{0, 13, 1, 12, 3, 14, 2, 15, 7, 10, 6, 11, 4, 9, 5, 8}, +{0, 14, 3, 13, 1, 15, 2, 12, 7, 9, 4, 10, 6, 8, 5, 11}, +{0, 15, 2, 13, 1, 14, 3, 12, 7, 8, 5, 10, 6, 9, 4, 11}, +{0, 13, 3, 14, 1, 12, 2, 15, 7, 10, 4, 9, 6, 11, 5, 8}, +{0, 11, 1, 10, 3, 8, 2, 9, 7, 12, 6, 13, 4, 15, 5, 14}, +{0, 11, 3, 8, 1, 10, 2, 9, 7, 12, 4, 15, 6, 13, 5, 14}, +{0, 12, 5, 9, 7, 11, 2, 14, 3, 15, 6, 10, 4, 8, 1, 13}, +{0, 12, 7, 11, 5, 9, 2, 14, 3, 15, 4, 8, 6, 10, 1, 13}, +{0, 15, 4, 11, 5, 10, 1, 14, 3, 12, 7, 8, 6, 9, 2, 13}, +{0, 15, 5, 10, 4, 11, 1, 14, 3, 12, 6, 9, 7, 8, 2, 13}, +{0, 10, 5, 15, 7, 13, 2, 8, 3, 9, 6, 12, 4, 14, 1, 11}, +{0, 9, 5, 12, 7, 14, 2, 11, 3, 10, 6, 15, 4, 13, 1, 8}, +{0, 10, 7, 13, 5, 15, 2, 8, 3, 9, 4, 14, 6, 12, 1, 11}, +{0, 9, 7, 14, 5, 12, 2, 11, 3, 10, 4, 13, 6, 15, 1, 8}, +{0, 14, 1, 15, 7, 9, 6, 8, 3, 13, 2, 12, 4, 10, 5, 11}, +{0, 13, 1, 12, 7, 10, 6, 11, 3, 14, 2, 15, 4, 9, 5, 8}, +{0, 15, 2, 13, 5, 10, 7, 8, 3, 12, 1, 14, 6, 9, 4, 11}, +{0, 15, 1, 14, 5, 10, 4, 11, 3, 12, 2, 13, 6, 9, 7, 8}, +{0, 14, 3, 13, 7, 9, 4, 10, 1, 15, 2, 12, 6, 8, 5, 11}, +{0, 15, 2, 13, 7, 8, 5, 10, 1, 14, 3, 12, 6, 9, 4, 11}, +{0, 15, 3, 12, 6, 9, 5, 10, 1, 14, 2, 13, 7, 8, 4, 11}, +{0, 13, 3, 14, 7, 10, 4, 9, 1, 12, 2, 15, 6, 11, 5, 8}, +{0, 11, 1, 10, 7, 12, 6, 13, 3, 8, 2, 9, 4, 15, 5, 14}, +{0, 11, 3, 8, 7, 12, 4, 15, 1, 10, 2, 9, 6, 13, 5, 14}, +{0, 14, 7, 9, 1, 15, 6, 8, 3, 13, 4, 10, 2, 12, 5, 11}, +{0, 15, 6, 9, 1, 14, 7, 8, 3, 12, 5, 10, 2, 13, 4, 11}, +{0, 13, 7, 10, 1, 12, 6, 11, 3, 14, 4, 9, 2, 15, 5, 8}, +{0, 15, 5, 10, 1, 14, 4, 11, 3, 12, 6, 9, 2, 13, 7, 8}, +{0, 14, 7, 9, 3, 13, 4, 10, 1, 15, 6, 8, 2, 12, 5, 11}, +{0, 15, 6, 9, 3, 12, 5, 10, 1, 14, 7, 8, 2, 13, 4, 11}, +{0, 15, 7, 8, 2, 13, 5, 10, 1, 14, 6, 9, 3, 12, 4, 11}, +{0, 13, 7, 10, 3, 14, 4, 9, 1, 12, 6, 11, 2, 15, 5, 8}, +{0, 15, 5, 10, 3, 12, 6, 9, 1, 14, 4, 11, 2, 13, 7, 8}, +{0, 11, 7, 12, 1, 10, 6, 13, 3, 8, 4, 15, 2, 9, 5, 14}, +{0, 11, 7, 12, 3, 8, 4, 15, 1, 10, 6, 13, 2, 9, 5, 14}, +{0, 2, 9, 11, 7, 5, 14, 12, 15, 13, 6, 4, 8, 10, 1, 3}, +{0, 4, 13, 9, 3, 7, 14, 10, 15, 11, 2, 6, 12, 8, 1, 5}, +{0, 10, 1, 11, 7, 13, 6, 12, 15, 5, 14, 4, 8, 2, 9, 3}, +{0, 13, 4, 9, 3, 14, 7, 10, 15, 2, 11, 6, 12, 1, 8, 5}, +{0, 10, 7, 13, 1, 11, 6, 12, 15, 5, 8, 2, 14, 4, 9, 3}, +{0, 11, 6, 13, 1, 10, 7, 12, 15, 4, 9, 2, 14, 5, 8, 3}, +{0, 1, 3, 2, 7, 6, 4, 5, 15, 14, 12, 13, 8, 9, 11, 10}, +{0, 3, 1, 2, 7, 4, 6, 5, 15, 12, 14, 13, 8, 11, 9, 10}, +{0, 1, 7, 6, 3, 2, 4, 5, 15, 14, 8, 9, 12, 13, 11, 10}, +{0, 3, 7, 4, 1, 2, 6, 5, 15, 12, 8, 11, 14, 13, 9, 10}, +{0, 7, 1, 6, 3, 4, 2, 5, 15, 8, 14, 9, 12, 11, 13, 10}, +{0, 7, 3, 4, 1, 6, 2, 5, 15, 8, 12, 11, 14, 9, 13, 10}, +{0, 8, 13, 5, 11, 3, 6, 14, 7, 15, 10, 2, 12, 4, 1, 9}, +{0, 13, 8, 5, 11, 6, 3, 14, 7, 10, 15, 2, 12, 1, 4, 9}, +{0, 13, 11, 6, 8, 5, 3, 14, 7, 10, 12, 1, 15, 2, 4, 9}, +{0, 10, 9, 3, 11, 1, 2, 8, 7, 13, 14, 4, 12, 6, 5, 15}, +{0, 10, 11, 1, 9, 3, 2, 8, 7, 13, 12, 6, 14, 4, 5, 15}, +{0, 11, 10, 1, 9, 2, 3, 8, 7, 12, 13, 6, 14, 5, 4, 15}, +{0, 2, 9, 11, 15, 13, 6, 4, 7, 5, 14, 12, 8, 10, 1, 3}, +{0, 4, 13, 9, 11, 15, 6, 2, 7, 3, 10, 14, 12, 8, 1, 5}, +{0, 2, 13, 15, 11, 9, 6, 4, 7, 5, 10, 8, 12, 14, 1, 3}, +{0, 2, 15, 13, 9, 11, 6, 4, 7, 5, 8, 10, 14, 12, 1, 3}, +{0, 6, 9, 15, 11, 13, 2, 4, 7, 1, 14, 8, 12, 10, 5, 3}, +{0, 6, 11, 13, 9, 15, 2, 4, 7, 1, 12, 10, 14, 8, 5, 3}, +{0, 3, 9, 10, 11, 8, 2, 1, 7, 4, 14, 13, 12, 15, 5, 6}, +{0, 3, 11, 8, 9, 10, 2, 1, 7, 4, 12, 15, 14, 13, 5, 6}, +{0, 4, 13, 9, 15, 11, 2, 6, 3, 7, 14, 10, 12, 8, 1, 5}, +{0, 4, 15, 11, 13, 9, 2, 6, 3, 7, 12, 8, 14, 10, 1, 5}, +{0, 7, 12, 11, 13, 10, 1, 6, 3, 4, 15, 8, 14, 9, 2, 5}, +{0, 7, 13, 10, 12, 11, 1, 6, 3, 4, 14, 9, 15, 8, 2, 5}, +{0, 10, 1, 11, 15, 5, 14, 4, 7, 13, 6, 12, 8, 2, 9, 3}, +{0, 13, 4, 9, 11, 6, 15, 2, 7, 10, 3, 14, 12, 1, 8, 5}, +{0, 10, 5, 15, 11, 1, 14, 4, 7, 13, 2, 8, 12, 6, 9, 3}, +{0, 11, 6, 13, 9, 2, 15, 4, 7, 12, 1, 10, 14, 5, 8, 3}, +{0, 14, 1, 15, 11, 5, 10, 4, 7, 9, 6, 8, 12, 2, 13, 3}, +{0, 15, 2, 13, 9, 6, 11, 4, 7, 8, 5, 10, 14, 1, 12, 3}, +{0, 9, 3, 10, 11, 2, 8, 1, 7, 14, 4, 13, 12, 5, 15, 6}, +{0, 11, 3, 8, 9, 2, 10, 1, 7, 12, 4, 15, 14, 5, 13, 6}, +{0, 13, 4, 9, 15, 2, 11, 6, 3, 14, 7, 10, 12, 1, 8, 5}, +{0, 12, 7, 11, 13, 1, 10, 6, 3, 15, 4, 8, 14, 2, 9, 5}, +{0, 13, 7, 10, 12, 1, 11, 6, 3, 14, 4, 9, 15, 2, 8, 5}, +{0, 15, 4, 11, 13, 2, 9, 6, 3, 12, 7, 8, 14, 1, 10, 5}, +{0, 10, 7, 13, 15, 5, 8, 2, 1, 11, 6, 12, 14, 4, 9, 3}, +{0, 11, 6, 13, 15, 4, 9, 2, 1, 10, 7, 12, 14, 5, 8, 3}, +{0, 11, 7, 12, 14, 5, 9, 2, 1, 10, 6, 13, 15, 4, 8, 3}, +{0, 1, 3, 2, 15, 14, 12, 13, 7, 6, 4, 5, 8, 9, 11, 10}, +{0, 3, 1, 2, 15, 12, 14, 13, 7, 4, 6, 5, 8, 11, 9, 10}, +{0, 6, 5, 3, 11, 13, 14, 8, 7, 1, 2, 4, 12, 10, 9, 15}, +{0, 3, 5, 6, 11, 8, 14, 13, 7, 4, 2, 1, 12, 15, 9, 10}, +{0, 5, 3, 6, 11, 14, 8, 13, 7, 2, 4, 1, 12, 9, 15, 10}, +{0, 6, 7, 1, 13, 11, 10, 12, 3, 5, 4, 2, 14, 8, 9, 15}, +{0, 7, 6, 1, 13, 10, 11, 12, 3, 4, 5, 2, 14, 9, 8, 15}, +{0, 5, 7, 2, 13, 8, 10, 15, 3, 6, 4, 1, 14, 11, 9, 12}, +{0, 7, 5, 2, 13, 10, 8, 15, 3, 4, 6, 1, 14, 9, 11, 12}, +{0, 1, 7, 6, 15, 14, 8, 9, 3, 2, 4, 5, 12, 13, 11, 10}, +{0, 3, 7, 4, 15, 12, 8, 11, 1, 2, 6, 5, 14, 13, 9, 10}, +{0, 7, 1, 6, 15, 8, 14, 9, 3, 4, 2, 5, 12, 11, 13, 10}, +{0, 7, 3, 4, 15, 8, 12, 11, 1, 6, 2, 5, 14, 9, 13, 10}, +{0, 12, 13, 1, 3, 15, 14, 2, 7, 11, 10, 6, 4, 8, 9, 5}, +{0, 13, 12, 1, 3, 14, 15, 2, 7, 10, 11, 6, 4, 9, 8, 5}, +{0, 10, 15, 5, 1, 11, 14, 4, 7, 13, 8, 2, 6, 12, 9, 3}, +{0, 11, 14, 5, 1, 10, 15, 4, 7, 12, 9, 2, 6, 13, 8, 3}, +{0, 14, 11, 5, 1, 15, 10, 4, 7, 9, 12, 2, 6, 8, 13, 3}, +{0, 15, 10, 5, 1, 14, 11, 4, 7, 8, 13, 2, 6, 9, 12, 3}, +{0, 9, 11, 2, 3, 10, 8, 1, 7, 14, 12, 5, 4, 13, 15, 6}, +{0, 11, 9, 2, 3, 8, 10, 1, 7, 12, 14, 5, 4, 15, 13, 6}, +{0, 12, 13, 1, 7, 11, 10, 6, 3, 15, 14, 2, 4, 8, 9, 5}, +{0, 13, 12, 1, 7, 10, 11, 6, 3, 14, 15, 2, 4, 9, 8, 5}, +{0, 13, 15, 2, 4, 9, 11, 6, 3, 14, 12, 1, 7, 10, 8, 5}, +{0, 15, 13, 2, 4, 11, 9, 6, 3, 12, 14, 1, 7, 8, 10, 5}, +{0, 10, 15, 5, 7, 13, 8, 2, 1, 11, 14, 4, 6, 12, 9, 3}, +{0, 11, 14, 5, 7, 12, 9, 2, 1, 10, 15, 4, 6, 13, 8, 3}, +{0, 11, 15, 4, 6, 13, 9, 2, 1, 10, 14, 5, 7, 12, 8, 3}, +{0, 14, 11, 5, 7, 9, 12, 2, 1, 15, 10, 4, 6, 8, 13, 3}, +{0, 15, 10, 5, 7, 8, 13, 2, 1, 14, 11, 4, 6, 9, 12, 3}, +{0, 15, 11, 4, 6, 9, 13, 2, 1, 14, 10, 5, 7, 8, 12, 3}, +{0, 9, 11, 2, 7, 14, 12, 5, 3, 10, 8, 1, 4, 13, 15, 6}, +{0, 11, 9, 2, 7, 12, 14, 5, 3, 8, 10, 1, 4, 15, 13, 6}, +{0, 6, 13, 11, 3, 5, 14, 8, 7, 1, 10, 12, 4, 2, 9, 15}, +{0, 5, 13, 8, 3, 6, 14, 11, 7, 2, 10, 15, 4, 1, 9, 12}, +{0, 1, 15, 14, 3, 2, 12, 13, 7, 6, 8, 9, 4, 5, 11, 10}, +{0, 3, 15, 12, 1, 2, 14, 13, 7, 4, 8, 11, 6, 5, 9, 10}, +{0, 5, 11, 14, 3, 6, 8, 13, 7, 2, 12, 9, 4, 1, 15, 10}, +{0, 6, 13, 11, 7, 1, 10, 12, 3, 5, 14, 8, 4, 2, 9, 15}, +{0, 5, 13, 8, 7, 2, 10, 15, 3, 6, 14, 11, 4, 1, 9, 12}, +{0, 7, 14, 9, 5, 2, 11, 12, 3, 4, 13, 10, 6, 1, 8, 15}, +{0, 7, 13, 10, 5, 2, 8, 15, 3, 4, 14, 9, 6, 1, 11, 12}, +{0, 1, 15, 14, 7, 6, 8, 9, 3, 2, 12, 13, 4, 5, 11, 10}, +{0, 3, 15, 12, 7, 4, 8, 11, 1, 2, 14, 13, 6, 5, 9, 10}, +{0, 5, 11, 14, 7, 2, 12, 9, 3, 6, 8, 13, 4, 1, 15, 10}, +{0, 7, 11, 12, 5, 2, 14, 9, 3, 4, 8, 15, 6, 1, 13, 10}, +{0, 7, 15, 8, 1, 6, 14, 9, 3, 4, 12, 11, 2, 5, 13, 10}, +{0, 7, 15, 8, 3, 4, 12, 11, 1, 6, 14, 9, 2, 5, 13, 10}, +{0, 14, 5, 11, 3, 13, 6, 8, 7, 9, 2, 12, 4, 10, 1, 15}, +{0, 13, 5, 8, 3, 14, 6, 11, 7, 10, 2, 15, 4, 9, 1, 12}, +{0, 11, 5, 14, 3, 8, 6, 13, 7, 12, 2, 9, 4, 15, 1, 10}, +{0, 15, 1, 14, 3, 12, 2, 13, 7, 8, 6, 9, 4, 11, 5, 10}, +{0, 15, 3, 12, 1, 14, 2, 13, 7, 8, 4, 11, 6, 9, 5, 10}, +{0, 14, 5, 11, 7, 9, 2, 12, 3, 13, 6, 8, 4, 10, 1, 15}, +{0, 13, 5, 8, 7, 10, 2, 15, 3, 14, 6, 11, 4, 9, 1, 12}, +{0, 14, 7, 9, 5, 11, 2, 12, 3, 13, 4, 10, 6, 8, 1, 15}, +{0, 13, 7, 10, 5, 8, 2, 15, 3, 14, 4, 9, 6, 11, 1, 12}, +{0, 11, 5, 14, 7, 12, 2, 9, 3, 8, 6, 13, 4, 15, 1, 10}, +{0, 11, 7, 12, 5, 14, 2, 9, 3, 8, 4, 15, 6, 13, 1, 10}, +{0, 15, 1, 14, 7, 8, 6, 9, 3, 12, 2, 13, 4, 11, 5, 10}, +{0, 15, 3, 12, 7, 8, 4, 11, 1, 14, 2, 13, 6, 9, 5, 10}, +{0, 15, 7, 8, 1, 14, 6, 9, 3, 12, 4, 11, 2, 13, 5, 10}, +{0, 15, 7, 8, 3, 12, 4, 11, 1, 14, 6, 9, 2, 13, 5, 10}, +{0, 10, 9, 3, 7, 13, 14, 4, 15, 5, 6, 12, 8, 2, 1, 11}, +{0, 1, 11, 10, 7, 6, 12, 13, 15, 14, 4, 5, 8, 9, 3, 2}, +{0, 3, 9, 10, 7, 4, 14, 13, 15, 12, 6, 5, 8, 11, 1, 2}, +{0, 6, 13, 11, 3, 5, 14, 8, 15, 9, 2, 4, 12, 10, 1, 7}, +{0, 5, 13, 8, 3, 6, 14, 11, 15, 10, 2, 7, 12, 9, 1, 4}, +{0, 9, 3, 10, 7, 14, 4, 13, 15, 6, 12, 5, 8, 1, 11, 2}, +{0, 11, 1, 10, 7, 12, 6, 13, 15, 4, 14, 5, 8, 3, 9, 2}, +{0, 14, 5, 11, 3, 13, 6, 8, 15, 1, 10, 4, 12, 2, 9, 7}, +{0, 13, 5, 8, 3, 14, 6, 11, 15, 2, 10, 7, 12, 1, 9, 4}, +{0, 9, 7, 14, 3, 10, 4, 13, 15, 6, 8, 1, 12, 5, 11, 2}, +{0, 11, 5, 14, 3, 8, 6, 13, 15, 4, 10, 1, 12, 7, 9, 2}, +{0, 11, 7, 12, 1, 10, 6, 13, 15, 4, 8, 3, 14, 5, 9, 2}, +{0, 5, 3, 6, 7, 2, 4, 1, 15, 10, 12, 9, 8, 13, 11, 14}, +{0, 5, 7, 2, 3, 6, 4, 1, 15, 10, 8, 13, 12, 9, 11, 14}, +{0, 7, 5, 2, 3, 4, 6, 1, 15, 8, 10, 13, 12, 11, 9, 14}, +{0, 10, 9, 3, 15, 5, 6, 12, 7, 13, 14, 4, 8, 2, 1, 11}, +{0, 9, 13, 4, 11, 2, 6, 15, 7, 14, 10, 3, 12, 5, 1, 8}, +{0, 10, 15, 5, 9, 3, 6, 12, 7, 13, 8, 2, 14, 4, 1, 11}, +{0, 13, 9, 4, 11, 6, 2, 15, 7, 10, 14, 3, 12, 1, 5, 8}, +{0, 15, 10, 5, 9, 6, 3, 12, 7, 8, 13, 2, 14, 1, 4, 11}, +{0, 13, 11, 6, 9, 4, 2, 15, 7, 10, 12, 1, 14, 3, 5, 8}, +{0, 1, 11, 10, 15, 14, 4, 5, 7, 6, 12, 13, 8, 9, 3, 2}, +{0, 3, 9, 10, 15, 12, 6, 5, 7, 4, 14, 13, 8, 11, 1, 2}, +{0, 5, 13, 8, 11, 14, 6, 3, 7, 2, 10, 15, 12, 9, 1, 4}, +{0, 1, 15, 14, 11, 10, 4, 5, 7, 6, 8, 9, 12, 13, 3, 2}, +{0, 3, 13, 14, 11, 8, 6, 5, 7, 4, 10, 9, 12, 15, 1, 2}, +{0, 3, 15, 12, 9, 10, 6, 5, 7, 4, 8, 11, 14, 13, 1, 2}, +{0, 6, 13, 11, 15, 9, 2, 4, 3, 5, 14, 8, 12, 10, 1, 7}, +{0, 5, 13, 8, 15, 10, 2, 7, 3, 6, 14, 11, 12, 9, 1, 4}, +{0, 6, 15, 9, 13, 11, 2, 4, 3, 5, 12, 10, 14, 8, 1, 7}, +{0, 5, 15, 10, 13, 8, 2, 7, 3, 6, 12, 9, 14, 11, 1, 4}, +{0, 9, 3, 10, 15, 6, 12, 5, 7, 14, 4, 13, 8, 1, 11, 2}, +{0, 11, 1, 10, 15, 4, 14, 5, 7, 12, 6, 13, 8, 3, 9, 2}, +{0, 13, 5, 8, 11, 6, 14, 3, 7, 10, 2, 15, 12, 1, 9, 4}, +{0, 13, 3, 14, 11, 6, 8, 5, 7, 10, 4, 9, 12, 1, 15, 2}, +{0, 15, 1, 14, 11, 4, 10, 5, 7, 8, 6, 9, 12, 3, 13, 2}, +{0, 15, 3, 12, 9, 6, 10, 5, 7, 8, 4, 11, 14, 1, 13, 2}, +{0, 14, 5, 11, 15, 1, 10, 4, 3, 13, 6, 8, 12, 2, 9, 7}, +{0, 13, 5, 8, 15, 2, 10, 7, 3, 14, 6, 11, 12, 1, 9, 4}, +{0, 15, 6, 9, 13, 2, 11, 4, 3, 12, 5, 10, 14, 1, 8, 7}, +{0, 15, 5, 10, 13, 2, 8, 7, 3, 12, 6, 9, 14, 1, 11, 4}, +{0, 9, 7, 14, 15, 6, 8, 1, 3, 10, 4, 13, 12, 5, 11, 2}, +{0, 11, 5, 14, 15, 4, 10, 1, 3, 8, 6, 13, 12, 7, 9, 2}, +{0, 11, 7, 12, 13, 6, 10, 1, 3, 8, 4, 15, 14, 5, 9, 2}, +{0, 11, 7, 12, 15, 4, 8, 3, 1, 10, 6, 13, 14, 5, 9, 2}, +{0, 5, 3, 6, 15, 10, 12, 9, 7, 2, 4, 1, 8, 13, 11, 14}, +{0, 5, 7, 2, 15, 10, 8, 13, 3, 6, 4, 1, 12, 9, 11, 14}, +{0, 7, 5, 2, 15, 8, 10, 13, 3, 4, 6, 1, 12, 11, 9, 14}, +{0, 9, 15, 6, 3, 10, 12, 5, 7, 14, 8, 1, 4, 13, 11, 2}, +{0, 11, 13, 6, 3, 8, 14, 5, 7, 12, 10, 1, 4, 15, 9, 2}, +{0, 11, 15, 4, 1, 10, 14, 5, 7, 12, 8, 3, 6, 13, 9, 2}, +{0, 13, 11, 6, 3, 14, 8, 5, 7, 10, 12, 1, 4, 9, 15, 2}, +{0, 15, 9, 6, 3, 12, 10, 5, 7, 8, 14, 1, 4, 11, 13, 2}, +{0, 15, 11, 4, 1, 14, 10, 5, 7, 8, 12, 3, 6, 9, 13, 2}, +{0, 14, 15, 1, 5, 11, 10, 4, 3, 13, 12, 2, 6, 8, 9, 7}, +{0, 15, 14, 1, 5, 10, 11, 4, 3, 12, 13, 2, 6, 9, 8, 7}, +{0, 13, 15, 2, 5, 8, 10, 7, 3, 14, 12, 1, 6, 11, 9, 4}, +{0, 15, 13, 2, 5, 10, 8, 7, 3, 12, 14, 1, 6, 9, 11, 4}, +{0, 9, 15, 6, 7, 14, 8, 1, 3, 10, 12, 5, 4, 13, 11, 2}, +{0, 11, 13, 6, 7, 12, 10, 1, 3, 8, 14, 5, 4, 15, 9, 2}, +{0, 11, 15, 4, 5, 14, 10, 1, 3, 8, 12, 7, 6, 13, 9, 2}, +{0, 11, 15, 4, 7, 12, 8, 3, 1, 10, 14, 5, 6, 13, 9, 2}, +{0, 13, 11, 6, 7, 10, 12, 1, 3, 14, 8, 5, 4, 9, 15, 2}, +{0, 15, 9, 6, 7, 8, 14, 1, 3, 12, 10, 5, 4, 11, 13, 2}, +{0, 15, 11, 4, 5, 10, 14, 1, 3, 12, 8, 7, 6, 9, 13, 2}, +{0, 15, 11, 4, 7, 8, 12, 3, 1, 14, 10, 5, 6, 9, 13, 2}, +{0, 5, 15, 10, 3, 6, 12, 9, 7, 2, 8, 13, 4, 1, 11, 14}, +{0, 5, 15, 10, 7, 2, 8, 13, 3, 6, 12, 9, 4, 1, 11, 14}, +{0, 7, 15, 8, 5, 2, 10, 13, 3, 4, 12, 11, 6, 1, 9, 14}, +{0, 15, 5, 10, 3, 12, 6, 9, 7, 8, 2, 13, 4, 11, 1, 14}, +{0, 15, 5, 10, 7, 8, 2, 13, 3, 12, 6, 9, 4, 11, 1, 14}, +{0, 15, 7, 8, 5, 10, 2, 13, 3, 12, 4, 11, 6, 9, 1, 14}, +{0, 9, 11, 2, 7, 14, 12, 5, 15, 6, 4, 13, 8, 1, 3, 10}, +{0, 11, 9, 2, 7, 12, 14, 5, 15, 4, 6, 13, 8, 3, 1, 10}, +{0, 5, 11, 14, 7, 2, 12, 9, 15, 10, 4, 1, 8, 13, 3, 6}, +{0, 7, 13, 10, 3, 4, 14, 9, 15, 8, 2, 5, 12, 11, 1, 6}, +{0, 13, 3, 14, 7, 10, 4, 9, 15, 2, 12, 1, 8, 5, 11, 6}, +{0, 13, 7, 10, 3, 14, 4, 9, 15, 2, 8, 5, 12, 1, 11, 6}, +{0, 9, 11, 2, 15, 6, 4, 13, 7, 14, 12, 5, 8, 1, 3, 10}, +{0, 11, 9, 2, 15, 4, 6, 13, 7, 12, 14, 5, 8, 3, 1, 10}, +{0, 14, 13, 3, 11, 5, 6, 8, 7, 9, 10, 4, 12, 2, 1, 15}, +{0, 9, 15, 6, 11, 2, 4, 13, 7, 14, 8, 1, 12, 5, 3, 10}, +{0, 11, 15, 4, 9, 2, 6, 13, 7, 12, 8, 3, 14, 5, 1, 10}, +{0, 15, 9, 6, 11, 4, 2, 13, 7, 8, 14, 1, 12, 3, 5, 10}, +{0, 15, 11, 4, 9, 6, 2, 13, 7, 8, 12, 3, 14, 1, 5, 10}, +{0, 5, 11, 14, 15, 10, 4, 1, 7, 2, 12, 9, 8, 13, 3, 6}, +{0, 5, 15, 10, 11, 14, 4, 1, 7, 2, 8, 13, 12, 9, 3, 6}, +{0, 7, 13, 10, 15, 8, 2, 5, 3, 4, 14, 9, 12, 11, 1, 6}, +{0, 7, 15, 8, 13, 10, 2, 5, 3, 4, 12, 11, 14, 9, 1, 6}, +{0, 13, 3, 14, 15, 2, 12, 1, 7, 10, 4, 9, 8, 5, 11, 6}, +{0, 15, 5, 10, 11, 4, 14, 1, 7, 8, 2, 13, 12, 3, 9, 6}, +{0, 13, 7, 10, 15, 2, 8, 5, 3, 14, 4, 9, 12, 1, 11, 6}, +{0, 15, 7, 8, 13, 2, 10, 5, 3, 12, 4, 11, 14, 1, 9, 6}, +{0, 13, 15, 2, 3, 14, 12, 1, 7, 10, 8, 5, 4, 9, 11, 6}, +{0, 15, 13, 2, 3, 12, 14, 1, 7, 8, 10, 5, 4, 11, 9, 6}, +{0, 13, 15, 2, 7, 10, 8, 5, 3, 14, 12, 1, 4, 9, 11, 6}, +{0, 15, 13, 2, 7, 8, 10, 5, 3, 12, 14, 1, 4, 11, 9, 6}, +{0, 13, 11, 6, 7, 10, 12, 1, 15, 2, 4, 9, 8, 5, 3, 14}, +{0, 13, 11, 6, 15, 2, 4, 9, 7, 10, 12, 1, 8, 5, 3, 14}, +{0, 13, 15, 2, 11, 6, 4, 9, 7, 10, 8, 5, 12, 1, 3, 14}, +{0, 15, 13, 2, 11, 4, 6, 9, 7, 8, 10, 5, 12, 3, 1, 14}, +}; + +#endif //AFFINEMATRIXROWPEBOX_H__ \ No newline at end of file diff --git a/HadamardMatrix.hpp b/HadamardMatrix.hpp new file mode 100644 index 0000000..1dc0fe4 --- /dev/null +++ b/HadamardMatrix.hpp @@ -0,0 +1,579 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#ifndef HADAMARDMATRIX_H__ +#define HADAMARDMATRIX_H__ + +#include +#include +#include +#include +#include + +using namespace std; + +#define ALIGNED_(x) __attribute__((aligned(x))) +#define ALIGNED_TYPE_(t,x) t ALIGNED_(x) +const ALIGNED_TYPE_(int, 16) H3[1<<3][1<<3] = +{ +{ 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1 }, +}; + +const ALIGNED_TYPE_(int, 16) H4[1<<4][1<<4] = +{ +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1 }, +}; + +const ALIGNED_TYPE_(int, 16) H5[1<<5][1<<5] = +{ +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1 }, +}; + +const ALIGNED_TYPE_(int, 16) H6[1<<6][1<<6] = +{ +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1 }, +}; + +const ALIGNED_TYPE_(int, 16) H7[1<<7][1<<7] = +{ +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1 }, +}; + +const ALIGNED_TYPE_(int, 16) H8[1<<8][1<<8] = +{ +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1 }, +{ 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1 }, +{ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1 }, +{ 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1 }, +{ 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1 }, +{ 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1 }, +{ 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1 }, +{ 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1 }, +}; + +#endif \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fe16837 --- /dev/null +++ b/Makefile @@ -0,0 +1,88 @@ +CC=g++ -fopenmp +CFLAGS= -mavx2 -std=c++14 -O3 -Wall -Wextra -pedantic +LDFLAGS= +EXEC=evaluate evaluate_single search_GC_n3 search_GC_n4 search_depth_n3 search_depth_n4 filter_n3 filter_n4 gen_n3 gen_n4 + +all: $(EXEC) + +evaluate: evaluate.o + $(CC) -o $@ $^ $(LDFLAGS) -DEVA + +evaluate.o: main.cpp + $(CC) -o $@ -c $< $(CFLAGS) -DEVA + +evaluate_single: evaluate_single.o + $(CC) -o $@ $^ $(LDFLAGS) -DEVA_Single + +evaluate_single.o: main.cpp + $(CC) -o $@ -c $< $(CFLAGS) -DEVA_Single + +search_GC_n3: search_GC_n3.o + $(CC) -o $@ $^ $(LDFLAGS) -DGC -DSIZEINBIT=3 + +search_GC_n3.o: main.cpp + $(CC) -o $@ -c $< $(CFLAGS) -DGC -DSIZEINBIT=3 + +search_GC_n4: search_GC_n4.o + $(CC) -o $@ $^ $(LDFLAGS) -DGC -DSIZEINBIT=4 + +search_GC_n4.o: main.cpp + $(CC) -o $@ -c $< $(CFLAGS) -DGC -DSIZEINBIT=4 + + +search_depth_n3: search_depth_n3.o + $(CC) -o $@ $^ $(LDFLAGS) -DDEPTH -DSIZEINBIT=3 + +search_depth_n3.o: main.cpp + $(CC) -o $@ -c $< $(CFLAGS) -DDEPTH -DSIZEINBIT=3 + +search_depth_n4: search_depth_n4.o + $(CC) -o $@ $^ $(LDFLAGS) -DDEPTH -DSIZEINBIT=4 + +search_depth_n4.o: main.cpp + $(CC) -o $@ -c $< $(CFLAGS) -DDEPTH -DSIZEINBIT=4 + + +filter_n3: filter_n3.o + $(CC) -o $@ $^ $(LDFLAGS) -DFILTER -DSIZEINBIT=3 + +filter_n3.o: main.cpp + $(CC) -o $@ -c $< $(CFLAGS) -DFILTER -DSIZEINBIT=3 + +filter_n4: filter_n4.o + $(CC) -o $@ $^ $(LDFLAGS) -DFILTER -DSIZEINBIT=4 + +filter_n4.o: main.cpp + $(CC) -o $@ -c $< $(CFLAGS) -DFILTER -DSIZEINBIT=4 + + +gen_n3: gen_n3.o + $(CC) -o $@ $^ $(LDFLAGS) -DGEN -DSIZEINBIT=3 + +gen_n3.o: main.cpp + $(CC) -o $@ -c $< $(CFLAGS) -DGEN -DSIZEINBIT=3 + +gen_n4: gen_n4.o + $(CC) -o $@ $^ $(LDFLAGS) -DGEN -DSIZEINBIT=4 + +gen_n4.o: main.cpp + $(CC) -o $@ -c $< $(CFLAGS) -DGEN -DSIZEINBIT=4 + + +clean: + rm -rf *.o + +mrproper: clean + rm -rf $(EXEC) + rm test_faster + rm test_lighter + +gencheck: + find ./R -maxdepth 1 -name "*.c" -print > results.c + $(CC) $(CFLAGS) genTest.cpp -o genTest && ./genTest + +checkfaster: + $(CC) $(CFLAGS) faster_test.cpp -o test_faster && ./test_faster + +checklighter: + $(CC) $(CFLAGS) faster_test.cpp -o test_lighter && ./test_lighter \ No newline at end of file diff --git a/MakefileSage b/MakefileSage new file mode 100644 index 0000000..7c6a646 --- /dev/null +++ b/MakefileSage @@ -0,0 +1,41 @@ +# To build, rename this file as Makefile +# Compile inside the Sage shell: sage -sh -c make +# Then start Sage: from peigen import Lighter +# : A = Lighter() +# : A.evaluate_4bit() +# Similiar for Faster: from peigen import Faster +CC= g++ +CXXFLAGS= -fopenmp -mavx2 -std=c++14 -O3 -fPIC -fwrapv -fno-strict-aliasing \ + -Wall -Wextra -pedantic -g -pg \ + -I$(SAGE_LOCAL)/include \ + -I$(SAGE_LOCAL)/include/python2.7 +LDFLAGS = -fopenmp -lm -lstdc++ + +OUTPUT_OPTION = -MMD -MP -o $@ +-include $(DEP) + +EXEC = peigen + +SRC = peigen.cpp +OBJ = $(SRC:.cpp=.o) +DEP = $(SRC:.cpp=.d) + +all: peigen.so + +peigen.cpp: peigen.pyx peigen.pxd + cython --cplus $< + +$(OBJ):$(SRC) + +peigen.so: $(OBJ) + $(CC) -shared -pthread -o peigen.so $^ $(LDFLAGS) + +$(EXEC): $(OBJ) + $(CC) $(LDFLAGS) $^ -o $@ + +clean: + rm -f $(OBJ) $(DEP) $(EXEC) + +.PHONY: clean + +-include $(DEP) \ No newline at end of file diff --git a/OptimalSboxes.hpp b/OptimalSboxes.hpp new file mode 100644 index 0000000..cbb017b --- /dev/null +++ b/OptimalSboxes.hpp @@ -0,0 +1,62 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#ifndef GSBOXES_H__ +#define GSBOXES_H__ + +#define OPTIMAL_G4_N 16 + +const ALIGNED_TYPE_(uint8_t, 16) G4[OPTIMAL_G4_N][16] = { +/* Optimal_S0 */ {0, 1, 2, 13, 4, 7, 15, 6, 8, 11, 12, 9, 3, 14, 10, 5}, +/* Optimal_S1 */ {0, 1, 2, 13, 4, 7, 15, 6, 8, 11, 14, 3, 5, 9, 10, 12}, +/* Optimal_S2 */ {0, 1, 2, 13, 4, 7, 15, 6, 8, 11, 14, 3, 10, 12, 5, 9}, +/* Optimal_S3 */ {0, 1, 2, 13, 4, 7, 15, 6, 8, 12, 5, 3, 10, 14, 11, 9}, +/* Optimal_S4 */ {0, 1, 2, 13, 4, 7, 15, 6, 8, 12, 9, 11, 10, 14, 5, 3}, +/* Optimal_S5 */ {0, 1, 2, 13, 4, 7, 15, 6, 8, 12, 11, 9, 10, 14, 3, 5}, +/* Optimal_S6 */ {0, 1, 2, 13, 4, 7, 15, 6, 8, 12, 11, 9, 10, 14, 5, 3}, +/* Optimal_S7 */ {0, 1, 2, 13, 4, 7, 15, 6, 8, 12, 14, 11, 10, 9, 3, 5}, +/* Optimal_S8 */ {0, 1, 2, 13, 4, 7, 15, 6, 8, 14, 9, 5, 10, 11, 3, 12}, +/* Optimal_S9 */ {0, 1, 2, 13, 4, 7, 15, 6, 8, 14, 11, 3, 5, 9, 10, 12}, +/* Optimal_S10 */ {0, 1, 2, 13, 4, 7, 15, 6, 8, 14, 11, 5, 10, 9, 3, 12}, +/* Optimal_S11 */ {0, 1, 2, 13, 4, 7, 15, 6, 8, 14, 11, 10, 5, 9, 12, 3}, +/* Optimal_S12 */ {0, 1, 2, 13, 4, 7, 15, 6, 8, 14, 11, 10, 9, 3, 12, 5}, +/* Optimal_S13 */ {0, 1, 2, 13, 4, 7, 15, 6, 8, 14, 12, 9, 5, 11, 10, 3}, +/* Optimal_S14 */ {0, 1, 2, 13, 4, 7, 15, 6, 8, 14, 12, 11, 3, 9, 5, 10}, +/* Optimal_S15 */ {0, 1, 2, 13, 4, 7, 15, 6, 8, 14, 12, 11, 9, 3, 10, 5} +}; + +#endif //GSBOXES_H__ \ No newline at end of file diff --git a/README.md b/README.md index 77d6ec0..ab9c69d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes +# PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes PEIGEN is a tool for study S-boxes. @@ -145,25 +145,25 @@ Concrete usage are as follows. - same as in [LIGHTER](http://jeremy.jean.free.fr/pub/fse2018_layer_implementations.tar.gz), if `[-a]` is not enabled, available logic gates should be explicitly added in this command line (and weight of each gate should be specified in the provided configure file): --not1 - + --and2 - + --nand2 - + --or2 - + --nor2 - + --nand3 - + --nor3 - + --xor2 - + --xnor2 - + --maoi1 - + --moai1 - use `sboxn_GC.pre_compute(args);` to precompute the graph, this will expand the graph from the Identity function, with parameters encoded in `args`, and store the generated graph in binary files. For each configuration (the library of gates `-f ` and the limitation for precomputation `-c `), this can be done once for all. Thus, if this has been done, the generated binary files are stored and available, we can directly call the search function. @@ -293,13 +293,11 @@ Concrete usage are as follows. - Sorry for the inconvenience again, but for small efficiency gain, bitslicing of the S-boxes is done in little endian byte and little endian bit order (this is inconsistent with that in [LIGHTER](http://jeremy.jean.free.fr/pub/fse2018_layer_implementations.tar.gz)): the least significant value is placed at the leftmost side in memory and, the least significant bit of the value is placed at the leftmost side in the value, e.g., suppose the LUT: LUT in hexadecimal (**big endian byte** order and **little endian bit** order): - | 0x0 | 0x1 | 0x2 | 0x3 | 0x4 | 0x5 | 0x6 | 0x7 | 0x8 | 0x9 | 0xa | 0xb | 0xc | 0xd | 0xe | 0xf | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | 0xc | 0x5 | 0x6 | 0xb | 0x9 | 0x0 | 0xa | 0xd | 0x3 | 0xe | 0xf | 0x8 | 0x4 | 0x7 | 0x1 | 0x2 | LUT in binary (**big endian byte** order and **little endian bit** order): - | 0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 | | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | | 1100 | 0101 | 0110 | 1011 | 1001 | 0000 | 1010 | 1101 | 0011 | 1110 | 1111 | 1000 | 0100 | 0111 | 0001 | 0010 | @@ -307,19 +305,16 @@ Concrete usage are as follows. In PEIGEN, bitslicing is done as follows: LUT in binary in memory (**little endian byte** order and **little endian bit** order): - | 1111 | 1110 | 1101 | 1100 | 1011 | 1010 | 1001 | 1000 | 0111 | 0110 | 0101 | 0100 | 0011 | 0010 | 0001 | 0000 | | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | | 0010 | 0001 | 0111 | 0100 | 1000 | 1111 | 1110 | 0011 | 1101 | 1010 | 0000 | 1001 | 1011 | 0110 | 0101 | 1100 | Bitslicing (**little endian byte** order and **little endian bit** order): - | 1111111100000000 | 1111000011110000 | 1100110011001100 | 1010101010101010 | | ---------------- | ---------------- | ---------------- | ---------------- | | 0000111011011001 | 0011011010000111 | 1010011101001100 | 0110010110011010 | Condensed bitsliced representation (directly indicate memory): - | ff00_f0f0_cccc_aaaa | | ------------------- | | 0ed9_3687_a74c_659a | diff --git a/STM65nm.conf b/STM65nm.conf new file mode 100644 index 0000000..5916d09 --- /dev/null +++ b/STM65nm.conf @@ -0,0 +1,15 @@ +not1_cost=0.5 +and2_cost=1.5 +nand2_cost=1.00 +or2_cost=1.5 +nor2_cost=1.00 +xor2_cost=2.00 +xnor2_cost=2.00 +maoi1_cost=2.50 +moai1_cost=2.50 +nand3_cost=1.5 +nor3_cost=1.5 +and3_cost=2 +or3_cost=2 +andn2_cost=1.5 +orn2_cost=1.5 diff --git a/TSMC65nm.conf b/TSMC65nm.conf new file mode 100644 index 0000000..132589a --- /dev/null +++ b/TSMC65nm.conf @@ -0,0 +1,30 @@ +not1_cost=0.5 +and2_cost=1.5 +nand2_cost=1.00 +or2_cost=1.5 +nor2_cost=1.00 +xor2_cost=3.00 +xnor2_cost=3.00 +maoi1_cost=2.50 +moai1_cost=2.50 +nand3_cost=1.5 +nor3_cost=1.5 +and3_cost=2 +or3_cost=2 +andn2_cost=1.5 +orn2_cost=1.5 +#not1_cost=0.5 +#and2_cost=1.5 +#nand2_cost=1.00 +#or2_cost=1.5 +#nor2_cost=1.00 +#xor2_cost=2.00 +#xnor2_cost=2.00 +#maoi1_cost=2.50 +#moai1_cost=2.50 +#nand3_cost=1.5 +#nor3_cost=1.5 +#and3_cost=2 +#or3_cost=2 +#andn2_cost=1.5 +#orn2_cost=1.5 diff --git a/UMC180nm.conf b/UMC180nm.conf new file mode 100644 index 0000000..e248907 --- /dev/null +++ b/UMC180nm.conf @@ -0,0 +1,15 @@ +not1_cost=0.67 +and2_cost=1.33 +nand2_cost=1.00 +or2_cost=1.33 +nor2_cost=1.00 +xor2_cost=3.00 +xnor2_cost=3.00 +maoi1_cost=2.67 +moai1_cost=2.00 +nand3_cost=1.33 +nor3_cost=1.33 +and3_cost=2.33 +or3_cost=2.33 +andn2_cost=1.67 +orn2_cost=1.67 \ No newline at end of file diff --git a/constants.hpp b/constants.hpp new file mode 100644 index 0000000..02ab0e2 --- /dev/null +++ b/constants.hpp @@ -0,0 +1,1198 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#ifndef CONSTANTS_H__ +#define CONSTANTS_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "omp.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#include "static_sort.h" +#pragma GCC diagnostic pop +#include "HadamardMatrix.hpp" + +using namespace std; + +namespace Peigen +{ + +namespace depth +{ +template class faster; +template struct bool_function_t; +template struct function_t; +struct bool_op_t; +} + +template struct function_t; +template struct evaluator; + +namespace weight +{ +template class lighter; +struct bool_op_t; +} + +} + + +#define NDEBUG +#ifdef NDEBUG +#define PRINT(x) ((void)0) +#else +#define PRINT(x) x +#endif + +//#define NCHECKR +#ifdef NCHECKR +#define CHECKR(x) ((void)0) +#else +#define CHECKR(x) x +#endif +//#define NCHECKAB +#ifdef NCHECKAB +#define CHECK(x) ((void)0) +#else +#define CHECK(x) x +#endif + +#define ALIGNED_(x) __attribute__((aligned(x))) +#define ALIGNED_TYPE_(t,x) t ALIGNED_(x) + +template +using UINT_ = + typename std::conditional::type + >::type + >::type + >::type; + +#define UINT_MAX (numeric_limits >::max()) + +#define FACT_(n) \ + (n == 0 ? 1 : \ + (n == 1 ? 1 : \ + (n == 2 ? 2 : \ + (n == 3 ? 6 : \ + (n == 4 ? 24 : \ + (n == 5 ? 120 : \ + (n == 6 ? 720 : \ + (n == 7 ? 5040 : \ + (n == 8 ? 40320 : \ + (n == 9 ? 362880 : \ + (n == 10 ? 3628800 : \ + (n == 11 ? 39916800 : \ + (n == 12 ? 479001600 : \ + (numeric_limits::max()) \ + ) \ + ) \ + ) \ + ) \ + ) \ + ) \ + ) \ + ) \ + ) \ + ) \ + ) \ + ) \ + ) + +#define C(n, k) (FACT_(n) /(FACT_(k) * FACT_(n - (k)))) + +#define digits(x) \ +{ \ + ((x) < 10 ? 1 : \ + ((x) < 100 ? 2 : \ + ((x) < 1000 ? 3 : \ + ((x) < 10000 ? 4 : \ + ((x) < 100000 ? 5 : \ + ((x) < 1000000 ? 6 : \ + ((x) < 10000000 ? 7 : \ + ((x) < 100000000 ? 8 : \ + ((x) < 1000000000 ? 9 : \ + 10))))))))) \ +} + + +#define UNIT_BIT_N (8*sizeof(UINT_)) +#define UNIT_NIBBLE_N (2*sizeof(UINT_)) +#define BIT_SLICE_BITS_N (1 << N) +#define BIT_SLICE_NIBBLES_N ((1 << N) >> 2 ) +#define BIT_SLICE_BYTES_N ((1 << N) >> 3 ) +#define BIT_SLICE_ULLS_N ((1 << N) >> 6 ) + +#define UNIT_N (BIT_SLICE_BYTES_N/sizeof(UINT_)) + + +#define LUT_UNIT_N (1 << N) +#define LUT_UNIT_NIBBLE_N ((N+3)/4) +#define LUT_NIBBLE_N (LUT_UNIT_NIBBLE_N * LUT_UNIT_N) +#define LUT_UNIT_BYTE_N ((N+7)/8) +#define LUT_BYTE_N (LUT_UNIT_BYTE_N * LUT_UNIT_N) + +#define LUT_ULL_N ((LUT_BYTE_N + 7) / 8) +#define LUT_XMM_N ((LUT_UNIT_N + 15) / 16) + +template +using bit_slice_l_t = std::array, UNIT_N>; + +template +using bit_slice_t = std::array, N>; + +template +using bit_slice_l_PE_t = std::pair, int>; + +template +using bit_slice_PE_t = std::array, N>; + +template +inline bool operator < (const bit_slice_l_PE_t & lhs, const bit_slice_l_PE_t & rhs) +{ + return (lhs.first < rhs.first); +} + +template +inline bool operator == (const bit_slice_l_PE_t & lhs, const bit_slice_l_PE_t & rhs) +{ + return (lhs.first == rhs.first); +} + +template +inline T operator ^ (const T& lhs, const T& rhs) +{ + T tmp; + for(int i = 0; i < lhs.size(); i++) + { + tmp[i] = lhs[i] ^ rhs[i]; + } + return tmp; +} + +template +inline T operator | (const T& lhs, const T& rhs) +{ + T tmp; + for(int i = 0; i < lhs.size(); i++) + { + tmp[i] = lhs[i] | rhs[i]; + } + return tmp; +} + +template +inline T operator & (const T& lhs, const T& rhs) +{ + T tmp; + for(int i = 0; i < lhs.size(); i++) + { + tmp[i] = lhs[i] & rhs[i]; + } + return tmp; +} + +template +inline T operator ~ (const T& rhs) +{ + T tmp; + for(int i = 0; i < rhs.size(); i++) + { + tmp[i] = ~rhs[i]; + } + return tmp; +} + +const __m128i zero_128 = _mm_setzero_si128(); +const __m256i zero_256 = _mm256_setzero_si256(); +const __m128i one_128 = _mm_set_epi64x(0xffffffffffffffffULL, 0xffffffffffffffffULL); + +const __m128i S4_I = _mm_set_epi8(0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0); + +const __m128i x = _mm_set_epi8(0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0); +const __m128i x0 = _mm_set_epi8(0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0); +const __m128i x1 = _mm_set_epi8(0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1); +const __m128i x2 = _mm_set_epi8(0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2); +const __m128i x3 = _mm_set_epi8(0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3); +const __m128i x4 = _mm_set_epi8(0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4); +const __m128i x5 = _mm_set_epi8(0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5); +const __m128i x6 = _mm_set_epi8(0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6); +const __m128i x7 = _mm_set_epi8(0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7); +const __m128i x8 = _mm_set_epi8(0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8); +const __m128i x9 = _mm_set_epi8(0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9); +const __m128i xa = _mm_set_epi8(0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa); +const __m128i xb = _mm_set_epi8(0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb); +const __m128i xc = _mm_set_epi8(0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc); +const __m128i xd = _mm_set_epi8(0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd); +const __m128i xe = _mm_set_epi8(0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe); +const __m128i xf = _mm_set_epi8(0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf); + +const __m128i S3_valv_mask8 = _mm_set_epi64x(0x0ULL, 0xffffffULL); +const __m128i S3_valv_mask4 = _mm_set_epi64x(0x0ULL, 0xf0f0f0ULL); +const __m128i S3_valv_mask2 = _mm_set_epi64x(0x0ULL, 0xccccccULL); +const __m128i S3_valv_mask1 = _mm_set_epi64x(0x0ULL, 0xaaaaaaULL); + +const __m128i S3_comps_ind_mask = _mm_set_epi64x(0x0ULL, 0x8080808080808080ULL); +const __m128i S3_comps_deg_mask3 = _mm_set_epi64x(0x0ULL, 0x8080808080808080ULL); // 1000 0000 +const __m128i S3_comps_deg_mask2 = _mm_set_epi64x(0x0ULL, 0x6868686868686868ULL); // 0110 1000 +const __m128i S3_comps_deg_mask1 = _mm_set_epi64x(0x0ULL, 0x1616161616161616ULL); // 0001 0110 + +const __m128i S3_coors_ind_mask = _mm_set_epi64x(0x0ULL, 0x808080ULL); +const __m128i S3_coors_deg_mask3 = _mm_set_epi64x(0x0ULL, 0x808080ULL); // 1000 0000 +const __m128i S3_coors_deg_mask2 = _mm_set_epi64x(0x0ULL, 0x686868ULL); // 0110 1000 +const __m128i S3_coors_deg_mask1 = _mm_set_epi64x(0x0ULL, 0x161616ULL); // 0001 0110 + +const __m128i S4_valv_maskg = _mm_set_epi64x(0x0ULL, 0xffffffffffffffffULL); +const __m128i S4_valv_mask8 = _mm_set_epi64x(0x0ULL, 0xff00ff00ff00ff00ULL); +const __m128i S4_valv_mask4 = _mm_set_epi64x(0x0ULL, 0xf0f0f0f0f0f0f0f0ULL); +const __m128i S4_valv_mask2 = _mm_set_epi64x(0x0ULL, 0xccccccccccccccccULL); +const __m128i S4_valv_mask1 = _mm_set_epi64x(0x0ULL, 0xaaaaaaaaaaaaaaaaULL); + +const __m256i S4_comps_ind_mask = _mm256_set_epi64x(0x8000800080008000ULL, 0x8000800080008000ULL, 0x8000800080008000ULL, 0x8000800080008000ULL); // fedc ba98 7654 3210 +const __m256i S4_comps_deg_mask4 = _mm256_set_epi64x(0x8000800080008000ULL, 0x8000800080008000ULL, 0x8000800080008000ULL, 0x8000800080008000ULL); // 1000 0000 0000 0000 +const __m256i S4_comps_deg_mask3 = _mm256_set_epi64x(0x6880688068806880ULL, 0x6880688068806880ULL, 0x6880688068806880ULL, 0x6880688068806880ULL); // 0110 1000 1000 0000 +const __m256i S4_comps_deg_mask2 = _mm256_set_epi64x(0x1668166816681668ULL, 0x1668166816681668ULL, 0x1668166816681668ULL, 0x1668166816681668ULL); // 0001 0110 0110 1000 +const __m256i S4_comps_deg_mask1 = _mm256_set_epi64x(0x0116011601160116ULL, 0x0116011601160116ULL, 0x0116011601160116ULL, 0x0116011601160116ULL); // 0000 0001 0001 0110 + +const __m128i S4_coors_ind_mask = _mm_set_epi64x(0x0ULL, 0x8000800080008000ULL); // fedc ba98 7654 3210 +const __m128i S4_coors_deg_mask4 = _mm_set_epi64x(0x0ULL, 0x8000800080008000ULL); // 1000 0000 0000 0000 +const __m128i S4_coors_deg_mask3 = _mm_set_epi64x(0x0ULL, 0x6880688068806880ULL); // 0110 1000 1000 0000 +const __m128i S4_coors_deg_mask2 = _mm_set_epi64x(0x0ULL, 0x1668166816681668ULL); // 0001 0110 0110 1000 +const __m128i S4_coors_deg_mask1 = _mm_set_epi64x(0x0ULL, 0x0116011601160116ULL); // 0000 0001 0001 0110 + +template +const array HWorder_off = [&] +{ + array aHWorder_off; + aHWorder_off[0] = 0; + for (int i = 1; i <= N; i++) + { + aHWorder_off[i] = aHWorder_off[i - 1] + C(N, i-1); + } + return aHWorder_off; +}(); + +template +const array HWorder = [&] +{ + array aHWorder; + int offset[N + 1] = { 0 }; + offset[0] = 0; + for (int i = 1; i <= N; i++) + { + offset[i] = offset[i - 1] + C(N, i-1); + } + for (int i = 0; i < (1< +struct OPs_t +{ + bit_slice_l_t sll (const bit_slice_l_t & lhs, const int offsite) const + { + bit_slice_l_t res = {{}}; + + UINT_ inter_offsite = (UINT_)offsite / (UINT_)UNIT_BIT_N; + + UINT_ * src_inter_right = (UINT_ *)lhs.data(); + UINT_ * dst_inter_right = (UINT_ *)res.data(); + UINT_ * dst_inter_left = dst_inter_right + inter_offsite; + + memcpy(dst_inter_left, src_inter_right, (UNIT_N - inter_offsite)*sizeof(UINT_)); + + UINT_ intra_offsite = (UINT_)offsite % (UINT_)UNIT_BIT_N; + + if (intra_offsite != 0) + { + bit_slice_l_t left = {{}}; + bit_slice_l_t right = {{}}; + + UINT_ * src_intra = (UINT_ *)res.data(); + UINT_ * dst_intra_right = (UINT_ *)right.data(); + UINT_ * dst_intra_left = (UINT_ *)left.data(); + for (int i = 0; i < UNIT_N - 1; i++) + { + dst_intra_right[i+1] = src_intra[i] >> ((UINT_)UNIT_BIT_N - intra_offsite); + } + dst_intra_right[0] = 0; + for (int i = 0; i < UNIT_N; i++) + { + dst_intra_left[i] = src_intra[i] << intra_offsite; + src_intra[i] = dst_intra_left[i] | dst_intra_right[i]; + } + } + return res; + } + + bit_slice_l_t srl (const bit_slice_l_t & lhs, const int offsite) const + { + bit_slice_l_t res = {{}}; + + UINT_ inter_offsite = (UINT_)offsite / (UINT_)UNIT_BIT_N; + + UINT_ * src_inter_right = (UINT_ *)lhs.data(); + UINT_ * src_inter_left = src_inter_right + inter_offsite; + UINT_ * dst_inter_right = (UINT_ *)res.data(); + + memcpy(dst_inter_right, src_inter_left, (UNIT_N - inter_offsite)*sizeof(UINT_)); + + UINT_ intra_offsite = (UINT_)offsite % (UINT_)UNIT_BIT_N; + + if (intra_offsite != 0) + { + bit_slice_l_t left = {{}}; + bit_slice_l_t right = {{}}; + + UINT_ * src_intra = (UINT_ *)res.data(); + UINT_ * dst_intra_right = (UINT_ *)right.data(); + UINT_ * dst_intra_left = (UINT_ *)left.data(); + for (int i = 0; i < UNIT_N - 1; i++) + { + dst_intra_left[i] = src_intra[i+1] << ((UINT_)UNIT_BIT_N - intra_offsite); + } + dst_intra_left[UNIT_N - 1] = 0; + for (int i = 0; i < UNIT_N; i++) + { + dst_intra_right[i] = src_intra[i] >> intra_offsite; + src_intra[i] = dst_intra_left[i] | dst_intra_right[i]; + } + } + return res; + } + + bit_slice_l_t rotl (const bit_slice_l_t & lhs, const int offsite) const + { + bit_slice_l_t res = {{}}; + + UINT_ inter_offsite = (UINT_) offsite / (UINT_) UNIT_BIT_N; + + UINT_ * src_inter_right = (UINT_ *)lhs.data(); + UINT_ * src_inter_left = src_inter_right + ((UINT_)UNIT_N - inter_offsite); + UINT_ * dst_inter_right = (UINT_ *)res.data(); + UINT_ * dst_inter_left = dst_inter_right + inter_offsite; + + if (inter_offsite != 0) + { + memcpy(dst_inter_right, src_inter_left, inter_offsite * sizeof(UINT_)); + memcpy(dst_inter_left, src_inter_right, ((UINT_) UNIT_N - inter_offsite)*sizeof(UINT_)); + } + else + { + memcpy(dst_inter_left, src_inter_right, (UINT_)UNIT_N * sizeof(UINT_)); + } + + UINT_ intra_offsite = offsite % UNIT_BIT_N; + + if (intra_offsite != 0) + { + bit_slice_l_t left = {{}}; + bit_slice_l_t right = {{}}; + + UINT_ * src_intra = (UINT_ *)res.data(); + UINT_ * dst_intra_right = (UINT_ *)right.data(); + UINT_ * dst_intra_left = (UINT_ *)left.data(); + for (int i = 0; i < UNIT_N; i++) + { + dst_intra_right[(i+1) % UNIT_N] = src_intra[i] >> ((UINT_) UNIT_BIT_N - intra_offsite); + } + for (int i = 0; i < UNIT_N; i++) + { + dst_intra_left[i] = src_intra[i] << intra_offsite; + src_intra[i] = dst_intra_left[i] | dst_intra_right[i]; + } + } + return res; + } + + int get_bit(const bit_slice_l_t & lhs, const int offsite) const + { + int bit; + UINT_ * src = (UINT_ *)lhs.data(); + bit = (src[(UINT_) offsite / (UINT_) UNIT_BIT_N] >> ((UINT_) offsite % (UINT_) UNIT_BIT_N)) & 1; + return bit; + } + + bit_slice_l_t set_bit(const bit_slice_l_t & lhs, const int offsite) const + { + bit_slice_l_t res = lhs; + UINT_ inter_offsite = (UINT_)offsite / (UINT_)UNIT_BIT_N; + UINT_ intra_offsite = (UINT_)offsite % (UINT_)UNIT_BIT_N; + res[inter_offsite] |= ((UINT_)1 << intra_offsite); + return res; + } + + void set_bit_inplace(bit_slice_l_t & lhs, const int offsite) const + { + UINT_ inter_offsite = (UINT_)offsite / (UINT_)UNIT_BIT_N; + UINT_ intra_offsite = (UINT_)offsite % (UINT_)UNIT_BIT_N; + lhs[inter_offsite] |= ((UINT_)1 << intra_offsite); + } + + bit_slice_l_t unset_bit(const bit_slice_l_t & lhs, const int offsite) const + { + bit_slice_l_t res = lhs; + UINT_ inter_offsite = (UINT_)offsite / (UINT_)UNIT_BIT_N; + UINT_ intra_offsite = (UINT_)offsite % (UINT_)UNIT_BIT_N; + res[inter_offsite] &= ~((UINT_)1 << intra_offsite); + return res; + } + + void unset_bit_inplace(bit_slice_l_t & lhs, const int offsite) const + { + UINT_ inter_offsite = (UINT_)offsite / (UINT_)UNIT_BIT_N; + UINT_ intra_offsite = (UINT_)offsite % (UINT_)UNIT_BIT_N; + lhs[inter_offsite] &= ~((UINT_)1 << intra_offsite); + } + + bit_slice_l_t set_zero() const + { + bit_slice_l_t res = { {0} }; + return res; + } + + void set_zero_inplace(bit_slice_l_t & lhs) const + { + lhs = lhs ^ lhs; + } + + bit_slice_l_t set_one() const + { + bit_slice_l_t res = { {0} }; + + UINT_ inter_offsite = (UINT_)BIT_SLICE_BITS_N / (UINT_)UNIT_BIT_N; + for (int i = 0; i < inter_offsite; i++) + { + res[i] = numeric_limits >::max(); + } + return res; + } + + void set_one_inplace(bit_slice_l_t & lhs) const + { + UINT_ inter_offsite = (UINT_)BIT_SLICE_BITS_N / (UINT_)UNIT_BIT_N; + for (int i = 0; i < inter_offsite; i++) + { + lhs[i] = numeric_limits >::max(); + } + } + + int lsb_idx(bit_slice_l_t & lhs) const + { + if (lhs == set_zero()) return -1; + + int base = 0; + + for (int i = 0; i < UNIT_N; i++) + { + if (lhs[i] != 0) return base + __builtin_ffsll(lhs[i]) - 1; + base += UNIT_BIT_N; + } + cout << "Should not run into here:" << __LINE__ << endl; + return -1; + } + + int unset_lsb_idx_inplace(bit_slice_l_t & lhs) const + { + if (lhs == set_zero()) return -1; + int base = 0; + for (int i = 0; i < UNIT_N; i++) + { + if (lhs[i] != 0) + { + int offsite = __builtin_ffsll(lhs[i]) - 1; + lhs[i] &= ~((UINT_)1 << (UINT_)offsite); + return base + offsite; + } + base += UNIT_BIT_N; + } + cout << "Should not run into here:" << __LINE__ << endl; + return -1; + } + + int next_lsb_idx(const bit_slice_l_t & lhs, int idx) const + { + if (idx == (LUT_UNIT_N-1)) return -1; + idx = idx + 1; + bit_slice_l_t tmp = lhs; + UINT_ inter_offsite = (UINT_)idx / (UINT_)UNIT_BIT_N; + UINT_ intra_offsite = (UINT_)idx % (UINT_)UNIT_BIT_N; + for (int i = 0; i < inter_offsite; i++) + { + tmp[i] = 0; + } + if (intra_offsite != 0) + { + tmp[inter_offsite] = tmp[inter_offsite] & (~(((UINT_)1 << intra_offsite) - (UINT_)1)); + } + + return lsb_idx(tmp); + } + + int cnt_1(const bit_slice_l_t & lhs) const + { + int sum = 0; + if (UNIT_BIT_N <= 64) + { + sum = _mm_popcnt_u64((uint64_t)lhs[0]); + return sum; + } + for (int i = 0; i < UNIT_N; i++) + { + sum += _mm_popcnt_u64((uint64_t)lhs[i]); + } + return sum; + } + + int rank(set > X) + { + set > X1 = X; + set > X2; + while (X1.size() != 0) + { + int maxx = *(X1.begin()); + X1.erase(maxx); + X2.insert(maxx); + if (X1.size() == 0) break; + int b1 = __builtin_clz(maxx); + set > X1t; + for (auto xr : X1) + { + int b2 = __builtin_clz(xr); + uint8_t xn = (b1 == b2) ? (xr ^ maxx) : xr; + X1t.insert(xn); + } + X1 = X1t; + X1t.clear(); + } + // cout << "X2 = {" << hex; + // for (auto nx : X2) cout << nx + '\0' << "," << endl; + // cout << "}" << dec << endl; + return X2.size(); + } + + void gen_subspaces(vector > > & subspaces) + { + #define N0 N + #define Ci(x) (C0[x+1]-1) + + int A0[N0 + 1], T0[N0 + 1], F0[N0 + 1], H0[N0 + 1], C0[N0 + 1], X0, Y0, I0, L0, Z0; + int M0; + + array, N> m; + vector > free_positions; + + vector > newdim_subspaces; + vector anew_subspace; + + for (int dim = 1; dim < N; dim++) + { + newdim_subspaces.clear(); + + M0 = dim; + + for (int i=0; i<=(N0-M0); i++) A0[i] = 0; for (int i=N0-M0+1; i<=N0; i++) A0[i] = 1; + for (int i = 1; i<=M0; i++) { C0[i] = N0 - M0 + i; H0[N0-M0+i] = i; } + T0[N0-M0] = -1; T0[1] = 0; F0[N0] = N0 - M0 + 1; I0 = N0 - M0; L0 = N0; + + do { + for (int i = 0; i < dim; i++) m[i].fill(0); + free_positions.clear(); + for (int i = 0; i < dim; i++) + { + m[i][Ci(i)] = 1; + for (int j = Ci(i) + 1; j < N; j++) + { + if (A0[j+1] != 1) + { + free_positions.push_back(pair(i, j)); + } + } + } + + int num_free_pos = free_positions.size(); + for (uint64_t v = 0; v < (1<> (uint64_t)fi) & 1ULL); + } + anew_subspace.clear(); + for (int ri = 0; ri < dim; ri++) + { + uint8_t vec = 0; + for (int ci = 0; ci < N; ci++) vec |= (m[ri][ci] << ci); + anew_subspace.push_back(vec); + } + anew_subspace.shrink_to_fit(); + newdim_subspaces.push_back(anew_subspace); + } + + if (I0 == 0) + { + break; + } + else + { + if (T0[I0] < 0) { if ((-T0[I0]) != (I0-1)){ T0[I0-1] = T0[I0]; } T0[I0] = I0-1; } + if ( A0[I0]==0 ) + { + X0 = I0; Y0 = F0[L0]; if (A0[I0-1] == 1){ F0[I0] = F0[I0 - 1]; } else { F0[I0] = I0; } + if (F0[L0] == L0) { L0 = I0; I0 = T0[I0]; goto CHANGE; } + if (L0 == N0) { T0[F0[N0]] = -I0 - 1; T0[I0 + 1] = T0[I0]; I0 = F0[N0]; F0[N0] = F0[N0] + 1; goto CHANGE; } + T0[L0] = -I0-1; T0[I0+1] = T0[I0]; F0[L0] = F0[L0] + 1; I0 = L0; goto CHANGE; + } + Y0 = I0; + if (I0 != L0) + { + F0[L0] = X0 = F0[L0] - 1; F0[I0 - 1] = F0[I0]; + if (L0 == N0) + { + if (I0 == (F0[N0] - 1)) { I0 = T0[I0]; goto CHANGE; } + T0[F0[N0]-1] = -I0-1; T0[I0+1] = T0[I0]; I0 = F0[N0] - 1; goto CHANGE; + } + T0[L0] = -I0 -1; T0[I0 + 1] = T0[I0]; I0 = L0; goto CHANGE; + } + X0 = N0; F0[L0 - 1] = F0[L0]; F0[N0] = N0; L0 = N0; + if (I0 == N0 - 1) { I0 = T0[N0 - 1]; goto CHANGE; } + T0[N0 - 1] = -I0 - 1; T0[I0 + 1] = T0[I0]; I0 = N0 - 1; +CHANGE: + A0[X0] = 1; A0[Y0] = 0; H0[X0] = Z0 = H0[Y0]; C0[Z0] = X0; + } + } while (true); + + newdim_subspaces.shrink_to_fit(); + subspaces.push_back(newdim_subspaces); + } + #undef Ci + #undef N0 + } + + void show_subspaces(ofstream &fout) + { + fout << hex << setfill('0'); + vector > > subspaces; + gen_subspaces(subspaces); + for (auto newdim_subspaces: subspaces) + { + fout << "{" << endl; + int i = 0; + for (auto new_subspaces: newdim_subspaces) + { + fout << "{"; + for (auto new_base: new_subspaces) + { + fout << "0x" << setw(2) << new_base + '\0' << ","; + } + fout << "}, "; + if ((i + 1) % 10 == 0) fout << endl; + } + fout << endl << "}," << endl; + } + } + + Peigen::function_t composite(const uint8_t sb2[], const uint8_t sb1[]) + { + Peigen::function_t func; + + __m128i xmmsb1[LUT_XMM_N]; + __m128i xmmsb2[LUT_XMM_N]; + __m128i xmmsb3[LUT_XMM_N]; + + if (N == 3) + { + xmmsb2[0] = _mm_load_si128((__m128i *)(sb2)); + xmmsb1[0] = _mm_load_si128((__m128i *)(sb1)); + + __m128i mask = _mm_set_epi64x(0x0, 0xffffffffffffffffULL); + xmmsb2[0] = _mm_and_si128(xmmsb2[0], mask); + xmmsb1[0] = _mm_and_si128(xmmsb1[0], mask); + xmmsb3[0] = _mm_shuffle_epi8(xmmsb2[0], xmmsb1[0]); + } + else if (N == 4) + { + xmmsb2[0] = _mm_load_si128((__m128i *)(sb2)); + xmmsb1[0] = _mm_load_si128((__m128i *)(sb1)); + + xmmsb3[0] = _mm_shuffle_epi8(xmmsb2[0], xmmsb1[0]); + } + else + { + uint8_t sb3[LUT_UNIT_N]; + for (int i = 0; i < LUT_UNIT_N; i++) + { + sb3[i] = sb2[sb1[i]]; + } + for (int i = 0; i < LUT_XMM_N; i++) + { + xmmsb3[i] = _mm_loadu_si128((__m128i *)(sb3 + i * 16)); + } + } + + func.bit_slice = bit_slice(xmmsb3); + + return func; + } + + void composite(__m128i dst_xmm[LUT_XMM_N], const __m128i src_xmm2[LUT_XMM_N], const __m128i src_xmm1[LUT_XMM_N]) + { + if ((N == 3) || (N == 4)) + { + dst_xmm[0] = _mm_shuffle_epi8(src_xmm2[0], src_xmm1[0]); + } + else if (N <= 8) + { + uint8_t * sb3 = (uint8_t *) dst_xmm; + uint8_t * sb2 = (uint8_t *) src_xmm2; + uint8_t * sb1 = (uint8_t *) src_xmm1; + for (int i = 0; i < LUT_UNIT_N; i++) + { + sb3[i] = sb2[sb1[i]]; + } + } + } + + bit_slice_t composite(const __m128i xmmsb2[LUT_XMM_N], const __m128i xmmsb1[LUT_XMM_N]) + { + bit_slice_t s = {{}}; + __m128i xmmsb3[LUT_XMM_N]; + + composite(xmmsb3, xmmsb2, xmmsb1); + + for (int i = 0; i < LUT_XMM_N; i++) + { + xmmsb3[i] = _mm_slli_epi16(xmmsb3[i], 8 - N); + } + + for (int i = N - 1; i >= 0; i--) + { + for (int j = LUT_XMM_N - 1; j >= 0; j--) + { + s[i][(j*16)/UNIT_BIT_N] <<= 16; + s[i][(j*16)/UNIT_BIT_N] |= (unsigned short)_mm_movemask_epi8(xmmsb3[j]); + xmmsb3[j] = _mm_slli_epi16(xmmsb3[j], 1); + } + } + + return s; + } + + + // From https://www.agner.org/optimize/#vectorclass + // C++ vector class library + // Horizontal add: Calculates the sum of all vector elements. + // Overflow will wrap around + int32_t horizontal_add (__m128i const & a) { + #ifdef __XOP__ // AMD XOP instruction set + __m128i sum1 = _mm_haddq_epi32(a); + __m128i sum2 = _mm_shuffle_epi32(sum1,0x0E); // high element + __m128i sum3 = _mm_add_epi32(sum1,sum2); // sum + return _mm_cvtsi128_si32(sum3); // truncate to 32 bits + #elif INSTRSET >= 4 // SSSE3 + __m128i sum1 = _mm_hadd_epi32(a,a); // horizontally add 4 elements in 2 steps + __m128i sum2 = _mm_hadd_epi32(sum1,sum1); + return _mm_cvtsi128_si32(sum2); // 32 bit sum + #else // SSE2 + __m128i sum1 = _mm_shuffle_epi32(a,0x0E); // 2 high elements + __m128i sum2 = _mm_add_epi32(a,sum1); // 2 sums + __m128i sum3 = _mm_shuffle_epi32(sum2,0x01); // 1 high element + __m128i sum4 = _mm_add_epi32(sum2,sum3); // 2 sums + return _mm_cvtsi128_si32(sum4); // 32 bit sum + #endif + } + + void mulHM (int D[LUT_UNIT_N][LUT_UNIT_N], const int S1[LUT_UNIT_N][LUT_UNIT_N]) + { + #if 0 + __m128i HC[(LUT_UNIT_N >> 2)]; + for (int ci = 0; ci < LUT_UNIT_N; ci++) + { + switch (N) + { + case 3: for (int wi = 0; wi < (LUT_UNIT_N >> 2); wi++) HC[wi] = _mm_load_si128(((__m128i *)(H3[ci])) + wi); break; + case 4: for (int wi = 0; wi < (LUT_UNIT_N >> 2); wi++) HC[wi] = _mm_load_si128(((__m128i *)(H4[ci])) + wi); break; + case 5: for (int wi = 0; wi < (LUT_UNIT_N >> 2); wi++) HC[wi] = _mm_load_si128(((__m128i *)(H5[ci])) + wi); break; + case 6: for (int wi = 0; wi < (LUT_UNIT_N >> 2); wi++) HC[wi] = _mm_load_si128(((__m128i *)(H6[ci])) + wi); break; + case 7: for (int wi = 0; wi < (LUT_UNIT_N >> 2); wi++) HC[wi] = _mm_load_si128(((__m128i *)(H7[ci])) + wi); break; + case 8: for (int wi = 0; wi < (LUT_UNIT_N >> 2); wi++) HC[wi] = _mm_load_si128(((__m128i *)(H8[ci])) + wi); break; + default: break; + } + + for (int ri = 0; ri < LUT_UNIT_N; ri++) + { + int sum = 0; + __m128i S1r; + for (int wi = 0; wi < (LUT_UNIT_N >> 2); wi++) + { + S1r = _mm_loadu_si128(((__m128i *)(S1[ri])) + wi); + sum += horizontal_add(_mm_mullo_epi32(S1r, HC[wi])); + } + D[ri][ci] = sum; + } + } + #endif + + #if 1 + int HC[LUT_UNIT_N]; + for (int ci = 0; ci < LUT_UNIT_N; ci++) + { + switch (N) + { + case 3: for (int ri = 0; ri < LUT_UNIT_N; ri++) HC[ri] = H3[ri][ci]; break; + case 4: for (int ri = 0; ri < LUT_UNIT_N; ri++) HC[ri] = H4[ri][ci]; break; + case 5: for (int ri = 0; ri < LUT_UNIT_N; ri++) HC[ri] = H5[ri][ci]; break; + case 6: for (int ri = 0; ri < LUT_UNIT_N; ri++) HC[ri] = H6[ri][ci]; break; + case 7: for (int ri = 0; ri < LUT_UNIT_N; ri++) HC[ri] = H7[ri][ci]; break; + case 8: for (int ri = 0; ri < LUT_UNIT_N; ri++) HC[ri] = H8[ri][ci]; break; + default: break; + } + + for (int ri = 0; ri < LUT_UNIT_N; ri++) + { + int sum = 0; + for (int wi = 0; wi < LUT_UNIT_N; wi++) + { + sum += S1[ri][wi] * HC[wi]; + } + D[ri][ci] = sum; + } + } + #endif + } + + template + string show_matrix(T * matrix, int rn, int cn) + { + stringstream ss; + ss << "{" << endl; + for (int i = 0; i < rn; i++) + { + ss << "{"; + for (int j = 0; j < cn; j++) + { + ss << setw((int)log10(1< + string show_matrix_HWorder(T matrix[LUT_UNIT_N][LUT_UNIT_N]) + { + stringstream ss; + ss << "{" << endl; + ss << setw((N+3)/4 + 3) << " " << hex; + for (int j = 0; j < LUT_UNIT_N; j++) + { + ss << setw((int)log10(1<[j] << "| "; + } + ss << endl << setfill(' ') << dec; + for (int i = 0; i < LUT_UNIT_N; i++) + { + ss << "{"; + ss << setfill('0') << hex << setw((N+3)/4) << HWorder[i] << ": "; + ss << setfill(' ') << dec; + for (int j = 0; j < LUT_UNIT_N; j++) + { + ss << setw((int)log10(1<[i]][HWorder[j]] << ", "; + } + ss <<"}," << endl; + } + ss << "};" << endl; + return ss.str(); + } + + template + string show_indicate_matrix_HWorder(T matrix[LUT_UNIT_N][LUT_UNIT_N]) + { + stringstream ss; + ss << "{" << endl; + ss << setw((N+3)/4 + 3) << " " << hex; + for (int j = 0; j < LUT_UNIT_N; j++) + { + ss << setw((N+3)/4 + 1) << HWorder[j] << "| "; + } + ss << endl << setfill(' ') << dec; + for (int i = 0; i < LUT_UNIT_N; i++) + { + ss << "{"; + ss << setfill('0') << hex << setw((N+3)/4) << HWorder[i] << ": "; + ss << setfill(' ') << dec; + for (int j = 0; j < LUT_UNIT_N; j++) + { + if (matrix[HWorder[i]][HWorder[j]] == 0) + { + ss << setw((N+3)/4 + 1) << " " << ", "; + } + else + { + ss << setw((N+3)/4 + 1) << "x" << ", "; + } + } + ss <<"}," << endl; + } + ss << "};" << endl; + return ss.str(); + } + + +}; + +template +OPs_t OPs; + +template +struct Perm_t +{ + Peigen::function_t * func = NULL; + int idx[FACT_(N)][N]; + + Perm_t() + { + Peigen::function_t I = Peigen::function_t::INPUT_DEFAULT(); + func = new Peigen::function_t[FACT_(N)]; + __m128i a; + __m128i x; + uint8_t * akp = (uint8_t *)&a; + uint8_t * xkp = (uint8_t *)&x; + int k; + int ct = 0; + + __m128i shuf[16]; + shuf[0xf] = _mm_set_epi8(0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1); + shuf[0xe] = _mm_set_epi8(15, 0, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1); + shuf[0xd] = _mm_set_epi8(15, 14, 0, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1); + shuf[0xc] = _mm_set_epi8(15, 14, 13, 0, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1); + shuf[0xb] = _mm_set_epi8(15, 14, 13, 12, 0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1); + shuf[0xa] = _mm_set_epi8(15, 14, 13, 12, 11, 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1); + shuf[0x9] = _mm_set_epi8(15, 14, 13, 12, 11, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1); + shuf[0x8] = _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 0, 8, 7, 6, 5, 4, 3, 2, 1); + shuf[0x7] = _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 0, 7, 6, 5, 4, 3, 2, 1); + shuf[0x6] = _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 0, 6, 5, 4, 3, 2, 1); + shuf[0x5] = _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 0, 5, 4, 3, 2, 1); + shuf[0x4] = _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 0, 4, 3, 2, 1); + shuf[0x3] = _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 0, 3, 2, 1); + shuf[0x2] = _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 0, 2, 1); + shuf[0x1] = _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 0, 1); + shuf[0x0] = _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); + + x = _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); + a = x; + + C2: + for (int i = 0; i < N; i++) + { + func[ct].bit_slice[i] = I.bit_slice[akp[i]]; + idx[ct][i] = akp[i]; + } + func[ct].bit_slice_to_LUT(); + + ct++; + k = N - 1; + + C3: + a = _mm_shuffle_epi8(a, shuf[k]); + if (akp[k] != xkp[k]) goto C2; + + k = k - 1; + if (k > 0) goto C3; + } + + ~Perm_t() + { + if (func != NULL) + { + delete [] func; + } + } +}; + + +template +const Perm_t PE; + +template +const array, (1< XE = [&] +{ + Peigen::function_t I = Peigen::function_t::INPUT_DEFAULT(); + array, (1< aXE; + for (int ix = 0; ix < (1<> i) & 1) aXE[ix].bit_slice[i] = ~I.bit_slice[i]; + else aXE[ix].bit_slice[i] = I.bit_slice[i]; + } + aXE[ix].bit_slice_to_LUT(); + } + return aXE; +}(); + +template +const bit_slice_t masks = [&] +{ + bit_slice_t amask; + bit_slice_l_t mask_init; + memset(mask_init.data(), 0xff, sizeof(bit_slice_l_t)); + amask[N - 1] = OPs.sll(mask_init, 1ULL << (N - 1ULL)); + for (int i = N - 2; i >= 0; i--) + { + mask_init = OPs.rotl(amask[i+1], 1ULL << i); + mask_init = mask_init ^ amask[i+1]; + mask_init = ~mask_init; + amask[i] = mask_init; + } + return amask; +}(); + +#include "OptimalSboxes.hpp" +#include "AffineMatrixColPEBox.hpp" +#include "AffineMatrixRowPEBox.hpp" + +template +struct LExinfoGx_t +{ + int Gx; + array L1; + array L2; + uint8_t c; +}; + +template +const map, LExinfoGx_t > G_Rs = [&] +{ + map, LExinfoGx_t > aG_Rs; + if (N == 4) + { + Peigen::function_t GaF; + Peigen::function_t GF; + LExinfoGx_t LExinfo; + array R; + uint8_t * Rp = R.data(); + for (int i = 0; i < OPTIMAL_G4_N; i++) + { + LExinfo.Gx = i; + GF.LUT[0] = _mm_load_si128((__m128i *)G4[i]); + for (int ai = 0; ai < (1 << N); ai++) + { + LExinfo.c = ai; + OPs.composite(GaF.LUT, GF.LUT, XE[ai].LUT); + GaF.LE_representative(Rp, LExinfo.L1.data(), LExinfo.L2.data()); + aG_Rs.insert(pair, LExinfoGx_t >(R, LExinfo)); + } + } + } + return aG_Rs; +}(); + +template +void optimalAEclass_split(array >, OPTIMAL_G4_N> & optimalPXEreps) +{ + if (N == 4) + { + string fn; + ofstream fout; + Peigen::function_t GF; + for (int i = 0; i < OPTIMAL_G4_N; i++) + { + fn = "optimalPXEreps_G" + to_string(i); + fn += ".txt"; + fout.open(fn.c_str(), ios::app); + GF.LUT[0] = _mm_load_si128((__m128i *)G4[i]); + GF.get_AE_PXEreps(optimalPXEreps[i]); + int idx = 0; + for (auto per : optimalPXEreps[i]) + { + fout << idx << "," << per.LUT_to_string() << endl; + idx++; + } + fout.close(); + } + } + else + { + cout << "Not support for N != 4" << endl; + } +} + + +#include "subspaces.hpp" +#endif //#define CONSTANTS_H__ + diff --git a/criteria.conf b/criteria.conf new file mode 100644 index 0000000..8faf6cd --- /dev/null +++ b/criteria.conf @@ -0,0 +1,14 @@ +Diff=2 +Lin=4 +#Diff1=2 +#Lin1=2 +#CardL1=6 +#DiffFreq=18 +Cost=6 +#LinFreq=32 +#MaxDeg=3 +#MinDeg=2 +#CardD1=4 +#CardL1=8 +#MaxDegFreq=14 +#MinDegFreq=1 diff --git a/criteria_filter.conf b/criteria_filter.conf new file mode 100644 index 0000000..77d6ee8 --- /dev/null +++ b/criteria_filter.conf @@ -0,0 +1,14 @@ +Diff=4 +Lin=8 +Diff1=0 +#Lin1=2 +#CardL1=6 +#DiffFreq=18 +#Cost=6 +#LinFreq=32 +#MaxDeg=3 +#MinDeg=2 +#CardD1=4 +#CardL1=8 +#MaxDegFreq=14 +#MinDegFreq=1 diff --git a/evaluator.hpp b/evaluator.hpp new file mode 100644 index 0000000..d21fe66 --- /dev/null +++ b/evaluator.hpp @@ -0,0 +1,259 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#ifndef EVALUATOR_H__ +#define EVALUATOR_H__ + +#include "constants.hpp" +#include "func.hpp" + +using namespace Peigen; +using namespace std; + +template +struct Peigen::evaluator +{ + string name; + string LUT_str; + string bit_slice_str; + + bool is_perm_flag; + bool is_inv_flag; + + int DDT[LUT_UNIT_N][LUT_UNIT_N]; + int DDT_spectrum[LUT_UNIT_N+1]; + int Diff; + int DiffFreq; + + int DDT1_spectrum[LUT_UNIT_N+1]; + int Diff1; + int CardD1; + + int LAT[LUT_UNIT_N][LUT_UNIT_N]; + int LAT_spectrum[LUT_UNIT_N+1]; + int Lin; + int LinFreq; + + int LAT1_spectrum[LUT_UNIT_N+1]; + int Lin1; + int CardL1; + + int max_degree; + int min_degree; + int deg_spectrum[N+1]; + int max_degree_freq; + int min_degree_freq; + + int dkT[N+1]; + int LS_nubmer; + pair max_v; + pair max_w; + + int whichG = -1; + double cost = -1.0; + string costisBest = "-"; + + string inv_LUT_str = "-"; + string inv_bit_slice_str = "-"; + int inv_max_degree = -1; + int inv_min_degree = -1; + int inv_deg_spectrum[N+1]; + int inv_max_degree_freq = -1; + int inv_min_degree_freq = -1; + int inv_dkT[N+1]; + int inv_LS_nubmer; + pair inv_max_v; + pair inv_max_w; + + int inv_whichG = -1; + + evaluator(const function_t & f, string aname = "-", int acost = -1, string acostisBest = "Unknown") + { + name = aname; + cost = (double)acost/100.0; + costisBest = acostisBest; + + LUT_str = f.LUT_to_string(); + bit_slice_str = f.to_string(); + + is_perm_flag = f.is_permutation(); + is_inv_flag = f.is_involution(); + + f.difference_distribution_matrix(DDT, Diff, DDT_spectrum, Diff1, DDT1_spectrum); + DiffFreq = DDT_spectrum[Diff]; + CardD1 = N * N - DDT1_spectrum[0]; + + f.linear_approximation_matrix(LAT, Lin, LAT_spectrum, Lin1, LAT1_spectrum); + LinFreq = LAT_spectrum[Lin]; + CardL1 = N * N - LAT1_spectrum[0]; + + f.degree(deg_spectrum, max_degree, min_degree); + max_degree_freq = deg_spectrum[max_degree]; + min_degree_freq = deg_spectrum[min_degree]; + + f.k_product_degree_table(dkT); + LS_nubmer = f.number_linear_structures(); + f.max_v_w_linear(max_v, max_w); + + whichG = f.which_optimal(); + + if (is_perm_flag) + { + function_t f_inv; + f_inv = f.inverse(); + + inv_LUT_str = f_inv.LUT_to_string(); + inv_bit_slice_str = f_inv.to_string(); + + f_inv.degree(inv_deg_spectrum, inv_max_degree, inv_min_degree); + inv_max_degree_freq = inv_deg_spectrum[inv_max_degree]; + inv_min_degree_freq = inv_deg_spectrum[inv_min_degree]; + + f_inv.k_product_degree_table(inv_dkT); + inv_LS_nubmer = f_inv.number_linear_structures(); + f_inv.max_v_w_linear(inv_max_v, inv_max_w); + + inv_whichG = f_inv.which_optimal(); + } + } + + string hash() const + { + stringstream ss; + ss << setfill('0') + << setw(1) << (is_perm_flag ? "T" : "F") << "_" + << setw(1) << (is_inv_flag ? "T" : "F") << "_" + << setw(digits(LUT_UNIT_N)) << Diff << "_" + << setw(digits(LUT_UNIT_N * LUT_UNIT_N)) << DiffFreq << "_" + << setw(digits(LUT_UNIT_N)) << Diff1 << "_" + << setw(digits(N * N)) << CardD1 << "_" + << setw(digits(LUT_UNIT_N)) << Lin << "_" + << setw(digits(LUT_UNIT_N * LUT_UNIT_N)) << LinFreq << "_" + << setw(digits(LUT_UNIT_N)) << Lin1 << "_" + << setw(digits(N * N)) << CardL1 << "_" + << setw(digits(N)) << max_degree << "_" + << setw(digits(N)) << min_degree << "_" + << setw(digits(LUT_UNIT_N)) << max_degree_freq << "_" + << setw(digits(LUT_UNIT_N)) << min_degree_freq << "_" + << setw(2) << whichG << "_" + << setw(5) << cost << "_" + << setw(digits(N)) << inv_max_degree << "_" + << setw(digits(N)) << inv_min_degree << "_" + << setw(digits(LUT_UNIT_N)) << inv_max_degree_freq << "_" + << setw(digits(LUT_UNIT_N)) << inv_min_degree_freq << "_" + << setw(2) << inv_whichG; + return ss.str(); + } + + string show() const + { + stringstream ss; + string dkTstr = "["; + for (int k = 1; k <= N; k++) dkTstr += std::to_string(dkT[k]) + "|"; + dkTstr.pop_back(); + dkTstr += "]"; + + ss + << name << "," + << LUT_str << "," + << bit_slice_str << "," + << (is_perm_flag ? "True" : "False") << "," + << (is_inv_flag ? "True" : "False") << "," + << Diff << "," + << DiffFreq << "," + << Diff1 << "," + << CardD1 << "," + << Lin << "," + << LinFreq << "," + << Lin1 << "," + << CardL1 << "," + << max_degree << "," + << min_degree << "," + << max_degree_freq << "," + << min_degree_freq << "," + << dkTstr << "," + << LS_nubmer << "," + << "\"(" << max_v.first << ", " << max_v.second << ")\"" << "," + << "\"(" << max_w.first << ", " << max_w.second << ")\"" << "," + << (whichG == -1 ? "-" : "G" + std::to_string(whichG)) << "," + << cost << "," + << costisBest << ","; + + if (is_perm_flag) + { + string inv_dkTstr = "["; + for (int k = 1; k <= N; k++) inv_dkTstr += std::to_string(inv_dkT[k]) + "|"; + inv_dkTstr.pop_back(); + inv_dkTstr += "]"; + + ss + << inv_LUT_str << "," + << inv_bit_slice_str << "," + << inv_max_degree << "," + << inv_min_degree << "," + << inv_max_degree_freq << "," + << inv_min_degree_freq << "," + << inv_dkTstr << "," + << inv_LS_nubmer << "," + << "\"(" << inv_max_v.first << ", " << inv_max_v.second << ")\"" << "," + << "\"(" << inv_max_w.first << ", " << inv_max_w.second << ")\"" << "," + << (inv_whichG == -1 ? "-" : "G" + std::to_string(inv_whichG)) + << endl; + } + else + { + ss + << "-" << "," + << "-" << "," + << "-" << "," + << "-" << "," + << "-" << "," + << "-" << "," + << "-" << "," + << "-" << "," + << "-" << "," + << "-" << "," + << "-" + << endl; + } + return ss.str(); + } + + ~evaluator(){}; +}; + +#endif // EVALUATOR_H__ diff --git a/faster.hpp b/faster.hpp new file mode 100644 index 0000000..b855e66 --- /dev/null +++ b/faster.hpp @@ -0,0 +1,282 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#ifndef FASTER_H__ +#define FASTER_H__ + +#include "faster_func.hpp" + +using namespace Peigen::depth; +using namespace std; + +template +class Peigen::depth::faster +{ +public: + int pre_l = -1; + + string imp_info = ""; + ofstream statistician; + string old_instance_name = ""; + string instance_name = ""; + + int max_GE = 0; + vector b; // The boolean instructions (B in the paper) + + map> > f1_succ; + + function_t start; + function_t target; + + uint32_t flag = 0UL; + + int count_list; + int shortest_path = MAX_COST; + int smallest_area = MAX_COST; + set* > area_sum; + + bool verbose = false; + int l = 1200; + + bool write_in_file = false; + int omp_nb_threads = 20; + string conf_file = "TSMC65nm.conf"; + long max_ram = 160; + long nodes_cmp = 0; + long max_nodes; + int cost_1; + + int gate_not1 = false; + int gate_and2 = false; + int gate_nand2 = false; + int gate_or2 = false; + int gate_nor2 = false; + int gate_xor2 = false; + int gate_xnor2 = false; + int gate_moai1 = false; + int gate_maoi1 = false; + int gate_nand3 = false; + int gate_nor3 = false; + int gate_or3 = false; + int gate_and3 = false; + int gate_andn2 = false; + int gate_orn2 = false; + + struct option longopts[16] = { + { "not1", no_argument, &gate_not1, 1}, + { "and2", no_argument, &gate_and2, 1}, + { "nand2", no_argument, &gate_nand2, 1}, + { "or2", no_argument, &gate_or2, 1}, + { "nor2", no_argument, &gate_nor2, 1}, + { "xor2", no_argument, &gate_xor2, 1}, + { "xnor2", no_argument, &gate_xnor2, 1}, + { "moai1", no_argument, &gate_moai1, 1}, + { "maoi1", no_argument, &gate_maoi1, 1}, + { "nand3", no_argument, &gate_nand3, 1}, + { "nor3", no_argument, &gate_nor3, 1}, + { "and3", no_argument, &gate_and3, 1}, + { "or3", no_argument, &gate_or3, 1}, + { "andn2", no_argument, &gate_andn2, 1}, + { "orn2", no_argument, &gate_orn2, 1}, + {0, 0, 0, 0} + }; + + int not1_cost; + int and2_cost; + int nand2_cost; + int or2_cost; + int nor2_cost; + int xor2_cost; + int xnor2_cost; + int maoi1_cost; + int moai1_cost; + int nand3_cost; + int nor3_cost; + int and3_cost; + int or3_cost; + int andn2_cost; + int orn2_cost; + + void init_varbles() + { + shortest_path = MAX_COST; + smallest_area = MAX_COST; + area_sum.clear(); + if (pre_l < 0) + { + f1_succ.clear(); + } + flag = 0UL; + } + + void reset() + { + pre_l = -1; + imp_info = ""; + instance_name = ""; + old_instance_name = ""; + + start = function_t::INPUT_DEFAULT(); + target = function_t::INPUT_DEFAULT(); + flag = 0UL; + + max_GE = 0; + b.clear(); + f1_succ.clear(); + shortest_path = MAX_COST; + smallest_area = MAX_COST; + area_sum.clear(); + verbose = false; + l = 1200; + write_in_file = false; + omp_nb_threads = 20; + conf_file = "TSMC65nm.conf"; + max_ram = 160; + nodes_cmp = 0; + + gate_not1 = false; + gate_and2 = false; + gate_nand2 = false; + gate_or2 = false; + gate_nor2 = false; + gate_xor2 = false; + gate_xnor2 = false; + gate_moai1 = false; + gate_maoi1 = false; + gate_nand3 = false; + gate_nor3 = false; + gate_or3 = false; + gate_and3 = false; + gate_andn2 = false; + gate_orn2 = false; + } + + int cur_shortest_path() + { + return shortest_path; + } + + int cur_smallest_area() + { + return smallest_area; + } + + void all_gates(); + + void genImpInfo(); + + int bool_op_cost(uint8_t op); + + string bool_op_str(uint8_t op); + + void init_b(string conf_file); + + void bool_op_one_input(const bool_function_t *f, vector > *v, bool_op_t bool_op); + + void bool_op_two_inputs(const bool_function_t *f1, const bool_function_t *f2, vector > *v, bool_op_t bool_op); + + void bool_op_three_inputs(const bool_function_t *f1, const bool_function_t *f2, const bool_function_t *f3, vector > *v, bool_op_t bool_op); + + string get_implementation(bool_function_t f1); + + void get_implementation(); + + void print_graphe_info(); + + void exit_m(); + + void graphe_to_file(map > > *graphe, + string graphe_name); + + void print_uint16(uint16_t n); + + void print_uint8(uint8_t n); + + string uint8_to_string(uint8_t n); + + void print_uint32(uint32_t n); + + void mitm(function_t f1); + + void expand(int lambda); + + void v_list_process(int lambda, int op_cost, vector > *tmp, + set > *to_insert); + + bool is_in_graphe(int lambda, int op_cost, bool_function_t f); + + bool is_in_graphe_collision(int lambda, int op_cost, bool_function_t f); + + void pre_compute(string args); + void pre_computing(); + void write_pre_bin(); + void read_pre_bin(); + void search_batch(string args); + double search_single(string args); + + faster(){}; + + ~faster(){}; +}; + +#include "faster_bool_op.hpp" +#include "faster_mitm.hpp" +#include "faster_utils.hpp" +#include "faster_impl_info.hpp" + +#undef NOP +#undef NOT1 +#undef AND2 +#undef NAND2 +#undef OR2 +#undef NOR2 +#undef XOR2 +#undef XNOR2 +#undef MOAI1 +#undef MAOI1 +#undef NAND3 +#undef NOR3 +#undef OR3 +#undef AND3 +#undef ANDN2 +#undef ORN2 +#undef MAX_COST +#undef FOUND_ALL + +#endif /* FASTER_H__ */ + + diff --git a/faster_bool_op.hpp b/faster_bool_op.hpp new file mode 100644 index 0000000..4ca5316 --- /dev/null +++ b/faster_bool_op.hpp @@ -0,0 +1,273 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#ifndef FASTER_BOOL_OP_H__ +#define FASTER_BOOL_OP_H__ + +using namespace std; +using namespace Peigen::depth; + +template +void faster::all_gates() +{ + gate_not1 = true; + gate_and2 = true; + gate_nand2 = true; + gate_or2 = true; + gate_nor2 = true; + gate_xor2 = true; + gate_xnor2 = true; + gate_moai1 = true; + gate_maoi1 = true; + gate_nand3 = true; + gate_nor3 = true; + gate_or3 = true; + gate_and3 = true; + gate_andn2 = true; + gate_orn2 = true; +} + +template +void faster::genImpInfo() +{ + imp_info = ""; + if (gate_not1 ) imp_info += "-not1"; + if (gate_and2 ) imp_info += "-and2"; + if (gate_nand2 ) imp_info += "-nand2"; + if (gate_or2 ) imp_info += "-or2"; + if (gate_nor2 ) imp_info += "-nor2"; + if (gate_xor2 ) imp_info += "-xor2"; + if (gate_xnor2 ) imp_info += "-xnor2"; + if (gate_moai1 ) imp_info += "-moai1"; + if (gate_maoi1 ) imp_info += "-maoi1"; + if (gate_nand3 ) imp_info += "-nand3"; + if (gate_nor3 ) imp_info += "-nor3"; + if (gate_or3 ) imp_info += "-or3"; + if (gate_and3 ) imp_info += "-and3"; + if (gate_andn2 ) imp_info += "-andn2"; + if (gate_orn2 ) imp_info += "-orn2"; + + imp_info += "-f"; + imp_info += conf_file.substr(0, conf_file.rfind('.')); + imp_info += "-N"; + imp_info += to_string(N); +} + +template +int faster::bool_op_cost(uint8_t op) +{ + switch (op) + { + case NOT1 : return not1_cost; + case AND2 : return and2_cost; + case NAND2 : return nand2_cost; + case OR2 : return or2_cost; + case NOR2 : return nor2_cost; + case XOR2 : return xor2_cost; + case XNOR2 : return xnor2_cost; + case MOAI1 : return moai1_cost; + case MAOI1 : return maoi1_cost; + case NAND3 : return nand3_cost; + case NOR3 : return nor3_cost; + case OR3 : return or3_cost; + case AND3 : return and3_cost; + case ANDN2 : return andn2_cost; + case ORN2 : return orn2_cost; + case NOP : return 0; + default : return -1; + } +} + +template +string faster::bool_op_str(uint8_t op) +{ + switch (op) + { + case NOT1 : return "NOT1"; + case AND2 : return "AND2"; + case NAND2 : return "NAND2"; + case OR2 : return "OR2"; + case NOR2 : return "NOR2"; + case XOR2 : return "XOR2"; + case XNOR2 : return "XNOR2"; + case MOAI1 : return "MOAI1"; + case MAOI1 : return "MAOI1"; + case NAND3 : return "NAND3"; + case NOR3 : return "NOR3"; + case OR3 : return "OR3"; + case AND3 : return "AND3"; + case ANDN2 : return "ANDN2"; + case ORN2 : return "ORN2"; + + default : return "--"; + } +} + + +template +void faster::init_b(string conf_file) +{ + ifstream input(conf_file); + string key, value; + for(string line; getline( input, line ); ) + { + istringstream is_line(line); + if(getline(is_line, key, '=')) + { + if(getline(is_line, value)) + { + if(key == "not1_cost") not1_cost = stod(value)*100; + if(key == "and2_cost") and2_cost = stod(value)*100; + if(key == "nand2_cost") nand2_cost = stod(value)*100; + if(key == "or2_cost") or2_cost = stod(value)*100; + if(key == "nor2_cost") nor2_cost = stod(value)*100; + if(key == "xor2_cost") xor2_cost = stod(value)*100; + if(key == "xnor2_cost") xnor2_cost = stod(value)*100; + if(key == "maoi1_cost") maoi1_cost = stod(value)*100; + if(key == "moai1_cost") moai1_cost = stod(value)*100; + if(key == "andn2_cost") andn2_cost = stod(value)*100; + if(key == "orn2_cost") orn2_cost = stod(value)*100; + if (N >= 4) + { + if(key == "nand3_cost") nand3_cost = stod(value)*100; + if(key == "nor3_cost") nor3_cost = stod(value)*100; + if(key == "and3_cost") and3_cost = stod(value)*100; + if(key == "or3_cost") or3_cost = stod(value)*100; + } + + } + } + } + + /* + initilisation of the set of bolean operations + */ + if(gate_not1 ) b.push_back({NOT1 , not1_cost}); + if(gate_and2 ) b.push_back({AND2 , and2_cost}); + if(gate_nand2) b.push_back({NAND2, nand2_cost}); + if(gate_or2 ) b.push_back({OR2 , or2_cost}); + if(gate_nor2 ) b.push_back({NOR2 , nor2_cost}); + if(gate_xor2 ) b.push_back({XOR2 , xor2_cost}); + if(gate_xnor2) b.push_back({XNOR2, xnor2_cost}); + if(gate_moai1) b.push_back({MOAI1, moai1_cost}); + if(gate_maoi1) b.push_back({MAOI1, maoi1_cost}); + if(gate_andn2) b.push_back({ANDN2, andn2_cost}); + if(gate_orn2 ) b.push_back({ORN2 , orn2_cost}); + if (N >= 4) + { + if(gate_nand3) b.push_back({NAND3, nand3_cost}); + if(gate_nor3 ) b.push_back({NOR3 , nor3_cost}); + if(gate_or3 ) b.push_back({OR3 , or3_cost}); + if(gate_and3 ) b.push_back({AND3 , and3_cost}); + } + + sort(b.begin(), b.end()); + + max_GE = (b.back()).op_cost; +} + +template +void faster::bool_op_one_input(const bool_function_t *f, vector > *v, bool_op_t bool_op) +{ + int operation = bool_op.op_id; + int cost = bool_op.op_cost; + + bool_function_t fun_tmp; + + if (operation == NOT1) + { + fun_tmp.bit_slice = ~((*f).bit_slice); + fun_tmp.area = (*f).area + cost; + fun_tmp.info_op = (uint8_t)operation; + fun_tmp.operands[0] = f; + v->push_back(fun_tmp); + } +} + +template +void faster::bool_op_two_inputs(const bool_function_t *f1, const bool_function_t *f2, vector > *v, bool_op_t bool_op) +{ + int operation = bool_op.op_id; + int cost = bool_op.op_cost; + + bool_function_t fun_tmp; + + switch(operation) + { + case AND2 : fun_tmp.bit_slice = (*f1).bit_slice & (*f2).bit_slice ; break; + case OR2 : fun_tmp.bit_slice = (*f1).bit_slice | (*f2).bit_slice ; break; + case NAND2 : fun_tmp.bit_slice = ~((*f1).bit_slice & (*f2).bit_slice); break; + case NOR2 : fun_tmp.bit_slice = ~((*f1).bit_slice | (*f2).bit_slice); break; + case ANDN2 : fun_tmp.bit_slice = (~(*f1).bit_slice) & (*f2).bit_slice ; break; + case ORN2 : fun_tmp.bit_slice = (~(*f1).bit_slice) | (*f2).bit_slice ; break; + case MAOI1 : + case XOR2 : fun_tmp.bit_slice = (*f1).bit_slice ^ (*f2).bit_slice ; break; + case MOAI1 : + case XNOR2 : fun_tmp.bit_slice = ~((*f1).bit_slice ^ (*f2).bit_slice); break; + } + fun_tmp.area = (*f1).area + (*f2).area + cost; + fun_tmp.info_op = (uint8_t)operation; + fun_tmp.operands[0] = f1; + fun_tmp.operands[1] = f2; + v->push_back(fun_tmp); +} + +template +void faster::bool_op_three_inputs(const bool_function_t *f1, const bool_function_t *f2, const bool_function_t *f3, vector > *v, bool_op_t bool_op) +{ + int operation = bool_op.op_id; + int cost = bool_op.op_cost; + + bool_function_t fun_tmp; + + switch(operation) + { + case AND3 : fun_tmp.bit_slice = (*f1).bit_slice & (*f2).bit_slice & (*f3).bit_slice; break; + case OR3 : fun_tmp.bit_slice = (*f1).bit_slice | (*f2).bit_slice | (*f3).bit_slice; break; + case NAND3 : fun_tmp.bit_slice = ~((*f1).bit_slice & (*f2).bit_slice & (*f3).bit_slice); break; + case NOR3 : fun_tmp.bit_slice = ~((*f1).bit_slice | (*f2).bit_slice | (*f3).bit_slice); break; + } + + fun_tmp.area = (*f1).area + (*f2).area + (*f3).area + cost; + fun_tmp.info_op = (uint8_t)operation; + fun_tmp.operands[0] = f1; + fun_tmp.operands[1] = f2; + fun_tmp.operands[2] = f3; + v->push_back(fun_tmp); +} + +#endif // #ifndef FASTER_BOOL_OP_H__ \ No newline at end of file diff --git a/faster_func.hpp b/faster_func.hpp new file mode 100644 index 0000000..4284ee0 --- /dev/null +++ b/faster_func.hpp @@ -0,0 +1,435 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#ifndef FASTER_FUNC_H__ +#define FASTER_FUNC_H__ + +#include "constants.hpp" + +using namespace std; +using namespace Peigen::depth; + +#define FOUND_ALL ((1<::max()) + +template +struct Peigen::depth::bool_function_t +{ + /* + the bit-sliced representation of the function + */ + bit_slice_l_t bit_slice{ {} }; + + /* + identifier for last boolean operator + */ + uint8_t info_op = NOP; + + int area = 0; + + const bool_function_t * operands[3]; + + bool_function_t() + { + info_op = NOP; + area = 0; + operands[0] = NULL; + operands[1] = NULL; + operands[2] = NULL; + }; + + bool_function_t(const bit_slice_l_t a) + { + bit_slice = a; + info_op = NOP; + area = 0; + operands[0] = NULL; + operands[1] = NULL; + operands[2] = NULL; + } + + bool_function_t(const bool_function_t& f) + { + bit_slice = f.bit_slice; + info_op = f.info_op; + area = f.area; + operands[0] = f.operands[0]; + operands[1] = f.operands[1]; + operands[2] = f.operands[2]; + } + + bool operator < (const bool_function_t& f) const + { + return (bit_slice < f.bit_slice) || ((bit_slice == f.bit_slice) && (area < f.area)); + } + + bool operator == (const bool_function_t& f) const + { + return (bit_slice == f.bit_slice) && (area == f.area); + } + + string to_bitstring() const + { + string str = ""; + + for (int j = 0; j < UNIT_N ; j++) + { + bitset tmp(bit_slice[j]); + str += tmp.to_string(); + } + + return str; + } + + string to_string() const + { + stringstream ss; + ss << hex << setfill('0'); + + for (int j = UNIT_N - 1; j >= 0; j--) + { + ss << setw(UNIT_NIBBLE_N) << bit_slice[j] + '\0'; + } + + string res = ss.str(); + return res; + } + + bool is_balanced() const + { + uint64_t sum = 0; + uint64_t * pt = bit_slice.data(); + + for (int j = 0; j < (BIT_SLICE_BITS_N>>6) - 1; j++) + { + sum += _mm_popcnt_u64((uint64_t)(pt[j])); + } + sum += _mm_popcnt_u64( + (uint64_t)(pt[BIT_SLICE_BITS_N >> 6]) & + ((1ULL << (BIT_SLICE_BITS_N & 0x3fULL)) - 1ULL)); + + return (sum == (BIT_SLICE_BITS_N >> 1ULL)); + } +}; + +template +using coordinates_t = std::array, N>; + +template +struct Peigen::depth::function_t +{ + coordinates_t coordinates; + + array depth = { }; + + function_t() + { + for (int i = 0; i < N; i++) + { + depth[i] = MAX_COST; + } + }; + + function_t(const bit_slice_t& a) + { + for (int i = 0; i < N; i++) + { + coordinates[i].bit_slice = a[i]; + coordinates[i].area = 0; + depth[i] = MAX_COST; + } + } + + function_t(const coordinates_t& a) + { + coordinates = a; + for (int i = 0; i < N; i++) + { + depth[i] = MAX_COST; + } + } + + function_t(const function_t& f) + { + coordinates = f.coordinates; + depth = f.depth; + } + + function_t(const string str) + { + parse_function(str); + for (int i = 0; i < N; i++) + { + depth[i] = MAX_COST; + } + } + + static function_t INPUT_DEFAULT() + { + function_t I; + uint8_t LUT[LUT_UNIT_N]; + for (int i = 0; i < LUT_UNIT_N; i++) + { + LUT[i] = i & 0xff; + } + I.LUT_to_bit_slice(LUT); + return I; + } + + void parse_function(const string str) + { + if(str.size() == (BIT_SLICE_NIBBLES_N * N) + (N - 1)) + { + string_to_bit_slice(str); + } + else if(str.size() == (BIT_SLICE_BITS_N<<1)) + { + uint8_t LUT[LUT_UNIT_N]; + string_to_LUT(str, LUT); + LUT_to_bit_slice(LUT); + } + else + { + cout << "Error: Length of the string representing the function should be either " + << (BIT_SLICE_NIBBLES_N * N) + (N - 1) << " or " + << (BIT_SLICE_BITS_N<<1) << endl; + } + for (int i = 0; i < N; i++) + { + coordinates[i].area = 0; + depth[i] = MAX_COST; + } + } + + void string_to_bit_slice(const string& str) + { + if(str.size() == (BIT_SLICE_NIBBLES_N * N) + (N - 1)) + { + stringstream ss(str); + string number; + int i = N - 1; + while(getline(ss, number, '_') && i >= 0) + { + if(number.length() != BIT_SLICE_NIBBLES_N) + { + cout << "Length of each substring error."; + exit(1); + } + for (int j = UNIT_N - 1; j >= 0; j--) + { + coordinates[i].bit_slice[j] = stoul(number.substr((UNIT_N - 1 - j)*UNIT_NIBBLE_N, UNIT_NIBBLE_N), nullptr, 16); + } + i--; + } + } + else + { + cout << "Error: Length of the string representing the function in bitsliced form should be " + << (BIT_SLICE_NIBBLES_N * N) + (N - 1) << endl; + exit(1); + } + } + + // !!! Only applicable for N = 3, 4, 5, 6, 7, 8 + void string_to_LUT(const string& str, uint8_t LUT[]) + { + if(str.size() == (BIT_SLICE_BITS_N<<1)) + { + string tmp; + for (int i = 0; i < LUT_BYTE_N; i++) + { + tmp = str.substr(i * 2, 2); + LUT[i] = (uint8_t) stoul(tmp, nullptr, 16); + } + } + else + { + cout << "Error: Length of the string representing the function in LUT form should be " + << (BIT_SLICE_BITS_N<<1) << endl; + exit(1); + } + } + + // !!! Only applicable for N = 3, 4, 5, 6, 7, 8 + void LUT_to_bit_slice(uint8_t x[]) + { + __m128i LUT[LUT_XMM_N]; + for (int i = 0; i < LUT_XMM_N; i++) + { + LUT[i] = _mm_loadu_si128((__m128i *)(x + i * 16)); + } + + bit_slice_t bit_slice{{}}; + + if (N == 3) + { + const __m128i mask = _mm_set_epi64x(0x0, 0xffffffffffffffffULL); + LUT[0] = _mm_and_si128(LUT[0], mask); + bit_slice[2][0] = _mm_movemask_epi8(_mm_slli_epi16(LUT[0], 5)); + bit_slice[1][0] = _mm_movemask_epi8(_mm_slli_epi16(LUT[0], 6)); + bit_slice[0][0] = _mm_movemask_epi8(_mm_slli_epi16(LUT[0], 7)); + + } + else if (N == 4) + { + bit_slice[3][0] = _mm_movemask_epi8(_mm_slli_epi16(LUT[0], 4)); + bit_slice[2][0] = _mm_movemask_epi8(_mm_slli_epi16(LUT[0], 5)); + bit_slice[1][0] = _mm_movemask_epi8(_mm_slli_epi16(LUT[0], 6)); + bit_slice[0][0] = _mm_movemask_epi8(_mm_slli_epi16(LUT[0], 7)); + } + else if (N <= 8) + { + __m128i xmm[LUT_XMM_N]; + + for (int i = 0; i < LUT_XMM_N; i++) + { + xmm[i] = _mm_slli_epi16(LUT[i], 8 - N); + } + + for (int i = N - 1; i >= 0; i--) + { + for (int j = LUT_XMM_N - 1; j >= 0; j--) + { + bit_slice[i][(j*16)/UNIT_BIT_N] <<= 16; + bit_slice[i][(j*16)/UNIT_BIT_N] |= (unsigned short)_mm_movemask_epi8(xmm[j]); + xmm[j] = _mm_slli_epi16(xmm[j], 1); + } + } + } + + for (int i = 0; i < N; i++) + { + coordinates[i].bit_slice = bit_slice[i]; + coordinates[i].area = 0; + } + } + + bool operator < (const function_t& f) const + { + return coordinates < f.coordinates; + } + + bool operator == (const function_t& f) const + { + return coordinates == f.coordinates; + } + + string to_bitstring() const + { + string str = ""; + + for (int i = N - 1; i >= 0; i--) + { + for (int j = UNIT_N - 1; j >= 0; j--) + { + bitset tmp(coordinates[i].bit_slice[j]); + str += tmp.to_string(); + } + str += "_"; + } + str.pop_back(); + return str; + } + + string to_string() const + { + stringstream ss; + ss << hex << setfill('0'); + + for (int i = N - 1; i >= 0; i--) + { + for (int j = UNIT_N - 1; j >= 0; j--) + { + ss << setw(UNIT_NIBBLE_N) << coordinates[i].bit_slice[j] + '\0'; + } + ss << "_"; + } + string res = ss.str(); + res.pop_back(); + return res; + } + /* + Sort the lines of the function matrix + Ascending order + Ex : + 0101010101010101 + 0011001100110011 + 0000111100001111 + 0000000000000000 + */ + void sort() + { + StaticSort staticSort; + staticSort(coordinates); + } +}; + +struct Peigen::depth::bool_op_t +{ + int op_id; + int op_cost; + + bool operator < (const bool_op_t& b) const + { + return op_cost < b.op_cost; + } + + bool operator == (const bool_op_t& b) const + { + return op_cost == b.op_cost; + } +}; + +#endif // #ifndef FASTER_FUNC_H__ \ No newline at end of file diff --git a/faster_impl_info.hpp b/faster_impl_info.hpp new file mode 100644 index 0000000..cf7bf77 --- /dev/null +++ b/faster_impl_info.hpp @@ -0,0 +1,146 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#ifndef FASTER_IMPL_INFO_H__ +#define FASTER_IMPL_INFO_H__ + +using namespace std; +using namespace Peigen::depth; + +template +string faster::get_implementation(bool_function_t f1) +{ + string s = ""; + uint8_t op = f1.info_op; + + switch(op) + { + case NOT1 : + s = bool_op_str(op) + "( "+ get_implementation(*f1.operands[0]) + " ) "; + area_sum.insert(f1.operands[0]); + break; + case AND2 : + case NAND2 : + case OR2 : + case NOR2 : + case XOR2 : + case XNOR2 : + case ANDN2 : + case ORN2 : + case MOAI1 : + case MAOI1 : + s = bool_op_str(op) + "( "+ + get_implementation(*f1.operands[0]) + ", " + + get_implementation(*f1.operands[1]) + " ) "; + area_sum.insert(f1.operands[0]); + area_sum.insert(f1.operands[1]); + break; + case NAND3 : + case NOR3 : + case OR3 : + case AND3 : + s = bool_op_str(op) + "( "+ + get_implementation(*f1.operands[0]) + ", " + + get_implementation(*f1.operands[1]) + ", " + + get_implementation(*f1.operands[2]) + " ) "; + area_sum.insert(f1.operands[0]); + area_sum.insert(f1.operands[1]); + break; + case NOP : + for (int i = 0; i < N; i++) + { + if (f1 == start.coordinates[i]) + { + s = "X[" + to_string(i) + "]"; + break; + } + } + } + return s; +} + +template +void faster::get_implementation() +{ + int depth_cost = 0; + int area_cost = 0; + area_sum.clear(); + for (int i = 0; i < N; i++) + { + depth_cost = target.depth[i] >= depth_cost ? target.depth[i] : depth_cost; + area_cost += target.coordinates[i].area; + } + + if ((depth_cost < shortest_path) || ((depth_cost == shortest_path) && (area_cost < smallest_area))) + { + shortest_path = depth_cost; + smallest_area = area_cost; + + static int impl_number = 0; + if(instance_name != old_instance_name) + { + impl_number = 0; + old_instance_name = instance_name; + } + + cout << "Generating implementation " << instance_name << " " << impl_number << endl; + + ofstream impl(imp_info + "_" + instance_name + "_" + to_string(impl_number) + ".c"); + impl << "// from : " + start.to_string() + "\n"; + area_cost = 0; + for (int i = 0; i < N; i++) + { + area_cost += bool_op_cost(target.coordinates[i].info_op); + impl << "F[" << i << "] = " << get_implementation(target.coordinates[i]) << ";" << endl; + } + + for (auto it : area_sum) + { + area_cost += bool_op_cost((*it).info_op); + } + smallest_area = area_cost; + + impl << "// to : " + target.to_string() + "\n"; + impl << "// Depth : " << double(depth_cost)/100 << endl; + impl << "// Area : " << double(area_cost)/100 << endl; + cout << "Depth : " << double(depth_cost)/100 << " Area : " << double(area_cost)/100 << endl; + impl.close(); + impl_number ++; + } +} + +#endif // #ifndef FASTER_IMPL_INFO_H__ \ No newline at end of file diff --git a/faster_mitm.hpp b/faster_mitm.hpp new file mode 100644 index 0000000..d5e21c3 --- /dev/null +++ b/faster_mitm.hpp @@ -0,0 +1,851 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#ifndef FASTER_MITM_H__ +#define FASTER_MITM_H__ + +using namespace std; +using namespace Peigen::depth; + +#define NDEBUG +#include + +#ifdef PRINT +#undef PRINT +#endif +#ifdef MAX_VEC_NODES +#undef MAX_VEC_NODES +#endif +#ifdef pre_parse_args +#undef pre_parse_args +#endif +#ifdef post_parse_args +#undef post_parse_args +#endif + +#ifdef NDEBUG + #define PRINT(fmt, ...) ((void)0) +#else + #define PRINT(fmt, ...) printf(fmt, (unsigned long)(__LINE__), ##__VA_ARGS__) +#endif + +#define MAX_VEC_NODES (max_nodes>>1UL) + +#define pre_parse_args() \ + int myargc = 0; \ + char* myargv[50]; \ + string args_tmp = "x " + args; \ + char * args_str = new char [args_tmp.length() + 1]; \ + strcpy(args_str, args_tmp.c_str()); \ + char * p = strtok(args_str," "); \ + while( p != 0) \ + { \ + myargv[myargc] = p; \ + p = strtok(NULL, " "); \ + myargc++; \ + } \ + myargv[myargc] = NULL; \ + int opt; \ + optind = 0; + + +#define post_parse_args() \ + delete [] args_str; \ + max_nodes = max_ram * 100000000UL / sizeof(bool_function_t); \ + if (gate_maoi1 == 1) gate_xor2 = 0; \ + if (gate_moai1 == 1) gate_xnor2 = 0; + +template +void faster::pre_compute(string args) +{ + clock_t t1 = clock(); + + pre_parse_args(); + while ((opt = getopt_long(myargc, myargv, "c:vap:f:r:", longopts, NULL)) != EOF) + { + switch(opt) + { + case 'a': all_gates(); break; + case 'v': verbose = true; break; + case 'c': pre_l = abs(atoi(optarg))*100; cout << "Precompute Limit : "<< optarg << endl; break; + case 'p': omp_nb_threads = (atoi(optarg)); cout << "Thread number : "<< omp_nb_threads< +void faster::pre_computing() +{ + function_t f1 = function_t::INPUT_DEFAULT(); + f1.sort(); + + f1_succ.clear(); + f1_succ.insert(pair > >(0, {f1.coordinates[0]})); + for (int i = 1; i < N; i++ ) f1_succ[0].insert(f1.coordinates[i]); + + count_list = 0; + while (count_list <= pre_l) + { + expand(count_list); + if(verbose && (count_list%100) == 0) cout << count_list << endl; + count_list++; + write_pre_bin(); + } +} + +template +void faster::write_pre_bin() +{ + auto find = [](const set > & func_set, const bit_slice_l_t & bit_slice) + { + for(auto set_it = func_set.begin(); set_it != func_set.end(); set_it++) + { + if (set_it->bit_slice == bit_slice) + { + return set_it; + } + } + return func_set.end(); + }; + + ofstream fout; + size_t bool_function_s = sizeof(bool_function_t); + for (auto map_it = f1_succ.begin(); map_it != f1_succ.end(); map_it++) + { + string pre_filen = "pre_" + imp_info + "_" + to_string(map_it->first) + ".bin"; + fout.open(pre_filen, ios::binary); + for (auto set_it = map_it->second.begin(); set_it != map_it->second.end(); set_it++) + { + fout.write((char *)&(set_it->bit_slice), sizeof(bit_slice_l_t)); + fout.write((char *)&(set_it->info_op), sizeof(uint8_t)); + + bit_slice_l_t opv_tmp; + auto map2_it = f1_succ.begin(); + + switch(set_it->info_op) + { + case NOP : break; + case NAND3 : + case NOR3 : + case OR3 : + case AND3 : + opv_tmp = set_it->operands[2]->bit_slice; + map2_it = f1_succ.begin(); + while (map2_it->first < map_it->first) + { + auto func_t = find(map2_it->second, opv_tmp); + if (func_t != map2_it->second.end()) + { + fout.write((char *)&(map2_it->first), sizeof(int)); + fout.write((char *)&(opv_tmp), sizeof(bit_slice_l_t)); + break; + } + map2_it++; + } + assert(map2_it->first < map_it->first); + case AND2 : + case NAND2 : + case OR2 : + case NOR2 : + case XOR2 : + case XNOR2 : + case MOAI1 : + case MAOI1 : + case ANDN2 : + case ORN2 : + opv_tmp = set_it->operands[1]->bit_slice; + map2_it = f1_succ.begin(); + while (map2_it->first < map_it->first) + { + auto func_t = find(map2_it->second, opv_tmp); + if (func_t != map2_it->second.end()) + { + fout.write((char *)&(map2_it->first), sizeof(int)); + fout.write((char *)&(opv_tmp), sizeof(bit_slice_l_t)); + break; + } + map2_it++; + } + assert(map2_it->first < map_it->first); + case NOT1 : + opv_tmp = set_it->operands[0]->bit_slice; + map2_it = f1_succ.begin(); + while (map2_it->first < map_it->first) + { + auto func_t = find(map2_it->second, opv_tmp); + if (func_t != map2_it->second.end()) + { + fout.write((char *)&(map2_it->first), sizeof(int)); + fout.write((char *)&(opv_tmp), sizeof(bit_slice_l_t)); + break; + } + map2_it++; + } + assert(map2_it->first < map_it->first); + break; + default : cout << "Write file error. Exit." << endl; exit(1); + } + fout.write((char *)&(set_it->area), sizeof(int)); + } + fout.close(); + } +} + +template +void faster::read_pre_bin() +{ + auto find = [](const set > & func_set, const bit_slice_l_t & bit_slice) + { + for(auto set_it = func_set.begin(); set_it != func_set.end(); set_it++) + { + if (set_it->bit_slice == bit_slice) + { + return set_it; + } + } + return func_set.end(); + }; + + ifstream fin; + bool_function_t f_t; + int lambda = 0; + size_t bool_function_s = sizeof(bool_function_t); + f1_succ.clear(); + while (lambda <= (pre_l + max_GE)) + { + string pre_filen = "pre_" + imp_info + "_" + to_string(lambda) + ".bin"; + fin.open(pre_filen, ios::binary); + if (fin) + { + set > new_list; + f1_succ.insert(pair > >(lambda, new_list)); + auto to_insert = &(f1_succ[lambda]); + + int depth; + bit_slice_l_t opv_tmp; + + while (fin.read((char*)&(f_t.bit_slice), sizeof(bit_slice_l_t))) + { + assert(fin); + fin.read((char *)&(f_t.info_op), sizeof(uint8_t)); + assert(fin); + f_t.operands[0] = NULL; + f_t.operands[1] = NULL; + f_t.operands[2] = NULL; + switch(f_t.info_op) + { + case NOP : + break; + case NAND3 : + case NOR3 : + case OR3 : + case AND3 :assert(fin); + fin.read((char *)&(depth), sizeof(int)); + assert(fin); + fin.read((char *)&(opv_tmp), sizeof(bit_slice_l_t)); + f_t.operands[2] = &(*(find(f1_succ[depth],opv_tmp))); + case AND2 : + case NAND2 : + case OR2 : + case NOR2 : + case XOR2 : + case XNOR2 : + case MOAI1 : + case MAOI1 : + case ANDN2 : + case ORN2 :assert(fin); + fin.read((char *)&(depth), sizeof(int)); + assert(fin); + fin.read((char *)&(opv_tmp), sizeof(bit_slice_l_t)); + f_t.operands[1] = &(*(find(f1_succ[depth],opv_tmp))); + case NOT1 :assert(fin); + fin.read((char *)&(depth), sizeof(int)); + assert(fin); + fin.read((char *)&(opv_tmp), sizeof(bit_slice_l_t)); + f_t.operands[0] = &(*(find(f1_succ[depth],opv_tmp))); + break; + default : + cout << "Read binary file error. Exit. "; + fin.close(); + exit(1); + } + assert(fin); + fin.read((char *)&(f_t.area), sizeof(int)); + to_insert->insert(f_t); + } + } + fin.close(); + lambda++; + } +} + +template +void faster::search_batch(string args) +{ + function_t f1 = function_t::INPUT_DEFAULT(); + target = function_t::INPUT_DEFAULT(); + string sboxesfile = "sboxes" + to_string(N) + ".txt"; + + pre_parse_args(); + while ((opt = getopt_long(myargc, myargv, "i:o:l:c:vwap:f:r:", longopts, NULL)) != EOF) + { + switch(opt) + { + case 'c': pre_l = atoi(optarg)*100; cout << "Precomputed Limit : "<< optarg << endl; break; + case 'a': all_gates(); break; + case 'v': verbose = true; break; + case 'w': write_in_file = true; break; + case 'i': f1.parse_function(optarg); break; + case 'o': sboxesfile = optarg; break; + case 'l': l = abs(atoi(optarg))*100; cout << "Lambda : "<< optarg << endl; break; + case 'p': omp_nb_threads = (atoi(optarg)); cout << "Thread number : "<< omp_nb_threads< +double faster::search_single(string args) +{ + clock_t t1 = clock(); + + function_t f1 = function_t::INPUT_DEFAULT(); + target = function_t::INPUT_DEFAULT(); + string instance_LUT; + instance_name = ""; + + pre_parse_args(); + while ((opt = getopt_long(myargc, myargv, "i:o:c:l:vwap:f:r:n:", longopts, NULL)) != EOF) + { + switch(opt) + { + case 'c': pre_l = atoi(optarg)*100; cout << "Precomputed Limit : "<< optarg << endl; break; + case 'a': all_gates(); break; + case 'v': verbose = true; break; + case 'w': write_in_file = true; break; + case 'i': f1.parse_function(optarg); break; + case 'o': target.parse_function(optarg); instance_LUT = optarg; break; + case 'n': instance_name = optarg; break; + case 'l': l = abs(atoi(optarg))*100; cout << "Lambda : "<< optarg << endl; break; + case 'p': omp_nb_threads = (atoi(optarg)); cout << "Thread number : "<< omp_nb_threads< +void faster::mitm(function_t f1) +{ + start = f1; + count_list = 0; + + if (pre_l < 0) + { + f1_succ.insert(pair > >(0, {f1.coordinates[0]})); + for (int i = 1; i < N; i++) f1_succ[0].insert(f1.coordinates[i]); + } + else + { + auto map_it = f1_succ.begin(); + while (map_it != f1_succ.end()) + { + count_list = map_it->first; + if (count_list <= shortest_path) + { + for (auto it_tmp = (*map_it).second.begin(); it_tmp != (*map_it).second.end(); it_tmp++) + { + if(is_in_graphe_collision(count_list, 0, (*it_tmp))) + { + get_implementation(); + } + } + map_it++; + } + else + { + break; + } + } + + if(shortest_path <= pre_l) + { + return; + } + count_list = pre_l + 1; + } + + while(count_list <= l) + { + if (count_list > pre_l) + { + expand(count_list); + pre_l = count_list; + } + + if(verbose && (count_list%100) == 0) cout << "\033[0;32m" << count_list << "\033[0m" << endl; + count_list++; + if(count_list > shortest_path) + { + if (count_list > pre_l) write_pre_bin(); + exit_m(); + return; + } + } + if(verbose) print_graphe_info(); + if (count_list > pre_l) + { + write_pre_bin(); + pre_l = count_list; + } + exit_m(); + return; +} + + +template +void faster::expand(int lambda) +{ + auto remove_dup = [](vector > * succ_vecs_pt, int i) + { + vector > * tmp0 = &(succ_vecs_pt[i]); + vector > * tmp1 = &(succ_vecs_pt[i^1]); + tmp1->clear(); + sort(tmp0->begin(), tmp0->end()); + auto it0 = tmp0->begin(); + tmp1->push_back(*it0); + it0++; + while (it0 != tmp0->end()) + { + if (it0->bit_slice != tmp1->back().bit_slice) + { + tmp1->push_back(*it0); + } + it0++; + } + }; + + auto to_expand = f1_succ.find(lambda); + if(to_expand != f1_succ.end()) + { + for(auto bool_op : b) + { + int bool_op_cost = bool_op.op_cost; + vector > successors[2]; + successors[0].reserve(MAX_VEC_NODES); + successors[1].reserve(MAX_VEC_NODES); + vector >* succ_vec_pt; + int i = 0; + succ_vec_pt = &(successors[i]); + + switch (bool_op.op_id) + { + case NOT1 : + for(auto f1 = (*to_expand).second.begin(); f1 != (*to_expand).second.end(); f1++) + { + bool_op_one_input(&(*f1), succ_vec_pt, bool_op); + if (succ_vec_pt->size() >= MAX_VEC_NODES) + { + PRINT("Line:: %lu: Before remove_dup successors.size(): %lu\n", succ_vec_pt->size()); + remove_dup(successors, i); + i = i ^ 1; + succ_vec_pt = &(successors[i]); + PRINT("Line:: %lu: After remove_dup successors.size(): %lu\n", succ_vec_pt->size()); + } + } + break; + case AND2 : + case OR2 : + case NAND2 : + case NOR2 : + case XOR2 : + case XNOR2 : + case MAOI1 : + case MOAI1 : + for(auto f1 = (*to_expand).second.begin(); f1 != (*to_expand).second.end(); f1++) + { + for(auto f2map = f1_succ.begin(); f2map != to_expand; f2map++) + { + for (auto f2 = (*f2map).second.begin(); f2 != (*f2map).second.end(); f2++) + { + bool_op_two_inputs(&(*f1), &(*f2), succ_vec_pt, bool_op); + if (succ_vec_pt->size() >= MAX_VEC_NODES) + { + PRINT("Line:: %lu: Before remove_dup successors.size(): %lu\n", succ_vec_pt->size()); + remove_dup(successors, i); + i = i ^ 1; + succ_vec_pt = &(successors[i]); + PRINT("Line:: %lu: After remove_dup successors.size(): %lu\n", succ_vec_pt->size()); + } + } + } + for (auto f2 = (*to_expand).second.begin(); f2 != f1; f2++) + { + bool_op_two_inputs(&(*f1), &(*f2), succ_vec_pt, bool_op); + if (succ_vec_pt->size() >= MAX_VEC_NODES) + { + PRINT("Line:: %lu: Before remove_dup successors.size(): %lu\n", succ_vec_pt->size()); + remove_dup(successors, i); + i = i ^ 1; + succ_vec_pt = &(successors[i]); + PRINT("Line:: %lu: After remove_dup successors.size(): %lu\n", succ_vec_pt->size()); + } + } + } + break; + case ANDN2 : + case ORN2 : + for(auto f1 = (*to_expand).second.begin(); f1 != (*to_expand).second.end(); f1++) + { + for(auto f2map = f1_succ.begin(); f2map != to_expand; f2map++) + { + for (auto f2 = (*f2map).second.begin(); f2 != (*f2map).second.end(); f2++) + { + bool_op_two_inputs(&(*f1), &(*f2), succ_vec_pt, bool_op); + bool_op_two_inputs(&(*f2), &(*f1), succ_vec_pt, bool_op); + if (succ_vec_pt->size() >= MAX_VEC_NODES) + { + PRINT("Line:: %lu: Before remove_dup successors.size(): %lu\n", succ_vec_pt->size()); + remove_dup(successors, i); + i = i ^ 1; + succ_vec_pt = &(successors[i]); + PRINT("Line:: %lu: After remove_dup successors.size(): %lu\n", succ_vec_pt->size()); + } + } + } + for (auto f2 = (*to_expand).second.begin(); f2 != f1; f2++) + { + bool_op_two_inputs(&(*f1), &(*f2), succ_vec_pt, bool_op); + bool_op_two_inputs(&(*f2), &(*f1), succ_vec_pt, bool_op); + if (succ_vec_pt->size() >= MAX_VEC_NODES) + { + PRINT("Line:: %lu: Before remove_dup successors.size(): %lu\n", succ_vec_pt->size()); + remove_dup(successors, i); + i = i ^ 1; + succ_vec_pt = &(successors[i]); + PRINT("Line:: %lu: After remove_dup successors.size(): %lu\n", succ_vec_pt->size()); + } + } + } + break; + case AND3 : + case OR3 : + case NAND3 : + case NOR3 : + for(auto f1 = (*to_expand).second.begin(); f1 != (*to_expand).second.end(); f1++) + { + for(auto f2map = f1_succ.begin(); f2map != to_expand; f2map++) + { + for (auto f2 = (*f2map).second.begin(); f2 != (*f2map).second.end(); f2++) + { + for(auto f3map = f1_succ.begin(); f3map != f2map; f3map++) + { + for (auto f3 = (*f3map).second.begin(); f3 != (*f3map).second.end(); f3++) + { + bool_op_three_inputs(&(*f1), &(*f2), &(*f3), succ_vec_pt, bool_op); + if (succ_vec_pt->size() >= MAX_VEC_NODES) + { + PRINT("Line:: %lu: Before remove_dup successors.size(): %lu\n", succ_vec_pt->size()); + remove_dup(successors, i); + i = i ^ 1; + succ_vec_pt = &(successors[i]); + PRINT("Line:: %lu: After remove_dup successors.size(): %lu\n", succ_vec_pt->size()); + } + } + } + for (auto f3 = (*f2map).second.begin(); f3 != f2; f3++) + { + bool_op_three_inputs(&(*f1), &(*f2), &(*f3), succ_vec_pt, bool_op); + if (succ_vec_pt->size() >= MAX_VEC_NODES) + { + PRINT("Line:: %lu: Before remove_dup successors.size(): %lu\n", succ_vec_pt->size()); + remove_dup(successors, i); + i = i ^ 1; + succ_vec_pt = &(successors[i]); + PRINT("Line:: %lu: After remove_dup successors.size(): %lu\n", succ_vec_pt->size()); + } + } + } + } + for (auto f2 = (*to_expand).second.begin(); f2 != f1; f2++) + { + for (auto f3 = (*to_expand).second.begin(); f3 != f2; f3++) + { + bool_op_three_inputs(&(*f1), &(*f2), &(*f3), succ_vec_pt, bool_op); + if (succ_vec_pt->size() >= MAX_VEC_NODES) + { + PRINT("Line:: %lu: Before remove_dup successors.size(): %lu\n", succ_vec_pt->size()); + remove_dup(successors, i); + i = i ^ 1; + succ_vec_pt = &(successors[i]); + PRINT("Line:: %lu: After remove_dup successors.size(): %lu\n", succ_vec_pt->size()); + } + } + } + } + break; + } + + if(succ_vec_pt->size() != 0) + { + PRINT("Line:: %lu: Before remove_dup successors.size(): %lu\n", succ_vec_pt->size()); + remove_dup(successors, i); + i = i ^ 1; + succ_vec_pt = &(successors[i]); + PRINT("Line:: %lu: After remove_dup successors.size(): %lu\n", succ_vec_pt->size()); + + auto s = f1_succ.find(lambda + bool_op_cost); + set > new_list; + v_list_process(lambda, bool_op_cost, succ_vec_pt, &new_list); + + if(s != f1_succ.end()) + { + s->second.insert(new_list.begin(), new_list.end()); + } + else + { + f1_succ.insert(pair > >(lambda + bool_op_cost, new_list)); + } + } + } + } +} + +template +void faster::v_list_process(int lambda, int op_cost, vector > *tmp, + set > *to_insert) +{ + PRINT("Entering v_list_process\n"); + + #pragma omp parallel for num_threads(omp_nb_threads) + for(auto it_tmp = tmp->begin(); it_tmp < tmp->end(); it_tmp++) + { + if(!is_in_graphe(lambda, op_cost, (*it_tmp))) + { + #pragma omp critical + { + to_insert->insert(*it_tmp); + nodes_cmp++; + if(nodes_cmp == max_nodes) + { + exit_m(); + } + + if(lambda > pre_l) + { + if(is_in_graphe_collision(lambda, op_cost, (*it_tmp))) + { + get_implementation(); + } + } + } + } + } + PRINT("Leaving v_list_process\n"); +} + +template +bool faster::is_in_graphe(int lambda, int op_cost, bool_function_t f) +{ + auto find = [](const set > & func_set, const bit_slice_l_t & bit_slice) + { + for(auto set_it = func_set.begin(); set_it != func_set.end(); set_it++) + { + if (set_it->bit_slice == bit_slice) + { + return set_it; + } + } + return func_set.end(); + }; + + for(auto it = f1_succ.begin(); it != f1_succ.end() ; it++) + { + auto found_f = find((*it).second, f.bit_slice); + if(found_f != (*it).second.end()) + { + if(((*it).first > lambda+op_cost) || (((*it).first == lambda+op_cost) && ((*found_f).area > f.area))) + { + #pragma omp critical (UpdateGraphe) + { + (*it).second.erase(found_f); + } + return false; + } + return true; + } + } + return false; +} + +template +bool faster::is_in_graphe_collision(int lambda, int op_cost, bool_function_t f) +{ + for (int i = 0; i < N; i++) + { + if (f.bit_slice == target.coordinates[i].bit_slice) + { + if(lambda + op_cost <= (int)target.depth[i]) + { + target.depth[i] = lambda + op_cost; + target.coordinates[i] = f; + + flag |= (1UL << i); + + if (flag == FOUND_ALL) + { + return true; + } + } + break; + } + } + return false; +} + +#ifdef PRINT +#undef PRINT +#endif +#ifdef MAX_VEC_NODES +#undef MAX_VEC_NODES +#endif +#ifdef pre_parse_args +#undef pre_parse_args +#endif +#ifdef post_parse_args +#undef post_parse_args +#endif + +#endif // #define FASTER_MITM_H__ \ No newline at end of file diff --git a/faster_test.cpp b/faster_test.cpp new file mode 100644 index 0000000..b27e4c7 --- /dev/null +++ b/faster_test.cpp @@ -0,0 +1,169 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#include +#include +#include +#include +#include +#include "faster_func.hpp" + +using namespace Peigen::depth; +using namespace std; + +#undef NOP +#undef NOT1 +#undef AND2 +#undef NAND2 +#undef OR2 +#undef NOR2 +#undef XOR2 +#undef XNOR2 +#undef MOAI1 +#undef MAOI1 +#undef NAND3 +#undef NOR3 +#undef OR3 +#undef AND3 +#undef ANDN2 +#undef ORN2 + +template +inline T MAOI1(T x, T y, T z, T t) +{ + return (~((x & y) | (~(z | t)))); +} + +template +inline T MOAI1(T x, T y, T z, T t) +{ + return (~((x | y) & (~(z & t)))); +} + +template +inline T NAND2(T x, T y) +{ + return ~(x & y); +} + +template +inline T NOR2(T x, T y) +{ + return ~(x | y); +} + +template +inline T NAND3(T x, T y, T z) +{ + return ~(x & y & z); +} + +template +inline T NOR3(T x, T y, T z) +{ + return ~(x | y | z); +} + +template +inline T AND3(T x, T y, T z) +{ + return (x & y & z); +} + +template +inline T OR3(T x, T y, T z) +{ + return (x | y | z); +} + +template +inline T AND2(T x, T y) +{ + return (x & y); +} + +template +inline T ANDN2(T x, T y) +{ + return (~x & y); +} + +template +inline T OR2(T x, T y) +{ + return (x | y); +} + +template +inline T ORN2(T x, T y) +{ + return ((~x) | y); +} + +template +inline T XOR2(T x, T y) +{ + return (x ^ y); +} + +template +inline T XNOR2(T x, T y) +{ + return ~(x ^ y); +} + +template +inline T NOT1(T x) +{ + return ~x; +} + +int main() +{ + ofstream fout("results_test.txt"); + +#define n 4 + function_t I = function_t::INPUT_DEFAULT(); + bit_slice_t X; + bit_slice_t F; + + #include "testcode.cpp" + + fout.close(); + + return 0; +} diff --git a/faster_utils.hpp b/faster_utils.hpp new file mode 100644 index 0000000..ab95741 --- /dev/null +++ b/faster_utils.hpp @@ -0,0 +1,180 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#ifndef FASTER_UTILS_H +#define FASTER_UTILS_H + +using namespace Peigen::depth; +using namespace std; + +template +void faster::print_graphe_info() +{ + int tot_f1 = 0; + + cout << "\n\033[0;31mf1_succ : \033[0m"; + + auto it = f1_succ.begin(); + + while (it != f1_succ.end()) + { + cout << (*it).first << " (" << (*it).second.size() << ") "; + tot_f1 += ((*it).second.size()); + it++; + } + cout << "\n\033[1;33mTotal = " << tot_f1; +} + +template +void faster::exit_m() +{ + if(write_in_file) + { + graphe_to_file(&f1_succ, "f1"); + } + //system("notify-send 'EXIT'"); + //exit(0); +} + +template +void faster::graphe_to_file(map > > *graphe, + string graphe_name) +{ + string name; + int tot_number = 0; + /* + Generating file_info + */ + ofstream file_info(graphe_name + "_infos.txt"); + file_info << "Number of lists in graph : " + << graphe->size() + << endl; + auto it_graphe = graphe->begin(); + while(it_graphe != graphe->end()) + { + if((*it_graphe).second.size() != 0) + { + tot_number += (*it_graphe).second.size(); + file_info << "Number of nodes in list " + << (*it_graphe).first + << " : " + << (*it_graphe).second.size() + << endl; + } + if((*it_graphe).second.size() != 0) + { + name = graphe_name + + string("_list_") + + to_string((*it_graphe).first) + + string(".txt"); + + ofstream file_list(name); + /* + Generating *_list_n.txt + */ + if(file_list.is_open()) + { + auto it_curr = (*it_graphe).second.begin(); + while(it_curr != (*it_graphe).second.end()) + { + file_list << (*it_curr).to_string()< +void faster::print_uint16(uint16_t n){ + int i = 16; + while(i != 0) + { + printf("%c", (n & 0x8000) == 0 ? '0' : '1'); + n <<= 1; + i--; + } + printf("\n"); +} + +template +void faster::print_uint8(uint8_t n){ + int i = 8; + while(i != 0) + { + printf("%c", (n & 0x80) == 0 ? '0' : '1'); + n <<= 1; + i--; + } + printf("\n"); +} + +template +string faster::uint8_to_string(uint8_t n) +{ + string s; + int i = 8; + while(i != 0) + { + (n & 0x80) == 0 ? s+='0' : s+='1'; + n <<= 1; + i--; + } + return s; +} + +template +void faster::print_uint32(uint32_t n){ + int i = 32; + while(i != 0) + { + printf("%c", (n & 0x80000000) == 0 ? '0' : '1'); + n <<= 1; + i--; + } + printf("\n"); +} + +#endif // #ifndef FASTER_UTILS_H + + diff --git a/func.hpp b/func.hpp new file mode 100644 index 0000000..443a32f --- /dev/null +++ b/func.hpp @@ -0,0 +1,4687 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#ifndef FUNC_H__ +#define FUNC_H__ + +#include "constants.hpp" +using namespace Peigen; +using namespace std; + +#define NDEBUG +#ifdef NDEBUG +#define PRINT(x) ((void)0) +#else +#define PRINT(x) x +#endif + +#ifdef NCHECKR +#define CHECKR(x) ((void)0) +#else +#define CHECKR(x) x +#endif +#ifdef NCHECKAB +#define CHECK(x) ((void)0) +#else +#define CHECK(x) x +#endif + +template +struct Peigen::function_t +{ + /* + the bit-sliced representation of the function + */ + bit_slice_t bit_slice{{}}; + /* + the line which was in the previous function + */ + bit_slice_l_t prev = {{ 0 }}; + + /* + index of changed line + */ + uint8_t info_line = 0; + /* + identifier for last boolean operation + */ + uint8_t info_op = 0; + + __m128i LUT[LUT_XMM_N]; + + function_t() { }; + + function_t(const bit_slice_t a) + { + for (int i = 0; i < N; i++) + { + bit_slice[i] = a[i]; + } + bit_slice_to_LUT(); + } + + function_t(const uint8_t aLUT[LUT_UNIT_N]) + { + const __m128i * aLUTp = (const __m128i *)aLUT; + for (int i = 0; i < LUT_XMM_N; i++) + { + LUT[i] = _mm_loadu_si128(aLUTp + i); + } + LUT_to_bit_slice(); + } + + function_t(const string str) + { + parse_function(str); + } + + static function_t INPUT_DEFAULT() + { + function_t I; + + uint8_t * x = (uint8_t *)I.LUT; + for (int i = 0; i < LUT_UNIT_N; i++) + { + x[i] = i & 0xff; + } + I.LUT_to_bit_slice(); + + return I; + } + + void parse_function(const string str) + { + if(str.size() == (BIT_SLICE_NIBBLES_N * N) + (N - 1)) + { + string_to_bit_slice(str); + bit_slice_to_LUT(); + } + else if(str.size() == (BIT_SLICE_BITS_N<<1)) + { + string_to_LUT(str); + LUT_to_bit_slice(); + } + else + { + cout << "Error: Length of the string representing the function should be either " + << (BIT_SLICE_NIBBLES_N * N) + (N - 1) << " or " + << (BIT_SLICE_BITS_N<<1) << endl; + } + } + + bool operator < (const function_t& f) const + { + return bit_slice < f.bit_slice; + } + + bool operator == (const function_t& f) const + { + return bit_slice == f.bit_slice; + } + + string to_string() const + { + stringstream ss; + ss << hex << setfill('0'); + + for (int i = N - 1; i >= 0; i--) + { + for (int j = UNIT_N - 1; j >= 0; j--) + { + ss << setw(UNIT_NIBBLE_N) << bit_slice[i][j] + '\0'; + } + ss << "_"; + } + string res = ss.str(); + res.pop_back(); + return res; + } + + // !!! Only applicable for N = 3, 4, 5, 6, 7, 8 + string LUT_to_string() const + { + stringstream ss; + ss << hex << setfill('0'); + + uint8_t * S = (uint8_t *) LUT; + + for (int i = 0; i < LUT_UNIT_N; i++) + { + ss << setw(2) << S[i] + '\0'; + } + + return ss.str(); + } + + string show_LUT() const + { + stringstream ss; + ss << "LUT = {" << endl; + ss << hex << setfill('0'); + + uint8_t * S = (uint8_t *) LUT; + for (int i = 0; i < LUT_UNIT_N; i++) + { + ss << "0x" << setw(2) << S[i] + '\0' << ","; + } + ss << endl; + ss << "};" << endl; + return ss.str(); + } + + void show_LUT(ofstream & fout) const + { + fout << show_LUT(); + } + + void string_to_bit_slice(const string& str) + { + if(str.size() == (BIT_SLICE_NIBBLES_N * N) + (N - 1)) + { + stringstream ss(str); + string number; + int i = N - 1; + while(getline(ss, number, '_') && i >= 0) + { + if(number.length() != BIT_SLICE_NIBBLES_N) + { + cout << "Length of each substring error."; + exit(1); + } + for (int j = UNIT_N - 1; j >= 0; j--) + { + bit_slice[i][j] = stoul(number.substr((UNIT_N - 1 - j)*UNIT_NIBBLE_N, UNIT_NIBBLE_N), nullptr, 16); + } + i--; + } + } + else + { + cout << "Error: Length of the string representing the function in bitsliced form should be " + << (BIT_SLICE_NIBBLES_N * N) + (N - 1) << endl; + exit(1); + } + } + + // !!! Only applicable for N = 3, 4, 5, 6, 7, 8 + void string_to_LUT(const string& str) + { + if(str.size() == (BIT_SLICE_BITS_N<<1)) + { + string tmp; + uint8_t * x = (uint8_t *)LUT; + + for (int i = 0; i < LUT_BYTE_N; i++) + { + tmp = str.substr(i * 2, 2); + x[i] = (uint8_t) stoul(tmp, nullptr, 16); + } + } + else + { + cout << "Error: Length of the string representing the function in LUT form should be " + << (BIT_SLICE_BITS_N<<1) << endl; + exit(1); + } + } + + string show() const + { + stringstream ss; + ss << hex << setfill('0'); + + uint8_t * S = (uint8_t *) LUT; + ss << "{"; + for (int i = 0; i < LUT_UNIT_N; i++) + { + ss << "0x" << setw(LUT_UNIT_NIBBLE_N) << S[i] + '\0' << ","; + } + string res = ss.str(); + res.pop_back(); + res = res + "}"; + return res; + } + + string show_concise() const + { + stringstream ss; + ss << hex << setfill('0'); + + uint8_t * S = (uint8_t *) LUT; + for (int i = 0; i < LUT_UNIT_N; i++) + { + ss << setw(LUT_UNIT_NIBBLE_N) << S[i] + '\0'; + } + string res = ss.str(); + return res; + } + + void sort() + { + bit_slice_l_t mod = bit_slice[info_line]; + StaticSort staticSort; + staticSort(bit_slice); + for(int i = 0; i < N; i++) + { + if(bit_slice[i] == mod) info_line = i; + } + } + + void sort(int iPE[N]) + { + bit_slice_PE_t b_c_tmp; + + for (int i = 0; i < N; i++) + { + b_c_tmp[i].first = bit_slice[i]; + b_c_tmp[i].second = i; + } + + StaticSort staticSort; + staticSort(b_c_tmp); + + for (int i = 0; i < N; i++) + { + bit_slice[i] = b_c_tmp[i].first; + iPE[b_c_tmp[i].second] = i; + } + } + + // !!! Only applicable for N = 3, 4, 5, 6, 7, 8 + void LUT_to_bit_slice() + { + if (N == 3) + { + const __m128i mask = _mm_set_epi64x(0x0, 0xffffffffffffffffULL); + LUT[0] = _mm_and_si128(LUT[0], mask); + bit_slice[2][0] = _mm_movemask_epi8(_mm_slli_epi16(LUT[0], 5)); + bit_slice[1][0] = _mm_movemask_epi8(_mm_slli_epi16(LUT[0], 6)); + bit_slice[0][0] = _mm_movemask_epi8(_mm_slli_epi16(LUT[0], 7)); + + } + else if (N == 4) + { + bit_slice[3][0] = _mm_movemask_epi8(_mm_slli_epi16(LUT[0], 4)); + bit_slice[2][0] = _mm_movemask_epi8(_mm_slli_epi16(LUT[0], 5)); + bit_slice[1][0] = _mm_movemask_epi8(_mm_slli_epi16(LUT[0], 6)); + bit_slice[0][0] = _mm_movemask_epi8(_mm_slli_epi16(LUT[0], 7)); + } + else if (N <= 8) + { + __m128i xmm[LUT_XMM_N]; + + for (int i = 0; i < LUT_XMM_N; i++) + { + xmm[i] = _mm_slli_epi16(LUT[i], 8 - N); + } + + for (int i = N - 1; i >= 0; i--) + { + for (int j = LUT_XMM_N - 1; j >= 0; j--) + { + bit_slice[i][(j*16)/UNIT_BIT_N] <<= 16; + bit_slice[i][(j*16)/UNIT_BIT_N] |= (unsigned short)_mm_movemask_epi8(xmm[j]); + xmm[j] = _mm_slli_epi16(xmm[j], 1); + } + } + } + } + + // !!! Only applicable for N = 3, 4, 5, 6, 7, 8 + void LUT_to_bit_slice(uint8_t x[]) + { + for (int i = 0; i < LUT_XMM_N; i++) + { + LUT[i] = _mm_loadu_si128((__m128i *)(x + i * 16)); + } + LUT_to_bit_slice(); + } + + // !!! Only applicable for N = 3, 4, 5, 6, 7, 8 + void bit_slice_to_LUT() + { + uint8_t * x = (uint8_t *) LUT; + + if (N == 3) + { + const __m128i mask = _mm_set_epi64x(0x0, 0xffffffULL); + __m128i xmm[1]; + xmm[0] = _mm_loadu_si128((__m128i *)((*bit_slice.data()).data())); + xmm[0] = _mm_and_si128(xmm[0], mask); + for (int i = 7; i >= 0; i--) + { + int tmp = _mm_movemask_epi8(xmm[0]); + x[i] = tmp & 0x7; + xmm[0] = _mm_slli_epi16(xmm[0], 1); + } + } + else if (N == 4) + { + const __m128i smm = _mm_set_epi8(0x80, 0x80, 0x80, 0x80, 0x7, 0x5, 0x3, 0x1, 0x80, 0x80, 0x80, 0x80, 0x6, 0x4, 0x2, 0x0); + __m128i xmm[1]; + xmm[0] = _mm_loadu_si128((__m128i *)((*bit_slice.data()).data())); + xmm[0] = _mm_shuffle_epi8(xmm[0], smm); + for (int i = 7; i >= 0; i--) + { + int tmp = _mm_movemask_epi8(xmm[0]); + x[i + 8] = (tmp >> 8) & 0xf; x[i] = tmp & 0xf; + xmm[0] = _mm_slli_epi16(xmm[0], 1); + } + } + else if (N == 5) + { + const __m128i mmmask = _mm_set_epi32(0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff); + const __m128i smm = _mm_set_epi8(0xf, 0xb, 0x7, 0x3, 0xe, 0xa, 0x6, 0x2, 0xd, 0x9, 0x5, 0x1, 0xc, 0x8, 0x4, 0x0); + __m128i xmm[2]; + xmm[0] = _mm_loadu_si128((__m128i *)bit_slice.data()); // 3_3 3_2 3_1 3_0 2_3 2_2 2_1 2_0 1_3 1_2 1_1 1_0 0_3 0_2 0_1 0_0 + xmm[1] = _mm_loadu_si128(((__m128i *)bit_slice.data()) + 1); // x_3 x_2 x_1 x_0 x_3 x_2 x_1 x_0 x_3 x_2 x_1 x_0 4_3 4_2 4_1 4_0 + xmm[0] = _mm_shuffle_epi8(xmm[0], smm); // 3_3 2_3 1_3 0_3 3_2 2_2 1_2 0_2 3_1 2_1 1_1 0_1 3_0 2_0 1_0 0_0 + xmm[1] = _mm_shuffle_epi8(xmm[1], smm); + xmm[1] = _mm_and_si128(xmm[1], mmmask); // x_3 x_3 x_3 4_3 x_2 x_2 x_2 4_2 x_1 x_1 x_1 4_1 x_0 x_0 x_0 4_0 + + for (int i = 7; i >= 0; i--) + { + int tmp0 = _mm_movemask_epi8(xmm[0]); + int tmp1 = _mm_movemask_epi8(xmm[1]); + x[i + 8 * 3] = ((tmp0 >> (4 * 3)) & 0xf) | ((tmp1 >> (4 * 2)) & 0x10); + x[i + 8 * 2] = ((tmp0 >> (4 * 2)) & 0xf) | ((tmp1 >> (4 * 1)) & 0x10); + x[i + 8 * 1] = ((tmp0 >> (4 * 1)) & 0xf) | ((tmp1 >> (4 * 0)) & 0x10); + x[i + 8 * 0] = ((tmp0 >> (4 * 0)) & 0xf) | ((tmp1 << (4 * 1)) & 0x10); + xmm[0] = _mm_slli_epi16(xmm[0], 1); + xmm[1] = _mm_slli_epi16(xmm[1], 1); + } + } + else if (N == 6) + { + const __m128i smm = _mm_set_epi8(0xf, 0x7, 0xe, 0x6, 0xd, 0x5, 0xc, 0x4, 0xb, 0x3, 0xa, 0x2, 0x9, 0x1, 0x8, 0x0); + __m128i xmm[4]; + __m128i tmm[4]; + xmm[0] = _mm_loadu_si128(((__m128i *)bit_slice.data()) + 0); // 1_7 1_6 1_5 1_4 1_3 1_2 1_1 1_0 0_7 0_6 0_5 0_4 0_3 0_2 0_1 0_0 + xmm[1] = _mm_loadu_si128(((__m128i *)bit_slice.data()) + 1); // 3_7 3_6 3_5 3_4 3_3 3_2 3_1 3_0 2_7 2_6 2_5 2_4 2_3 2_2 2_1 2_0 + xmm[2] = _mm_loadu_si128(((__m128i *)bit_slice.data()) + 2); // 5_7 5_6 5_5 5_4 5_3 5_2 5_1 5_0 4_7 4_6 4_5 4_4 4_3 4_2 4_1 4_0 + + xmm[0] = _mm_shuffle_epi8(xmm[0], smm); // 1_7 0_7 1_6 0_6 1_5 0_5 1_4 0_4 1_3 0_3 1_2 0_2 1_1 0_1 1_0 0_0 + xmm[1] = _mm_shuffle_epi8(xmm[1], smm); // 3_7 2_7 3_6 2_6 3_5 2_5 3_4 2_4 3_3 2_3 3_2 2_2 3_1 2_1 3_0 2_0 + xmm[2] = _mm_shuffle_epi8(xmm[2], smm); // 5_7 4_7 5_6 4_6 5_5 4_5 5_4 4_4 5_3 4_3 5_2 4_2 5_1 4_1 5_0 4_0 + xmm[3] = _mm_setzero_si128(); // x_7 x_7 x_6 x_6 x_5 x_5 x_4 x_4 x_3 x_3 x_2 x_2 x_1 x_1 x_0 x_0 + + tmm[0] = _mm_unpacklo_epi16(xmm[0], xmm[1]); // 3_3 2_3 1_3 0_3 3_2 2_2 1_2 0_2 3_1 2_1 1_1 0_1 3_0 2_0 1_0 0_0 + tmm[1] = _mm_unpackhi_epi16(xmm[0], xmm[1]); // 3_7 2_7 1_7 0_7 3_6 2_6 1_6 0_6 3_5 2_5 1_5 0_5 3_4 2_4 1_4 0_4 + tmm[2] = _mm_unpacklo_epi16(xmm[2], xmm[3]); // x_3 x_3 5_3 4_3 x_2 x_2 5_2 4_2 x_1 x_1 5_1 4_1 x_0 x_0 5_0 4_0 + tmm[3] = _mm_unpackhi_epi16(xmm[2], xmm[3]); // x_7 x_7 5_7 4_7 x_6 x_6 5_6 4_6 x_5 x_5 5_5 4_5 x_4 x_4 5_4 4_4 + + xmm[0] = _mm_unpacklo_epi32(tmm[0], tmm[2]); // x_1 x_1 5_1 4_1 3_1 2_1 1_1 0_1 x_0 x_0 5_0 4_0 3_0 2_0 1_0 0_0 + xmm[1] = _mm_unpackhi_epi32(tmm[0], tmm[2]); // x_3 x_3 5_3 4_3 3_3 2_3 1_3 0_3 x_2 x_2 5_2 4_2 3_2 2_2 1_2 0_2 + xmm[2] = _mm_unpacklo_epi32(tmm[1], tmm[3]); // x_5 x_5 5_5 4_5 3_5 2_5 1_5 0_5 x_4 x_4 5_4 4_4 3_4 2_4 1_4 0_4 + xmm[3] = _mm_unpackhi_epi32(tmm[1], tmm[3]); // x_7 x_7 5_7 4_7 3_7 2_7 1_7 0_7 x_6 x_6 5_6 4_6 3_6 2_6 1_6 0_6 + + for (int i = 7; i >= 0; i--) + { + int tmp0 = _mm_movemask_epi8(xmm[0]); + int tmp1 = _mm_movemask_epi8(xmm[1]); + int tmp2 = _mm_movemask_epi8(xmm[2]); + int tmp3 = _mm_movemask_epi8(xmm[3]); + + x[i + 8 * 7] = ((tmp3 >> (8 * 1)) & 0x3f); x[i + 8 * 6] = ((tmp3 >> (8 * 0)) & 0x3f); + x[i + 8 * 5] = ((tmp2 >> (8 * 1)) & 0x3f); x[i + 8 * 4] = ((tmp2 >> (8 * 0)) & 0x3f); + x[i + 8 * 3] = ((tmp1 >> (8 * 1)) & 0x3f); x[i + 8 * 2] = ((tmp1 >> (8 * 0)) & 0x3f); + x[i + 8 * 1] = ((tmp0 >> (8 * 1)) & 0x3f); x[i + 8 * 0] = ((tmp0 >> (8 * 0)) & 0x3f); + + xmm[0] = _mm_slli_epi16(xmm[0], 1); + xmm[1] = _mm_slli_epi16(xmm[1], 1); + xmm[2] = _mm_slli_epi16(xmm[2], 1); + xmm[3] = _mm_slli_epi16(xmm[3], 1); + } + } + else if (N == 7) + { + __m128i xmm[8]; + __m128i tmm[8]; + for (int i = 0; i < 7; i++) + { + xmm[i] = _mm_loadu_si128(((__m128i *)bit_slice.data()) + i); + } + xmm[7] = _mm_setzero_si128(); + // 0_f 0_e 0_d 0_c 0_b 0_a 0_9 0_8 0_7 0_6 0_5 0_4 0_3 0_2 0_1 0_0 + // 1_f 1_e 1_d 1_c 1_b 1_a 1_9 1_8 1_7 1_6 1_5 1_4 1_3 1_2 1_1 1_0 + // 2_f 2_e 2_d 2_c 2_b 2_a 2_9 2_8 2_7 2_6 2_5 2_4 2_3 2_2 2_1 2_0 + // 3_f 3_e 3_d 3_c 3_b 3_a 3_9 3_8 3_7 3_6 3_5 3_4 3_3 3_2 3_1 3_0 + // 4_f 4_e 4_d 4_c 4_b 4_a 4_9 4_8 4_7 4_6 4_5 4_4 4_3 4_2 4_1 4_0 + // 5_f 5_e 5_d 5_c 5_b 5_a 5_9 5_8 5_7 5_6 5_5 5_4 5_3 5_2 5_1 5_0 + // 6_f 6_e 6_d 6_c 6_b 6_a 6_9 6_8 6_7 6_6 6_5 6_4 6_3 6_2 6_1 6_0 + // x_f x_e x_d x_c x_b x_a x_9 x_8 x_7 x_6 x_5 x_4 x_3 x_2 x_1 x_0 + + tmm[0] = _mm_unpacklo_epi8(xmm[0], xmm[1]); // 1_7 0_7 1_6 0_6 1_5 0_5 1_4 0_4 1_3 0_3 1_2 0_2 1_1 0_1 1_0 0_0 + tmm[1] = _mm_unpackhi_epi8(xmm[0], xmm[1]); // 1_f 0_f 1_e 0_e 1_d 0_d 1_c 0_c 1_b 0_b 1_a 0_a 1_9 0_9 1_8 0_8 + tmm[2] = _mm_unpacklo_epi8(xmm[2], xmm[3]); // 3_7 2_7 3_6 2_6 3_5 2_5 3_4 2_4 3_3 2_3 3_2 2_2 3_1 2_1 3_0 2_0 + tmm[3] = _mm_unpackhi_epi8(xmm[2], xmm[3]); // 3_f 2_f 3_e 2_e 3_d 2_d 3_c 2_c 3_b 2_b 3_a 2_a 3_9 2_9 3_8 2_8 + tmm[4] = _mm_unpacklo_epi8(xmm[4], xmm[5]); // 5_7 4_7 5_6 4_6 5_5 4_5 5_4 4_4 5_3 4_3 5_2 4_2 5_1 4_1 5_0 4_0 + tmm[5] = _mm_unpackhi_epi8(xmm[4], xmm[5]); // 5_f 4_f 5_e 4_e 5_d 4_d 5_c 4_c 5_b 4_b 5_a 4_a 5_9 4_9 5_8 4_8 + tmm[6] = _mm_unpacklo_epi8(xmm[6], xmm[7]); // x_7 6_7 x_6 6_6 x_5 6_5 x_4 6_4 x_3 6_3 x_2 6_2 x_1 6_1 x_0 6_0 + tmm[7] = _mm_unpackhi_epi8(xmm[6], xmm[7]); // x_f 6_f x_e 6_e x_d 6_d x_c 6_c x_b 6_b x_a 6_a x_9 6_9 x_8 6_8 + + xmm[0] = _mm_unpacklo_epi16(tmm[0], tmm[2]); // 3_3 2_3 1_3 0_3 3_2 2_2 1_2 0_2 3_1 2_1 1_1 0_1 3_0 2_0 1_0 0_0 + xmm[1] = _mm_unpackhi_epi16(tmm[0], tmm[2]); // 3_7 2_7 1_7 0_7 3_6 2_6 1_6 0_6 3_5 2_5 1_5 0_5 3_4 2_4 1_4 0_4 + xmm[2] = _mm_unpacklo_epi16(tmm[1], tmm[3]); // 3_b 2_b 1_b 0_b 3_a 2_a 1_a 0_a 3_9 2_9 1_9 0_9 3_8 2_8 1_8 0_8 + xmm[3] = _mm_unpackhi_epi16(tmm[1], tmm[3]); // 3_f 2_f 1_f 0_f 3_e 2_e 1_e 0_e 3_d 2_d 1_d 0_d 3_c 2_c 1_c 0_c + xmm[4] = _mm_unpacklo_epi16(tmm[4], tmm[6]); // x_3 6_3 5_3 4_3 x_2 6_2 5_2 4_2 x_1 6_1 5_1 4_1 x_0 6_0 5_0 4_0 + xmm[5] = _mm_unpackhi_epi16(tmm[4], tmm[6]); // x_7 6_7 5_7 4_7 x_6 6_6 5_6 4_6 x_5 6_5 5_5 4_5 x_4 6_4 5_4 4_4 + xmm[6] = _mm_unpacklo_epi16(tmm[5], tmm[7]); // x_b 6_b 5_b 4_b x_a 6_a 5_a 4_a x_9 6_9 5_9 4_9 x_8 6_8 5_8 4_8 + xmm[7] = _mm_unpackhi_epi16(tmm[5], tmm[7]); // x_f 6_f 5_f 4_f x_e 6_e 5_e 4_e x_d 6_d 5_d 4_d x_c 6_c 5_c 4_c + + tmm[0] = _mm_unpacklo_epi32(xmm[0], xmm[4]); // x_1 6_1 5_1 4_1 3_1 2_1 1_1 0_1 x_0 6_0 5_0 4_0 3_0 2_0 1_0 0_0 + tmm[1] = _mm_unpackhi_epi32(xmm[0], xmm[4]); // x_3 6_3 5_3 4_3 3_3 2_3 1_3 0_3 x_2 6_2 5_2 4_2 3_2 2_2 1_2 0_2 + tmm[2] = _mm_unpacklo_epi32(xmm[1], xmm[5]); // x_5 6_5 5_5 4_5 3_5 2_5 1_5 0_5 x_4 6_4 5_4 4_4 3_4 2_4 1_4 0_4 + tmm[3] = _mm_unpackhi_epi32(xmm[1], xmm[5]); // x_7 6_7 5_7 4_7 3_7 2_7 1_7 0_7 x_6 6_6 5_6 4_6 3_6 2_6 1_6 0_6 + tmm[4] = _mm_unpacklo_epi32(xmm[2], xmm[6]); // x_9 6_9 5_9 4_9 3_9 2_9 1_9 0_9 x_8 6_8 5_8 4_8 3_8 2_8 1_8 0_8 + tmm[5] = _mm_unpackhi_epi32(xmm[2], xmm[6]); // x_b 6_b 5_b 4_b 3_b 2_b 1_b 0_b x_a 6_a 5_a 4_a 3_a 2_a 1_a 0_a + tmm[6] = _mm_unpacklo_epi32(xmm[3], xmm[7]); // x_d 6_d 5_d 4_d 3_d 2_d 1_d 0_d x_c 6_c 5_c 4_c 3_c 2_c 1_c 0_c + tmm[7] = _mm_unpackhi_epi32(xmm[3], xmm[7]); // x_f 6_f 5_f 4_f 3_f 2_f 1_f 0_f x_e 6_e 5_e 4_e 3_e 2_e 1_e 0_e + + int tmp[8]; + for (int i = 7; i >= 0; i--) + { + tmp[0] = _mm_movemask_epi8(tmm[0]); + tmp[1] = _mm_movemask_epi8(tmm[1]); + tmp[2] = _mm_movemask_epi8(tmm[2]); + tmp[3] = _mm_movemask_epi8(tmm[3]); + tmp[4] = _mm_movemask_epi8(tmm[4]); + tmp[5] = _mm_movemask_epi8(tmm[5]); + tmp[6] = _mm_movemask_epi8(tmm[6]); + tmp[7] = _mm_movemask_epi8(tmm[7]); + + x[i + 16 * 7 + 8] = ((tmp[7] >> (8 * 1)) & 0x7f); x[i + 16 * 7] = ((tmp[7] >> (8 * 0)) & 0x7f); + x[i + 16 * 6 + 8] = ((tmp[6] >> (8 * 1)) & 0x7f); x[i + 16 * 6] = ((tmp[6] >> (8 * 0)) & 0x7f); + x[i + 16 * 5 + 8] = ((tmp[5] >> (8 * 1)) & 0x7f); x[i + 16 * 5] = ((tmp[5] >> (8 * 0)) & 0x7f); + x[i + 16 * 4 + 8] = ((tmp[4] >> (8 * 1)) & 0x7f); x[i + 16 * 4] = ((tmp[4] >> (8 * 0)) & 0x7f); + x[i + 16 * 3 + 8] = ((tmp[3] >> (8 * 1)) & 0x7f); x[i + 16 * 3] = ((tmp[3] >> (8 * 0)) & 0x7f); + x[i + 16 * 2 + 8] = ((tmp[2] >> (8 * 1)) & 0x7f); x[i + 16 * 2] = ((tmp[2] >> (8 * 0)) & 0x7f); + x[i + 16 * 1 + 8] = ((tmp[1] >> (8 * 1)) & 0x7f); x[i + 16 * 1] = ((tmp[1] >> (8 * 0)) & 0x7f); + x[i + 16 * 0 + 8] = ((tmp[0] >> (8 * 1)) & 0x7f); x[i + 16 * 0] = ((tmp[0] >> (8 * 0)) & 0x7f); + + tmm[0] = _mm_slli_epi16(tmm[0], 1); + tmm[1] = _mm_slli_epi16(tmm[1], 1); + tmm[2] = _mm_slli_epi16(tmm[2], 1); + tmm[3] = _mm_slli_epi16(tmm[3], 1); + tmm[4] = _mm_slli_epi16(tmm[4], 1); + tmm[5] = _mm_slli_epi16(tmm[5], 1); + tmm[6] = _mm_slli_epi16(tmm[6], 1); + tmm[7] = _mm_slli_epi16(tmm[7], 1); + } + } + else if (N == 8) + { + __m128i xmm[8]; + __m128i tmm[8]; + for (int k = 1; k >= 0; k--) + { + for (int i = 0; i < 8; i++) + { + xmm[i] = _mm_loadu_si128(((__m128i *)bit_slice.data()) + i * 2 + k); + } + tmm[0] = _mm_unpacklo_epi8(xmm[0], xmm[1]); // 1_7 0_7 1_6 0_6 1_5 0_5 1_4 0_4 1_3 0_3 1_2 0_2 1_1 0_1 1_0 0_0 + tmm[1] = _mm_unpackhi_epi8(xmm[0], xmm[1]); // 1_f 0_f 1_e 0_e 1_d 0_d 1_c 0_c 1_b 0_b 1_a 0_a 1_9 0_9 1_8 0_8 + tmm[2] = _mm_unpacklo_epi8(xmm[2], xmm[3]); // 3_7 2_7 3_6 2_6 3_5 2_5 3_4 2_4 3_3 2_3 3_2 2_2 3_1 2_1 3_0 2_0 + tmm[3] = _mm_unpackhi_epi8(xmm[2], xmm[3]); // 3_f 2_f 3_e 2_e 3_d 2_d 3_c 2_c 3_b 2_b 3_a 2_a 3_9 2_9 3_8 2_8 + tmm[4] = _mm_unpacklo_epi8(xmm[4], xmm[5]); // 5_7 4_7 5_6 4_6 5_5 4_5 5_4 4_4 5_3 4_3 5_2 4_2 5_1 4_1 5_0 4_0 + tmm[5] = _mm_unpackhi_epi8(xmm[4], xmm[5]); // 5_f 4_f 5_e 4_e 5_d 4_d 5_c 4_c 5_b 4_b 5_a 4_a 5_9 4_9 5_8 4_8 + tmm[6] = _mm_unpacklo_epi8(xmm[6], xmm[7]); // 7_7 6_7 7_6 6_6 7_5 6_5 7_4 6_4 7_3 6_3 7_2 6_2 7_1 6_1 7_0 6_0 + tmm[7] = _mm_unpackhi_epi8(xmm[6], xmm[7]); // 7_f 6_f 7_e 6_e 7_d 6_d 7_c 6_c 7_b 6_b 7_a 6_a 7_9 6_9 7_8 6_8 + + xmm[0] = _mm_unpacklo_epi16(tmm[0], tmm[2]); // 3_3 2_3 1_3 0_3 3_2 2_2 1_2 0_2 3_1 2_1 1_1 0_1 3_0 2_0 1_0 0_0 + xmm[1] = _mm_unpackhi_epi16(tmm[0], tmm[2]); // 3_7 2_7 1_7 0_7 3_6 2_6 1_6 0_6 3_5 2_5 1_5 0_5 3_4 2_4 1_4 0_4 + xmm[2] = _mm_unpacklo_epi16(tmm[1], tmm[3]); // 3_b 2_b 1_b 0_b 3_a 2_a 1_a 0_a 3_9 2_9 1_9 0_9 3_8 2_8 1_8 0_8 + xmm[3] = _mm_unpackhi_epi16(tmm[1], tmm[3]); // 3_f 2_f 1_f 0_f 3_e 2_e 1_e 0_e 3_d 2_d 1_d 0_d 3_c 2_c 1_c 0_c + xmm[4] = _mm_unpacklo_epi16(tmm[4], tmm[6]); // 7_3 6_3 5_3 4_3 7_2 6_2 5_2 4_2 7_1 6_1 5_1 4_1 7_0 6_0 5_0 4_0 + xmm[5] = _mm_unpackhi_epi16(tmm[4], tmm[6]); // 7_7 6_7 5_7 4_7 7_6 6_6 5_6 4_6 7_5 6_5 5_5 4_5 7_4 6_4 5_4 4_4 + xmm[6] = _mm_unpacklo_epi16(tmm[5], tmm[7]); // 7_b 6_b 5_b 4_b 7_a 6_a 5_a 4_a 7_9 6_9 5_9 4_9 7_8 6_8 5_8 4_8 + xmm[7] = _mm_unpackhi_epi16(tmm[5], tmm[7]); // 7_f 6_f 5_f 4_f 7_e 6_e 5_e 4_e 7_d 6_d 5_d 4_d 7_c 6_c 5_c 4_c + + tmm[0] = _mm_unpacklo_epi32(xmm[0], xmm[4]); // 7_1 6_1 5_1 4_1 3_1 2_1 1_1 0_1 7_0 6_0 5_0 4_0 3_0 2_0 1_0 0_0 + tmm[1] = _mm_unpackhi_epi32(xmm[0], xmm[4]); // 7_3 6_3 5_3 4_3 3_3 2_3 1_3 0_3 7_2 6_2 5_2 4_2 3_2 2_2 1_2 0_2 + tmm[2] = _mm_unpacklo_epi32(xmm[1], xmm[5]); // 7_5 6_5 5_5 4_5 3_5 2_5 1_5 0_5 7_4 6_4 5_4 4_4 3_4 2_4 1_4 0_4 + tmm[3] = _mm_unpackhi_epi32(xmm[1], xmm[5]); // 7_7 6_7 5_7 4_7 3_7 2_7 1_7 0_7 7_6 6_6 5_6 4_6 3_6 2_6 1_6 0_6 + tmm[4] = _mm_unpacklo_epi32(xmm[2], xmm[6]); // 7_9 6_9 5_9 4_9 3_9 2_9 1_9 0_9 7_8 6_8 5_8 4_8 3_8 2_8 1_8 0_8 + tmm[5] = _mm_unpackhi_epi32(xmm[2], xmm[6]); // 7_b 6_b 5_b 4_b 3_b 2_b 1_b 0_b 7_a 6_a 5_a 4_a 3_a 2_a 1_a 0_a + tmm[6] = _mm_unpacklo_epi32(xmm[3], xmm[7]); // 7_d 6_d 5_d 4_d 3_d 2_d 1_d 0_d 7_c 6_c 5_c 4_c 3_c 2_c 1_c 0_c + tmm[7] = _mm_unpackhi_epi32(xmm[3], xmm[7]); // 7_f 6_f 5_f 4_f 3_f 2_f 1_f 0_f 7_e 6_e 5_e 4_e 3_e 2_e 1_e 0_e + + int tmp[8]; + for (int i = 7; i >= 0; i--) + { + tmp[0] = _mm_movemask_epi8(tmm[0]); + tmp[1] = _mm_movemask_epi8(tmm[1]); + tmp[2] = _mm_movemask_epi8(tmm[2]); + tmp[3] = _mm_movemask_epi8(tmm[3]); + tmp[4] = _mm_movemask_epi8(tmm[4]); + tmp[5] = _mm_movemask_epi8(tmm[5]); + tmp[6] = _mm_movemask_epi8(tmm[6]); + tmp[7] = _mm_movemask_epi8(tmm[7]); + + x[128 * k + i + 16 * 7 + 8] = ((tmp[7] >> (8 * 1)) & 0xff); x[128 * k + i + 16 * 7] = ((tmp[7] >> (8 * 0)) & 0xff); + x[128 * k + i + 16 * 6 + 8] = ((tmp[6] >> (8 * 1)) & 0xff); x[128 * k + i + 16 * 6] = ((tmp[6] >> (8 * 0)) & 0xff); + x[128 * k + i + 16 * 5 + 8] = ((tmp[5] >> (8 * 1)) & 0xff); x[128 * k + i + 16 * 5] = ((tmp[5] >> (8 * 0)) & 0xff); + x[128 * k + i + 16 * 4 + 8] = ((tmp[4] >> (8 * 1)) & 0xff); x[128 * k + i + 16 * 4] = ((tmp[4] >> (8 * 0)) & 0xff); + x[128 * k + i + 16 * 3 + 8] = ((tmp[3] >> (8 * 1)) & 0xff); x[128 * k + i + 16 * 3] = ((tmp[3] >> (8 * 0)) & 0xff); + x[128 * k + i + 16 * 2 + 8] = ((tmp[2] >> (8 * 1)) & 0xff); x[128 * k + i + 16 * 2] = ((tmp[2] >> (8 * 0)) & 0xff); + x[128 * k + i + 16 * 1 + 8] = ((tmp[1] >> (8 * 1)) & 0xff); x[128 * k + i + 16 * 1] = ((tmp[1] >> (8 * 0)) & 0xff); + x[128 * k + i + 16 * 0 + 8] = ((tmp[0] >> (8 * 1)) & 0xff); x[128 * k + i + 16 * 0] = ((tmp[0] >> (8 * 0)) & 0xff); + + tmm[0] = _mm_slli_epi16(tmm[0], 1); + tmm[1] = _mm_slli_epi16(tmm[1], 1); + tmm[2] = _mm_slli_epi16(tmm[2], 1); + tmm[3] = _mm_slli_epi16(tmm[3], 1); + tmm[4] = _mm_slli_epi16(tmm[4], 1); + tmm[5] = _mm_slli_epi16(tmm[5], 1); + tmm[6] = _mm_slli_epi16(tmm[6], 1); + tmm[7] = _mm_slli_epi16(tmm[7], 1); + } + } + } + else + { + cout << "bit_slice_to_LUT():: Not support for N > 8." << endl; + } + } + + // !!! Only applicable for N = 3, 4, 5, 6, 7, 8 + void bit_slice_to_LUT(uint8_t x[]) + { + bit_slice_to_LUT(); + for (int i = 0; i < LUT_XMM_N; i++) + { + _mm_storeu_si128((__m128i *)(x + i * 16), LUT[i]); + } + } + + // !!! Only applicable for N = 3, 4, 5, 6, 7, 8 + void bit_slice_to_iLUT(uint8_t x[]) + { + if (N == 3) + { + static const __m128i mask = _mm_set_epi64x(0x0, 0xffffffULL); + __m128i xmm[1]; + xmm[0] = _mm_loadu_si128((__m128i *)bit_slice.data()); + xmm[0] = _mm_and_si128(xmm[0], mask); + for (int i = 7; i >= 0; i--) + { + int tmp = _mm_movemask_epi8(xmm[0]); + x[tmp & 0x7] = i; + xmm[0] = _mm_slli_epi16(xmm[0], 1); + } + } + else if (N == 4) + { + static const __m128i smm = _mm_set_epi8(0x80, 0x80, 0x80, 0x80, 0x7, 0x5, 0x3, 0x1, 0x80, 0x80, 0x80, 0x80, 0x6, 0x4, 0x2, 0x0); + __m128i xmm[1]; + xmm[0] = _mm_loadu_si128((__m128i *)bit_slice.data()); + xmm[0] = _mm_shuffle_epi8(xmm[0], smm); + for (int i = 7; i >= 0; i--) + { + int tmp = _mm_movemask_epi8(xmm[0]); + x[(tmp >> 8) & 0xf] = i + 8; x[tmp & 0xf] = i; + xmm[0] = _mm_slli_epi16(xmm[0], 1); + } + } + else if (N == 5) + { + static const __m128i mmmask = _mm_set_epi32(0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff); + static const __m128i smm = _mm_set_epi8(0xf, 0xb, 0x7, 0x3, 0xe, 0xa, 0x6, 0x2, 0xd, 0x9, 0x5, 0x1, 0xc, 0x8, 0x4, 0x0); + __m128i xmm[2]; + xmm[0] = _mm_loadu_si128((__m128i *)bit_slice.data()); // 3_3 3_2 3_1 3_0 2_3 2_2 2_1 2_0 1_3 1_2 1_1 1_0 0_3 0_2 0_1 0_0 + xmm[1] = _mm_loadu_si128(((__m128i *)bit_slice.data()) + 1); // x_3 x_2 x_1 x_0 x_3 x_2 x_1 x_0 x_3 x_2 x_1 x_0 4_3 4_2 4_1 4_0 + xmm[0] = _mm_shuffle_epi8(xmm[0], smm); // 3_3 2_3 1_3 0_3 3_2 2_2 1_2 0_2 3_1 2_1 1_1 0_1 3_0 2_0 1_0 0_0 + xmm[1] = _mm_shuffle_epi8(xmm[1], smm); + xmm[1] = _mm_and_si128(xmm[1], mmmask); // x_3 x_3 x_3 4_3 x_2 x_2 x_2 4_2 x_1 x_1 x_1 4_1 x_0 x_0 x_0 4_0 + + for (int i = 7; i >= 0; i--) + { + int tmp0 = _mm_movemask_epi8(xmm[0]); + int tmp1 = _mm_movemask_epi8(xmm[1]); + x[((tmp0 >> (4 * 3)) & 0xf) | ((tmp1 >> (4 * 2)) & 0x10)] = i + 8 * 3; + x[((tmp0 >> (4 * 2)) & 0xf) | ((tmp1 >> (4 * 1)) & 0x10)] = i + 8 * 2; + x[((tmp0 >> (4 * 1)) & 0xf) | ((tmp1 >> (4 * 0)) & 0x10)] = i + 8 * 1; + x[((tmp0 >> (4 * 0)) & 0xf) | ((tmp1 << (4 * 1)) & 0x10)] = i + 8 * 0; + xmm[0] = _mm_slli_epi16(xmm[0], 1); + xmm[1] = _mm_slli_epi16(xmm[1], 1); + } + } + else if (N == 6) + { + static const __m128i smm = _mm_set_epi8(0xf, 0x7, 0xe, 0x6, 0xd, 0x5, 0xc, 0x4, 0xb, 0x3, 0xa, 0x2, 0x9, 0x1, 0x8, 0x0); + __m128i xmm[4]; + __m128i tmm[4]; + xmm[0] = _mm_loadu_si128(((__m128i *)bit_slice.data()) + 0); // 1_7 1_6 1_5 1_4 1_3 1_2 1_1 1_0 0_7 0_6 0_5 0_4 0_3 0_2 0_1 0_0 + xmm[1] = _mm_loadu_si128(((__m128i *)bit_slice.data()) + 1); // 3_7 3_6 3_5 3_4 3_3 3_2 3_1 3_0 2_7 2_6 2_5 2_4 2_3 2_2 2_1 2_0 + xmm[2] = _mm_loadu_si128(((__m128i *)bit_slice.data()) + 2); // 5_7 5_6 5_5 5_4 5_3 5_2 5_1 5_0 4_7 4_6 4_5 4_4 4_3 4_2 4_1 4_0 + + xmm[0] = _mm_shuffle_epi8(xmm[0], smm); // 1_7 0_7 1_6 0_6 1_5 0_5 1_4 0_4 1_3 0_3 1_2 0_2 1_1 0_1 1_0 0_0 + xmm[1] = _mm_shuffle_epi8(xmm[1], smm); // 3_7 2_7 3_6 2_6 3_5 2_5 3_4 2_4 3_3 2_3 3_2 2_2 3_1 2_1 3_0 2_0 + xmm[2] = _mm_shuffle_epi8(xmm[2], smm); // 5_7 4_7 5_6 4_6 5_5 4_5 5_4 4_4 5_3 4_3 5_2 4_2 5_1 4_1 5_0 4_0 + xmm[3] = _mm_setzero_si128(); // x_7 x_7 x_6 x_6 x_5 x_5 x_4 x_4 x_3 x_3 x_2 x_2 x_1 x_1 x_0 x_0 + + tmm[0] = _mm_unpacklo_epi16(xmm[0], xmm[1]); // 3_3 2_3 1_3 0_3 3_2 2_2 1_2 0_2 3_1 2_1 1_1 0_1 3_0 2_0 1_0 0_0 + tmm[1] = _mm_unpackhi_epi16(xmm[0], xmm[1]); // 3_7 2_7 1_7 0_7 3_6 2_6 1_6 0_6 3_5 2_5 1_5 0_5 3_4 2_4 1_4 0_4 + tmm[2] = _mm_unpacklo_epi16(xmm[2], xmm[3]); // x_3 x_3 5_3 4_3 x_2 x_2 5_2 4_2 x_1 x_1 5_1 4_1 x_0 x_0 5_0 4_0 + tmm[3] = _mm_unpackhi_epi16(xmm[2], xmm[3]); // x_7 x_7 5_7 4_7 x_6 x_6 5_6 4_6 x_5 x_5 5_5 4_5 x_4 x_4 5_4 4_4 + + xmm[0] = _mm_unpacklo_epi32(tmm[0], tmm[2]); // x_1 x_1 5_1 4_1 3_1 2_1 1_1 0_1 x_0 x_0 5_0 4_0 3_0 2_0 1_0 0_0 + xmm[1] = _mm_unpackhi_epi32(tmm[0], tmm[2]); // x_3 x_3 5_3 4_3 3_3 2_3 1_3 0_3 x_2 x_2 5_2 4_2 3_2 2_2 1_2 0_2 + xmm[2] = _mm_unpacklo_epi32(tmm[1], tmm[3]); // x_5 x_5 5_5 4_5 3_5 2_5 1_5 0_5 x_4 x_4 5_4 4_4 3_4 2_4 1_4 0_4 + xmm[3] = _mm_unpackhi_epi32(tmm[1], tmm[3]); // x_7 x_7 5_7 4_7 3_7 2_7 1_7 0_7 x_6 x_6 5_6 4_6 3_6 2_6 1_6 0_6 + + for (int i = 7; i >= 0; i--) + { + int tmp0 = _mm_movemask_epi8(xmm[0]); + int tmp1 = _mm_movemask_epi8(xmm[1]); + int tmp2 = _mm_movemask_epi8(xmm[2]); + int tmp3 = _mm_movemask_epi8(xmm[3]); + + x[((tmp3 >> (8 * 1)) & 0x3f)] = i + 8 * 7; x[((tmp3 >> (8 * 0)) & 0x3f)] = i + 8 * 6; + x[((tmp2 >> (8 * 1)) & 0x3f)] = i + 8 * 5; x[((tmp2 >> (8 * 0)) & 0x3f)] = i + 8 * 4; + x[((tmp1 >> (8 * 1)) & 0x3f)] = i + 8 * 3; x[((tmp1 >> (8 * 0)) & 0x3f)] = i + 8 * 2; + x[((tmp0 >> (8 * 1)) & 0x3f)] = i + 8 * 1; x[((tmp0 >> (8 * 0)) & 0x3f)] = i + 8 * 0; + + xmm[0] = _mm_slli_epi16(xmm[0], 1); + xmm[1] = _mm_slli_epi16(xmm[1], 1); + xmm[2] = _mm_slli_epi16(xmm[2], 1); + xmm[3] = _mm_slli_epi16(xmm[3], 1); + } + } + else if (N == 7) + { + __m128i xmm[8]; + __m128i tmm[8]; + for (int i = 0; i < 7; i++) + { + xmm[i] = _mm_loadu_si128(((__m128i *)bit_slice.data()) + i); + } + xmm[7] = _mm_setzero_si128(); + // 0_f 0_e 0_d 0_c 0_b 0_a 0_9 0_8 0_7 0_6 0_5 0_4 0_3 0_2 0_1 0_0 + // 1_f 1_e 1_d 1_c 1_b 1_a 1_9 1_8 1_7 1_6 1_5 1_4 1_3 1_2 1_1 1_0 + // 2_f 2_e 2_d 2_c 2_b 2_a 2_9 2_8 2_7 2_6 2_5 2_4 2_3 2_2 2_1 2_0 + // 3_f 3_e 3_d 3_c 3_b 3_a 3_9 3_8 3_7 3_6 3_5 3_4 3_3 3_2 3_1 3_0 + // 4_f 4_e 4_d 4_c 4_b 4_a 4_9 4_8 4_7 4_6 4_5 4_4 4_3 4_2 4_1 4_0 + // 5_f 5_e 5_d 5_c 5_b 5_a 5_9 5_8 5_7 5_6 5_5 5_4 5_3 5_2 5_1 5_0 + // 6_f 6_e 6_d 6_c 6_b 6_a 6_9 6_8 6_7 6_6 6_5 6_4 6_3 6_2 6_1 6_0 + // x_f x_e x_d x_c x_b x_a x_9 x_8 x_7 x_6 x_5 x_4 x_3 x_2 x_1 x_0 + + tmm[0] = _mm_unpacklo_epi8(xmm[0], xmm[1]); // 1_7 0_7 1_6 0_6 1_5 0_5 1_4 0_4 1_3 0_3 1_2 0_2 1_1 0_1 1_0 0_0 + tmm[1] = _mm_unpackhi_epi8(xmm[0], xmm[1]); // 1_f 0_f 1_e 0_e 1_d 0_d 1_c 0_c 1_b 0_b 1_a 0_a 1_9 0_9 1_8 0_8 + tmm[2] = _mm_unpacklo_epi8(xmm[2], xmm[3]); // 3_7 2_7 3_6 2_6 3_5 2_5 3_4 2_4 3_3 2_3 3_2 2_2 3_1 2_1 3_0 2_0 + tmm[3] = _mm_unpackhi_epi8(xmm[2], xmm[3]); // 3_f 2_f 3_e 2_e 3_d 2_d 3_c 2_c 3_b 2_b 3_a 2_a 3_9 2_9 3_8 2_8 + tmm[4] = _mm_unpacklo_epi8(xmm[4], xmm[5]); // 5_7 4_7 5_6 4_6 5_5 4_5 5_4 4_4 5_3 4_3 5_2 4_2 5_1 4_1 5_0 4_0 + tmm[5] = _mm_unpackhi_epi8(xmm[4], xmm[5]); // 5_f 4_f 5_e 4_e 5_d 4_d 5_c 4_c 5_b 4_b 5_a 4_a 5_9 4_9 5_8 4_8 + tmm[6] = _mm_unpacklo_epi8(xmm[6], xmm[7]); // x_7 6_7 x_6 6_6 x_5 6_5 x_4 6_4 x_3 6_3 x_2 6_2 x_1 6_1 x_0 6_0 + tmm[7] = _mm_unpackhi_epi8(xmm[6], xmm[7]); // x_f 6_f x_e 6_e x_d 6_d x_c 6_c x_b 6_b x_a 6_a x_9 6_9 x_8 6_8 + + xmm[0] = _mm_unpacklo_epi16(tmm[0], tmm[2]); // 3_3 2_3 1_3 0_3 3_2 2_2 1_2 0_2 3_1 2_1 1_1 0_1 3_0 2_0 1_0 0_0 + xmm[1] = _mm_unpackhi_epi16(tmm[0], tmm[2]); // 3_7 2_7 1_7 0_7 3_6 2_6 1_6 0_6 3_5 2_5 1_5 0_5 3_4 2_4 1_4 0_4 + xmm[2] = _mm_unpacklo_epi16(tmm[1], tmm[3]); // 3_b 2_b 1_b 0_b 3_a 2_a 1_a 0_a 3_9 2_9 1_9 0_9 3_8 2_8 1_8 0_8 + xmm[3] = _mm_unpackhi_epi16(tmm[1], tmm[3]); // 3_f 2_f 1_f 0_f 3_e 2_e 1_e 0_e 3_d 2_d 1_d 0_d 3_c 2_c 1_c 0_c + xmm[4] = _mm_unpacklo_epi16(tmm[4], tmm[6]); // x_3 6_3 5_3 4_3 x_2 6_2 5_2 4_2 x_1 6_1 5_1 4_1 x_0 6_0 5_0 4_0 + xmm[5] = _mm_unpackhi_epi16(tmm[4], tmm[6]); // x_7 6_7 5_7 4_7 x_6 6_6 5_6 4_6 x_5 6_5 5_5 4_5 x_4 6_4 5_4 4_4 + xmm[6] = _mm_unpacklo_epi16(tmm[5], tmm[7]); // x_b 6_b 5_b 4_b x_a 6_a 5_a 4_a x_9 6_9 5_9 4_9 x_8 6_8 5_8 4_8 + xmm[7] = _mm_unpackhi_epi16(tmm[5], tmm[7]); // x_f 6_f 5_f 4_f x_e 6_e 5_e 4_e x_d 6_d 5_d 4_d x_c 6_c 5_c 4_c + + tmm[0] = _mm_unpacklo_epi32(xmm[0], xmm[4]); // x_1 6_1 5_1 4_1 3_1 2_1 1_1 0_1 x_0 6_0 5_0 4_0 3_0 2_0 1_0 0_0 + tmm[1] = _mm_unpackhi_epi32(xmm[0], xmm[4]); // x_3 6_3 5_3 4_3 3_3 2_3 1_3 0_3 x_2 6_2 5_2 4_2 3_2 2_2 1_2 0_2 + tmm[2] = _mm_unpacklo_epi32(xmm[1], xmm[5]); // x_5 6_5 5_5 4_5 3_5 2_5 1_5 0_5 x_4 6_4 5_4 4_4 3_4 2_4 1_4 0_4 + tmm[3] = _mm_unpackhi_epi32(xmm[1], xmm[5]); // x_7 6_7 5_7 4_7 3_7 2_7 1_7 0_7 x_6 6_6 5_6 4_6 3_6 2_6 1_6 0_6 + tmm[4] = _mm_unpacklo_epi32(xmm[2], xmm[6]); // x_9 6_9 5_9 4_9 3_9 2_9 1_9 0_9 x_8 6_8 5_8 4_8 3_8 2_8 1_8 0_8 + tmm[5] = _mm_unpackhi_epi32(xmm[2], xmm[6]); // x_b 6_b 5_b 4_b 3_b 2_b 1_b 0_b x_a 6_a 5_a 4_a 3_a 2_a 1_a 0_a + tmm[6] = _mm_unpacklo_epi32(xmm[3], xmm[7]); // x_d 6_d 5_d 4_d 3_d 2_d 1_d 0_d x_c 6_c 5_c 4_c 3_c 2_c 1_c 0_c + tmm[7] = _mm_unpackhi_epi32(xmm[3], xmm[7]); // x_f 6_f 5_f 4_f 3_f 2_f 1_f 0_f x_e 6_e 5_e 4_e 3_e 2_e 1_e 0_e + + int tmp[8]; + for (int i = 7; i >= 0; i--) + { + tmp[0] = _mm_movemask_epi8(tmm[0]); + tmp[1] = _mm_movemask_epi8(tmm[1]); + tmp[2] = _mm_movemask_epi8(tmm[2]); + tmp[3] = _mm_movemask_epi8(tmm[3]); + tmp[4] = _mm_movemask_epi8(tmm[4]); + tmp[5] = _mm_movemask_epi8(tmm[5]); + tmp[6] = _mm_movemask_epi8(tmm[6]); + tmp[7] = _mm_movemask_epi8(tmm[7]); + + x[((tmp[7] >> (8 * 1)) & 0x7f)] = i + 16 * 7 + 8; x[((tmp[7] >> (8 * 0)) & 0x7f)] = i + 16 * 7; + x[((tmp[6] >> (8 * 1)) & 0x7f)] = i + 16 * 6 + 8; x[((tmp[6] >> (8 * 0)) & 0x7f)] = i + 16 * 6; + x[((tmp[5] >> (8 * 1)) & 0x7f)] = i + 16 * 5 + 8; x[((tmp[5] >> (8 * 0)) & 0x7f)] = i + 16 * 5; + x[((tmp[4] >> (8 * 1)) & 0x7f)] = i + 16 * 4 + 8; x[((tmp[4] >> (8 * 0)) & 0x7f)] = i + 16 * 4; + x[((tmp[3] >> (8 * 1)) & 0x7f)] = i + 16 * 3 + 8; x[((tmp[3] >> (8 * 0)) & 0x7f)] = i + 16 * 3; + x[((tmp[2] >> (8 * 1)) & 0x7f)] = i + 16 * 2 + 8; x[((tmp[2] >> (8 * 0)) & 0x7f)] = i + 16 * 2; + x[((tmp[1] >> (8 * 1)) & 0x7f)] = i + 16 * 1 + 8; x[((tmp[1] >> (8 * 0)) & 0x7f)] = i + 16 * 1; + x[((tmp[0] >> (8 * 1)) & 0x7f)] = i + 16 * 0 + 8; x[((tmp[0] >> (8 * 0)) & 0x7f)] = i + 16 * 0; + + tmm[0] = _mm_slli_epi16(tmm[0], 1); + tmm[1] = _mm_slli_epi16(tmm[1], 1); + tmm[2] = _mm_slli_epi16(tmm[2], 1); + tmm[3] = _mm_slli_epi16(tmm[3], 1); + tmm[4] = _mm_slli_epi16(tmm[4], 1); + tmm[5] = _mm_slli_epi16(tmm[5], 1); + tmm[6] = _mm_slli_epi16(tmm[6], 1); + tmm[7] = _mm_slli_epi16(tmm[7], 1); + } + } + else if (N == 8) + { + __m128i xmm[8]; + __m128i tmm[8]; + for (int k = 1; k >= 0; k--) + { + for (int i = 0; i < 8; i++) + { + xmm[i] = _mm_loadu_si128(((__m128i *)bit_slice.data()) + i * 2 + k); + } + tmm[0] = _mm_unpacklo_epi8(xmm[0], xmm[1]); // 1_7 0_7 1_6 0_6 1_5 0_5 1_4 0_4 1_3 0_3 1_2 0_2 1_1 0_1 1_0 0_0 + tmm[1] = _mm_unpackhi_epi8(xmm[0], xmm[1]); // 1_f 0_f 1_e 0_e 1_d 0_d 1_c 0_c 1_b 0_b 1_a 0_a 1_9 0_9 1_8 0_8 + tmm[2] = _mm_unpacklo_epi8(xmm[2], xmm[3]); // 3_7 2_7 3_6 2_6 3_5 2_5 3_4 2_4 3_3 2_3 3_2 2_2 3_1 2_1 3_0 2_0 + tmm[3] = _mm_unpackhi_epi8(xmm[2], xmm[3]); // 3_f 2_f 3_e 2_e 3_d 2_d 3_c 2_c 3_b 2_b 3_a 2_a 3_9 2_9 3_8 2_8 + tmm[4] = _mm_unpacklo_epi8(xmm[4], xmm[5]); // 5_7 4_7 5_6 4_6 5_5 4_5 5_4 4_4 5_3 4_3 5_2 4_2 5_1 4_1 5_0 4_0 + tmm[5] = _mm_unpackhi_epi8(xmm[4], xmm[5]); // 5_f 4_f 5_e 4_e 5_d 4_d 5_c 4_c 5_b 4_b 5_a 4_a 5_9 4_9 5_8 4_8 + tmm[6] = _mm_unpacklo_epi8(xmm[6], xmm[7]); // 7_7 6_7 7_6 6_6 7_5 6_5 7_4 6_4 7_3 6_3 7_2 6_2 7_1 6_1 7_0 6_0 + tmm[7] = _mm_unpackhi_epi8(xmm[6], xmm[7]); // 7_f 6_f 7_e 6_e 7_d 6_d 7_c 6_c 7_b 6_b 7_a 6_a 7_9 6_9 7_8 6_8 + + xmm[0] = _mm_unpacklo_epi16(tmm[0], tmm[2]); // 3_3 2_3 1_3 0_3 3_2 2_2 1_2 0_2 3_1 2_1 1_1 0_1 3_0 2_0 1_0 0_0 + xmm[1] = _mm_unpackhi_epi16(tmm[0], tmm[2]); // 3_7 2_7 1_7 0_7 3_6 2_6 1_6 0_6 3_5 2_5 1_5 0_5 3_4 2_4 1_4 0_4 + xmm[2] = _mm_unpacklo_epi16(tmm[1], tmm[3]); // 3_b 2_b 1_b 0_b 3_a 2_a 1_a 0_a 3_9 2_9 1_9 0_9 3_8 2_8 1_8 0_8 + xmm[3] = _mm_unpackhi_epi16(tmm[1], tmm[3]); // 3_f 2_f 1_f 0_f 3_e 2_e 1_e 0_e 3_d 2_d 1_d 0_d 3_c 2_c 1_c 0_c + xmm[4] = _mm_unpacklo_epi16(tmm[4], tmm[6]); // 7_3 6_3 5_3 4_3 7_2 6_2 5_2 4_2 7_1 6_1 5_1 4_1 7_0 6_0 5_0 4_0 + xmm[5] = _mm_unpackhi_epi16(tmm[4], tmm[6]); // 7_7 6_7 5_7 4_7 7_6 6_6 5_6 4_6 7_5 6_5 5_5 4_5 7_4 6_4 5_4 4_4 + xmm[6] = _mm_unpacklo_epi16(tmm[5], tmm[7]); // 7_b 6_b 5_b 4_b 7_a 6_a 5_a 4_a 7_9 6_9 5_9 4_9 7_8 6_8 5_8 4_8 + xmm[7] = _mm_unpackhi_epi16(tmm[5], tmm[7]); // 7_f 6_f 5_f 4_f 7_e 6_e 5_e 4_e 7_d 6_d 5_d 4_d 7_c 6_c 5_c 4_c + + tmm[0] = _mm_unpacklo_epi32(xmm[0], xmm[4]); // 7_1 6_1 5_1 4_1 3_1 2_1 1_1 0_1 7_0 6_0 5_0 4_0 3_0 2_0 1_0 0_0 + tmm[1] = _mm_unpackhi_epi32(xmm[0], xmm[4]); // 7_3 6_3 5_3 4_3 3_3 2_3 1_3 0_3 7_2 6_2 5_2 4_2 3_2 2_2 1_2 0_2 + tmm[2] = _mm_unpacklo_epi32(xmm[1], xmm[5]); // 7_5 6_5 5_5 4_5 3_5 2_5 1_5 0_5 7_4 6_4 5_4 4_4 3_4 2_4 1_4 0_4 + tmm[3] = _mm_unpackhi_epi32(xmm[1], xmm[5]); // 7_7 6_7 5_7 4_7 3_7 2_7 1_7 0_7 7_6 6_6 5_6 4_6 3_6 2_6 1_6 0_6 + tmm[4] = _mm_unpacklo_epi32(xmm[2], xmm[6]); // 7_9 6_9 5_9 4_9 3_9 2_9 1_9 0_9 7_8 6_8 5_8 4_8 3_8 2_8 1_8 0_8 + tmm[5] = _mm_unpackhi_epi32(xmm[2], xmm[6]); // 7_b 6_b 5_b 4_b 3_b 2_b 1_b 0_b 7_a 6_a 5_a 4_a 3_a 2_a 1_a 0_a + tmm[6] = _mm_unpacklo_epi32(xmm[3], xmm[7]); // 7_d 6_d 5_d 4_d 3_d 2_d 1_d 0_d 7_c 6_c 5_c 4_c 3_c 2_c 1_c 0_c + tmm[7] = _mm_unpackhi_epi32(xmm[3], xmm[7]); // 7_f 6_f 5_f 4_f 3_f 2_f 1_f 0_f 7_e 6_e 5_e 4_e 3_e 2_e 1_e 0_e + + int tmp[8]; + for (int i = 7; i >= 0; i--) + { + tmp[0] = _mm_movemask_epi8(tmm[0]); + tmp[1] = _mm_movemask_epi8(tmm[1]); + tmp[2] = _mm_movemask_epi8(tmm[2]); + tmp[3] = _mm_movemask_epi8(tmm[3]); + tmp[4] = _mm_movemask_epi8(tmm[4]); + tmp[5] = _mm_movemask_epi8(tmm[5]); + tmp[6] = _mm_movemask_epi8(tmm[6]); + tmp[7] = _mm_movemask_epi8(tmm[7]); + + x[((tmp[7] >> (8 * 1)) & 0xff)] = 128 * k + i + 16 * 7 + 8; x[((tmp[7] >> (8 * 0)) & 0xff)] = 128 * k + i + 16 * 7; + x[((tmp[6] >> (8 * 1)) & 0xff)] = 128 * k + i + 16 * 6 + 8; x[((tmp[6] >> (8 * 0)) & 0xff)] = 128 * k + i + 16 * 6; + x[((tmp[5] >> (8 * 1)) & 0xff)] = 128 * k + i + 16 * 5 + 8; x[((tmp[5] >> (8 * 0)) & 0xff)] = 128 * k + i + 16 * 5; + x[((tmp[4] >> (8 * 1)) & 0xff)] = 128 * k + i + 16 * 4 + 8; x[((tmp[4] >> (8 * 0)) & 0xff)] = 128 * k + i + 16 * 4; + x[((tmp[3] >> (8 * 1)) & 0xff)] = 128 * k + i + 16 * 3 + 8; x[((tmp[3] >> (8 * 0)) & 0xff)] = 128 * k + i + 16 * 3; + x[((tmp[2] >> (8 * 1)) & 0xff)] = 128 * k + i + 16 * 2 + 8; x[((tmp[2] >> (8 * 0)) & 0xff)] = 128 * k + i + 16 * 2; + x[((tmp[1] >> (8 * 1)) & 0xff)] = 128 * k + i + 16 * 1 + 8; x[((tmp[1] >> (8 * 0)) & 0xff)] = 128 * k + i + 16 * 1; + x[((tmp[0] >> (8 * 1)) & 0xff)] = 128 * k + i + 16 * 0 + 8; x[((tmp[0] >> (8 * 0)) & 0xff)] = 128 * k + i + 16 * 0; + + tmm[0] = _mm_slli_epi16(tmm[0], 1); + tmm[1] = _mm_slli_epi16(tmm[1], 1); + tmm[2] = _mm_slli_epi16(tmm[2], 1); + tmm[3] = _mm_slli_epi16(tmm[3], 1); + tmm[4] = _mm_slli_epi16(tmm[4], 1); + tmm[5] = _mm_slli_epi16(tmm[5], 1); + tmm[6] = _mm_slli_epi16(tmm[6], 1); + tmm[7] = _mm_slli_epi16(tmm[7], 1); + } + } + } + else + { + cout << "bit_slice_to_iLUT():: Not support for N > 8." << endl; + } + } + + #define EQU(w, u) (_mm_testz_si128(one_128, _mm_xor_si128(w, u)) == 1) + #define PERM(w) (_mm_extract_epi16(_mm_cmpestrm(w, 16, S4_I, 16, 0x00), 0) == 0xffff) + + bool is_permutation() const + { + if (N == 4) + { + return PERM(LUT[0]); + } + else + { + const uint8_t * LUTp = (uint8_t *) LUT; + + bit_slice_l_t ind = { {0} }; + + for (int i = 0; i < LUT_UNIT_N; i++) + { + uint8_t y = LUTp[i]; + if (OPs.get_bit(ind, y) == 1) + { + return false; + } + OPs.set_bit_inplace(ind, y); + } + return true; + } + } + + void inverse(uint8_t iS[LUT_UNIT_N]) const + { + if (is_permutation()) + { + uint8_t * S = (uint8_t *) LUT; + for (int x = 0; x < LUT_UNIT_N; x++) + { + iS[S[x]] = x; + } + } + else + { + for (int x = 0; x < LUT_UNIT_N; x++) + { + iS[x] = 0; + } + } + }; + + function_t inverse() const + { + function_t res; + uint8_t * iS = (uint8_t *) res.LUT; + if (is_permutation()) + { + uint8_t * S = (uint8_t *) LUT; + for (int x = 0; x < LUT_UNIT_N; x++) + { + iS[S[x]] = x; + } + } + else + { + for (int x = 0; x < LUT_UNIT_N; x++) + { + iS[x] = 0; + } + } + res.LUT_to_bit_slice(); + return res; + }; + + bool is_involution() const + { + if (N == 4) + { + __m128i iLUT[LUT_XMM_N]; + inverse((uint8_t *)iLUT); + return EQU(LUT[0], iLUT[0]); + } + else + { + uint8_t iS[LUT_UNIT_N]; + uint8_t * S = (uint8_t *)LUT; + inverse(iS); + for (int i = 0; i < LUT_UNIT_N; i++) + { + if (S[i] != iS[i]) return false; + } + return true; + } + } + + void difference_distribution_matrix(int DDT[LUT_UNIT_N][LUT_UNIT_N], int & Diff, int DDT_spectrum[LUT_UNIT_N+1], int & Diff1, int DDT1_spectrum[LUT_UNIT_N+1]) const + { + memset(DDT[0], 0, sizeof(int) * LUT_UNIT_N * LUT_UNIT_N); + Diff = 0; + memset(DDT_spectrum, 0, sizeof(int) * (LUT_UNIT_N+1)); + Diff1 = 0; + memset(DDT1_spectrum, 0, sizeof(int) * (LUT_UNIT_N+1)); + + if (N == 4) + { + __m128i ton; + __m128i t1; + __m128i t2; + int cnt; + + #undef TestOd_1_1 + #define TestOd_1_1(id, od) \ + { \ + ton = _mm_cmpeq_epi8(x##od, t2); \ + cnt = _mm_popcnt_u32(_mm_movemask_epi8(ton)); \ + DDT[0x##id][0x##od] = cnt; \ + Diff = cnt > Diff ? cnt : Diff; \ + DDT_spectrum[cnt]++; \ + Diff1 = cnt > Diff1 ? cnt : Diff1; \ + DDT1_spectrum[cnt]++; \ + } + + #undef TestOd + #define TestOd(id, od) \ + { \ + ton = _mm_cmpeq_epi8(x##od, t2); \ + cnt = _mm_popcnt_u32(_mm_movemask_epi8(ton)); \ + DDT[0x##id][0x##od] = cnt; \ + Diff = cnt > Diff ? cnt : Diff; \ + DDT_spectrum[cnt]++; \ + } + + #undef TestId_1 + #define TestId_1(id) \ + { \ + t1 = _mm_xor_si128(x##id, x); \ + t2 = _mm_shuffle_epi8(LUT[0], t1); \ + t2 = _mm_xor_si128(LUT[0], t2); \ + TestOd_1_1(id, 1); \ + TestOd_1_1(id, 2); \ + TestOd_1_1(id, 4); \ + TestOd_1_1(id, 8); \ + TestOd(id, 0); \ + TestOd(id, 3); \ + TestOd(id, 5); \ + TestOd(id, 6); \ + TestOd(id, 9); \ + TestOd(id, a); \ + TestOd(id, c); \ + TestOd(id, 7); \ + TestOd(id, b); \ + TestOd(id, d); \ + TestOd(id, e); \ + TestOd(id, f); \ + } + + #undef TestId_n + #define TestId_n(id) \ + { \ + t1 = _mm_xor_si128(x##id, x); \ + t2 = _mm_shuffle_epi8(LUT[0], t1); \ + t2 = _mm_xor_si128(LUT[0], t2); \ + TestOd(id, 0); \ + TestOd(id, 1); \ + TestOd(id, 2); \ + TestOd(id, 4); \ + TestOd(id, 8); \ + TestOd(id, 3); \ + TestOd(id, 5); \ + TestOd(id, 6); \ + TestOd(id, 9); \ + TestOd(id, a); \ + TestOd(id, c); \ + TestOd(id, 7); \ + TestOd(id, b); \ + TestOd(id, d); \ + TestOd(id, e); \ + TestOd(id, f); \ + } + + TestId_1(1); + TestId_1(2); + TestId_1(4); + TestId_1(8); + TestId_n(3); + TestId_n(5); + TestId_n(6); + TestId_n(9); + TestId_n(a); + TestId_n(c); + TestId_n(7); + TestId_n(b); + TestId_n(d); + TestId_n(e); + TestId_n(f); + DDT[0][0] = 16; + DDT_spectrum[0] += 15; + DDT_spectrum[16]++; + } + else if (N <= 8) + { + int cnt; + + uint8_t * S = (uint8_t *)LUT; + for (int i = 1; i <= N; i++) + { + int id = HWorder[i]; + + for (int x = 0; x < LUT_UNIT_N; x++) + { + int od = S[x] ^ S[x ^ id]; + DDT[id][od]++; + } + for (int o = 1; o <= N; o++) + { + int od = HWorder[o]; + cnt = DDT[id][od]; + Diff = cnt > Diff ? cnt : Diff; + DDT_spectrum[cnt]++; + Diff1 = cnt > Diff1 ? cnt : Diff1; + DDT1_spectrum[cnt]++; + } + for (int o = N + 1; o <= LUT_UNIT_N; o++) + { + int od = HWorder[o%LUT_UNIT_N]; + cnt = DDT[id][od]; + DDT_spectrum[cnt]++; + Diff = cnt > Diff ? cnt : Diff; + } + } + for (int i = N + 1; i < LUT_UNIT_N; i++) + { + int id = HWorder[i]; + for (int x = 0; x < LUT_UNIT_N; x++) + { + int od = S[x] ^ S[x ^ id]; + DDT[id][od]++; + } + for (int od = 0; od < LUT_UNIT_N; od++) + { + cnt = DDT[id][od]; + DDT_spectrum[cnt]++; + Diff = cnt > Diff ? cnt : Diff; + } + } + DDT[0][0] = LUT_UNIT_N; + DDT_spectrum[LUT_UNIT_N]++; + DDT_spectrum[0] += LUT_UNIT_N - 1; + } + else + { + // cout << "difference_distribution_matrix():: not support for N < 3 and N > 8" << endl; + } + } + + string show_difference_distribution_matrix() const + { + int DDT[LUT_UNIT_N][LUT_UNIT_N]; + int Diff; + int DDT_spectrum[LUT_UNIT_N+1]; + int Diff1; + int DDT1_spectrum[LUT_UNIT_N+1]; + difference_distribution_matrix(DDT, Diff, DDT_spectrum, Diff1, DDT1_spectrum); + + stringstream ss; + ss << "DDT = " << endl; + //ss << OPs.show_matrix(DDT[0], LUT_UNIT_N, LUT_UNIT_N); + ss << OPs.show_matrix_HWorder(DDT); + ss << "Diff: " << Diff << ", " << "DDT_spectrum: {"; + for (int i = 0; i < LUT_UNIT_N+1; i++) + { + if (DDT_spectrum[i]!=0) ss << i << ":" << DDT_spectrum[i] << ", "; + } + ss << "};" << endl; + ss << "Diff1: " << Diff1 << ", " << "DDT1_spectrum: {"; + for (int i = 0; i < LUT_UNIT_N+1; i++) + { + if (DDT1_spectrum[i]!=0) ss << i << ":" << DDT1_spectrum[i] << ", "; + } + ss << "};" << endl; + return ss.str(); + } + + void show_difference_distribution_matrix(ofstream & fout) const + { + fout << show_difference_distribution_matrix(); + } + + bool difference_distribution_matrix_test(int MaxDiff_Bound, int MaxDiff1_Bound, int DiffFreq_Bound, int CardD1_Bound) const + { + bool computeAll = false; + + if (MaxDiff_Bound == -1) MaxDiff_Bound = LUT_UNIT_N; + else computeAll = true; + + if (DiffFreq_Bound == -1) DiffFreq_Bound = LUT_UNIT_N * LUT_UNIT_N; + else computeAll = true; + + if (MaxDiff1_Bound == -1) MaxDiff1_Bound = MaxDiff_Bound; + if (CardD1_Bound == -1) CardD1_Bound = N * N; + + int Diff = 0; + int CardD1 = 0; + int DDT_spectrum[LUT_UNIT_N+1] = {0}; + + if (N == 4) + { + __m128i ton; + __m128i t1; + __m128i t2; + int cnt; + + #undef TestOd_1_1 + #define TestOd_1_1(id, od) \ + { \ + ton = _mm_cmpeq_epi8(x##od, t2); \ + cnt = _mm_popcnt_u32(_mm_movemask_epi8(ton)); \ + if (cnt > MaxDiff1_Bound) return false; \ + CardD1 += (cnt == 0) ? 0 : 1; \ + if (CardD1 > CardD1_Bound) return false; \ + Diff = cnt > Diff ? cnt : Diff; \ + DDT_spectrum[cnt]++; \ + } + + #undef TestOd + #define TestOd(id, od) \ + { \ + ton = _mm_cmpeq_epi8(x##od, t2); \ + cnt = _mm_popcnt_u32(_mm_movemask_epi8(ton)); \ + if (cnt > MaxDiff_Bound) return false; \ + Diff = cnt > Diff ? cnt : Diff; \ + DDT_spectrum[cnt]++; \ + } + + #undef TestId_1 + #define TestId_1(id) \ + { \ + t1 = _mm_xor_si128(x##id, x); \ + t2 = _mm_shuffle_epi8(LUT[0], t1); \ + t2 = _mm_xor_si128(LUT[0], t2); \ + TestOd_1_1(id, 1); \ + TestOd_1_1(id, 2); \ + TestOd_1_1(id, 4); \ + TestOd_1_1(id, 8); \ + if (computeAll) \ + { \ + TestOd(id, 0); \ + TestOd(id, 3); \ + TestOd(id, 5); \ + TestOd(id, 6); \ + TestOd(id, 9); \ + TestOd(id, a); \ + TestOd(id, c); \ + TestOd(id, 7); \ + TestOd(id, b); \ + TestOd(id, d); \ + TestOd(id, e); \ + TestOd(id, f); \ + } \ + } + + #undef TestId_n + #define TestId_n(id) \ + { \ + t1 = _mm_xor_si128(x##id, x); \ + t2 = _mm_shuffle_epi8(LUT[0], t1); \ + t2 = _mm_xor_si128(LUT[0], t2); \ + TestOd(id, 0); \ + TestOd(id, 1); \ + TestOd(id, 2); \ + TestOd(id, 4); \ + TestOd(id, 8); \ + TestOd(id, 3); \ + TestOd(id, 5); \ + TestOd(id, 6); \ + TestOd(id, 9); \ + TestOd(id, a); \ + TestOd(id, c); \ + TestOd(id, 7); \ + TestOd(id, b); \ + TestOd(id, d); \ + TestOd(id, e); \ + TestOd(id, f); \ + } + + TestId_1(1); + TestId_1(2); + TestId_1(4); + TestId_1(8); + if (computeAll) + { + TestId_n(3); + TestId_n(5); + TestId_n(6); + TestId_n(9); + TestId_n(a); + TestId_n(c); + TestId_n(7); + TestId_n(b); + TestId_n(d); + TestId_n(e); + TestId_n(f); + if (DDT_spectrum[Diff] > DiffFreq_Bound) return false; + } + return true; + } + else if (N <= 8) + { + int DDT_id[LUT_UNIT_N]; + int cnt; + + uint8_t * S = (uint8_t *)LUT; + for (int i = 1; i <= N; i++) + { + int id = HWorder[i]; + memset(DDT_id, 0, sizeof(int) * LUT_UNIT_N); + for (int x = 0; x < LUT_UNIT_N; x++) + { + int od = S[x] ^ S[x ^ id]; + DDT_id[od]++; + } + for (int o = 1; o <= N; o++) + { + int od = HWorder[o]; + cnt = DDT_id[od]; + if (cnt > MaxDiff1_Bound) return false; + CardD1 += (cnt == 0) ? 0 : 1; + if (CardD1 > CardD1_Bound) return false; + Diff = cnt > Diff ? cnt : Diff; + DDT_spectrum[cnt]++; + } + if (computeAll) + { + for (int o = N + 1; o <= LUT_UNIT_N; o++) + { + int od = HWorder[o%LUT_UNIT_N]; + cnt = DDT_id[od]; + if (cnt > MaxDiff_Bound) return false; + Diff = cnt > Diff ? cnt : Diff; + DDT_spectrum[cnt]++; + } + } + } + if (computeAll) + { + for (int i = N + 1; i < LUT_UNIT_N; i++) + { + int id = HWorder[i]; + memset(DDT_id, 0, sizeof(int) * LUT_UNIT_N); + for (int x = 0; x < LUT_UNIT_N; x++) + { + int od = S[x] ^ S[x ^ id]; + DDT_id[od]++; + } + for (int od = 0; od < LUT_UNIT_N; od++) + { + cnt = DDT_id[od]; + if (cnt > MaxDiff_Bound) return false; + Diff = cnt > Diff ? cnt : Diff; + DDT_spectrum[cnt]++; + } + } + if (DDT_spectrum[Diff] > DiffFreq_Bound) return false; + } + return true; + } + else + { + // cout << "linear_approximation_matrix():: not support for N < 3 and N > 8" << endl; + } + } + + void boomerang_connection_table(int BCT[LUT_UNIT_N][LUT_UNIT_N], int & BCT_uniformity, int BCT_spectrum[LUT_UNIT_N+1]) const + { + if (!is_permutation()) + { + return; + } + memset(BCT[0], 0, sizeof(int) * LUT_UNIT_N * LUT_UNIT_N); + BCT_uniformity = 0; + memset(BCT_spectrum, 0, sizeof(int) * (LUT_UNIT_N+1)); + + if ((N >= 3) && (N <= 8)) + { + int cnt; + uint8_t * S = (uint8_t *)LUT; + uint8_t iS[LUT_UNIT_N]; + inverse(iS); + + vector T[LUT_UNIT_N]; + for (int yi = 0; yi < LUT_UNIT_N; yi++) T[yi].reserve(LUT_UNIT_N); + for (int od = 0; od < LUT_UNIT_N; od++) + { + for (int x = 0; x < LUT_UNIT_N; x++) + { + uint8_t y = x ^ iS[S[x] ^ od]; + T[y].push_back(x); + } + for (int yi = 0; yi < LUT_UNIT_N; yi++) + { + for (int xii = 0; xii < T[yi].size(); xii++) + { + uint8_t xi = T[yi][xii]; + for (int xji = 0; xji < T[yi].size(); xji++) + { + uint8_t xj = T[yi][xji]; + uint8_t id = xi ^ xj; + BCT[id][od]++; + } + } + T[yi].clear(); + } + for (int id = 0; id < LUT_UNIT_N; id++) + { + if ((id != 0) && (od != 0)) {BCT_uniformity = BCT[id][od] > BCT_uniformity ? BCT[id][od] : BCT_uniformity;} + BCT_spectrum[BCT[id][od]]++; + } + } + } + else + { + // cout << "boomerang_connection_table():: not support for N < 3 and N > 8" << endl; + } + } + + string show_boomerang_connection_table() const + { + if (!is_permutation()) + { + return "The Sbox is not a permutation and I can not compute its BCT."; + } + int BCT[LUT_UNIT_N][LUT_UNIT_N]; + int BCT_uniformity; + int BCT_spectrum[LUT_UNIT_N+1]; + boomerang_connection_table(BCT, BCT_uniformity, BCT_spectrum); + + stringstream ss; + ss << "BCT = " << endl; + //ss << OPs.show_matrix(BCT[0], LUT_UNIT_N, LUT_UNIT_N); + ss << OPs.show_matrix_HWorder(BCT); + ss << "BCT_uniformity: " << BCT_uniformity << ", " << "BCT_spectrum: {"; + for (int i = 0; i < LUT_UNIT_N+1; i++) + { + if (BCT_spectrum[i]!=0) ss << i << ":" << BCT_spectrum[i] << ", "; + } + ss << "};" << endl; + return ss.str(); + } + + void show_boomerang_connection_table(ofstream & fout) const + { + fout << show_boomerang_connection_table(); + } + + bool boomerang_connection_table_test(int BCT_uniformity_Bound, int BCT_uniformity_Freq_Bound) const + { + int BCT[LUT_UNIT_N][LUT_UNIT_N]; + int BCT_uniformity; + int BCT_spectrum[LUT_UNIT_N+1]; + + memset(BCT[0], 0, sizeof(int) * LUT_UNIT_N * LUT_UNIT_N); + BCT_uniformity = 0; + memset(BCT_spectrum, 0, sizeof(int) * (LUT_UNIT_N+1)); + + if ((N >= 3) && (N <= 8)) + { + int cnt; + uint8_t * S = (uint8_t *)LUT; + uint8_t iS[LUT_UNIT_N]; + inverse(iS); + + vector T[LUT_UNIT_N]; + for (int yi = 0; yi < LUT_UNIT_N; yi++) T[yi].reserve(LUT_UNIT_N); + for (int od = 0; od < LUT_UNIT_N; od++) + { + for (int x = 0; x < LUT_UNIT_N; x++) + { + uint8_t y = x ^ iS[S[x] ^ od]; + T[y].push_back(x); + } + for (int yi = 0; yi < LUT_UNIT_N; yi++) + { + for (int xii = 0; xii < T[yi].size(); xii++) + { + uint8_t xi = T[yi][xii]; + for (int xji = 0; xji < T[yi].size(); xji++) + { + uint8_t xj = T[yi][xji]; + uint8_t id = xi ^ xj; + BCT[id][od]++; + } + } + T[yi].clear(); + } + for (int id = 0; id < LUT_UNIT_N; id++) + { + if ((id != 0) && (od != 0)) + { + BCT_uniformity = BCT[id][od] > BCT_uniformity ? BCT[id][od] : BCT_uniformity; + } + if (BCT_uniformity > BCT_uniformity_Bound) return false; + BCT_spectrum[BCT[id][od]]++; + } + } + if (BCT_spectrum[BCT_uniformity] > BCT_uniformity_Freq_Bound) return false; + return true; + } + else + { + // cout << "boomerang_connection_table():: not support for N < 3 and N > 8" << endl; + return false; + } + } + + void linear_approximation_matrix(int LAT[LUT_UNIT_N][LUT_UNIT_N], int & Lin, int LAT_spectrum[LUT_UNIT_N+1], int & Lin1, int LAT1_spectrum[LUT_UNIT_N+1]) const + { + memset(LAT[0], 0, sizeof(int) * LUT_UNIT_N * LUT_UNIT_N); + Lin = 0; + memset(LAT_spectrum, 0, sizeof(int) * (LUT_UNIT_N+1)); + Lin1 = 0; + memset(LAT1_spectrum, 0, sizeof(int) * (LUT_UNIT_N+1)); + + if (N == 4) + { + __m128i t1; + __m128i t2; + int cnt; + + #undef TestOm_1_1 + #define TestOm_1_1(im, om) \ + { \ + t2 = _mm_and_si128(x##om, LUT[0]); \ + t2 = _mm_xor_si128(t1, t2); \ + t2 = _mm_xor_si128(_mm_slli_epi16(t2, 4), _mm_slli_epi16(t2, 5)); \ + t2 = _mm_xor_si128(t2, _mm_slli_epi16(t2, 2)); \ + cnt = _mm_popcnt_u32(_mm_movemask_epi8(t2)); \ + cnt = 2 * abs(cnt - 8); \ + LAT[0x##im][0x##om] = cnt; \ + Lin = Lin >= cnt ? Lin : cnt; \ + LAT_spectrum[cnt]++; \ + Lin1 = Lin1 >= cnt ? Lin1 : cnt; \ + LAT1_spectrum[cnt]++; \ + } + + #undef TestOm + #define TestOm(im, om) \ + { \ + t2 = _mm_and_si128(x##om, LUT[0]); \ + t2 = _mm_xor_si128(t1, t2); \ + t2 = _mm_xor_si128(_mm_slli_epi16(t2, 4), _mm_slli_epi16(t2, 5)); \ + t2 = _mm_xor_si128(t2, _mm_slli_epi16(t2, 2)); \ + cnt = _mm_popcnt_u32(_mm_movemask_epi8(t2)); \ + cnt = 2 * abs(cnt - 8); \ + LAT[0x##im][0x##om] = cnt; \ + Lin = Lin >= cnt ? Lin : cnt; \ + LAT_spectrum[cnt]++; \ + } + + #undef TestIm_1 + #define TestIm_1(id) \ + { \ + t1 = _mm_and_si128(x##id, x); \ + TestOm_1_1(id, 1); \ + TestOm_1_1(id, 2); \ + TestOm_1_1(id, 4); \ + TestOm_1_1(id, 8); \ + TestOm(id, 3); \ + TestOm(id, 5); \ + TestOm(id, 6); \ + TestOm(id, 9); \ + TestOm(id, a); \ + TestOm(id, c); \ + TestOm(id, 7); \ + TestOm(id, b); \ + TestOm(id, d); \ + TestOm(id, e); \ + TestOm(id, f); \ + } + + #undef TestIm_n + #define TestIm_n(id) \ + { \ + t1 = _mm_and_si128(x##id, x); \ + TestOm(id, 1); \ + TestOm(id, 2); \ + TestOm(id, 4); \ + TestOm(id, 8); \ + TestOm(id, 3); \ + TestOm(id, 5); \ + TestOm(id, 6); \ + TestOm(id, 9); \ + TestOm(id, a); \ + TestOm(id, c); \ + TestOm(id, 7); \ + TestOm(id, b); \ + TestOm(id, d); \ + TestOm(id, e); \ + TestOm(id, f); \ + } + + TestIm_n(0); + TestIm_1(1); + TestIm_1(2); + TestIm_1(4); + TestIm_1(8); + TestIm_n(3); + TestIm_n(5); + TestIm_n(6); + TestIm_n(9); + TestIm_n(a); + TestIm_n(c); + TestIm_n(7); + TestIm_n(b); + TestIm_n(d); + TestIm_n(e); + TestIm_n(f); + LAT[0][0] = 16; + LAT_spectrum[0] += 15; + LAT_spectrum[16]++; + } + else if (N <= 8) + { + int cnt; + + uint8_t * S = (uint8_t *)LUT; + for (int i = 1; i <= N; i++) + { + int im = HWorder[i]; + for (int o = 1; o <= N; o++) + { + int om = HWorder[o]; + for (int x = 0; x < LUT_UNIT_N; x++) + { + int ia = im & x; + int oa = om & S[x]; + int p = _mm_popcnt_u32(ia ^ oa) & 1; + LAT[im][om] += p; + } + cnt = 2 * abs(LAT[im][om] - (int)(LUT_UNIT_N / 2)); + LAT[im][om] = cnt; + Lin = Lin >= cnt ? Lin : cnt; + LAT_spectrum[cnt]++; + Lin1 = Lin1 >= cnt ? Lin1 : cnt; + LAT1_spectrum[cnt]++; + } + for (int o = N + 1; o < LUT_UNIT_N; o++) + { + int om = HWorder[o]; + for (int x = 0; x < LUT_UNIT_N; x++) + { + int ia = im & x; + int oa = om & S[x]; + int p = _mm_popcnt_u32(ia ^ oa) & 1; + LAT[im][om] += p; + } + cnt = 2 * abs(LAT[im][om] - (int)(LUT_UNIT_N / 2)); + LAT[im][om] = cnt; + Lin = Lin >= cnt ? Lin : cnt; + LAT_spectrum[cnt]++; + } + } + for (int i = N + 1; i <= LUT_UNIT_N; i++) + { + int im = HWorder[i%LUT_UNIT_N]; + for (int om = 1; om < LUT_UNIT_N; om++) + { + for (int x = 0; x < LUT_UNIT_N; x++) + { + int ia = im & x; + int oa = om & S[x]; + int p = _mm_popcnt_u32(ia ^ oa) & 1; + LAT[im][om] += p; + } + cnt = 2 * abs(LAT[im][om] - (int)(LUT_UNIT_N / 2)); + LAT[im][om] = cnt; + Lin = Lin >= cnt ? Lin : cnt; + LAT_spectrum[cnt]++; + } + } + LAT[0][0] = LUT_UNIT_N; + LAT_spectrum[LUT_UNIT_N]++; + LAT_spectrum[0] += LUT_UNIT_N - 1; + } + } + + string show_linear_approximation_matrix() const + { + int LAT[LUT_UNIT_N][LUT_UNIT_N]; + int Lin; + int LAT_spectrum[LUT_UNIT_N+1]; + int Lin1; + int LAT1_spectrum[LUT_UNIT_N+1]; + linear_approximation_matrix(LAT, Lin, LAT_spectrum, Lin1, LAT1_spectrum); + + stringstream ss; + ss << "LAT = " << endl; + //ss << OPs.show_matrix(LAT[0], LUT_UNIT_N, LUT_UNIT_N); + ss << OPs.show_matrix_HWorder(LAT); + ss << "Lin: " << Lin << ", " << "LAT_spectrum: {"; + for (int i = 0; i < LUT_UNIT_N+1; i++) + { + if (LAT_spectrum[i] != 0) ss << i << ":" << LAT_spectrum[i] << ", "; + } + ss << "};" << endl; + ss << "Lin1: " << Lin1 << ", " << "LAT1_spectrum: {"; + for (int i = 0; i < LUT_UNIT_N+1; i++) + { + if (LAT1_spectrum[i] != 0) ss << i << ":" << LAT1_spectrum[i] << ", "; + } + ss << "};" << endl; + return ss.str(); + } + + void show_linear_approximation_matrix(ofstream & fout) const + { + fout << show_linear_approximation_matrix(); + } + + bool linear_approximation_matrix_test(int MaxLin_Bound, int MaxLin1_Bound, int LinFreq_Bound, int CardL1_Bound) const + { + bool computeAll = false; + + if (MaxLin_Bound == -1) MaxLin_Bound = LUT_UNIT_N; + else computeAll = true; + + if (LinFreq_Bound == -1) LinFreq_Bound = LUT_UNIT_N * LUT_UNIT_N; + else computeAll = true; + + if (MaxLin1_Bound == -1) MaxLin1_Bound = MaxLin_Bound; + if (CardL1_Bound == -1) CardL1_Bound = N * N; + + int Lin = 0; + int CardL1 = 0; + int LAT_spectrum[LUT_UNIT_N+1] = {0}; + + if (N == 4) + { + __m128i t1; + __m128i t2; + int cnt; + + #undef TestOm_1_1 + #define TestOm_1_1(im, om) \ + { \ + t2 = _mm_and_si128(x##om, LUT[0]); \ + t2 = _mm_xor_si128(t1, t2); \ + t2 = _mm_xor_si128(_mm_slli_epi16(t2, 4), _mm_slli_epi16(t2, 5)); \ + t2 = _mm_xor_si128(t2, _mm_slli_epi16(t2, 2)); \ + cnt = _mm_popcnt_u32(_mm_movemask_epi8(t2)); \ + cnt = 2 * abs(cnt - 8); \ + if (cnt > MaxLin1_Bound) return false; \ + CardL1 += (cnt == 0) ? 0 : 1; \ + if (CardL1 > CardL1_Bound) return false; \ + Lin = Lin >= cnt ? Lin : cnt; \ + LAT_spectrum[cnt]++; \ + } + + #undef TestOm + #define TestOm(im, om) \ + { \ + t2 = _mm_and_si128(x##om, LUT[0]); \ + t2 = _mm_xor_si128(t1, t2); \ + t2 = _mm_xor_si128(_mm_slli_epi16(t2, 4), _mm_slli_epi16(t2, 5)); \ + t2 = _mm_xor_si128(t2, _mm_slli_epi16(t2, 2)); \ + cnt = _mm_popcnt_u32(_mm_movemask_epi8(t2)); \ + cnt = 2 * abs(cnt - 8); \ + if (cnt > MaxLin_Bound) return false; \ + Lin = Lin >= cnt ? Lin : cnt; \ + LAT_spectrum[cnt]++; \ + } + + #undef TestIm_1_1 + #define TestIm_1_1(id) \ + { \ + t1 = _mm_and_si128(x##id, x); \ + TestOm_1_1(id, 1); \ + TestOm_1_1(id, 2); \ + TestOm_1_1(id, 4); \ + TestOm_1_1(id, 8); \ + } + + #undef TestIm_1_n + #define TestIm_1_n(id) \ + { \ + t1 = _mm_and_si128(x##id, x); \ + TestOm(id, 3); \ + TestOm(id, 5); \ + TestOm(id, 6); \ + TestOm(id, 9); \ + TestOm(id, a); \ + TestOm(id, c); \ + TestOm(id, 7); \ + TestOm(id, b); \ + TestOm(id, d); \ + TestOm(id, e); \ + TestOm(id, f); \ + } + + #undef TestIm_n + #define TestIm_n(id) \ + { \ + t1 = _mm_and_si128(x##id, x); \ + TestOm(id, 1); \ + TestOm(id, 2); \ + TestOm(id, 4); \ + TestOm(id, 8); \ + TestOm(id, 3); \ + TestOm(id, 5); \ + TestOm(id, 6); \ + TestOm(id, 9); \ + TestOm(id, a); \ + TestOm(id, c); \ + TestOm(id, 7); \ + TestOm(id, b); \ + TestOm(id, d); \ + TestOm(id, e); \ + TestOm(id, f); \ + } + + TestIm_1_1(1); + TestIm_1_1(2); + TestIm_1_1(4); + TestIm_1_1(8); + if (computeAll) + { + TestIm_1_n(1); + TestIm_1_n(2); + TestIm_1_n(4); + TestIm_1_n(8); + TestIm_n(0); + TestIm_n(3); + TestIm_n(5); + TestIm_n(6); + TestIm_n(9); + TestIm_n(a); + TestIm_n(c); + TestIm_n(7); + TestIm_n(b); + TestIm_n(d); + TestIm_n(e); + TestIm_n(f); + if (LAT_spectrum[Lin] > LinFreq_Bound) return false; + } + return true; + } + else if (N <= 8) + { + int cnt; + uint8_t * S = (uint8_t *)LUT; + for (int i = 1; i <= N; i++) + { + int im = HWorder[i]; + for (int o = 1; o <= N; o++) + { + int om = HWorder[o]; + cnt = 0; + for (int x = 0; x < LUT_UNIT_N; x++) + { + int ia = im & x; + int oa = om & S[x]; + int p = _mm_popcnt_u32(ia ^ oa) & 1; + cnt += p; + } + cnt = 2 * abs(cnt - (LUT_UNIT_N / 2)); + if (cnt > MaxLin1_Bound) return false; + CardL1 += (cnt == 0) ? 0 : 1; + if (CardL1 > CardL1_Bound) return false; + Lin = cnt > Lin ? cnt : Lin; + LAT_spectrum[cnt]++; + } + } + if (computeAll) + { + for (int i = 1; i <= N; i++) + { + int im = HWorder[i]; + for (int o = N + 1; o < LUT_UNIT_N; o++) + { + int om = HWorder[o]; + cnt = 0; + for (int x = 0; x < LUT_UNIT_N; x++) + { + int ia = im & x; + int oa = om & S[x]; + int p = _mm_popcnt_u32(ia ^ oa) & 1; + cnt += p; + } + cnt = 2 * abs(cnt - (LUT_UNIT_N / 2)); + if (cnt > MaxLin_Bound) return false; + Lin = cnt > Lin ? cnt : Lin; + LAT_spectrum[cnt]++; + } + } + for (int i = N + 1; i <= LUT_UNIT_N; i++) + { + int im = HWorder[i%LUT_UNIT_N]; + for (int om = 1; om < LUT_UNIT_N; om++) + { + cnt = 0; + for (int x = 0; x < LUT_UNIT_N; x++) + { + int ia = im & x; + int oa = om & S[x]; + int p = _mm_popcnt_u32(ia ^ oa) & 1; + cnt += p; + } + cnt = 2 * abs(cnt - (LUT_UNIT_N / 2)); + if (cnt > MaxLin_Bound) return false; + Lin = cnt > Lin ? cnt : Lin; + LAT_spectrum[cnt]++; + } + } + if (LAT_spectrum[Lin] > LinFreq_Bound) return false; + } + return true; + } + } + + void get_coordinates_ANF(bit_slice_t & coordinates_ANF) const + { + if (N == 3) + { + __m128i coor_ANF = _mm_loadu_si128((__m128i *)bit_slice.data()); + + coor_ANF = _mm_xor_si128(coor_ANF, S3_valv_mask8); + + // compute ANF for coordinates + coor_ANF = _mm_xor_si128(coor_ANF, _mm_and_si128(_mm_slli_epi16(coor_ANF, 4), S3_valv_mask4)); + coor_ANF = _mm_xor_si128(coor_ANF, _mm_and_si128(_mm_slli_epi16(coor_ANF, 2), S3_valv_mask2)); + coor_ANF = _mm_xor_si128(coor_ANF, _mm_and_si128(_mm_slli_epi16(coor_ANF, 1), S3_valv_mask1)); + + // ANF for coordinates + coordinates_ANF[0][0] = (UINT_)_mm_extract_epi8(coor_ANF, 0); + coordinates_ANF[1][0] = (UINT_)_mm_extract_epi8(coor_ANF, 1); + coordinates_ANF[2][0] = (UINT_)_mm_extract_epi8(coor_ANF, 2); + } + else if (N == 4) + { + __m128i coor_ANF = _mm_loadu_si128((__m128i *)bit_slice.data()); + + coor_ANF = _mm_xor_si128(coor_ANF, S4_valv_maskg); + + // compute ANF for coordinates + coor_ANF = _mm_xor_si128(coor_ANF, _mm_and_si128(_mm_slli_epi16(coor_ANF, 8), S4_valv_mask8)); + coor_ANF = _mm_xor_si128(coor_ANF, _mm_and_si128(_mm_slli_epi16(coor_ANF, 4), S4_valv_mask4)); + coor_ANF = _mm_xor_si128(coor_ANF, _mm_and_si128(_mm_slli_epi16(coor_ANF, 2), S4_valv_mask2)); + coor_ANF = _mm_xor_si128(coor_ANF, _mm_and_si128(_mm_slli_epi16(coor_ANF, 1), S4_valv_mask1)); + + // ANF for coordinates + coordinates_ANF[0][0] = (UINT_)_mm_extract_epi16(coor_ANF, 0); + coordinates_ANF[1][0] = (UINT_)_mm_extract_epi16(coor_ANF, 1); + coordinates_ANF[2][0] = (UINT_)_mm_extract_epi16(coor_ANF, 2); + coordinates_ANF[3][0] = (UINT_)_mm_extract_epi16(coor_ANF, 3); + } + else if (N <= 8) + { + coordinates_ANF = bit_slice; + // compute ANF for coordinates + for (int i = 0; i < N; i++) + { + for (int j = N - 1; j >= 0; j--) + { + coordinates_ANF[i] = coordinates_ANF[i] ^ (OPs.sll(coordinates_ANF[i], (1ULL<[j]); + } + } + } + else + { + // cout << "get_coordinates_ANF():: not support for N < 3 and N > 8" << endl; + } + } + + string show_coordinates_ANF() const + { + array, N> coordinates_ANF; + get_coordinates_ANF(coordinates_ANF); + + int related; + int deg, term_n, related_n; + + int degs[N] = {0}; + int maxdeg = 0; + for (int coori = 0; coori < N; coori++) + { + degs[coori] = degree_from_ANF(coordinates_ANF[coori]); + maxdeg = degs[coori] > maxdeg ? degs[coori] : maxdeg; + } + int endoff = (maxdeg == N) ? (1<[maxdeg+1]; + + stringstream ss; + ss << "ANF of coordinates:" << endl; + for (int coori = 0; coori < N; coori++) + { + deg = 0; term_n = 0; related_n = 0; related = 0; + string coorstr = "y" + std::to_string(coori) + " = "; + coorstr += (OPs.get_bit(coordinates_ANF[coori], 0) == 1) ? "1 + " : " + "; + for (int termi = 1; termi < endoff; termi++) + { + int termx = HWorder[termi]; + int width = _mm_popcnt_u32(termx); + + if (OPs.get_bit(coordinates_ANF[coori], termx) == 1) + { + for (int biti = 0; biti < N; biti++) + { + if (((termx >> biti) & 1) == 1) + { + related |= (1 << biti); + coorstr += "x" + std::to_string(biti); + } + } + } + else + { + for (int spacei = 0; spacei < width; spacei++) coorstr = coorstr + " "; + } + coorstr += " + "; + } + coorstr.pop_back();coorstr.pop_back();coorstr.pop_back(); + ss << coorstr << "; "; + deg = degs[coori]; + term_n = OPs.cnt_1(coordinates_ANF[coori]); + related_n = _mm_popcnt_u32(related); + + ss << "deg = " << setw((int)log10(N)+1) << deg << ", term_n = " << setw((int)log10(1< 8" << endl; + } + } + + string show_components_ANF() const + { + array, 1< components_ANF; + get_components_ANF(components_ANF); + + int related; + int deg, term_n, related_n; + int deg_spectrum[N+1] = {0}; + int max_degree = 0; + int min_degree = N; + + int degs[1< max_degree ? degs[compi] : max_degree; + min_degree = degs[compi] < min_degree ? degs[compi] : min_degree; + deg_spectrum[degs[compi]]++; + } + int endoff = (max_degree == N) ? (1<[max_degree+1]; + + stringstream ss; + ss << "ANF of components:" << endl; + for (int compi = 1; compi < (1<> biti) & 1) ? ("y" + std::to_string(biti)) : " ") + " + "; + } + compstr.pop_back();compstr.pop_back();compstr.pop_back(); + compstr += " = "; + compstr += (OPs.get_bit(components_ANF[compi], 0) == 1) ? "1 + " : " + "; + for (int termi = 1; termi < endoff; termi++) + { + int termx = HWorder[termi]; + int width = _mm_popcnt_u32(termx); + + if (OPs.get_bit(components_ANF[compi], termx) == 1) + { + for (int biti = 0; biti < N; biti++) + { + if (((termx >> biti) & 1) == 1) + { + related |= (1 << biti); + compstr += "x" + std::to_string(biti); + } + } + } + else + { + for (int spacei = 0; spacei < width; spacei++) compstr = compstr + " "; + } + compstr += " + "; + } + compstr.pop_back();compstr.pop_back();compstr.pop_back(); + ss << compstr << "; "; + deg = degs[compi]; + term_n = OPs.cnt_1(components_ANF[compi]); + related_n = _mm_popcnt_u32(related); + + ss << "deg = " << setw(digits(N)) << deg << ", term_n = " << setw(digits(1<::max(); + + if (N == 3) + { + __m128i deg3_ind; + __m128i deg2_ind; + __m128i deg1_ind; + + int deg3_cnt = 0; + int deg2_cnt = 0; + int deg1_cnt = 0; + int deg0_cnt = 0; + + // Compute ANF for components + array, 1< ALIGNED_(16) components_ANF = {{0}}; + get_components_ANF(components_ANF); + __m128i components_ANF_all = _mm_loadl_epi64((__m128i *)((*components_ANF.data()).data())); + + deg3_ind = _mm_cmpeq_epi8(_mm_and_si128(components_ANF_all, S3_comps_deg_mask3), zero_128); // is not deg3 0xffff, is deg3 0x0000 + deg3_cnt = _mm_popcnt_u32(_mm_movemask_epi8(_mm_xor_si128(_mm_and_si128(deg3_ind, S3_comps_ind_mask), S3_comps_ind_mask))); + + components_ANF_all = _mm_and_si128(components_ANF_all, deg3_ind); // if is not deg3 + deg2_ind = _mm_cmpeq_epi8(_mm_and_si128(components_ANF_all, S3_comps_deg_mask2), zero_128); // is not deg2 0xffff, is deg2 0x0000 + deg2_cnt = _mm_popcnt_u32(_mm_movemask_epi8(_mm_xor_si128(_mm_and_si128(deg2_ind, S3_comps_ind_mask), S3_comps_ind_mask))); + + components_ANF_all = _mm_and_si128(components_ANF_all, deg2_ind); // if is not deg2 + deg1_ind = _mm_cmpeq_epi8(_mm_and_si128(components_ANF_all, S3_comps_deg_mask1), zero_128); // is not deg1 0xffff, is deg1 0x0000 + deg1_cnt = _mm_popcnt_u32(_mm_movemask_epi8(_mm_xor_si128(_mm_and_si128(deg1_ind, S3_comps_ind_mask), S3_comps_ind_mask))); + + deg0_cnt = (1 << N) - deg3_cnt - deg2_cnt - deg1_cnt; + + if (deg3_cnt != 0) max_degree = 3; + else if (deg2_cnt != 0) max_degree = 2; + else if (deg1_cnt != 0) max_degree = 1; + else max_degree = 0; + + if (deg0_cnt != 1) min_degree = 0; + else if (deg1_cnt != 0) min_degree = 1; + else if (deg2_cnt != 0) min_degree = 2; + else min_degree = 3; + + if ((max_degree >= max_degree_bound) && (min_degree >= min_degree_bound)) + { + return true; + } + else + { + return false; + } + } + else if (N == 4) + { + __m256i deg4_ind; + __m256i deg3_ind; + __m256i deg2_ind; + __m256i deg1_ind; + + int deg4_cnt = 0; + int deg3_cnt = 0; + int deg2_cnt = 0; + int deg1_cnt = 0; + int deg0_cnt = 0; + + // Compute ANF for components + array, 1< ALIGNED_(32) components_ANF = {{0}}; + get_components_ANF(components_ANF); + __m256i components_ANF_all = _mm256_load_si256((__m256i *)(components_ANF.data())); + + deg4_ind = _mm256_cmpeq_epi16(_mm256_and_si256(components_ANF_all, S4_comps_deg_mask4), zero_256); // is not deg4 0xffff, is deg4 0x0000 + deg4_cnt = _mm_popcnt_u32(_mm256_movemask_epi8(_mm256_xor_si256(_mm256_and_si256(deg4_ind, S4_comps_ind_mask), S4_comps_ind_mask))); + + components_ANF_all = _mm256_and_si256(components_ANF_all, deg4_ind); // if is not deg4 + deg3_ind = _mm256_cmpeq_epi16(_mm256_and_si256(components_ANF_all, S4_comps_deg_mask3), zero_256); // is not deg3 0xffff, is deg3 0x0000 + deg3_cnt = _mm_popcnt_u32(_mm256_movemask_epi8(_mm256_xor_si256(_mm256_and_si256(deg3_ind, S4_comps_ind_mask), S4_comps_ind_mask))); + + components_ANF_all = _mm256_and_si256(components_ANF_all, deg3_ind); // if is not deg3 + deg2_ind = _mm256_cmpeq_epi16(_mm256_and_si256(components_ANF_all, S4_comps_deg_mask2), zero_256); // is not deg2 0xffff, is deg2 0x0000 + deg2_cnt = _mm_popcnt_u32(_mm256_movemask_epi8(_mm256_xor_si256(_mm256_and_si256(deg2_ind, S4_comps_ind_mask), S4_comps_ind_mask))); + + components_ANF_all = _mm256_and_si256(components_ANF_all, deg2_ind); // if is not deg2 + deg1_ind = _mm256_cmpeq_epi16(_mm256_and_si256(components_ANF_all, S4_comps_deg_mask1), zero_256); // is not deg1 0xffff, is deg1 0x0000 + deg1_cnt = _mm_popcnt_u32(_mm256_movemask_epi8(_mm256_xor_si256(_mm256_and_si256(deg1_ind, S4_comps_ind_mask), S4_comps_ind_mask))); + + deg0_cnt = (1 << N) - deg4_cnt - deg3_cnt - deg2_cnt - deg1_cnt; + + if (deg4_cnt != 0) max_degree = 4; + else if (deg3_cnt != 0) max_degree = 3; + else if (deg2_cnt != 0) max_degree = 2; + else if (deg1_cnt != 0) max_degree = 1; + else max_degree = 0; + + if (deg0_cnt != 1) min_degree = 0; + else if (deg1_cnt != 0) min_degree = 1; + else if (deg2_cnt != 0) min_degree = 2; + else if (deg3_cnt != 0) min_degree = 3; + else min_degree = 4; + + if ((max_degree >= max_degree_bound) && (min_degree >= min_degree_bound)) + { + return true; + } + else + { + return false; + } + } + else if (N <= 8) + { + // Compute ANF for components + array, 1< components_ANF = {{0}}; + get_components_ANF(components_ANF); + + int deg_ind[N + 1] = { 0 }; + for (int i = 1; i < (1 << N); i++) + { + memset(deg_ind, 0, sizeof(int)*(N + 1)); + for (int j = 0; j < (1 << N); j++) + { + deg_ind[_mm_popcnt_u32(j)] |= OPs.get_bit(components_ANF[i], j); + } + int deg_cur = 0; + for (int k = N; k >= 0; k--) + { + if (deg_ind[k] == 1) + { + deg_cur = k; + break; + } + } + max_degree = deg_cur > max_degree ? deg_cur : max_degree; + min_degree = deg_cur < min_degree ? deg_cur : min_degree; + } + + if ((max_degree >= max_degree_bound) && (min_degree >= min_degree_bound)) + { + return true; + } + else + { + return false; + } + } + } + + bool coordinates_degree_test(const int max_degree_bound, const int min_degree_bound) const + { + int max_degree = 0; + int min_degree = numeric_limits::max(); + + if (N == 3) + { + uint8_t a, b, c; + + __m128i deg3_ind; + __m128i deg2_ind; + __m128i deg1_ind; + + int deg3_cnt = 0; + int deg2_cnt = 0; + int deg1_cnt = 0; + int deg0_cnt = 0; + + // Compute ANF for coordinates + array, N> ALIGNED_(16) coordinates_ANF = {{0}}; + get_coordinates_ANF(coordinates_ANF); + __m128i coordinates_ANF_all = _mm_load_si128((__m128i *)((*coordinates_ANF.data()).data())); + coordinates_ANF_all = _mm_xor_si128(coordinates_ANF_all, S3_valv_mask8); + + deg3_ind = _mm_cmpeq_epi8(_mm_and_si128(coordinates_ANF_all, S3_coors_deg_mask3), zero_128); // is not deg3 0xffff, is deg3 0x0000 + deg3_cnt = _mm_popcnt_u32(_mm_movemask_epi8(_mm_xor_si128(_mm_and_si128(deg3_ind, S3_coors_ind_mask), S3_coors_ind_mask))); + + coordinates_ANF_all = _mm_and_si128(coordinates_ANF_all, deg3_ind); // if is not deg3 + deg2_ind = _mm_cmpeq_epi8(_mm_and_si128(coordinates_ANF_all, S3_coors_deg_mask2), zero_128); // is not deg2 0xffff, is deg2 0x0000 + deg2_cnt = _mm_popcnt_u32(_mm_movemask_epi8(_mm_xor_si128(_mm_and_si128(deg2_ind, S3_coors_ind_mask), S3_coors_ind_mask))); + + coordinates_ANF_all = _mm_and_si128(coordinates_ANF_all, deg2_ind); // if is not deg2 + deg1_ind = _mm_cmpeq_epi8(_mm_and_si128(coordinates_ANF_all, S3_coors_deg_mask1), zero_128); // is not deg1 0xffff, is deg1 0x0000 + deg1_cnt = _mm_popcnt_u32(_mm_movemask_epi8(_mm_xor_si128(_mm_and_si128(deg1_ind, S3_coors_ind_mask), S3_coors_ind_mask))); + + deg0_cnt = N - deg3_cnt - deg2_cnt - deg1_cnt; + + if (deg3_cnt != 0) { max_degree = 3; if (max_degree > max_degree_bound) return false;} + else if (deg2_cnt != 0) { max_degree = 2; if (max_degree > max_degree_bound) return false;} + else if (deg1_cnt != 0) { max_degree = 1; if (max_degree > max_degree_bound) return false;} + else max_degree = 0; + + if (deg0_cnt != 0) { min_degree = 0; if (min_degree < min_degree_bound) return false;} + else if (deg1_cnt != 0) { min_degree = 1; if (min_degree < min_degree_bound) return false;} + else if (deg2_cnt != 0) { min_degree = 2; if (min_degree < min_degree_bound) return false;} + else { min_degree = 3; if (min_degree < min_degree_bound) return false;} + return true; + } + else if (N == 4) + { + __m128i deg4_ind; + __m128i deg3_ind; + __m128i deg2_ind; + __m128i deg1_ind; + + int deg4_cnt = 0; + int deg3_cnt = 0; + int deg2_cnt = 0; + int deg1_cnt = 0; + int deg0_cnt = 0; + + // compute ANF for coordinates + array, N> ALIGNED_(16) coordinates_ANF = {{0}}; + get_coordinates_ANF(coordinates_ANF); + __m128i coordinates_ANF_all = _mm_load_si128((__m128i *)((*coordinates_ANF.data()).data())); + coordinates_ANF_all = _mm_xor_si128(coordinates_ANF_all, S4_valv_maskg); + + deg4_ind = _mm_cmpeq_epi16(_mm_and_si128(coordinates_ANF, S4_coors_deg_mask4), zero_128); // is not deg4 0xffff, is deg4 0x0000 + deg4_cnt = _mm_popcnt_u32(_mm_movemask_epi8(_mm_xor_si128(_mm_and_si128(deg4_ind, S4_coors_ind_mask), S4_coors_ind_mask))); + + coordinates_ANF = _mm_and_si128(coordinates_ANF, deg4_ind); // if is not deg4 + deg3_ind = _mm_cmpeq_epi16(_mm_and_si128(coordinates_ANF, S4_coors_deg_mask3), zero_128); // is not deg3 0xffff, is deg3 0x0000 + deg3_cnt = _mm_popcnt_u32(_mm_movemask_epi8(_mm_xor_si128(_mm_and_si128(deg3_ind, S4_coors_ind_mask), S4_coors_ind_mask))); + + coordinates_ANF = _mm_and_si128(coordinates_ANF, deg3_ind); // if is not deg3 + deg2_ind = _mm_cmpeq_epi16(_mm_and_si128(coordinates_ANF, S4_coors_deg_mask2), zero_128); // is not deg2 0xffff, is deg2 0x0000 + deg2_cnt = _mm_popcnt_u32(_mm_movemask_epi8(_mm_xor_si128(_mm_and_si128(deg2_ind, S4_coors_ind_mask), S4_coors_ind_mask))); + + coordinates_ANF = _mm_and_si128(coordinates_ANF, deg2_ind); // if is not deg2 + deg1_ind = _mm_cmpeq_epi16(_mm_and_si128(coordinates_ANF, S4_coors_deg_mask1), zero_128); // is not deg1 0xffff, is deg1 0x0000 + deg1_cnt = _mm_popcnt_u32(_mm_movemask_epi8(_mm_xor_si128(_mm_and_si128(deg1_ind, S4_coors_ind_mask), S4_coors_ind_mask))); + + deg0_cnt = N - deg4_cnt - deg3_cnt - deg2_cnt - deg1_cnt; + + if (deg4_cnt != 0) {max_degree = 4; if (max_degree > max_degree_bound) return false;} + else if (deg3_cnt != 0) {max_degree = 3; if (max_degree > max_degree_bound) return false;} + else if (deg2_cnt != 0) {max_degree = 2; if (max_degree > max_degree_bound) return false;} + else if (deg1_cnt != 0) {max_degree = 1; if (max_degree > max_degree_bound) return false;} + else max_degree = 0; + + + if (deg0_cnt != 0) {min_degree = 0; if (min_degree < min_degree_bound) return false;} + else if (deg1_cnt != 0) {min_degree = 1; if (min_degree < min_degree_bound) return false;} + else if (deg2_cnt != 0) {min_degree = 2; if (min_degree < min_degree_bound) return false;} + else if (deg3_cnt != 0) {min_degree = 3; if (min_degree < min_degree_bound) return false;} + else {min_degree = 4; if (min_degree < min_degree_bound) return false;} + return true; + } + else if (N <= 8) + { + bit_slice_t coordinates_ANF = bit_slice; + get_coordinates_ANF(coordinates_ANF); + + int deg_ind[N + 1] = { 0 }; + for (int i = 0; i < N; i++) + { + memset(deg_ind, 0, sizeof(int)*(N + 1)); + for (int j = 0; j < (1 << N); j++) + { + deg_ind[_mm_popcnt_u32(j)] |= OPs.get_bit(coordinates_ANF[i], j); + } + int deg_cur = 0; + for (int k = N; k >= 0; k--) + { + if (deg_ind[k] == 1) + { + deg_cur = k; + break; + } + } + max_degree = deg_cur > max_degree ? deg_cur : max_degree; + if (max_degree > max_degree_bound) return false; + min_degree = deg_cur < min_degree ? deg_cur : min_degree; + if (min_degree < min_degree_bound) return false; + } + return true; + } + } + + void degree(int deg_spectrum[N+1], int & max_degree, int & min_degree) const + { + memset(deg_spectrum, 0, sizeof(int) * (N + 1)); + max_degree = 0; + min_degree = numeric_limits::max(); + + if (N == 3) + { + __m128i deg3_ind; + __m128i deg2_ind; + __m128i deg1_ind; + + int deg3_cnt = 0; + int deg2_cnt = 0; + int deg1_cnt = 0; + + // Compute ANF for components + array, 1< ALIGNED_(16) components_ANF = {{0}}; + get_components_ANF(components_ANF); + __m128i components_ANF_all = _mm_loadl_epi64((__m128i *)((*components_ANF.data()).data())); + + deg3_ind = _mm_cmpeq_epi8(_mm_and_si128(components_ANF_all, S3_comps_deg_mask3), zero_128); // is not deg3 0xffff, is deg3 0x0000 + deg3_cnt = _mm_popcnt_u32(_mm_movemask_epi8(_mm_xor_si128(_mm_and_si128(deg3_ind, S3_comps_ind_mask), S3_comps_ind_mask))); + + components_ANF_all = _mm_and_si128(components_ANF_all, deg3_ind); // if is not deg3 + deg2_ind = _mm_cmpeq_epi8(_mm_and_si128(components_ANF_all, S3_comps_deg_mask2), zero_128); // is not deg2 0xffff, is deg2 0x0000 + deg2_cnt = _mm_popcnt_u32(_mm_movemask_epi8(_mm_xor_si128(_mm_and_si128(deg2_ind, S3_comps_ind_mask), S3_comps_ind_mask))); + + components_ANF_all = _mm_and_si128(components_ANF_all, deg2_ind); // if is not deg2 + deg1_ind = _mm_cmpeq_epi8(_mm_and_si128(components_ANF_all, S3_comps_deg_mask1), zero_128); // is not deg1 0xffff, is deg1 0x0000 + deg1_cnt = _mm_popcnt_u32(_mm_movemask_epi8(_mm_xor_si128(_mm_and_si128(deg1_ind, S3_comps_ind_mask), S3_comps_ind_mask))); + + deg_spectrum[3] = deg3_cnt; + deg_spectrum[2] = deg2_cnt; + deg_spectrum[1] = deg1_cnt; + deg_spectrum[0] = (1 << N) - deg3_cnt - deg2_cnt - deg1_cnt; + + if (deg3_cnt != 0) max_degree = 3; + else if (deg2_cnt != 0) max_degree = 2; + else if (deg1_cnt != 0) max_degree = 1; + else max_degree = 0; + + if (deg_spectrum[0] != 1) min_degree = 0; + else if (deg1_cnt != 0) min_degree = 1; + else if (deg2_cnt != 0) min_degree = 2; + else min_degree = 3; + } + else if (N == 4) + { + __m256i deg4_ind; + __m256i deg3_ind; + __m256i deg2_ind; + __m256i deg1_ind; + + int deg4_cnt = 0; + int deg3_cnt = 0; + int deg2_cnt = 0; + int deg1_cnt = 0; + + // Compute ANF for components + array, 1< ALIGNED_(32) components_ANF = {{0}}; + get_components_ANF(components_ANF); + __m256i components_ANF_all = _mm256_load_si256((__m256i *)(components_ANF.data())); + + deg4_ind = _mm256_cmpeq_epi16(_mm256_and_si256(components_ANF_all, S4_comps_deg_mask4), zero_256); // is not deg4 0xffff, is deg4 0x0000 + deg4_cnt = _mm_popcnt_u32(_mm256_movemask_epi8(_mm256_xor_si256(_mm256_and_si256(deg4_ind, S4_comps_ind_mask), S4_comps_ind_mask))); + + components_ANF_all = _mm256_and_si256(components_ANF_all, deg4_ind); // if is not deg4 + deg3_ind = _mm256_cmpeq_epi16(_mm256_and_si256(components_ANF_all, S4_comps_deg_mask3), zero_256); // is not deg3 0xffff, is deg3 0x0000 + deg3_cnt = _mm_popcnt_u32(_mm256_movemask_epi8(_mm256_xor_si256(_mm256_and_si256(deg3_ind, S4_comps_ind_mask), S4_comps_ind_mask))); + + components_ANF_all = _mm256_and_si256(components_ANF_all, deg3_ind); // if is not deg3 + deg2_ind = _mm256_cmpeq_epi16(_mm256_and_si256(components_ANF_all, S4_comps_deg_mask2), zero_256); // is not deg2 0xffff, is deg2 0x0000 + deg2_cnt = _mm_popcnt_u32(_mm256_movemask_epi8(_mm256_xor_si256(_mm256_and_si256(deg2_ind, S4_comps_ind_mask), S4_comps_ind_mask))); + + components_ANF_all = _mm256_and_si256(components_ANF_all, deg2_ind); // if is not deg2 + deg1_ind = _mm256_cmpeq_epi16(_mm256_and_si256(components_ANF_all, S4_comps_deg_mask1), zero_256); // is not deg1 0xffff, is deg1 0x0000 + deg1_cnt = _mm_popcnt_u32(_mm256_movemask_epi8(_mm256_xor_si256(_mm256_and_si256(deg1_ind, S4_comps_ind_mask), S4_comps_ind_mask))); + + deg_spectrum[4] = deg4_cnt; + deg_spectrum[3] = deg3_cnt; + deg_spectrum[2] = deg2_cnt; + deg_spectrum[1] = deg1_cnt; + deg_spectrum[0] = (1 << N) - deg4_cnt - deg3_cnt - deg2_cnt - deg1_cnt; + + if (deg4_cnt != 0) max_degree = 4; + else if (deg3_cnt != 0) max_degree = 3; + else if (deg2_cnt != 0) max_degree = 2; + else if (deg1_cnt != 0) max_degree = 1; + else max_degree = 0; + + if (deg_spectrum[0] != 1) min_degree = 0; + else if (deg1_cnt != 0) min_degree = 1; + else if (deg2_cnt != 0) min_degree = 2; + else if (deg3_cnt != 0) min_degree = 3; + else min_degree = 4; + } + else if (N <= 8) + { + // Compute ANF for components + array, 1< components_ANF = {{0}}; + get_components_ANF(components_ANF); + + int deg_ind[N + 1] = { 0 }; + for (int i = 1; i < (1 << N); i++) + { + memset(deg_ind, 0, sizeof(int)*(N + 1)); + for (int j = 0; j < (1 << N); j++) + { + deg_ind[_mm_popcnt_u32(j)] |= OPs.get_bit(components_ANF[i], j); + } + int deg_cur = 0; + for (int k = N; k >= 0; k--) + { + if (deg_ind[k] == 1) + { + deg_cur = k; + break; + } + } + deg_spectrum[deg_cur]++; + max_degree = deg_cur > max_degree ? deg_cur : max_degree; + min_degree = deg_cur < min_degree ? deg_cur : min_degree; + } + } + } + + int degree_from_ANF(bit_slice_l_t & bf_ANF) const + { + int deg = 0; + for (int j = (1 << N) - 1; j > 0; j--) + { + if (OPs.get_bit(bf_ANF, HWorder[j]) == 1) + { + deg = _mm_popcnt_u32(HWorder[j]); + break; + } + } + return deg; + } + + int degree_from_value(bit_slice_l_t & bf) const + { + bit_slice_l_t bf_ANF = bf; + + // compute ANF + for (int j = N - 1; j >= 0; j--) + { + bf_ANF = bf_ANF ^ (OPs.sll(bf_ANF, (1ULL<[j]); + } + + return degree_from_ANF(bf_ANF); + } + + int k_product_degree(const int k) const + { + int max_degree = 0; + bit_slice_l_t prod_bf = {{}}; + int end_off = (k == N) ? (1<[k+1]; + + for (int i = 1; i < end_off; i++) + { + int sel = HWorder[i]; + + OPs.set_one_inplace(prod_bf); + for (int bi = 0; bi < N; bi++) + { + if (((sel >> bi) & 1) == 1) + { + prod_bf = prod_bf & bit_slice[bi]; + } + } + int deg = degree_from_value(prod_bf); + max_degree = deg > max_degree ? deg : max_degree; + } + return max_degree; + } + + void k_product_degree_table(int dkT[N+1]) const + { + dkT[0] = 0; + for (int k = 1; k <= N; k++) + { + dkT[k] = k_product_degree(k); + } + } + + string show_k_product_degree_table() const + { + int dkT[N+1]; + k_product_degree_table(dkT); + + stringstream ss; + ss << "d_k table of the S-box:" << endl; + ss << setw(4) << "k" << "|"; + for (int k = 1; k <= N; k++) + { + ss << setw(4) << k << "|"; + } + ss << endl; + + ss << setw(4) << "dk" << "|"; + for (int k = 1; k <= N; k++) + { + ss << setw(4) << dkT[k] << "|"; + } + ss << endl; + return ss.str(); + } + + void show_k_product_degree_table(ofstream & fout) const + { + fout << show_k_product_degree_table(); + } + + void get_prec(vector & uPrec, const uint8_t u) const + { + int hw = _mm_popcnt_u32(((uint32_t)u) & 0xffU); + int offset = hw < N ? HWorder_off[hw+1] : (1<[i]; + if ( (x ^ (x & u)) == 0) + { + uPrec.push_back(x); + } + } + } + + void get_succ(vector & uSucc, const uint8_t u) const + { + int hw = _mm_popcnt_u32(((uint32_t)u) & 0xffU); + int offset = HWorder_off[hw]; + for (int i = offset; i < (1<[i]; + if ( (u ^ (x & u)) == 0) + { + uSucc.push_back(x); + } + } + } + + void from_value_to_ANF_inplace(bit_slice_l_t & f_anf) const + { + for (int j = N - 1; j >= 0; j--) + { + f_anf = f_anf ^ (OPs.sll(f_anf, (1ULL<[j]); + } + } + + void from_value_to_ANF(bit_slice_l_t & f_anf, const bit_slice_l_t f_val) const + { + f_anf = f_val; + from_value_to_ANF_inplace(f_anf); + } + + void get_product_of_coordinates_ANF(bit_slice_l_t & S_w, const uint8_t w) const + { + OPs.set_one_inplace(S_w); + + for (int i = 0; i < N; i++) + { + if (((w>>i) & 1) == 1) + { + S_w = S_w & bit_slice[i]; + } + } + from_value_to_ANF_inplace(S_w); + } + + bool is_in_ANF(bit_slice_l_t & S_w, uint8_t u) const + { + if (OPs.get_bit(S_w, u) == 1) return true; + return false; + } + + void V_S_table(int VST[LUT_UNIT_N][LUT_UNIT_N]) const + { + for (int i = 0; i < LUT_UNIT_N; i++) + { + for (int j = 0; j < LUT_UNIT_N; j++) + { + VST[i][j] = 0; + } + } + + for (int u = 0; u < LUT_UNIT_N; u++) + { + for (int w = 0; w < LUT_UNIT_N; w++) + { + bit_slice_l_t S_w; + get_product_of_coordinates_ANF(S_w, w); + if (is_in_ANF(S_w, u)) + { + VST[u][w] = 1; + } + } + } + + vector uSucc; + uSucc.reserve(LUT_UNIT_N); + for (int ui = LUT_UNIT_N - 1; ui >= 0; ui--) + { + uint8_t u = HWorder[ui]; + + get_succ(uSucc, u); + for (auto v : uSucc) + { + for (int w = 0; w < LUT_UNIT_N; w++) + { + if (VST[v][w] == 1) + { + VST[u][w] = 1; + } + } + } + uSucc.clear(); + } + } + + string show_V_S_table() const + { + int VST[LUT_UNIT_N][LUT_UNIT_N]; + V_S_table(VST); + stringstream ss; + ss << "VST = " << endl; + ss << OPs.show_indicate_matrix_HWorder(VST); + return ss.str(); + } + + void show_V_S_table(ofstream & fout) const + { + fout << show_V_S_table() << endl; + } + + bool is_linear() const + { + return coordinates_degree_test(1, 1); + } + + bool is_linear(const array & LUT_array) const + { + function_t func; + memcpy(func.LUT, LUT_array.data(), sizeof(uint8_t) * LUT_UNIT_N); + func.LUT_to_bit_slice(); + return func.coordinates_degree_test(1, 1); + } + + + void autocorrelation_matrix(int ACT[LUT_UNIT_N][LUT_UNIT_N]) const + { + int DDT[LUT_UNIT_N][LUT_UNIT_N]; + int Diff; + int DDT_spectrum[LUT_UNIT_N+1]; + int Diff1; + int DDT1_spectrum[LUT_UNIT_N+1]; + + difference_distribution_matrix(DDT, Diff, DDT_spectrum, Diff1, DDT1_spectrum); + OPs.mulHM(ACT, DDT); + } + + string show_autocorrelation_matrix() const + { + int ACT[LUT_UNIT_N][LUT_UNIT_N]; + autocorrelation_matrix(ACT); + + stringstream ss; + ss << "ACT = " << endl; + //ss << OPs.show_matrix(ACT[0], LUT_UNIT_N, LUT_UNIT_N); + ss << OPs.show_matrix_HWorder(ACT); + return ss.str(); + } + + void show_autocorrelation_matrix(ofstream & fout) const + { + fout << show_autocorrelation_matrix(); + } + + int linear_structures(vector > & LS) const + { + LS.clear(); + int ACT[LUT_UNIT_N][LUT_UNIT_N]; + autocorrelation_matrix(ACT); + + for (int ci = 1; ci < (1<> N)) >> 1); + LS.push_back(make_tuple(ci, ri, c)); + } + } + } + return LS.size(); + } + + string show_linear_structures() const + { + vector > LS; + linear_structures(LS); + + stringstream ss; + ss << "LS = { /* size " << LS.size() << " */" << endl; + for (auto ls: LS) + { + ss << "{" + << (*(new bitset(std::get<0>(ls)))).to_string() << "," + << (*(new bitset(std::get<1>(ls)))).to_string() << "," + << setw(1) << std::get<2>(ls) << "," + << "}, " << endl; + } + ss << "};" << endl; + return ss.str(); + } + + void show_linear_structures(ofstream & fout) const + { + fout << show_linear_structures(); + } + + int number_linear_structures() const + { + vector > LS; + linear_structures(LS); + return LS.size(); + } + + // require: bf be the ANF of a Boolean function, instead of the value vector + bool is_linear(const bit_slice_l_t & bf) const + { + int deg2_offset = HWorder_off[2]; + for (int i = deg2_offset; i < (1 << N); i++) + { + int j = HWorder[i]; + if (OPs.get_bit(bf, j) == 1) + { + return false; + } + } + return true; + } + + void get_coordinates_LUT(array, N> & coordinates_LUT) const + { + #if 1 + for (int yi = 0; yi < N; yi++) + { + for (int xi = 0; xi < LUT_UNIT_N; xi++) + { + uint8_t ybit = OPs.get_bit(bit_slice[yi], xi); + coordinates_LUT[yi][xi] = ybit; + } + } + #endif + + #if 0 + uint8_t * yp = (uint8_t *) LUT; + for (int xi = 0; xi < LUT_UNIT_N; xi++) + { + uint8_t y = yp[xi]; + for (int yi = 0; yi < N; yi++) + { + uint8_t ybit = (y >> yi) & 1; + coordinates_LUT[yi][xi] = ybit; + } + } + #endif + } + + void get_components_LUT(array, 1< & components_LUT) const + { + for (int i = 0; i < (1<, N> coordinates_LUT; + get_coordinates_LUT(coordinates_LUT); + + for (int i = 1; i < (1<>j) & 1) ) components_LUT[i] = components_LUT[i] ^ coordinates_LUT[j]; + } + } + } + + string show_components_LUT() const + { + array, 1< components_LUT; + get_components_LUT(components_LUT); + + stringstream ss; + ss << hex << setfill('0'); + for (int i = (1<= 0 ; i--) + { + ss << "{ 0x" << setw(2) << i << ", {"; + for (int j = LUT_UNIT_N - 1; j >= 0; j--) + { + ss << components_LUT[i][j] + '\0' << ", "; + } + ss << "}, " << endl; + } + return ss.str(); + } + + void v_w_linear(vector, vector> > & VW, int VW_n[N][N]) const + { + memset(VW_n, 0, N * N * sizeof(int)); + + array, 1< components_LUT; + get_components_LUT(components_LUT); + + bool testnextV = false; + + vector WholeSpaces; + for (int i = 0; i < (1< existWwithDw; + vector V; + vector V_span; + vector cV_span; + array components_isLinear; + set linearComponents; + vector > Ws; + vector appended; + + for (int v = N - 1; v > 0; v--) + { // enumerate on the dimension of V + int vn = SubSpaces[v - 1].size(); + // for (int vi = 0; vi < vn; vi++) + for (int vi = 0; vi < vn; vi++) + { // enumerate Subspaces V with dimension vi + + existWwithDw.fill(false); + testnextV = false; + + V.clear(); + V_span.clear(); + + V = SubSpaces[v - 1][vi]; + V_span.push_back(0); + for (int x = 1; x < (1 << v); x++) + { + uint8_t V_element = 0; + for (int i = 0; i < v; i++) + { + if ((x >> i) & 1) V_element ^= V[i]; + } + V_span.push_back(V_element); + } + std::sort(V_span.begin(), V_span.end()); + + cV_span.clear(); + cV_span.reserve((1<> i) & 1) + { + for (int compi = 1; compi < (1<, vector > (V, (*Ws_it))); + + for (int wi = 1; wi <= w; wi++) + { + existWwithDw[wi] = true; + } + Ws_it++; + } + + Ws.clear(); + for (int w = 1; w < N; w++) + { + if (existWwithDw[w]) VW_n[v][w]++; + } + } + } + } + } + + string show_v_w_linear() const + { + vector, vector> > VW; + int VW_n[N][N]; + v_w_linear(VW, VW_n); + + stringstream ss; + ss << "(v, w)-linearity: number N_(v, w) of subspaces V of dimension v for which there exists a w-dimensional W such that the S-box is (v, w)-linear with respect to (V, W)" << endl; + ss << " "; + for (int w = 1; w < N; w++) ss << " w=" << setw(1) << w << ", "; + ss << endl; + + for (int v = 1; v < N; v++) + { + ss << "v=" << setw(1) << v << " "; + for (int w = 1; w < N; w++) + { + ss << setw(6) << VW_n[v][w] << ", "; + } + ss << endl; + } + + ss << "(V_basis, W_spaned) pairs" << endl; + ss << hex << setfill('0'); + auto VW_it = VW.begin(); + while (VW_it != VW.end()) + { + vector V = (*VW_it).first; + vector W = (*VW_it).second; + ss << "{"; + ss << "{"; + for (auto vx: V) + { + ss << "0x" << setw(2) << vx + '\0' << ","; + } + ss << "}, "; + ss << "{"; + for (auto wx: W) + { + ss << "0x" << setw(2) << wx + '\0' << ","; + } + ss << "}"; + ss << "}," << endl; + VW_it++; + } + return ss.str(); + } + + void show_v_w_linear(ofstream & fout) const + { + fout << show_v_w_linear(); + } + + void max_v_w_linear(pair & max_v, pair & max_w) const + { + vector, vector> > VW; + int VW_n[N][N]; + v_w_linear(VW, VW_n); + + max_v.first = 0; + max_v.second = 0; + bool find = false; + for (int v = N - 1; v > 0; v--) + { + for (int w = N - 1; w > 0; w--) + { + if (VW_n[v][w] != 0) + { + find = true; + max_v.first = v; + max_v.second = w; + break; + } + } + if (find) break; + } + + max_w.first = 0; + max_w.second = 0; + find = false; + for (int w = N - 1; w > 0; w--) + { + for (int v = N - 1; v > 0; v--) + { + if (VW_n[v][w] != 0) + { + find = true; + max_w.first = v; + max_w.second = w; + break; + } + } + if (find) break; + } + } + + bool is_LE(const uint8_t S2[LUT_UNIT_N], uint8_t A[LUT_UNIT_N], uint8_t B[LUT_UNIT_N]) const + { + if (!is_permutation()) + { + return false; + } + + typedef bit_slice_l_t Set_t; + + uint8_t * S1 = (uint8_t *) LUT; + if (((S1[0] == 0) && (S2[0] != 0)) || ((S1[0] != 0) && (S2[0] == 0))) return false; + + uint8_t iS1[LUT_UNIT_N]; + uint8_t iS2[LUT_UNIT_N]; + for (int i = 0; i < LUT_UNIT_N; i++) + { + iS1[S1[i]] = i; + iS2[S2[i]] = i; + } + + array LA = {0}; + array LB = {0}; + + Set_t Z = { {0} }; + + Set_t UA = {{ 0 }}; + Set_t UB = {{ 0 }}; + Set_t CA = {{ 0 }}; + Set_t CB = {{ 0 }}; + Set_t NA = {{ 0 }}; + Set_t NB = {{ 0 }}; + Set_t GA = {{ 0 }}; + Set_t GB = {{ 0 }}; + uint8_t GS_old = 0; + + stack UA_stack; + stack UB_stack; + stack CA_stack; + stack CB_stack; + stack GA_stack; + stack GB_stack; + stack GS_stack; + stack > LA_stack; + stack > LB_stack; + + OPs.set_one_inplace(UA); OPs.unset_bit_inplace(UA, 0); + OPs.set_one_inplace(UB); OPs.unset_bit_inplace(UB, 0); + OPs.set_bit_inplace(CA, 0); + OPs.set_bit_inplace(CB, 0); + OPs.set_one_inplace(GA); OPs.unset_bit_inplace(GA, 0); + OPs.set_one_inplace(GB); OPs.unset_bit_inplace(GB, 0); + + memset(LA.data(), 0, sizeof(uint8_t) * LUT_UNIT_N); + memset(LB.data(), 0, sizeof(uint8_t) * LUT_UNIT_N); + + UA_stack.push(UA); + UB_stack.push(UB); + CA_stack.push(CA); + CB_stack.push(CB); + GA_stack.push(GA); + GB_stack.push(GB); + LA_stack.push(LA); + LB_stack.push(LB); + GS_stack.push(0); + + bool all_rejected = false; + bool last_rejected = false; + + while (((UA != Z) && (UB != Z)) || all_rejected) + { + if (all_rejected) + { + if (UA_stack.size() == 1) + { + return false; + } + PRINT(cout << "if (all_rejected)" << endl); + UA_stack.pop(); + UB_stack.pop(); + CA_stack.pop(); + CB_stack.pop(); + GA_stack.pop(); + GB_stack.pop(); + LA_stack.pop(); + LB_stack.pop(); + NA = Z; + NB = Z; + all_rejected = false; + last_rejected = true; + } + + if ((NA == Z) && (NB == Z)) + { + if (last_rejected) + { + PRINT(cout << "if (last_rejected)" << endl); + UA = UA_stack.top(); + UB = UB_stack.top(); + CA = CA_stack.top(); + CB = CB_stack.top(); + GA = GA_stack.top(); + GB = GB_stack.top(); + LA = LA_stack.top(); + LB = LB_stack.top(); + + GS_old = GS_stack.top(); GS_stack.pop(); + + uint8_t x = OPs.lsb_idx(UA); + PRINT(cout << "Reject Guess: LA[" << x + '\0' << "] = " << GS_old + '\0' << endl); + int next_guess = OPs.next_lsb_idx(GA, GS_old); + if (next_guess == -1) + { + PRINT(cout << "next_guess == -1" << endl); + all_rejected = true; + continue; + } + else + { + LA[x] = next_guess; + PRINT(cout << "Guess: LA[" << x + '\0' << "] = " << LA[x] + '\0' << endl); + GS_stack.push(next_guess); + OPs.unset_bit_inplace(GA, next_guess); + OPs.set_bit_inplace(NA, x); + OPs.unset_bit_inplace(UA, x); + } + last_rejected = false; + } + else + { + UA_stack.push(UA); + UB_stack.push(UB); + CA_stack.push(CA); + CB_stack.push(CB); + GA_stack.push(GA); + GB_stack.push(GB); + LA_stack.push(LA); + LB_stack.push(LB); + + uint8_t x = OPs.lsb_idx(UA); + uint8_t y = OPs.lsb_idx(GA); + LA[x] = y; + PRINT(cout << "Guess: LA[" << x + '\0' << "] = " << y + '\0' << endl); + GS_stack.push(y); + OPs.unset_bit_inplace(GA, y); + OPs.set_bit_inplace(NA, x); + OPs.unset_bit_inplace(UA, x); + } + } + + Set_t NA_tmp = NA; + while (NA != Z) + { + uint8_t x = OPs.unset_lsb_idx_inplace(NA); + Set_t CA_tmp = CA; + while (CA_tmp != Z) + { + uint8_t xa = OPs.unset_lsb_idx_inplace(CA_tmp); + uint8_t xax = xa ^ x; + uint8_t xay = LA[xa] ^ LA[x]; + int determinedA = OPs.get_bit(CA, xax); + int determinedNA = determinedA | OPs.get_bit(NA_tmp, xax); + if (((determinedNA == 1) && (LA[xax] != xay)) || ((determinedNA == 0) && (OPs.get_bit(GA, xay) == 0))) + { + PRINT(cout << "((OPs.get_bit(CA, xax) == 1) && (LA[xax] != xay)) || (OPs.get_bit(GA, xay) == 0)" << endl); + NA = Z; + NB = Z; + last_rejected = true; + break; + } + if (determinedA == 0) + { + LA[xax] = xay; + PRINT(cout << hex + << "From LA[" << xa + '\0' << "] = " << LA[xa] + '\0' + << " and LA[" << x + '\0' << "] = " << LA[x] + '\0' << ", " + << " get " << "LA[" << xax + '\0' << "] = " << xay + '\0' << dec << endl); + OPs.unset_bit_inplace(GA, xay); + OPs.set_bit_inplace(CA, xax); + uint8_t xb = S2[xax]; + uint8_t yb = S1[xay]; + int determinedNB = OPs.get_bit(CB, xb) | OPs.get_bit(NB, xb); + if (((determinedNB == 1) && (LB[xb] != yb)) || ((determinedNB == 0) && (OPs.get_bit(GB, yb) == 0))) + { + PRINT(cout << "((determinedNB == 1) && (LB[xb] != yb)) || ((determinedNB == 0) && (OPs.get_bit(GB, yb) == 0)))" << endl); + NA = Z; + NB = Z; + last_rejected = true; + break; + } + if (determinedNB == 0) + { + LB[xb] = yb; + PRINT(cout << hex + << "From S2[" << xax + '\0' << "] = " << xb + '\0' + << " and LA[" << xax + '\0' << "] = " << xay + '\0' + << " and S1[" << xay + '\0' << "] = " << yb + '\0' + << ", " << "get " << "LB[" << xb + '\0' << "] = " << yb + '\0' << dec << endl); + OPs.set_bit_inplace(NB, xb); + OPs.unset_bit_inplace(GB, yb); + } + } + } + if (last_rejected) break; + + double NBsize = (double)(OPs.cnt_1(NB)); + double CBsize = (double)(OPs.cnt_1(CB)); + PRINT(cout << "NBsize + log2(CBsize): " << NBsize + log2(CBsize) << endl); + if (NBsize + log2(CBsize) >= N) + { + PRINT(cout << "NBsize + log2(CBsize) >= N" << endl); + Set_t NBtmp = NB; + while (NB != Z) + { + uint8_t nx = OPs.unset_lsb_idx_inplace(NB); + Set_t CBtmp = CB; + while (CBtmp != Z) + { + uint8_t cx = OPs.unset_lsb_idx_inplace(CBtmp); + uint8_t ncx = nx ^ cx; + uint8_t ncy = LB[nx] ^ LB[cx]; + int determinedCB = OPs.get_bit(CB, ncx); + int determinedB = determinedCB | OPs.get_bit(NBtmp, ncx); + if (((determinedB == 1) && (LB[ncx] != ncy)) || ((determinedB == 0) && (OPs.get_bit(GB, ncy) == 0))) + { + PRINT(cout << "((determinedB == 1) && (LB[ncx] != ncy)) || ((determinedB == 0) && (OPs.get_bit(GB, ncy) == 0))" << endl); + NA = Z; + NB = Z; + last_rejected = true; + break; + } + if (determinedCB == 0) + { + LB[ncx] = ncy; + OPs.set_bit_inplace(CB, ncx); + OPs.unset_bit_inplace(GB, ncy); + PRINT(cout << hex + << "From LB[" << nx + '\0' << "] = " << LB[nx] + '\0' + << " and LB[" << cx + '\0' << "] = " << LB[cx] + '\0' << ", " + << " get " << "LB[" << ncx + '\0' << "] = " << ncy + '\0' << dec << endl); + } + } + if (last_rejected) break; + } + if (last_rejected) break; + + double NCBsize = (double)(OPs.cnt_1(CB)); + if (NCBsize == LUT_UNIT_N) + { + if (is_linear(LB)) + { + PRINT(cout << "is_linear(LB)" << endl); + Set_t UAtmp = UA ^ (UA & CA); + while (UAtmp != Z) + { + int ux = OPs.unset_lsb_idx_inplace(UAtmp); + LA[ux] = iS1[LB[S2[ux]]]; + } + if (is_linear(LA)) + { + PRINT(cout << "is_linear(LA)" << __LINE__ << endl); + memcpy(A, LA.data(), sizeof(uint8_t) * LUT_UNIT_N); + memcpy(B, LB.data(), sizeof(uint8_t) * LUT_UNIT_N); + return true; + } + else + { + last_rejected = true; + NA = Z; + NB = Z; + } + } + else + { + last_rejected = true; + NA = Z; + NB = Z; + } + } + } + } + + Set_t NB_tmp = NB; + while (NB != Z) + { + uint8_t x = OPs.unset_lsb_idx_inplace(NB); + Set_t CB_tmp = CB; + while (CB_tmp != Z) + { + uint8_t xb = OPs.unset_lsb_idx_inplace(CB_tmp); + uint8_t xbx = xb ^ x; + uint8_t xby = LB[xb] ^ LB[x]; + int determinedB = OPs.get_bit(CB, xbx); + int determinedNB = determinedB | OPs.get_bit(NB_tmp, xbx); + if (((determinedNB == 1) && (LB[xbx] != xby)) || ((determinedNB == 0) && (OPs.get_bit(GB, xby) == 0))) + { + PRINT(cout << "((determinedNB == 1) && (LB[xbx] != xby)) || ((determinedNB == 0) && (OPs.get_bit(GB, xby) == 0))" << endl); + NA = Z; + NB = Z; + last_rejected = true; + break; + } + if (determinedB == 0) + { + LB[xbx] = xby; + PRINT(cout << hex + << "From LB[" << xb + '\0' << "] = " << LB[xb] + '\0' + << " and LB[" << x + '\0' << "] = " << LB[x] + '\0' << ", " + << " get " << "LB[" << xbx + '\0' << "] = " << xby + '\0' << dec << endl); + OPs.unset_bit_inplace(GB, xby); + OPs.set_bit_inplace(CB, xbx); + uint8_t xa = iS2[xbx]; + uint8_t ya = iS1[xby]; + int determinedNA = OPs.get_bit(CA, xa) | OPs.get_bit(NA, xa); + if (((determinedNA == 1) && (LA[xa] != ya)) || ((determinedNA == 0) && (OPs.get_bit(GA, ya) == 0))) + { + PRINT(cout << "((determinedA == 1) && (LA[xa] != ya)) || ((determinedA == 0) && (OPs.get_bit(GA, ya) == 0))" << endl); + NA = Z; + NB = Z; + last_rejected = true; + break; + } + if (determinedNA == 0) + { + LA[xa] = ya; + PRINT(cout << hex + << "From iS2[" << xbx + '\0' << "] = " << xa + '\0' + << " and LB[" << xbx + '\0' << "] = " << xby + '\0' + << " and iS1[" << xby + '\0' << "] = " << ya + '\0' + << ", " << "get " << "LA[" << xa + '\0' << "] = " << ya + '\0' << dec << endl); + OPs.set_bit_inplace(NA, xa); + OPs.unset_bit_inplace(GA, ya); + } + } + } + if (last_rejected) break; + + double NAsize = (double)OPs.cnt_1(NA); + double CAsize = (double)(OPs.cnt_1(CA)); + PRINT(cout << "NAsize + log2(CAsize): " << NAsize + log2(CAsize) << endl); + if (NAsize + log2(CAsize) >= N) + { + PRINT(cout << "NAsize + log2(CAsize) >= N" << endl); + Set_t NAtmp = NA; + while (NA != Z) + { + uint8_t nx = OPs.unset_lsb_idx_inplace(NA); + Set_t CAtmp = CA; + while (CAtmp != Z) + { + uint8_t cx = OPs.unset_lsb_idx_inplace(CAtmp); + uint8_t ncx = nx ^ cx; + uint8_t ncy = LA[nx] ^ LA[cx]; + int determinedCA = OPs.get_bit(CA, ncx); + int determinedA = determinedCA | OPs.get_bit(NAtmp, ncx); + if (((determinedA == 1) && (LA[ncx] != ncy)) || ((determinedA == 0) && (OPs.get_bit(GA, ncy) == 0))) + { + PRINT(cout << "((determinedA == 1) && (LA[ncx] != ncy)) || ((determinedA == 0) && (OPs.get_bit(GA, ncy) == 0))" << endl); + NA = Z; + NB = Z; + last_rejected = true; + break; + } + if (determinedCA == 0) + { + LA[ncx] = ncy; + OPs.set_bit_inplace(CA, ncx); + OPs.unset_bit_inplace(GA, ncy); + PRINT(cout << hex + << "From LA[" << nx + '\0' << "] = " << LA[nx] + '\0' + << " and LA[" << cx + '\0' << "] = " << LA[cx] + '\0' << ", " + << " get " << "LA[" << ncx + '\0' << "] = " << ncy + '\0' << dec << endl); + } + } + if (last_rejected) break; + } + if (last_rejected) break; + + double NCAsize = (double)(OPs.cnt_1(CA)); + if (NCAsize == LUT_UNIT_N) + { + if (is_linear(LA)) + { + PRINT(cout << "is_linear(LA)" << endl); + Set_t UBtmp = UB ^ (UB & CB); + while (UBtmp != Z) + { + int ux = OPs.unset_lsb_idx_inplace(UBtmp); + LB[ux] = S1[LA[iS2[ux]]]; + } + if (is_linear(LB)) + { + PRINT(cout << "is_linear(LB)" << endl); + memcpy(A, LA.data(), sizeof(uint8_t) * LUT_UNIT_N); + memcpy(B, LB.data(), sizeof(uint8_t) * LUT_UNIT_N); + return true; + } + else + { + last_rejected = true; + NA = Z; + NB = Z; + } + } + else + { + last_rejected = true; + NA = Z; + NB = Z; + } + } + } + } + + if (last_rejected) continue; + + UA = UA ^ (UA & CA); + UB = UB ^ (UB & CB); + } + return false; + } + + bool is_LE(const function_t & S2, function_t & A, function_t & B) const + { + bool isLE = is_LE((uint8_t *)S2.LUT, (uint8_t *)A.LUT, (uint8_t *)B.LUT); + if (isLE) + { + A.LUT_to_bit_slice(); + B.LUT_to_bit_slice(); + } + return isLE; + } + + template + void PRINT_LUT(T RS) const + { + cout << hex << setfill('0'); + for (int i = 0; i < LUT_UNIT_N; i++) cout << RS[i] + '\0' << ", "; + cout << dec << endl; + } + + // Require: function in LUT form + void get_PE(vector, int> > & PE_vec) const + { + map, int> PEmap; + function_t func_perm_pi; + function_t func_perm_po; + for (int ip = 0; ip < FACT_(N); ip++) + { + OPs.composite(func_perm_pi.LUT, LUT, PE.func[ip].LUT); + for (int op = 0; op < FACT_(N); op++) + { + OPs.composite(func_perm_po.LUT, PE.func[op].LUT, func_perm_pi.LUT); + func_perm_po.LUT_to_bit_slice(); + PEmap.insert(pair, int >(func_perm_po, (ip<<16)|(op<<8))); + } + } + + for (auto map_it = PEmap.begin(); map_it != PEmap.end(); map_it++) + { + PE_vec.push_back(*map_it); + } + } + + // Require: function in LUT form + void get_PE(string outfile) const + { + ofstream outf(outfile.c_str(), ios::app); + vector, int> > PE_vec; + get_PE(PE_vec); + for (auto pe : PE_vec) + { + int insid = pe.second; + string name = "iXiPoPoX_" + std::to_string((insid>>24)&0xff) + "_" + std::to_string((insid>>16)&0xff) + "_" + std::to_string((insid>>8)&0xff) + "_" + std::to_string((insid)&0xff); + outf << name << "," << pe.first.LUT_to_string() << endl; + } + outf.close(); + } + + // Require: function in LUT form + void get_XE(vector, int> > & XE_vec) const + { + map, int> XEmap; + function_t func_perm_xi; + for (int ix = 0; ix < (1<.composite(func_perm_xi.LUT, LUT, XE[ix].LUT); + for (int ox = 0; ox < (1< func_comp; + OPs.composite(func_comp.LUT, XE[ox].LUT, func_perm_xi.LUT); + func_comp.LUT_to_bit_slice(); + XEmap.insert(pair, int >(func_comp, (ix<<24)|ox)); + } + } + + for (auto map_it = XEmap.begin(); map_it != XEmap.end(); map_it++) + { + XE_vec.push_back(*map_it); + } + } + + // Require: function in LUT form + void get_XE(string outfile) const + { + ofstream outf(outfile.c_str(), ios::app); + vector, int> > XE_vec; + get_XE(XE_vec); + for (auto xe : XE_vec) + { + int insid = xe.second; + string name = "iXiPoPoX_" + std::to_string((insid>>24)&0xff) + "_" + std::to_string((insid>>16)&0xff) + "_" + std::to_string((insid>>8)&0xff) + "_" + std::to_string((insid)&0xff); + outf << name << "," << xe.first.LUT_to_string() << endl; + } + outf.close(); + } + + function_t PE_representative() const + { + function_t func_min; + function_t func_perm; + + OPs.composite(func_perm.LUT, LUT, PE.func[0].LUT); + func_perm.LUT_to_bit_slice(); + func_perm.sort(); + func_min = func_perm; + + for (int ip = 1; ip < FACT_(N); ip++) + { + OPs.composite(func_perm.LUT, LUT, PE.func[ip].LUT); + func_perm.LUT_to_bit_slice(); + func_perm.sort(); + func_min = (func_perm < func_min) ? func_perm : func_min; + } + func_min.bit_slice_to_LUT(); + return func_min; + } + + // Require: function in LUT form + void get_PXE(vector, int> > & PXE_vec) const + { + map, int> PXEmap; + function_t func_perm_xi; + function_t func_perm_pi; + function_t func_perm_po; + for (int ix = 0; ix < (1<.composite(func_perm_xi.LUT, PE.func[ip].LUT, XE[ix].LUT); + OPs.composite(func_perm_pi.LUT, LUT, func_perm_xi.LUT); + for (int op = 0; op < FACT_(N); op++) + { + OPs.composite(func_perm_po.LUT, PE.func[op].LUT, func_perm_pi.LUT); + for (int ox = 0; ox < (1< func_comp; + OPs.composite(func_comp.LUT, XE[ox].LUT, func_perm_po.LUT); + func_comp.LUT_to_bit_slice(); + PXEmap.insert(pair, int >(func_comp, (ix<<24)|(ip<<16)|(op<<8)|ox)); + } + } + } + } + + for (auto map_it = PXEmap.begin(); map_it != PXEmap.end(); map_it++) + { + PXE_vec.push_back(*map_it); + } + } + + // Require: function in LUT form + void get_PXE(string outfile) const + { + ofstream outf(outfile.c_str(), ios::app); + vector, int> > PXE_vec; + get_PXE(PXE_vec); + for (auto pxe : PXE_vec) + { + int insid = pxe.second; + string name = "iXiPoPoX_" + std::to_string((insid>>24)&0xff) + "_" + std::to_string((insid>>16)&0xff) + "_" + std::to_string((insid>>8)&0xff) + "_" + std::to_string((insid)&0xff); + outf << name << "," << pxe.first.LUT_to_string() << endl; + } + outf.close(); + } + + function_t PXE_representative() const + { + function_t func_min; + func_min.bit_slice = bit_slice; + for (int i = 0; i < LUT_XMM_N; i++) func_min.LUT[i] = LUT[i]; + + function_t func_perm_xi; + function_t func_perm_pi; + function_t func_perm_po; + for (int ix = 0; ix < (1<.composite(func_perm_xi.LUT, PE.func[ip].LUT, XE[ix].LUT); + OPs.composite(func_perm_pi.LUT, LUT, func_perm_xi.LUT); + for (int op = 0; op < FACT_(N); op++) + { + OPs.composite(func_perm_po.LUT, PE.func[op].LUT, func_perm_pi.LUT); + for (int ox = 0; ox < (1< func_comp; + OPs.composite(func_comp.LUT, XE[ox].LUT, func_perm_po.LUT); + func_comp.LUT_to_bit_slice(); + func_min = (func_comp < func_min) ? func_comp : func_min; + } + } + } + } + return func_min; + } + + void LE_representative(uint8_t RS[LUT_UNIT_N], uint8_t A[LUT_UNIT_N], uint8_t B[LUT_UNIT_N]) const + { + if (!is_permutation()) + { + return; + } + PRINT(cout << __LINE__ << endl); + + typedef bit_slice_l_t Set_t; + + uint8_t * S = (uint8_t *) LUT; + uint8_t iS[LUT_UNIT_N]; + for (int i = 0; i < LUT_UNIT_N; i++) + { + iS[S[i]] = i; + } + const Set_t Z = { {0} }; + + array min_LR; + array min_LA; + array min_LB; + min_LR.fill(numeric_limits::max()); + + Set_t UA, UB, UR, DA, DB, DR, CA, CB, NA, NB, GA, GB, GR; + array LR, LA, LB, iLA, iLB; + uint8_t GS_old = 0; + + vector UA_stack; + vector UB_stack; + vector UR_stack; + vector DA_stack; + vector DB_stack; + vector DR_stack; + vector CA_stack; + vector CB_stack; + vector GA_stack; + vector GB_stack; + vector GR_stack; + vector GS_stack; + vector > LR_stack; + vector > LA_stack; + vector > LB_stack; + vector > iLA_stack; + vector > iLB_stack; + + UA.fill(UINT_MAX); OPs.unset_bit_inplace(UA, 0); + UB.fill(UINT_MAX); OPs.unset_bit_inplace(UB, 0); + DA.fill(0); OPs.set_bit_inplace(DA, 0); + DB.fill(0); OPs.set_bit_inplace(DB, 0); + GA.fill(UINT_MAX); OPs.unset_bit_inplace(GA, 0); + GB.fill(UINT_MAX); OPs.unset_bit_inplace(GB, 0); + LR.fill(0); LA.fill(0); LB.fill(0); iLA.fill(0); iLB.fill(0); + + if (S[0] == 0) + { + CA.fill(0); OPs.set_bit_inplace(CA, 0); + CB.fill(0); OPs.set_bit_inplace(CB, 0); + NA.fill(0); + NB.fill(0); + DR.fill(0); OPs.set_bit_inplace(DR, 0); + UR.fill(UINT_MAX); OPs.unset_bit_inplace(UR, 0); + GR.fill(UINT_MAX); OPs.unset_bit_inplace(GR, 0); + LR[0] = 0; + } + else + { + CA.fill(0); + CB.fill(0); + NA.fill(0); OPs.set_bit_inplace(NA, 0); + NB.fill(0); OPs.set_bit_inplace(NB, 0); + DR.fill(0); + UR.fill(UINT_MAX); + GR.fill(UINT_MAX); + } + + GS_stack.push_back(0); + UA_stack.push_back(UA); + UB_stack.push_back(UB); + UR_stack.push_back(UR); + DA_stack.push_back(DA); + DB_stack.push_back(DB); + DR_stack.push_back(DR); + CA_stack.push_back(CA); + CB_stack.push_back(CB); + GA_stack.push_back(GA); + GB_stack.push_back(GB); + GR_stack.push_back(GR); + LR_stack.push_back(LR); + LA_stack.push_back(LA); + LB_stack.push_back(LB); + iLA_stack.push_back(iLA); + iLB_stack.push_back(iLB); + + bool all_rejected = false; + bool last_rejected = false; + bool try_next = true; + + PRINT(cout << "GS_stack.size(): " << GS_stack.size() << " at line: " << __LINE__ << endl); + PRINT(cout << "UA_stack.size(): " << UA_stack.size() << " at line: " << __LINE__ << endl); + + while ((((UA != Z) && (UB != Z)) || all_rejected) || try_next) + { + try_next = false; + if (all_rejected) + { + PRINT(cout << "GS_stack.size(): " << GS_stack.size() << " at line: " << __LINE__ << endl); + PRINT(cout << "UA_stack.size(): " << UA_stack.size() << " at line: " << __LINE__ << endl); + if (GS_stack.size() == 1) + { + memcpy(RS, min_LR.data(), sizeof(uint8_t) * LUT_UNIT_N); + memcpy(A, min_LA.data(), sizeof(uint8_t) * LUT_UNIT_N); + memcpy(B, min_LB.data(), sizeof(uint8_t) * LUT_UNIT_N); + return; + } + PRINT(cout << "if (all_rejected)" << endl); + UA_stack.pop_back(); + UB_stack.pop_back(); + UR_stack.pop_back(); + DA_stack.pop_back(); + DB_stack.pop_back(); + DR_stack.pop_back(); + CA_stack.pop_back(); + CB_stack.pop_back(); + GA_stack.pop_back(); + GB_stack.pop_back(); + GR_stack.pop_back(); + LR_stack.pop_back(); + LA_stack.pop_back(); + LB_stack.pop_back(); + iLA_stack.pop_back(); + iLB_stack.pop_back(); + NA = Z; NB = Z; + all_rejected = false; + last_rejected = true; + try_next = true; + } + + if ((NA == Z) && (NB == Z)) + { + uint8_t x, y; + + if (last_rejected) + { + PRINT(cout << "if (last_rejected)" << endl); + UA = UA_stack.back(); + UB = UB_stack.back(); + UR = UR_stack.back(); + DA = DA_stack.back(); + DB = DB_stack.back(); + DR = DR_stack.back(); + CA = CA_stack.back(); + CB = CB_stack.back(); + GA = GA_stack.back(); + GB = GB_stack.back(); + GR = GR_stack.back(); + LR = LR_stack.back(); + LA = LA_stack.back(); + LB = LB_stack.back(); + iLA = iLA_stack.back(); + iLB = iLB_stack.back(); + + GS_old = GS_stack.back(); GS_stack.pop_back(); + + x = OPs.lsb_idx(UA); + PRINT(cout << "Reject Guess: LA[" << x + '\0' << "] = " << GS_old + '\0' << endl); + int next_guess = OPs.next_lsb_idx(GA, GS_old); + if (next_guess == -1) + { + PRINT(cout << "next_guess == -1" << endl); + all_rejected = true; + continue; + } + else + { + y = next_guess; + last_rejected = false; + } + } + else + { + UA_stack.push_back(UA); + UB_stack.push_back(UB); + UR_stack.push_back(UR); + DA_stack.push_back(DA); + DB_stack.push_back(DB); + DR_stack.push_back(DR); + CA_stack.push_back(CA); + CB_stack.push_back(CB); + GA_stack.push_back(GA); + GB_stack.push_back(GB); + GR_stack.push_back(GR); + LR_stack.push_back(LR); + LA_stack.push_back(LA); + LB_stack.push_back(LB); + iLA_stack.push_back(iLA); + iLB_stack.push_back(iLB); + + x = OPs.lsb_idx(UA); + y = OPs.lsb_idx(GA); + } + + LA[x] = y; iLA[y] = x; + PRINT(cout << "Guess: LA[" << x + '\0' << "] = " << y + '\0' << endl); + GS_stack.push_back(y); + + PRINT(cout << "GS_stack.size(): " << GS_stack.size() << " at line: " << __LINE__ << endl); + PRINT(cout << "UA_stack.size(): " << UA_stack.size() << " at line: " << __LINE__ << endl); + + Set_t DA_tmp = DA; + Set_t GA_old = GA; + while (DA_tmp != Z) + { + uint8_t xd = OPs.unset_lsb_idx_inplace(DA_tmp); + uint8_t yd = LA[xd]; + uint8_t xx = x ^ xd; + uint8_t xy = y ^ yd; + LA[xx] = xy; iLA[xy] = xx; + PRINT(cout << hex + << " From LA[" << x +'\0' << "] = " << y + '\0' + << " and LA[" << xd +'\0' << "] = " << yd + '\0' + << " get LA[" << xx +'\0' << "] = " << xy + '\0' + << endl); + OPs.set_bit_inplace(DA, xx); + OPs.unset_bit_inplace(UA, xx); + OPs.unset_bit_inplace(GA, xy); + } + Set_t ADy = GA_old ^ GA; + while (ADy != Z) + { + uint8_t cx = OPs.unset_lsb_idx_inplace(ADy); + uint8_t cy = iLA[cx]; + OPs.set_bit_inplace(NA, cy); + } + } + + while (NA != Z) + { + uint8_t xa = OPs.lsb_idx(NA); + uint8_t yb = S[LA[xa]]; + CHECK( + int determinedB = OPs.get_bit(GB, yb); + if (determinedB == 0) + { + last_rejected = true; + NA = Z; NB = Z; + break; + }); + uint8_t xb = OPs.lsb_idx(UB); + LB[xb] = yb; iLB[yb] = xb; + + LR[xa] = xb; + + PRINT(cout << hex + << " From LA[" << xa + '\0' << "] = " << LA[xa] + '\0' + << " and S[" << LA[xa] + '\0' << "] = " << yb + '\0' + << " and LB[" << xb + '\0' << "] = " << yb + '\0' + << " get LR[" << xa + '\0' << "] = " << xb + '\0' + << endl); + PRINT(PRINT_LUT(LR)); + + CHECKR( + if ((OPs.get_bit(DR, xa) == 1) || (OPs.get_bit(GR, xb) == 0)) + { + PRINT(cout << "(OPs.get_bit(DR, xa) == 1) || (OPs.get_bit(GR, xb) == 0)" << __LINE__ << endl); + last_rejected = true; + NA = Z; NB = Z; + break; + }); + OPs.set_bit_inplace(DR, xa); + OPs.unset_bit_inplace(UR, xa); + OPs.unset_bit_inplace(GR, xb); + int uk = OPs.lsb_idx(UR); + if (uk > xa) + { + if(lexicographical_compare( + min_LR.begin(), min_LR.begin() + uk, + LR.begin(), LR.begin() + uk)) + { + last_rejected = true; + NA = Z; NB = Z; + break; + } + } + + Set_t GB_old = GB; + Set_t DB_tmp = DB; + while (DB_tmp != Z) + { + uint8_t xbd = OPs.unset_lsb_idx_inplace(DB_tmp); + uint8_t ybd = LB[xbd]; + uint8_t xbx = xb ^ xbd; + uint8_t xby = yb ^ ybd; + LB[xbx] = xby; iLB[xby] = xbx; + PRINT(cout << hex + << " From LB[" << xb +'\0' << "] = " << yb + '\0' + << " and LB[" << xbd +'\0' << "] = " << ybd + '\0' + << " get LB[" << xbx +'\0' << "] = " << xby + '\0' + << endl); + OPs.set_bit_inplace(DB, xbx); + OPs.unset_bit_inplace(UB, xbx); + OPs.unset_bit_inplace(GB, xby); // maybe GB is not useful although the process + } + Set_t BDy = GB_old ^ GB; + + Set_t SAx = Z; + Set_t NA_tmp = NA; + while (NA_tmp != Z) + { + uint8_t xan = OPs.unset_lsb_idx_inplace(NA_tmp); + uint8_t ybn = S[LA[xan]]; + OPs.set_bit_inplace(SAx, ybn); + } + Set_t cap_BD_SA = BDy & SAx; + Set_t sub_BD_SA = BDy ^ cap_BD_SA; + while (cap_BD_SA != Z) + { + uint8_t cx = OPs.unset_lsb_idx_inplace(cap_BD_SA); + uint8_t cy = iLB[cx]; + OPs.set_bit_inplace(CB, cy); + cy = iLA[iS[cx]]; + OPs.set_bit_inplace(CA, cy); + OPs.unset_bit_inplace(NA, cy); + } + while (sub_BD_SA != Z) + { + uint8_t cx = OPs.unset_lsb_idx_inplace(sub_BD_SA); + uint8_t cy = iLB[cx]; + OPs.set_bit_inplace(NB, cy); + } + + while ((NA == Z) && (NB != Z)) + { + uint8_t xbn = OPs.lsb_idx(NB); + uint8_t ya = iS[LB[xbn]]; + CHECK( + int determinedA = OPs.get_bit(GA, ya); + if (determinedA == 0) + { + last_rejected = true; + NA = Z; NB = Z; + break; + }); + uint8_t xau = OPs.lsb_idx(UA); + LA[xau] = ya; iLA[ya] = xau; + PRINT(cout << hex + << " From LB[" << xbn + '\0' << "] = " << LB[xbn] + '\0' + << " and iS[" << LB[xbn] + '\0' << "] = " << ya + '\0' + << " and LA[" << xau + '\0' << "] = " << ya + '\0' + << " get LR[" << xau + '\0' << "] = " << xbn + '\0' + << endl); + + LR[xau] = xbn; + PRINT(PRINT_LUT(LR)); + + CHECKR( + if ((OPs.get_bit(DR, xau) == 1) || (OPs.get_bit(GR, xbn) == 0)) + { + PRINT(cout << "(OPs.get_bit(DR, xau) == 1) || (OPs.get_bit(GR, xbn) == 0)" << __LINE__ << endl); + NA = Z; NB = Z; + last_rejected = true; + break; + }); + OPs.set_bit_inplace(DR, xau); + OPs.unset_bit_inplace(UR, xau); + OPs.unset_bit_inplace(GR, xbn); + int uk = OPs.lsb_idx(UR); + if (uk > xau) + { + if(lexicographical_compare( + min_LR.begin(), min_LR.begin() + uk, + LR.begin(), LR.begin() + uk)) + { + NA = Z; NB = Z; + last_rejected = true; + break; + } + } + + Set_t DA_tmp = DA; + Set_t GA_old = GA; + while (DA_tmp != Z) + { + uint8_t xad = OPs.unset_lsb_idx_inplace(DA_tmp); + uint8_t yad = LA[xad]; + uint8_t xax = xau ^ xad; + uint8_t xay = ya ^ yad; + LA[xax] = xay; iLA[xay] = xax; + PRINT(cout << hex + << " From LA[" << xau +'\0' << "] = " << ya + '\0' + << " and LA[" << xad +'\0' << "] = " << yad + '\0' + << " get LA[" << xax +'\0' << "] = " << xay + '\0' + << endl); + OPs.set_bit_inplace(DA, xax); + OPs.unset_bit_inplace(UA, xax); + OPs.unset_bit_inplace(GA, xay); + } + Set_t ADy = GA_old ^ GA; + + Set_t iSBx = Z; + Set_t NB_tmp = NB; + while (NB_tmp != Z) + { + uint8_t xbnt = OPs.unset_lsb_idx_inplace(NB_tmp); + uint8_t yant = iS[LB[xbnt]]; + OPs.set_bit_inplace(iSBx, yant); + } + Set_t cap_AD_iSB = ADy & iSBx; + Set_t sub_AD_iSB = ADy ^ cap_AD_iSB; + while (cap_AD_iSB != Z) + { + uint8_t cx = OPs.unset_lsb_idx_inplace(cap_AD_iSB); + uint8_t cy = iLA[cx]; + OPs.set_bit_inplace(CA, cy); + cy = iLB[S[cx]]; + OPs.set_bit_inplace(CB, cy); + OPs.unset_bit_inplace(NB, cy); + } + while (sub_AD_iSB != Z) + { + uint8_t cx = OPs.unset_lsb_idx_inplace(sub_AD_iSB); + uint8_t cy = iLA[cx]; + OPs.set_bit_inplace(NA, cy); + } + } + + if ((UA == Z) && (UB == Z) && (!last_rejected)) + { + try_next = true; + + Set_t DR_tmp = ~DR; + while (DR_tmp != Z) + { + uint8_t x = OPs.unset_lsb_idx_inplace(DR_tmp); + uint8_t y = iLB[S[LA[x]]]; + CHECKR( + if (OPs.get_bit(GR, y) == 0) + { + PRINT(cout << "OPs.get_bit(GR, y) == 0" << __LINE__ << endl); + NA = Z; NB = Z; + last_rejected = true; + break; + }); + LR[x] = y; + OPs.set_bit_inplace(DR, x); + OPs.unset_bit_inplace(UR, x); + OPs.unset_bit_inplace(GR, y); + + int uk = OPs.lsb_idx(UR); + if (uk > x) + { + if(lexicographical_compare( + min_LR.begin(), min_LR.begin() + uk, + LR.begin(), LR.begin() + uk)) + { + NA = Z; NB = Z; + last_rejected = true; + break; + } + } + } + if (last_rejected) break; + PRINT(cout << " Update min_LR: "); + PRINT(PRINT_LUT(min_LR)); + PRINT(cout << " with LR: "); + PRINT(PRINT_LUT(LR)); + + min_LR = LR; + min_LA = LA; + min_LB = LB; + + last_rejected = true; + } + } + } + + memcpy(RS, min_LR.data(), sizeof(uint8_t) * LUT_UNIT_N); + memcpy(A, min_LA.data(), sizeof(uint8_t) * LUT_UNIT_N); + memcpy(B, min_LB.data(), sizeof(uint8_t) * LUT_UNIT_N); + } + + bool is_AE(const uint8_t S2[LUT_UNIT_N], uint8_t RS[LUT_UNIT_N], + uint8_t S1_L1[LUT_UNIT_N], uint8_t S1_L2[LUT_UNIT_N], uint8_t &a, + uint8_t S2_L1[LUT_UNIT_N], uint8_t S2_L2[LUT_UNIT_N], uint8_t &b) const + { + PRINT(cout << __LINE__ << endl); + if (!is_permutation()) + { + return false; + } + + struct LExinfo_t + { + array L1; + array L2; + uint8_t c; + }; + + map, LExinfo_t> S1_minRs; + map, LExinfo_t> S2_minRs; + + function_t S1aF; + function_t S2bF; + function_t S2F; + array R; + uint8_t * Rp = R.data(); + + for (int i = 0; i < LUT_XMM_N; i++) + { + S2F.LUT[i] = _mm_loadu_si128(((__m128i *)S2) + i); + } + + + for (int ai = 0; ai < (1<.composite(S1aF.LUT, LUT, XE[ai].LUT); + PRINT(cout << S1aF.LUT_to_string() << endl); + S1aF.LE_representative(Rp, LExinfo.L1.data(), LExinfo.L2.data()); + PRINT(cout << __LINE__ << endl); + PRINT(PRINT_LUT(R)); + S1_minRs.insert(pair, LExinfo_t>(R, LExinfo)); + + OPs.composite(S2bF.LUT, XE[ai].LUT, S2F.LUT); + PRINT(cout << S2bF.LUT_to_string() << endl); + S2bF.LE_representative(Rp, LExinfo.L1.data(), LExinfo.L2.data()); + PRINT(cout << __LINE__ << endl); + PRINT(PRINT_LUT(R)); + S2_minRs.insert(pair, LExinfo_t>(R, LExinfo)); + } + + PRINT(cout << __LINE__ << endl); + + for (const auto & each_S1_minR : S1_minRs) + { + PRINT(cout << __LINE__ << endl); + auto the_S2_minR = S2_minRs.find(each_S1_minR.first); + if (the_S2_minR != S2_minRs.end()) + { + memcpy(RS, each_S1_minR.first.data(), sizeof(uint8_t) * LUT_UNIT_N); + memcpy(S1_L1, each_S1_minR.second.L1.data(), sizeof(uint8_t) * LUT_UNIT_N); + memcpy(S1_L2, each_S1_minR.second.L2.data(), sizeof(uint8_t) * LUT_UNIT_N); + a = each_S1_minR.second.c; + + memcpy(S2_L1, (*the_S2_minR).second.L1.data(), sizeof(uint8_t) * LUT_UNIT_N); + memcpy(S2_L2, (*the_S2_minR).second.L2.data(), sizeof(uint8_t) * LUT_UNIT_N); + b = (*the_S2_minR).second.c; + + return true; + } + } + return false; + } + + bool is_AE(const function_t & S2, function_t & RS, + function_t & S1_L1, function_t & S1_L2, uint8_t &a, + function_t & S2_L1, function_t & S2_L2, uint8_t &b) const + { + bool isAE_flag = is_AE((uint8_t *)S2.LUT, (uint8_t *)RS.LUT, + (uint8_t *)S1_L1.LUT, (uint8_t *)S1_L2.LUT, a, + (uint8_t *)S2_L1.LUT, (uint8_t *)S2_L2.LUT, b); + if (isAE_flag) + { + RS.LUT_to_bit_slice(); + S1_L1.LUT_to_bit_slice(); + S1_L2.LUT_to_bit_slice(); + S2_L1.LUT_to_bit_slice(); + S2_L2.LUT_to_bit_slice(); + } + return isAE_flag; + } + + // B(Gi(A(x) + a)) + b = S(x) + bool is_optimal(int & gi, function_t & Gi, function_t & A, function_t & B, uint8_t & a, uint8_t & b) const + { + if (N == 4) + { + if (!is_permutation()) + { + return false; + } + + array R; + uint8_t * Rp = R.data(); + function_t SbF; + map, LExinfoGx_t > S_Rs; + for (int ai = 0; ai < (1< LExinfo; + LExinfo.c = ai; + + OPs.composite(SbF.LUT, XE[ai].LUT, LUT); + SbF.LE_representative(Rp, LExinfo.L1.data(), LExinfo.L2.data()); + S_Rs.insert(pair, LExinfoGx_t >(R, LExinfo)); + } + + function_t S_A; + function_t S_B; + function_t G_A; + function_t G_B; + + for (const auto & each_S_R : S_Rs) + { + auto the_G_R = G_Rs.find(each_S_R.first); + if (the_G_R != G_Rs.end()) + { + gi = (*the_G_R).second.Gx; + Gi.LUT[0] = _mm_load_si128((__m128i *)G4[gi]); Gi.LUT_to_bit_slice(); + + G_A.LUT[0] = _mm_loadu_si128((__m128i *)(*the_G_R).second.L1.data()); + G_B.LUT[0] = _mm_loadu_si128((__m128i *)(*the_G_R).second.L2.data()); + G_B = G_B.inverse(); + a = (*the_G_R).second.c; + + S_A.LUT[0] = _mm_loadu_si128((__m128i *)each_S_R.second.L1.data()); + S_A = S_A.inverse(); + S_B.LUT[0] = _mm_loadu_si128((__m128i *)each_S_R.second.L2.data()); + b = each_S_R.second.c; + + OPs.composite(A.LUT, G_A.LUT, S_A.LUT); A.LUT_to_bit_slice(); + OPs.composite(B.LUT, G_B.LUT, S_B.LUT); B.LUT_to_bit_slice(); + + return true; + } + } + return false; + } + else + { + // cout << "is_optimal(...):: Not support for N != 4" << endl; + return false; + } + } + + // B(Gi(A(x) + a)) + b = S(x) + bool is_optimal(int & gi, uint8_t Gi[LUT_UNIT_N], uint8_t A[LUT_UNIT_N], uint8_t B[LUT_UNIT_N], uint8_t & a, uint8_t & b) const + { + if (N == 4) + { + if (!is_permutation()) + { + return false; + } + function_t G_F; + function_t A_F; + function_t B_F; + bool flag = is_optimal(gi, G_F, A_F, B_F, a, b); + if (flag) + { + memcpy(Gi, (uint8_t *)G_F.LUT, sizeof(uint8_t)*LUT_UNIT_N); + memcpy(A, (uint8_t *)A_F.LUT, sizeof(uint8_t)*LUT_UNIT_N); + memcpy(B, (uint8_t *)B_F.LUT, sizeof(uint8_t)*LUT_UNIT_N); + } + return flag; + } + else + { + // cout << "is_optimal(...):: Not support for N != 4" << endl; + return false; + } + } + + void get_AE_PEreps(set & PEreps) const + { + if (N == 4) + { + function_t PB_S_AP; + for (int ai = 0; ai < AFFINE_MATRIX_COL_PE_BOX4_N; ai++) + { + __m128i AP_LUT[LUT_XMM_N]; + AP_LUT[0] = _mm_load_si128((__m128i *)AffineMatrixColPEBox4[ai]); + OPs.composite(AP_LUT, LUT, AP_LUT); + for (int bi = 0; bi < AFFINE_MATRIX_ROW_PE_BOX4_N; bi++) + { + __m128i PB_LUT[LUT_XMM_N]; + PB_LUT[0] = _mm_load_si128((__m128i *)AffineMatrixRowPEBox4[bi]); + OPs.composite(PB_S_AP.LUT, PB_LUT, AP_LUT); + PB_S_AP.LUT_to_bit_slice(); + PEreps.insert(PB_S_AP.PE_representative()); + } + } + } + else + { + // cout << "get_AE_PEreps(...):: Not support for N != 4" << endl; + } + } + + void get_AE_PEreps(string outfile) const + { + ofstream outf(outfile.c_str(), ios::app); + set PEreps; + get_AE_PEreps(PEreps); + int idx = 0; + for (auto per : PEreps) + { + outf << idx << "," << per.LUT_to_string() << endl; + idx++; + } + outf.close(); + } + + void get_AE_PXEreps(set & PXEreps) const + { + if (N == 4) + { + function_t PB_S_AP; + for (int ai = 0; ai < AFFINE_MATRIX_COL_PE_BOX4_N; ai++) + { + __m128i AP_LUT[LUT_XMM_N]; + AP_LUT[0] = _mm_load_si128((__m128i *)AffineMatrixColPEBox4[ai]); + OPs.composite(AP_LUT, LUT, AP_LUT); + for (int bi = 0; bi < AFFINE_MATRIX_ROW_PE_BOX4_N; bi++) + { + __m128i PB_LUT[LUT_XMM_N]; + PB_LUT[0] = _mm_load_si128((__m128i *)AffineMatrixRowPEBox4[bi]); + OPs.composite(PB_S_AP.LUT, PB_LUT, AP_LUT); + PB_S_AP.LUT_to_bit_slice(); + PXEreps.insert(PB_S_AP.PXE_representative()); + } + } + } + else + { + // cout << "get_AE_PXEreps(...):: Not support for N != 4" << endl; + } + } + + void get_AE_PXEreps(string outfile) const + { + ofstream outf(outfile.c_str(), ios::app); + set PXEreps; + get_AE_PXEreps(PXEreps); + int idx = 0; + for (auto per : PXEreps) + { + outf << idx << "," << per.LUT_to_string() << endl; + idx++; + } + outf.close(); + } + + int which_optimal() const + { + if (N == 4) + { + if (!is_permutation()) + { + return -1; + } + + array R; + uint8_t * Rp = R.data(); + function_t SbF; + map, LExinfoGx_t > S_Rs; + for (int ai = 0; ai < (1< LExinfo; + LExinfo.c = ai; + + OPs.composite(SbF.LUT, XE[ai].LUT, LUT); + SbF.LE_representative(Rp, LExinfo.L1.data(), LExinfo.L2.data()); + S_Rs.insert(pair, LExinfoGx_t >(R, LExinfo)); + } + + for (const auto & each_S_R : S_Rs) + { + auto the_G_R = G_Rs.find(each_S_R.first); + if (the_G_R != G_Rs.end()) + { + return (*the_G_R).second.Gx; + } + } + return -1; + } + else + { + //cout << "which_optimal():: Not support for N != 4" << endl; + return -1; + } + } + + string show_all_properties() const + { + stringstream ss; + ss << show_LUT() << endl; + ss << show_coordinates_ANF() << endl; + ss << show_components_ANF() << endl; + ss << show_difference_distribution_matrix() << endl; + ss << show_boomerang_connection_table() << endl; + ss << show_linear_approximation_matrix() << endl; + ss << show_autocorrelation_matrix() << endl; + ss << show_k_product_degree_table() << endl; + ss << show_V_S_table() << endl; + ss << show_linear_structures() << endl; + ss << show_v_w_linear() << endl; + return ss.str(); + } + + void show_all_properties(ofstream & fout) const + { + fout << show_all_properties(); + } +}; + +using namespace Peigen; +using namespace Peigen::weight; +struct Peigen::weight::bool_op_t +{ + int op_id; + int op_cost; + + bool operator < (const bool_op_t& b) const + { + return op_cost < b.op_cost; + } + + bool operator == (const bool_op_t& b) const + { + return op_cost == b.op_cost; + } +}; + +#ifdef NDEBUG +#undef NDEBUG +#endif +#ifdef NCHECKR +#undef NCHECKR +#endif +#ifdef NCHECKAB +#undef NCHECKAB +#endif + +#endif /* FUNC_H__ */ diff --git a/gatec.conf b/gatec.conf new file mode 100644 index 0000000..03e84df --- /dev/null +++ b/gatec.conf @@ -0,0 +1,15 @@ +not1_cost=1.00 +and2_cost=1.00 +nand2_cost=1.00 +or2_cost=1.00 +nor2_cost=1.00 +xor2_cost=1.00 +xnor2_cost=1.00 +maoi1_cost=1.00 +moai1_cost=1.00 +nand3_cost=1.00 +nor3_cost=1.00 +and3_cost=1.00 +or3_cost=1.00 +andn2_cost=1.00 +orn2_cost=1.00 diff --git a/genTest.cpp b/genTest.cpp new file mode 100644 index 0000000..d176e82 --- /dev/null +++ b/genTest.cpp @@ -0,0 +1,73 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#include +#include + +using namespace std; + +int main() +{ + ifstream files("results.c"); + ofstream codes("testcode.cpp"); + ofstream correct("results_correct.txt"); + for (string line; getline(files, line); ) + { + codes << "I = function_t::INPUT_DEFAULT(); for (int i = 0; i < n; i++) X[i] = I.coordinates[i].bit_slice;" << endl; + codes << "#include " << "\"" << line << "\"" << endl; + codes << "for (int i = 0; i < n; i++) I.coordinates[i].bit_slice = F[i]; fout << I.to_string() << endl;" << endl; + codes << endl; + + ifstream result(line.c_str()); + for (string resline; getline(result, resline); ) + { + string findstr = "// to : "; + if (resline.find(findstr) != std::string::npos) + { + resline.erase(resline.begin(), resline.begin()+findstr.length()); + correct << resline << endl; + break; + } + } + result.close(); + } + correct.close(); + files.close(); + codes.close(); + + return 0; +} \ No newline at end of file diff --git a/lighter.hpp b/lighter.hpp new file mode 100644 index 0000000..138aea5 --- /dev/null +++ b/lighter.hpp @@ -0,0 +1,834 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#ifndef LIGHTER_H__ +#define LIGHTER_H__ + +#include "func.hpp" +#include "evaluator.hpp" + +#define NOT1 1 +#define XOR2 2 +#define XNOR2 3 +#define MAOI1 4 +#define MOAI1 5 +#define MOAI1_AND2 6 +#define MOAI1_OR2 7 +#define MOAI1_NAND2 8 +#define MOAI1_NOR2 9 +#define MAOI1_NAND2 10 +#define MAOI1_NOR2 11 +#define MAOI1_AND2 12 +#define MAOI1_OR2 13 +#define MOAI1_NAND3 14 +#define MAOI1_AND3 15 +#define MOAI1_NAND2_AND2 16 +#define MAOI1_AND2_AND2 17 +#define MOAI1_NOR3 18 +#define MAOI1_OR3 19 +#define MOAI1_NOR2_OR2 20 +#define MAOI1_OR2_OR2 21 +#define MAOI1_NAND3 22 +#define MOAI1_AND3 23 +#define MAOI1_NAND2_AND2 24 +#define MOAI1_AND2_AND2 25 +#define MAOI1_NOR3 26 +#define MOAI1_OR3 27 +#define MAOI1_NOR2_OR2 28 +#define MOAI1_OR2_OR2 29 +#define MOAI1_AND2_NAND2 30 +#define MOAI1_OR2_NOR2 31 +#define MAOI1_NAND2_NAND2 32 +#define MAOI1_NOR2_NOR2 33 +#define MOAI1_NAND2_NAND2 34 +#define MOAI1_NOR2_NOR2 35 +#define MAOI1_AND2_NAND2 36 +#define MAOI1_OR2_NOR2 37 +#define MOAI1_NAND2_OR2 38 +#define MOAI1_NOR2_AND2 39 +#define MAOI1_AND2_OR2 40 +#define MAOI1_OR2_AND2 41 +#define MOAI1_NAND2_NOR2 42 +#define MOAI1_NOR2_NAND2 43 +#define MAOI1_AND2_NOR2 44 +#define MAOI1_OR2_NAND2 45 +#define MOAI1_AND2_OR2 46 +#define MOAI1_OR2_AND2 47 +#define MAOI1_NAND2_OR2 48 +#define MAOI1_NOR2_AND2 49 +#define MOAI1_AND2_NOR2 50 +#define MOAI1_OR2_NAND2 51 +#define MAOI1_NAND2_NOR2 52 +#define MAOI1_NOR2_NAND2 53 + +#define MOAI1_ANDN2L 54 +#define MOAI1_ANDN2R 55 +#define MOAI1_ORN2L 56 +#define MOAI1_ORN2R 57 +#define MOAI1_AND2_ANDN2L 58 +#define MOAI1_AND2_ANDN2R 59 +#define MOAI1_AND2_ORN2L 60 +#define MOAI1_AND2_ORN2R 61 +#define MOAI1_OR2_ANDN2L 62 +#define MOAI1_OR2_ANDN2R 63 +#define MOAI1_OR2_ORN2L 64 +#define MOAI1_OR2_ORN2R 65 +#define MOAI1_NAND2_ANDN2L 66 +#define MOAI1_NAND2_ANDN2R 67 +#define MOAI1_NAND2_ORN2L 68 +#define MOAI1_NAND2_ORN2R 69 +#define MOAI1_NOR2_ANDN2L 70 +#define MOAI1_NOR2_ANDN2R 71 +#define MOAI1_NOR2_ORN2L 72 +#define MOAI1_NOR2_ORN2R 73 +#define MOAI1_ANDN2L_AND2 74 +#define MOAI1_ANDN2L_OR2 75 +#define MOAI1_ANDN2L_NAND2 76 +#define MOAI1_ANDN2L_NOR2 77 +#define MOAI1_ANDN2L_ANDN2L 78 +#define MOAI1_ANDN2L_ANDN2R 79 +#define MOAI1_ANDN2L_ORN2L 80 +#define MOAI1_ANDN2L_ORN2R 81 +#define MOAI1_ANDN2R_AND2 82 +#define MOAI1_ANDN2R_OR2 83 +#define MOAI1_ANDN2R_NAND2 84 +#define MOAI1_ANDN2R_NOR2 85 +#define MOAI1_ANDN2R_ANDN2L 86 +#define MOAI1_ANDN2R_ANDN2R 87 +#define MOAI1_ANDN2R_ORN2L 88 +#define MOAI1_ANDN2R_ORN2R 89 +#define MOAI1_ORN2L_AND2 90 +#define MOAI1_ORN2L_OR2 91 +#define MOAI1_ORN2L_NAND2 92 +#define MOAI1_ORN2L_NOR2 93 +#define MOAI1_ORN2L_ANDN2L 94 +#define MOAI1_ORN2L_ANDN2R 95 +#define MOAI1_ORN2L_ORN2L 96 +#define MOAI1_ORN2L_ORN2R 97 +#define MOAI1_ORN2R_AND2 98 +#define MOAI1_ORN2R_OR2 99 +#define MOAI1_ORN2R_NAND2 100 +#define MOAI1_ORN2R_NOR2 101 +#define MOAI1_ORN2R_ANDN2L 102 +#define MOAI1_ORN2R_ANDN2R 103 +#define MOAI1_ORN2R_ORN2L 104 +#define MOAI1_ORN2R_ORN2R 105 +#define MAOI1_ANDN2L 106 +#define MAOI1_ANDN2R 107 +#define MAOI1_ORN2L 108 +#define MAOI1_ORN2R 109 +#define MAOI1_AND2_ANDN2L 110 +#define MAOI1_AND2_ANDN2R 111 +#define MAOI1_AND2_ORN2L 112 +#define MAOI1_AND2_ORN2R 113 +#define MAOI1_OR2_ANDN2L 114 +#define MAOI1_OR2_ANDN2R 115 +#define MAOI1_OR2_ORN2L 116 +#define MAOI1_OR2_ORN2R 117 +#define MAOI1_NAND2_ANDN2L 118 +#define MAOI1_NAND2_ANDN2R 119 +#define MAOI1_NAND2_ORN2L 120 +#define MAOI1_NAND2_ORN2R 121 +#define MAOI1_NOR2_ANDN2L 122 +#define MAOI1_NOR2_ANDN2R 123 +#define MAOI1_NOR2_ORN2L 124 +#define MAOI1_NOR2_ORN2R 125 +#define MAOI1_ANDN2L_AND2 126 +#define MAOI1_ANDN2L_OR2 127 +#define MAOI1_ANDN2L_NAND2 128 +#define MAOI1_ANDN2L_NOR2 129 +#define MAOI1_ANDN2L_ANDN2L 130 +#define MAOI1_ANDN2L_ANDN2R 131 +#define MAOI1_ANDN2L_ORN2L 132 +#define MAOI1_ANDN2L_ORN2R 133 +#define MAOI1_ANDN2R_AND2 134 +#define MAOI1_ANDN2R_OR2 135 +#define MAOI1_ANDN2R_NAND2 136 +#define MAOI1_ANDN2R_NOR2 137 +#define MAOI1_ANDN2R_ANDN2L 138 +#define MAOI1_ANDN2R_ANDN2R 139 +#define MAOI1_ANDN2R_ORN2L 140 +#define MAOI1_ANDN2R_ORN2R 141 +#define MAOI1_ORN2L_AND2 142 +#define MAOI1_ORN2L_OR2 143 +#define MAOI1_ORN2L_NAND2 144 +#define MAOI1_ORN2L_NOR2 145 +#define MAOI1_ORN2L_ANDN2L 146 +#define MAOI1_ORN2L_ANDN2R 147 +#define MAOI1_ORN2L_ORN2L 148 +#define MAOI1_ORN2L_ORN2R 149 +#define MAOI1_ORN2R_AND2 150 +#define MAOI1_ORN2R_OR2 151 +#define MAOI1_ORN2R_NAND2 152 +#define MAOI1_ORN2R_NOR2 153 +#define MAOI1_ORN2R_ANDN2L 154 +#define MAOI1_ORN2R_ANDN2R 155 +#define MAOI1_ORN2R_ORN2L 156 +#define MAOI1_ORN2R_ORN2R 157 + +#define XOR2_AND2 158 +#define XOR2_OR2 159 +#define XOR2_ANDN2L 160 +#define XOR2_ANDN2R 161 +#define XOR2_ORN2L 162 +#define XOR2_ORN2R 163 +#define XOR2_AND2_AND2 164 +#define XOR2_AND2_OR2 165 +#define XOR2_AND2_ANDN2L 166 +#define XOR2_AND2_ANDN2R 167 +#define XOR2_AND2_ORN2L 168 +#define XOR2_AND2_ORN2R 169 +#define XOR2_OR2_AND2 170 +#define XOR2_OR2_OR2 171 +#define XOR2_OR2_ANDN2L 172 +#define XOR2_OR2_ANDN2R 173 +#define XOR2_OR2_ORN2L 174 +#define XOR2_OR2_ORN2R 175 +#define XOR2_ANDN2L_AND2 176 +#define XOR2_ANDN2L_OR2 177 +#define XOR2_ANDN2L_ANDN2L 178 +#define XOR2_ANDN2L_ANDN2R 179 +#define XOR2_ANDN2L_ORN2L 180 +#define XOR2_ANDN2L_ORN2R 181 +#define XOR2_ANDN2R_AND2 182 +#define XOR2_ANDN2R_OR2 183 +#define XOR2_ANDN2R_ANDN2L 184 +#define XOR2_ANDN2R_ANDN2R 185 +#define XOR2_ANDN2R_ORN2L 186 +#define XOR2_ANDN2R_ORN2R 187 +#define XOR2_ORN2L_AND2 188 +#define XOR2_ORN2L_OR2 189 +#define XOR2_ORN2L_ANDN2L 190 +#define XOR2_ORN2L_ANDN2R 191 +#define XOR2_ORN2L_ORN2L 192 +#define XOR2_ORN2L_ORN2R 193 +#define XOR2_ORN2R_AND2 194 +#define XOR2_ORN2R_OR2 195 +#define XOR2_ORN2R_ANDN2L 196 +#define XOR2_ORN2R_ANDN2R 197 +#define XOR2_ORN2R_ORN2L 198 +#define XOR2_ORN2R_ORN2R 199 + +using namespace Peigen; +using namespace Peigen::weight; +using namespace std; + +template +class Peigen::weight::lighter +{ +public: + int pre_l = -1; + int cntpoint = -1; + + string imp_info = ""; + ofstream statistician; + string old_instance_name = ""; + string instance_name = ""; + + string sboxesfile = "new_sboxes" + to_string(N) + ".txt"; + + int max_GE = 0; + int min_GE = numeric_limits::max(); + vector b; // The boolean instructions (B in the paper) + set > > check_tt; + function_t start; + function_t arrival; + function_t unsorted_arrival; + function_t unsorted_function; + int PEo[N]; + map> > f1_succ; + map> > f2_succ; + + int count_list; + int shortest_path = numeric_limits::max(); + + bool verbose = false; + int l = 900; + bool one_expand = false; + bool write_in_file = false; + int omp_nb_threads = 20; + + string conf_file = "TSMC65nm.conf"; + string criteria_file = "criteria.conf"; + long max_ram = 160; + long nodes_cmp = 0; + long max_nodes; + int cost_1; + int cost_2; + + int good_number = 0; + int good_PE_number = 0; + + int gate_not1 = false; + int gate_and2 = false; + int gate_nand2 = false; + int gate_or2 = false; + int gate_nor2 = false; + int gate_xor2 = false; + int gate_xnor2 = false; + int gate_moai1 = false; + int gate_maoi1 = false; + int gate_nand3 = false; + int gate_nor3 = false; + int gate_or3 = false; + int gate_and3 = false; + int gate_andn2 = false; + int gate_orn2 = false; + + struct option longopts[16] = { + { "not1", no_argument, &gate_not1, 1}, + { "and2", no_argument, &gate_and2, 1}, + { "nand2", no_argument, &gate_nand2, 1}, + { "or2", no_argument, &gate_or2, 1}, + { "nor2", no_argument, &gate_nor2, 1}, + { "xor2", no_argument, &gate_xor2, 1}, + { "xnor2", no_argument, &gate_xnor2, 1}, + { "moai1", no_argument, &gate_moai1, 1}, + { "maoi1", no_argument, &gate_maoi1, 1}, + { "nand3", no_argument, &gate_nand3, 1}, + { "nor3", no_argument, &gate_nor3, 1}, + { "and3", no_argument, &gate_and3, 1}, + { "or3", no_argument, &gate_or3, 1}, + { "andn2", no_argument, &gate_andn2, 1}, + { "orn2", no_argument, &gate_orn2, 1}, + {0, 0, 0, 0} + }; + + int not1_cost; + int and2_cost; + int nand2_cost; + int or2_cost; + int nor2_cost; + int xor2_cost; + int xnor2_cost; + int maoi1_cost; + int moai1_cost; + int nand3_cost; + int nor3_cost; + int and3_cost; + int or3_cost; + int andn2_cost; + int orn2_cost; + + int Cost_Criteria = -1; + int Involution_Criteria = -1; + int Diff_Criteria = -1; + int DiffFreq_Criteria = -1; + int Diff1_Criteria = -1; + int CardD1_Criteria = -1; + int Lin_Criteria = -1; + int LinFreq_Criteria = -1; + int Lin1_Criteria = -1; + int CardL1_Criteria = -1; + int MaxDeg_Criteria = -1; + int MinDeg_Criteria = -1; + int MaxDegFreq_Criteria = -1; + int MinDegFreq_Criteria = -1; + + // void parse_args(string args); + + void reset() + { + pre_l = -1; + cntpoint = -1; + imp_info = ""; + instance_name = ""; + old_instance_name = ""; + sboxesfile = "new_sboxes" + to_string(N) + ".txt"; + max_GE = 0; + min_GE = numeric_limits::max(); + b.clear(); + check_tt.clear(); + f1_succ.clear(); + f2_succ.clear(); + shortest_path = numeric_limits::max(); + verbose = false; + l = 900; + one_expand = false; + write_in_file = false; + omp_nb_threads = 20; + conf_file = "TSMC65nm.conf"; + criteria_file = "criteria.conf"; + max_ram = 160; + nodes_cmp = 0; + good_number = 0; + good_PE_number = 0; + + gate_not1 = false; + gate_and2 = false; + gate_nand2 = false; + gate_or2 = false; + gate_nor2 = false; + gate_xor2 = false; + gate_xnor2 = false; + gate_moai1 = false; + gate_maoi1 = false; + gate_nand3 = false; + gate_nor3 = false; + gate_or3 = false; + gate_and3 = false; + gate_andn2 = false; + gate_orn2 = false; + + Cost_Criteria = -1; + Involution_Criteria = -1; + Diff_Criteria = -1; + DiffFreq_Criteria = -1; + Diff1_Criteria = -1; + CardD1_Criteria = -1; + Lin_Criteria = -1; + LinFreq_Criteria = -1; + Lin1_Criteria = -1; + CardL1_Criteria = -1; + MaxDeg_Criteria = -1; + MinDeg_Criteria = -1; + MaxDegFreq_Criteria = -1; + MinDegFreq_Criteria = -1; + } + + void init_varbles() + { + if (pre_l < 0) + { + f1_succ.clear(); + } + f2_succ.clear(); + + shortest_path = numeric_limits::max(); + } + + void all_gates(); + + void genImpInfo(); + + int bool_op_cost(uint8_t op); + + void init_b(string conf_file); + + void bool_op_simple_gates(function_t f, vector > *v, int operation); + + void bool_op_double_gates(function_t f, vector > *v, int operation); + + void bool_op_triple_gates(function_t f, vector > *v, int operation); + + void bool_op_fun(int op_id, function_t f, vector > *v); + + void write_c(string *s, vector tmp_tab); + + string bool_op_not(const function_t f, string s, bool reverse); + + string bool_op_two_inputs(const function_t f, int operation, string s, bool reverse); + + string bool_op_super_gates(const function_t f, int operation, string s, bool reverse); + + string bool_op_super_super_gates(const function_t f, int operation, string s, bool reverse); + + string bool_op_base_string(uint8_t op); + + string bool_op_to_string(const function_t f, uint8_t op, bool reverse); + + void update_unsorted_function(function_t f); + + string get_permut(function_t f1, function_t f2, char a, char b); + + void get_string_impl(string *s, function_t f, bool reverse); + + int get_semi_impl(function_t f, + int count_vlist, + map > > *g, + string *s, + bool reverse); + + void get_implementation(function_t f1, + function_t f2, + int v, + map > > *g1, + map > > *g2); + + //void get_implementation_concatenate(function_t f1, int v1, function_t f2, int v2, function_t f3); + + void get_implementation_concatenate(function_t f1, int v1, function_t f2, int v2, int perm[]); + + int get_semi_impl_concatenate(function_t f, + int count_vlist, string *s, + bool reverse, int perm[]); + + void print_graphe_info(); + + void exit_m(); + + void graphe_to_file(map > > *graphe, + string graphe_name); + + void print_uint16(uint16_t n); + + void print_uint8(uint8_t n); + + string uint8_to_string(uint8_t n); + + void print_uint32(uint32_t n); + + void mitm(function_t f1, function_t f2); + + void expand(map > > *current, + map > > *opposite, + int lambda); + + void v_list_process(int lambda, int op_cost, vector > *tmp, + map > > *current, + set > *to_insert, map > > *opposite); + + bool is_in_graphe(int lambda, int op_cost, function_t f, map > > *g); + + bool is_in_graphe_collision(int lambda, int op_cost, function_t f, + function_t *fun_found, + int *vect_found, + map > > *g); + + void pre_compute(string args); + void pre_computing(); + void write_pre_bin(int lambda); + void read_pre_bin(int completed); + void search_batch(string args); + double search_single(string args); + double search_single_concatenate(string args); + void search_batch_concatenate(string args); + + void concatenate(function_t f1, function_t f2); + void expand(int lambda); + void v_list_process(int lambda, int op_cost, vector > *tmp, + set > *to_insert); + bool is_in_graphe(int lambda, int op_cost, function_t f); + void match(int cost1); + //void check_dup(); + + void init_critiera(string conf_file); + void generate(string args); + void generate(); + void filter(int c1); + void filter_pre(int c1); + + int get_semi_impl_good(function_t f, int count_vlist, string *s); + + int get_semi_impl_good(function_t f, + int count_vlist, string *s, + const function_t & f_p, int pi); + + void get_implementation_good(function_t f1, int v1, + const function_t & fp, int pi, + function_t f2, int v2, + function_t f3, int good_idx); + + void get_implementation_good(function_t f1, int v, int good_idx); + + void evaluate_writeTitleLine(ofstream & outf); + + void evaluate(string sboxesfile, string outputfile); + + void evaluate_verbose(string sboxesfile, string outputfile_prefix); + + void evaluate_filter(string args); + + // require LUT and bit_slice forms are all computed + bool is_good(function_t & func) const + { + int max_degree; + int min_degree; + int deg_spectrum[N+1]; + int max_degree_freq; + int min_degree_freq; + + if (Involution_Criteria != -1) + { + if (func.is_involution() != Involution_Criteria) return false; + } + + if ((Diff_Criteria != -1) || (Diff1_Criteria != -1) || (DiffFreq_Criteria != -1) || (CardD1_Criteria != -1)) + { + if (!func.difference_distribution_matrix_test(Diff_Criteria, Diff1_Criteria, DiffFreq_Criteria, CardD1_Criteria)) return false; + } + + if ((Lin_Criteria != -1) || (Lin1_Criteria != -1) || (LinFreq_Criteria != -1) || (CardL1_Criteria != -1)) + { + if (!func.linear_approximation_matrix_test(Lin_Criteria, Lin1_Criteria, LinFreq_Criteria, CardL1_Criteria)) return false; + } + + if ((MaxDegFreq_Criteria != -1) || (MinDegFreq_Criteria != -1) || (MaxDeg_Criteria != -1) || (MinDeg_Criteria != -1)) + { + func.degree(deg_spectrum, max_degree, min_degree); + max_degree_freq = deg_spectrum[max_degree]; + min_degree_freq = deg_spectrum[min_degree]; + if (MinDeg_Criteria != -1) + { + if (min_degree < MinDeg_Criteria) return false; + } + if (MinDegFreq_Criteria != -1) + { + if (min_degree_freq > MinDegFreq_Criteria) return false; + } + if (MaxDeg_Criteria != -1) + { + if (max_degree < MaxDeg_Criteria) return false; + } + if (MaxDegFreq_Criteria != -1) + { + if (max_degree_freq < MaxDegFreq_Criteria) return false; + } + } + return true; + } + + void evaluate(string name, const function_t & f, int Cost, string CostisBest, ofstream & outf) + { + evaluator Eva(f, name, Cost, CostisBest); + outf << Eva.show(); + } + + lighter(){}; + + ~lighter(){}; +}; + +#include "lighter_bool_op.hpp" +#include "lighter_string_bool_op.hpp" +#include "lighter_mitm.hpp" +#include "lighter_utils.hpp" +#include "lighter_impl_info.hpp" + +#undef NOT1 +#undef XOR2 +#undef XNOR2 +#undef MAOI1 +#undef MOAI1 +#undef MOAI1_AND2 +#undef MOAI1_OR2 +#undef MOAI1_NAND2 +#undef MOAI1_NOR2 +#undef MAOI1_NAND2 +#undef MAOI1_NOR2 +#undef MAOI1_AND2 +#undef MAOI1_OR2 +#undef MOAI1_NAND3 +#undef MAOI1_AND3 +#undef MOAI1_NAND2_AND2 +#undef MAOI1_AND2_AND2 +#undef MOAI1_NOR3 +#undef MAOI1_OR3 +#undef MOAI1_NOR2_OR2 +#undef MAOI1_OR2_OR2 +#undef MAOI1_NAND3 +#undef MOAI1_AND3 +#undef MAOI1_NAND2_AND2 +#undef MOAI1_AND2_AND2 +#undef MAOI1_NOR3 +#undef MOAI1_OR3 +#undef MAOI1_NOR2_OR2 +#undef MOAI1_OR2_OR2 +#undef MOAI1_AND2_NAND2 +#undef MOAI1_OR2_NOR2 +#undef MAOI1_NAND2_NAND2 +#undef MAOI1_NOR2_NOR2 +#undef MOAI1_NAND2_NAND2 +#undef MOAI1_NOR2_NOR2 +#undef MAOI1_AND2_NAND2 +#undef MAOI1_OR2_NOR2 +#undef MOAI1_NAND2_OR2 +#undef MOAI1_NOR2_AND2 +#undef MAOI1_AND2_OR2 +#undef MAOI1_OR2_AND2 +#undef MOAI1_NAND2_NOR2 +#undef MOAI1_NOR2_NAND2 +#undef MAOI1_AND2_NOR2 +#undef MAOI1_OR2_NAND2 +#undef MOAI1_AND2_OR2 +#undef MOAI1_OR2_AND2 +#undef MAOI1_NAND2_OR2 +#undef MAOI1_NOR2_AND2 +#undef MOAI1_AND2_NOR2 +#undef MOAI1_OR2_NAND2 +#undef MAOI1_NAND2_NOR2 +#undef MAOI1_NOR2_NAND2 + +#undef MOAI1_ANDN2L +#undef MOAI1_ANDN2R +#undef MOAI1_ORN2L +#undef MOAI1_ORN2R +#undef MOAI1_AND2_ANDN2L +#undef MOAI1_AND2_ANDN2R +#undef MOAI1_AND2_ORN2L +#undef MOAI1_AND2_ORN2R +#undef MOAI1_OR2_ANDN2L +#undef MOAI1_OR2_ANDN2R +#undef MOAI1_OR2_ORN2L +#undef MOAI1_OR2_ORN2R +#undef MOAI1_NAND2_ANDN2L +#undef MOAI1_NAND2_ANDN2R +#undef MOAI1_NAND2_ORN2L +#undef MOAI1_NAND2_ORN2R +#undef MOAI1_NOR2_ANDN2L +#undef MOAI1_NOR2_ANDN2R +#undef MOAI1_NOR2_ORN2L +#undef MOAI1_NOR2_ORN2R +#undef MOAI1_ANDN2L_AND2 +#undef MOAI1_ANDN2L_OR2 +#undef MOAI1_ANDN2L_NAND2 +#undef MOAI1_ANDN2L_NOR2 +#undef MOAI1_ANDN2L_ANDN2L +#undef MOAI1_ANDN2L_ANDN2R +#undef MOAI1_ANDN2L_ORN2L +#undef MOAI1_ANDN2L_ORN2R +#undef MOAI1_ANDN2R_AND2 +#undef MOAI1_ANDN2R_OR2 +#undef MOAI1_ANDN2R_NAND2 +#undef MOAI1_ANDN2R_NOR2 +#undef MOAI1_ANDN2R_ANDN2L +#undef MOAI1_ANDN2R_ANDN2R +#undef MOAI1_ANDN2R_ORN2L +#undef MOAI1_ANDN2R_ORN2R +#undef MOAI1_ORN2L_AND2 +#undef MOAI1_ORN2L_OR2 +#undef MOAI1_ORN2L_NAND2 +#undef MOAI1_ORN2L_NOR2 +#undef MOAI1_ORN2L_ANDN2L +#undef MOAI1_ORN2L_ANDN2R +#undef MOAI1_ORN2L_ORN2L +#undef MOAI1_ORN2L_ORN2R +#undef MOAI1_ORN2R_AND2 +#undef MOAI1_ORN2R_OR2 +#undef MOAI1_ORN2R_NAND2 +#undef MOAI1_ORN2R_NOR2 +#undef MOAI1_ORN2R_ANDN2L +#undef MOAI1_ORN2R_ANDN2R +#undef MOAI1_ORN2R_ORN2L +#undef MOAI1_ORN2R_ORN2R +#undef MAOI1_ANDN2L +#undef MAOI1_ANDN2R +#undef MAOI1_ORN2L +#undef MAOI1_ORN2R +#undef MAOI1_AND2_ANDN2L +#undef MAOI1_AND2_ANDN2R +#undef MAOI1_AND2_ORN2L +#undef MAOI1_AND2_ORN2R +#undef MAOI1_OR2_ANDN2L +#undef MAOI1_OR2_ANDN2R +#undef MAOI1_OR2_ORN2L +#undef MAOI1_OR2_ORN2R +#undef MAOI1_NAND2_ANDN2L +#undef MAOI1_NAND2_ANDN2R +#undef MAOI1_NAND2_ORN2L +#undef MAOI1_NAND2_ORN2R +#undef MAOI1_NOR2_ANDN2L +#undef MAOI1_NOR2_ANDN2R +#undef MAOI1_NOR2_ORN2L +#undef MAOI1_NOR2_ORN2R +#undef MAOI1_ANDN2L_AND2 +#undef MAOI1_ANDN2L_OR2 +#undef MAOI1_ANDN2L_NAND2 +#undef MAOI1_ANDN2L_NOR2 +#undef MAOI1_ANDN2L_ANDN2L +#undef MAOI1_ANDN2L_ANDN2R +#undef MAOI1_ANDN2L_ORN2L +#undef MAOI1_ANDN2L_ORN2R +#undef MAOI1_ANDN2R_AND2 +#undef MAOI1_ANDN2R_OR2 +#undef MAOI1_ANDN2R_NAND2 +#undef MAOI1_ANDN2R_NOR2 +#undef MAOI1_ANDN2R_ANDN2L +#undef MAOI1_ANDN2R_ANDN2R +#undef MAOI1_ANDN2R_ORN2L +#undef MAOI1_ANDN2R_ORN2R +#undef MAOI1_ORN2L_AND2 +#undef MAOI1_ORN2L_OR2 +#undef MAOI1_ORN2L_NAND2 +#undef MAOI1_ORN2L_NOR2 +#undef MAOI1_ORN2L_ANDN2L +#undef MAOI1_ORN2L_ANDN2R +#undef MAOI1_ORN2L_ORN2L +#undef MAOI1_ORN2L_ORN2R +#undef MAOI1_ORN2R_AND2 +#undef MAOI1_ORN2R_OR2 +#undef MAOI1_ORN2R_NAND2 +#undef MAOI1_ORN2R_NOR2 +#undef MAOI1_ORN2R_ANDN2L +#undef MAOI1_ORN2R_ANDN2R +#undef MAOI1_ORN2R_ORN2L +#undef MAOI1_ORN2R_ORN2R + +#undef XOR2_AND2 +#undef XOR2_OR2 +#undef XOR2_ANDN2L +#undef XOR2_ANDN2R +#undef XOR2_ORN2L +#undef XOR2_ORN2R +#undef XOR2_AND2_AND2 +#undef XOR2_AND2_OR2 +#undef XOR2_AND2_ANDN2L +#undef XOR2_AND2_ANDN2R +#undef XOR2_AND2_ORN2L +#undef XOR2_AND2_ORN2R +#undef XOR2_OR2_AND2 +#undef XOR2_OR2_OR2 +#undef XOR2_OR2_ANDN2L +#undef XOR2_OR2_ANDN2R +#undef XOR2_OR2_ORN2L +#undef XOR2_OR2_ORN2R +#undef XOR2_ANDN2L_AND2 +#undef XOR2_ANDN2L_OR2 +#undef XOR2_ANDN2L_ANDN2L +#undef XOR2_ANDN2L_ANDN2R +#undef XOR2_ANDN2L_ORN2L +#undef XOR2_ANDN2L_ORN2R +#undef XOR2_ANDN2R_AND2 +#undef XOR2_ANDN2R_OR2 +#undef XOR2_ANDN2R_ANDN2L +#undef XOR2_ANDN2R_ANDN2R +#undef XOR2_ANDN2R_ORN2L +#undef XOR2_ANDN2R_ORN2R +#undef XOR2_ORN2L_AND2 +#undef XOR2_ORN2L_OR2 +#undef XOR2_ORN2L_ANDN2L +#undef XOR2_ORN2L_ANDN2R +#undef XOR2_ORN2L_ORN2L +#undef XOR2_ORN2L_ORN2R +#undef XOR2_ORN2R_AND2 +#undef XOR2_ORN2R_OR2 +#undef XOR2_ORN2R_ANDN2L +#undef XOR2_ORN2R_ANDN2R +#undef XOR2_ORN2R_ORN2L +#undef XOR2_ORN2R_ORN2R + +#endif /* LIGHTER_H__ */ diff --git a/lighter_bool_op.hpp b/lighter_bool_op.hpp new file mode 100644 index 0000000..1d8ce93 --- /dev/null +++ b/lighter_bool_op.hpp @@ -0,0 +1,1704 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#ifndef LIGHTER_BOOL_OP_H__ +#define LIGHTER_BOOL_OP_H__ + +using namespace Peigen; +using namespace Peigen::weight; +using namespace std; + +template +void lighter::all_gates() +{ + gate_not1 = true; + gate_and2 = true; + gate_nand2 = true; + gate_or2 = true; + gate_nor2 = true; + gate_xor2 = true; + gate_xnor2 = true; + gate_moai1 = true; + gate_maoi1 = true; + gate_nand3 = true; + gate_nor3 = true; + gate_or3 = true; + gate_and3 = true; + gate_andn2 = true; + gate_orn2 = true; +} + +template +void lighter::genImpInfo() +{ + imp_info = ""; + if (gate_not1 ) imp_info += "-not1"; + if (gate_and2 ) imp_info += "-and2"; + if (gate_nand2 ) imp_info += "-nand2"; + if (gate_or2 ) imp_info += "-or2"; + if (gate_nor2 ) imp_info += "-nor2"; + if (gate_xor2 ) imp_info += "-xor2"; + if (gate_xnor2 ) imp_info += "-xnor2"; + if (gate_moai1 ) imp_info += "-moai1"; + if (gate_maoi1 ) imp_info += "-maoi1"; + if (gate_nand3 ) imp_info += "-nand3"; + if (gate_nor3 ) imp_info += "-nor3"; + if (gate_or3 ) imp_info += "-or3"; + if (gate_and3 ) imp_info += "-and3"; + if (gate_andn2 ) imp_info += "-andn2"; + if (gate_orn2 ) imp_info += "-orn2"; + + imp_info += "-f"; + imp_info += conf_file.substr(0, conf_file.rfind('.')); + imp_info += "-N"; + imp_info += to_string(N); +} + +template +int lighter::bool_op_cost(uint8_t op) +{ + switch (op) + { + case NOT1 : return not1_cost; + case XOR2 : return xor2_cost; + case XNOR2 : return xnor2_cost; + case MAOI1 : return maoi1_cost; + case MOAI1 : return moai1_cost; + case MOAI1_NAND2 : return moai1_cost + nand2_cost; + case MOAI1_NOR2 : return moai1_cost + nor2_cost; + case MOAI1_AND2 : return moai1_cost + and2_cost; + case MOAI1_OR2 : return moai1_cost + or2_cost; + case MOAI1_NAND3 : return moai1_cost + nand3_cost; + case MOAI1_NOR3 : return moai1_cost + nor3_cost; + case MAOI1_NAND3 : return maoi1_cost + nand3_cost; + case MAOI1_NOR3 : return maoi1_cost + nor3_cost; + case MOAI1_AND2_NAND2 : return moai1_cost + and2_cost + nand2_cost; + case MOAI1_OR2_NOR2 : return moai1_cost + or2_cost + nor2_cost; + case MOAI1_NAND2_NAND2 : return moai1_cost + 2*nand2_cost; + case MOAI1_NOR2_NOR2 : return moai1_cost + 2*nor2_cost; + case MOAI1_NAND2_OR2 : return moai1_cost + nand2_cost + or2_cost; + case MOAI1_NOR2_AND2 : return moai1_cost + nor2_cost + and2_cost; + case MOAI1_NAND2_NOR2 : return moai1_cost + nand2_cost + nor2_cost; + case MOAI1_NOR2_NAND2 : return moai1_cost + nor2_cost + nand2_cost; + case MOAI1_AND2_OR2 : return moai1_cost + and2_cost + or2_cost; + case MOAI1_OR2_AND2 : return moai1_cost + or2_cost + and2_cost; + case MOAI1_AND2_NOR2 : return moai1_cost + and2_cost + nor2_cost; + case MOAI1_OR2_NAND2 : return moai1_cost + or2_cost + nand2_cost; + case MAOI1_NAND2_NAND2 : return maoi1_cost + nand2_cost + nand2_cost; + case MAOI1_NOR2_NOR2 : return maoi1_cost + nor2_cost + nor2_cost; + case MAOI1_AND2_NAND2 : return maoi1_cost + and2_cost + nand2_cost; + case MAOI1_OR2_NOR2 : return maoi1_cost + or2_cost + nor2_cost; + case MAOI1_AND2_OR2 : return maoi1_cost + and2_cost + or2_cost; + case MAOI1_OR2_AND2 : return maoi1_cost + or2_cost + and2_cost; + case MAOI1_AND2_NOR2 : return maoi1_cost + and2_cost + nor2_cost; + case MAOI1_OR2_NAND2 : return maoi1_cost + or2_cost + nand2_cost; + case MAOI1_NAND2_OR2 : return maoi1_cost + nand2_cost + or2_cost; + case MAOI1_NOR2_AND2 : return maoi1_cost + nor2_cost + and2_cost; + case MAOI1_NAND2_NOR2 : return maoi1_cost + nand2_cost + nor2_cost; + case MAOI1_NOR2_NAND2 : return maoi1_cost + nor2_cost + nand2_cost; + case MAOI1_NAND2 : return maoi1_cost + nand2_cost; + case MAOI1_NOR2 : return maoi1_cost + nor2_cost; + case MAOI1_AND2 : return maoi1_cost + and2_cost; + case MAOI1_OR2 : return maoi1_cost + or2_cost; + case MAOI1_AND3 : return maoi1_cost + and3_cost; + case MOAI1_NAND2_AND2 : return moai1_cost + nand2_cost + and2_cost; + case MAOI1_AND2_AND2 : return maoi1_cost + and2_cost + and2_cost; + case MAOI1_OR3 : return maoi1_cost + or3_cost; + case MOAI1_NOR2_OR2 : return moai1_cost + nor2_cost + or2_cost; + case MAOI1_OR2_OR2 : return maoi1_cost + or2_cost + or2_cost; + case MOAI1_AND3 : return moai1_cost + and3_cost; + case MAOI1_NAND2_AND2 : return maoi1_cost + nand2_cost + and2_cost; + case MOAI1_AND2_AND2 : return moai1_cost + and2_cost + and2_cost; + case MOAI1_OR3 : return moai1_cost + or3_cost; + case MAOI1_NOR2_OR2 : return maoi1_cost + nor2_cost + or2_cost; + case MOAI1_OR2_OR2 : return moai1_cost + or2_cost + or2_cost; + + case MOAI1_ANDN2L : return moai1_cost + andn2_cost; + case MOAI1_ANDN2R : return moai1_cost + andn2_cost; + case MOAI1_ORN2L : return moai1_cost + orn2_cost; + case MOAI1_ORN2R : return moai1_cost + orn2_cost; + case MOAI1_AND2_ANDN2L : return moai1_cost + and2_cost + andn2_cost; + case MOAI1_AND2_ANDN2R : return moai1_cost + and2_cost + andn2_cost; + case MOAI1_AND2_ORN2L : return moai1_cost + and2_cost + orn2_cost; + case MOAI1_AND2_ORN2R : return moai1_cost + and2_cost + orn2_cost; + case MOAI1_OR2_ANDN2L : return moai1_cost + or2_cost + andn2_cost; + case MOAI1_OR2_ANDN2R : return moai1_cost + or2_cost + andn2_cost; + case MOAI1_OR2_ORN2L : return moai1_cost + or2_cost + orn2_cost; + case MOAI1_OR2_ORN2R : return moai1_cost + or2_cost + orn2_cost; + case MOAI1_NAND2_ANDN2L : return moai1_cost + nand2_cost + andn2_cost; + case MOAI1_NAND2_ANDN2R : return moai1_cost + nand2_cost + andn2_cost; + case MOAI1_NAND2_ORN2L : return moai1_cost + nand2_cost + orn2_cost; + case MOAI1_NAND2_ORN2R : return moai1_cost + nand2_cost + orn2_cost; + case MOAI1_NOR2_ANDN2L : return moai1_cost + nor2_cost + andn2_cost; + case MOAI1_NOR2_ANDN2R : return moai1_cost + nor2_cost + andn2_cost; + case MOAI1_NOR2_ORN2L : return moai1_cost + nor2_cost + orn2_cost; + case MOAI1_NOR2_ORN2R : return moai1_cost + nor2_cost + orn2_cost; + case MOAI1_ANDN2L_AND2 : return moai1_cost + andn2_cost + and2_cost; + case MOAI1_ANDN2L_OR2 : return moai1_cost + andn2_cost + or2_cost; + case MOAI1_ANDN2L_NAND2 : return moai1_cost + andn2_cost + nand2_cost; + case MOAI1_ANDN2L_NOR2 : return moai1_cost + andn2_cost + nor2_cost; + case MOAI1_ANDN2L_ANDN2L : return moai1_cost + andn2_cost + andn2_cost; + case MOAI1_ANDN2L_ANDN2R : return moai1_cost + andn2_cost + andn2_cost; + case MOAI1_ANDN2L_ORN2L : return moai1_cost + andn2_cost + orn2_cost; + case MOAI1_ANDN2L_ORN2R : return moai1_cost + andn2_cost + orn2_cost; + case MOAI1_ANDN2R_AND2 : return moai1_cost + andn2_cost + and2_cost; + case MOAI1_ANDN2R_OR2 : return moai1_cost + andn2_cost + or2_cost; + case MOAI1_ANDN2R_NAND2 : return moai1_cost + andn2_cost + nand2_cost; + case MOAI1_ANDN2R_NOR2 : return moai1_cost + andn2_cost + nor2_cost; + case MOAI1_ANDN2R_ANDN2L : return moai1_cost + andn2_cost + andn2_cost; + case MOAI1_ANDN2R_ANDN2R : return moai1_cost + andn2_cost + andn2_cost; + case MOAI1_ANDN2R_ORN2L : return moai1_cost + andn2_cost + orn2_cost; + case MOAI1_ANDN2R_ORN2R : return moai1_cost + andn2_cost + orn2_cost; + case MOAI1_ORN2L_AND2 : return moai1_cost + orn2_cost + and2_cost; + case MOAI1_ORN2L_OR2 : return moai1_cost + orn2_cost + or2_cost; + case MOAI1_ORN2L_NAND2 : return moai1_cost + orn2_cost + nand2_cost; + case MOAI1_ORN2L_NOR2 : return moai1_cost + orn2_cost + nor2_cost; + case MOAI1_ORN2L_ANDN2L : return moai1_cost + orn2_cost + andn2_cost; + case MOAI1_ORN2L_ANDN2R : return moai1_cost + orn2_cost + andn2_cost; + case MOAI1_ORN2L_ORN2L : return moai1_cost + orn2_cost + orn2_cost; + case MOAI1_ORN2L_ORN2R : return moai1_cost + orn2_cost + orn2_cost; + case MOAI1_ORN2R_AND2 : return moai1_cost + orn2_cost + and2_cost; + case MOAI1_ORN2R_OR2 : return moai1_cost + orn2_cost + or2_cost; + case MOAI1_ORN2R_NAND2 : return moai1_cost + orn2_cost + nand2_cost; + case MOAI1_ORN2R_NOR2 : return moai1_cost + orn2_cost + nor2_cost; + case MOAI1_ORN2R_ANDN2L : return moai1_cost + orn2_cost + andn2_cost; + case MOAI1_ORN2R_ANDN2R : return moai1_cost + orn2_cost + andn2_cost; + case MOAI1_ORN2R_ORN2L : return moai1_cost + orn2_cost + orn2_cost; + case MOAI1_ORN2R_ORN2R : return moai1_cost + orn2_cost + orn2_cost; + case MAOI1_ANDN2L : return maoi1_cost + andn2_cost; + case MAOI1_ANDN2R : return maoi1_cost + andn2_cost; + case MAOI1_ORN2L : return maoi1_cost + orn2_cost; + case MAOI1_ORN2R : return maoi1_cost + orn2_cost; + case MAOI1_AND2_ANDN2L : return maoi1_cost + and2_cost + andn2_cost; + case MAOI1_AND2_ANDN2R : return maoi1_cost + and2_cost + andn2_cost; + case MAOI1_AND2_ORN2L : return maoi1_cost + and2_cost + orn2_cost; + case MAOI1_AND2_ORN2R : return maoi1_cost + and2_cost + orn2_cost; + case MAOI1_OR2_ANDN2L : return maoi1_cost + or2_cost + andn2_cost; + case MAOI1_OR2_ANDN2R : return maoi1_cost + or2_cost + andn2_cost; + case MAOI1_OR2_ORN2L : return maoi1_cost + or2_cost + orn2_cost; + case MAOI1_OR2_ORN2R : return maoi1_cost + or2_cost + orn2_cost; + case MAOI1_NAND2_ANDN2L : return maoi1_cost + nand2_cost + andn2_cost; + case MAOI1_NAND2_ANDN2R : return maoi1_cost + nand2_cost + andn2_cost; + case MAOI1_NAND2_ORN2L : return maoi1_cost + nand2_cost + orn2_cost; + case MAOI1_NAND2_ORN2R : return maoi1_cost + nand2_cost + orn2_cost; + case MAOI1_NOR2_ANDN2L : return maoi1_cost + nor2_cost + andn2_cost; + case MAOI1_NOR2_ANDN2R : return maoi1_cost + nor2_cost + andn2_cost; + case MAOI1_NOR2_ORN2L : return maoi1_cost + nor2_cost + orn2_cost; + case MAOI1_NOR2_ORN2R : return maoi1_cost + nor2_cost + orn2_cost; + case MAOI1_ANDN2L_AND2 : return maoi1_cost + andn2_cost + and2_cost; + case MAOI1_ANDN2L_OR2 : return maoi1_cost + andn2_cost + or2_cost; + case MAOI1_ANDN2L_NAND2 : return maoi1_cost + andn2_cost + nand2_cost; + case MAOI1_ANDN2L_NOR2 : return maoi1_cost + andn2_cost + nor2_cost; + case MAOI1_ANDN2L_ANDN2L : return maoi1_cost + andn2_cost + andn2_cost; + case MAOI1_ANDN2L_ANDN2R : return maoi1_cost + andn2_cost + andn2_cost; + case MAOI1_ANDN2L_ORN2L : return maoi1_cost + andn2_cost + orn2_cost; + case MAOI1_ANDN2L_ORN2R : return maoi1_cost + andn2_cost + orn2_cost; + case MAOI1_ANDN2R_AND2 : return maoi1_cost + andn2_cost + and2_cost; + case MAOI1_ANDN2R_OR2 : return maoi1_cost + andn2_cost + or2_cost; + case MAOI1_ANDN2R_NAND2 : return maoi1_cost + andn2_cost + nand2_cost; + case MAOI1_ANDN2R_NOR2 : return maoi1_cost + andn2_cost + nor2_cost; + case MAOI1_ANDN2R_ANDN2L : return maoi1_cost + andn2_cost + andn2_cost; + case MAOI1_ANDN2R_ANDN2R : return maoi1_cost + andn2_cost + andn2_cost; + case MAOI1_ANDN2R_ORN2L : return maoi1_cost + andn2_cost + orn2_cost; + case MAOI1_ANDN2R_ORN2R : return maoi1_cost + andn2_cost + orn2_cost; + case MAOI1_ORN2L_AND2 : return maoi1_cost + orn2_cost + and2_cost; + case MAOI1_ORN2L_OR2 : return maoi1_cost + orn2_cost + or2_cost; + case MAOI1_ORN2L_NAND2 : return maoi1_cost + orn2_cost + nand2_cost; + case MAOI1_ORN2L_NOR2 : return maoi1_cost + orn2_cost + nor2_cost; + case MAOI1_ORN2L_ANDN2L : return maoi1_cost + orn2_cost + andn2_cost; + case MAOI1_ORN2L_ANDN2R : return maoi1_cost + orn2_cost + andn2_cost; + case MAOI1_ORN2L_ORN2L : return maoi1_cost + orn2_cost + orn2_cost; + case MAOI1_ORN2L_ORN2R : return maoi1_cost + orn2_cost + orn2_cost; + case MAOI1_ORN2R_AND2 : return maoi1_cost + orn2_cost + and2_cost; + case MAOI1_ORN2R_OR2 : return maoi1_cost + orn2_cost + or2_cost; + case MAOI1_ORN2R_NAND2 : return maoi1_cost + orn2_cost + nand2_cost; + case MAOI1_ORN2R_NOR2 : return maoi1_cost + orn2_cost + nor2_cost; + case MAOI1_ORN2R_ANDN2L : return maoi1_cost + orn2_cost + andn2_cost; + case MAOI1_ORN2R_ANDN2R : return maoi1_cost + orn2_cost + andn2_cost; + case MAOI1_ORN2R_ORN2L : return maoi1_cost + orn2_cost + orn2_cost; + case MAOI1_ORN2R_ORN2R : return maoi1_cost + orn2_cost + orn2_cost; + + case XOR2_AND2 : return xor2_cost + and2_cost; + case XOR2_OR2 : return xor2_cost + or2_cost; + case XOR2_ANDN2L : return xor2_cost + andn2_cost; + case XOR2_ANDN2R : return xor2_cost + andn2_cost; + case XOR2_ORN2L : return xor2_cost + orn2_cost; + case XOR2_ORN2R : return xor2_cost + orn2_cost; + + case XOR2_AND2_AND2 : return xor2_cost + 2*and2_cost; + case XOR2_AND2_OR2 : return xor2_cost + and2_cost + or2_cost; + case XOR2_AND2_ANDN2L : return xor2_cost + and2_cost + andn2_cost; + case XOR2_AND2_ANDN2R : return xor2_cost + and2_cost + andn2_cost; + case XOR2_AND2_ORN2L : return xor2_cost + and2_cost + orn2_cost; + case XOR2_AND2_ORN2R : return xor2_cost + and2_cost + orn2_cost; + + case XOR2_OR2_AND2 : return xor2_cost + and2_cost + or2_cost; + case XOR2_OR2_OR2 : return xor2_cost + 2*or2_cost; + case XOR2_OR2_ANDN2L : return xor2_cost + andn2_cost + or2_cost; + case XOR2_OR2_ANDN2R : return xor2_cost + andn2_cost + or2_cost; + case XOR2_OR2_ORN2L : return xor2_cost + or2_cost + orn2_cost; + case XOR2_OR2_ORN2R : return xor2_cost + or2_cost + orn2_cost; + + case XOR2_ANDN2L_AND2 : return xor2_cost + andn2_cost + and2_cost; + case XOR2_ANDN2R_AND2 : return xor2_cost + andn2_cost + and2_cost; + case XOR2_ANDN2L_OR2 : return xor2_cost + andn2_cost + or2_cost; + case XOR2_ANDN2R_OR2 : return xor2_cost + andn2_cost + or2_cost; + case XOR2_ANDN2L_ANDN2L : return xor2_cost + 2*andn2_cost; + case XOR2_ANDN2L_ANDN2R : return xor2_cost + 2*andn2_cost; + case XOR2_ANDN2R_ANDN2L : return xor2_cost + 2*andn2_cost; + case XOR2_ANDN2R_ANDN2R : return xor2_cost + 2*andn2_cost; + case XOR2_ANDN2L_ORN2L : return xor2_cost + andn2_cost + orn2_cost; + case XOR2_ANDN2L_ORN2R : return xor2_cost + andn2_cost + orn2_cost; + case XOR2_ANDN2R_ORN2L : return xor2_cost + andn2_cost + orn2_cost; + case XOR2_ANDN2R_ORN2R : return xor2_cost + andn2_cost + orn2_cost; + + case XOR2_ORN2L_AND2 : return xor2_cost + and2_cost + orn2_cost; + case XOR2_ORN2R_AND2 : return xor2_cost + and2_cost + orn2_cost; + case XOR2_ORN2L_OR2 : return xor2_cost + orn2_cost + or2_cost; + case XOR2_ORN2R_OR2 : return xor2_cost + orn2_cost + or2_cost; + case XOR2_ORN2L_ANDN2L : return xor2_cost + andn2_cost + orn2_cost; + case XOR2_ORN2L_ANDN2R : return xor2_cost + andn2_cost + orn2_cost; + case XOR2_ORN2R_ANDN2L : return xor2_cost + andn2_cost + orn2_cost; + case XOR2_ORN2R_ANDN2R : return xor2_cost + andn2_cost + orn2_cost; + case XOR2_ORN2L_ORN2L : return xor2_cost + 2*orn2_cost; + case XOR2_ORN2L_ORN2R : return xor2_cost + 2*orn2_cost; + case XOR2_ORN2R_ORN2L : return xor2_cost + 2*orn2_cost; + case XOR2_ORN2R_ORN2R : return xor2_cost + 2*orn2_cost; + + default : return -1; + } +} + + +template +void lighter::init_b(string conf_file) +{ + ifstream input(conf_file); + string key, value; + for(string line; getline( input, line ); ) + { + istringstream is_line(line); + if(getline(is_line, key, '=')) + { + if(getline(is_line, value)) + { + if(key == "not1_cost") not1_cost = stod(value)*100; + if(key == "and2_cost") and2_cost = stod(value)*100; + if(key == "nand2_cost") nand2_cost = stod(value)*100; + if(key == "or2_cost") or2_cost = stod(value)*100; + if(key == "nor2_cost") nor2_cost = stod(value)*100; + if(key == "xor2_cost") xor2_cost = stod(value)*100; + if(key == "xnor2_cost") xnor2_cost = stod(value)*100; + if(key == "maoi1_cost") maoi1_cost = stod(value)*100; + if(key == "moai1_cost") moai1_cost = stod(value)*100; + if (N >= 4) + { + if(key == "nand3_cost") nand3_cost = stod(value)*100; + if(key == "nor3_cost") nor3_cost = stod(value)*100; + if(key == "and3_cost") and3_cost = stod(value)*100; + if(key == "or3_cost") or3_cost = stod(value)*100; + } + if(key == "andn2_cost") andn2_cost = stod(value)*100; + if(key == "orn2_cost") orn2_cost = stod(value)*100; + } + } + } + + /* + initilisation of the set of bolean operations + */ + if(gate_not1) b.push_back({NOT1, bool_op_cost(NOT1)}); + if(gate_xor2) + { + b.push_back({XOR2, bool_op_cost(XOR2)}); + if(gate_and2) + { + b.push_back({XOR2_AND2, bool_op_cost(XOR2_AND2)}); + if (N >= 4) + { + b.push_back({XOR2_AND2_AND2, bool_op_cost(XOR2_AND2_AND2)}); + if(gate_or2) b.push_back({XOR2_AND2_OR2, bool_op_cost(XOR2_AND2_OR2)}); + if(gate_andn2) + { + b.push_back({XOR2_AND2_ANDN2L, bool_op_cost(XOR2_AND2_ANDN2L)}); + b.push_back({XOR2_AND2_ANDN2R, bool_op_cost(XOR2_AND2_ANDN2R)}); + } + if(gate_orn2) + { + b.push_back({XOR2_AND2_ORN2L, bool_op_cost(XOR2_AND2_ORN2L)}); + b.push_back({XOR2_AND2_ORN2R, bool_op_cost(XOR2_AND2_ORN2R)}); + } + } + } + if(gate_or2) + { + b.push_back({XOR2_OR2, bool_op_cost(XOR2_OR2)}); + if (N >= 4) + { + b.push_back({XOR2_OR2_OR2, bool_op_cost(XOR2_OR2_OR2)}); + if(gate_and2) b.push_back({XOR2_OR2_AND2, bool_op_cost(XOR2_OR2_AND2)}); + if(gate_andn2) + { + b.push_back({XOR2_OR2_ANDN2L, bool_op_cost(XOR2_OR2_ANDN2L)}); + b.push_back({XOR2_OR2_ANDN2R, bool_op_cost(XOR2_OR2_ANDN2R)}); + } + if(gate_orn2) + { + b.push_back({XOR2_OR2_ORN2L, bool_op_cost(XOR2_OR2_ORN2L)}); + b.push_back({XOR2_OR2_ORN2R, bool_op_cost(XOR2_OR2_ORN2R)}); + } + } + } + if(gate_andn2) + { + b.push_back({XOR2_ANDN2L, bool_op_cost(XOR2_ANDN2L)}); + b.push_back({XOR2_ANDN2R, bool_op_cost(XOR2_ANDN2R)}); + if (N >= 4) + { + b.push_back({XOR2_ANDN2L_ANDN2L, bool_op_cost(XOR2_ANDN2L_ANDN2L)}); + b.push_back({XOR2_ANDN2L_ANDN2R, bool_op_cost(XOR2_ANDN2L_ANDN2R)}); + b.push_back({XOR2_ANDN2R_ANDN2L, bool_op_cost(XOR2_ANDN2R_ANDN2L)}); + b.push_back({XOR2_ANDN2R_ANDN2R, bool_op_cost(XOR2_ANDN2R_ANDN2R)}); + + if(gate_or2) + { + b.push_back({XOR2_ANDN2L_OR2, bool_op_cost(XOR2_ANDN2L_OR2)}); + b.push_back({XOR2_ANDN2R_OR2, bool_op_cost(XOR2_ANDN2R_OR2)}); + } + if(gate_and2) + { + b.push_back({XOR2_ANDN2L_AND2, bool_op_cost(XOR2_ANDN2L_AND2)}); + b.push_back({XOR2_ANDN2R_AND2, bool_op_cost(XOR2_ANDN2R_AND2)}); + } + if(gate_orn2) + { + b.push_back({XOR2_ANDN2L_ORN2L, bool_op_cost(XOR2_ANDN2L_ORN2L)}); + b.push_back({XOR2_ANDN2L_ORN2R, bool_op_cost(XOR2_ANDN2L_ORN2R)}); + b.push_back({XOR2_ANDN2R_ORN2L, bool_op_cost(XOR2_ANDN2R_ORN2L)}); + b.push_back({XOR2_ANDN2R_ORN2R, bool_op_cost(XOR2_ANDN2R_ORN2R)}); + } + } + } + if(gate_orn2) + { + b.push_back({XOR2_ORN2L, bool_op_cost(XOR2_ORN2L)}); + b.push_back({XOR2_ORN2R, bool_op_cost(XOR2_ORN2R)}); + if (N >= 4) + { + b.push_back({XOR2_ORN2L_ORN2L, bool_op_cost(XOR2_ORN2L_ORN2L)}); + b.push_back({XOR2_ORN2L_ORN2R, bool_op_cost(XOR2_ORN2L_ORN2R)}); + b.push_back({XOR2_ORN2R_ORN2L, bool_op_cost(XOR2_ORN2R_ORN2L)}); + b.push_back({XOR2_ORN2R_ORN2R, bool_op_cost(XOR2_ORN2R_ORN2R)}); + + if(gate_and2) + { + b.push_back({XOR2_ORN2L_AND2, bool_op_cost(XOR2_ORN2L_AND2)}); + b.push_back({XOR2_ORN2R_AND2, bool_op_cost(XOR2_ORN2R_AND2)}); + } + if(gate_or2) + { + b.push_back({XOR2_ORN2L_OR2, bool_op_cost(XOR2_ORN2L_OR2)}); + b.push_back({XOR2_ORN2R_OR2, bool_op_cost(XOR2_ORN2R_OR2)}); + } + if(gate_andn2) + { + b.push_back({XOR2_ORN2L_ANDN2L, bool_op_cost(XOR2_ORN2L_ANDN2L)}); + b.push_back({XOR2_ORN2L_ANDN2R, bool_op_cost(XOR2_ORN2L_ANDN2R)}); + b.push_back({XOR2_ORN2R_ANDN2L, bool_op_cost(XOR2_ORN2R_ANDN2L)}); + b.push_back({XOR2_ORN2R_ANDN2R, bool_op_cost(XOR2_ORN2R_ANDN2R)}); + } + } + } + } + if(gate_xnor2) b.push_back({XNOR2, bool_op_cost(XNOR2)}); + + if(gate_maoi1) + { + b.push_back({MAOI1, bool_op_cost(MAOI1)}); + + if (N >= 4) + { + if(gate_and3) b.push_back({MAOI1_AND3, bool_op_cost(MAOI1_AND3)}); + if(gate_or3) b.push_back({MAOI1_OR3, bool_op_cost(MAOI1_OR3)}); + if(gate_nor3) b.push_back({MAOI1_NOR3, bool_op_cost(MAOI1_NOR3)}); + if(gate_nand3) b.push_back({MAOI1_NAND3, bool_op_cost(MAOI1_NAND3)}); + } + + if(gate_nand2) + { + b.push_back({MAOI1_NAND2, bool_op_cost(MAOI1_NAND2)}); + if (N >= 4) + { + b.push_back({MAOI1_NAND2_NAND2, bool_op_cost(MAOI1_NAND2_NAND2)}); + if(gate_nor2) b.push_back({MAOI1_NAND2_NOR2, bool_op_cost(MAOI1_NAND2_NOR2)}); + if(gate_and2) b.push_back({MAOI1_NAND2_AND2, bool_op_cost(MAOI1_NAND2_AND2)}); + if(gate_or2) b.push_back({MAOI1_NAND2_OR2, bool_op_cost(MAOI1_NAND2_OR2)}); + if(gate_andn2) + { + b.push_back({MAOI1_NAND2_ANDN2L, bool_op_cost(MAOI1_NAND2_ANDN2L)}); + b.push_back({MAOI1_NAND2_ANDN2R, bool_op_cost(MAOI1_NAND2_ANDN2R)}); + } + if(gate_orn2) + { + b.push_back({MAOI1_NAND2_ORN2L, bool_op_cost(MAOI1_NAND2_ORN2L)}); + b.push_back({MAOI1_NAND2_ORN2R, bool_op_cost(MAOI1_NAND2_ORN2R)}); + } + } + } + if(gate_nor2) + { + b.push_back({MAOI1_NOR2, bool_op_cost(MAOI1_NOR2)}); + if (N >= 4) + { + b.push_back({MAOI1_NOR2_NOR2, bool_op_cost(MAOI1_NOR2_NOR2)}); + if(gate_and2) b.push_back({MAOI1_NOR2_AND2, bool_op_cost(MAOI1_NOR2_AND2)}); + if(gate_or2) b.push_back({MAOI1_NOR2_OR2, bool_op_cost(MAOI1_NOR2_OR2)}); + if(gate_nand2) b.push_back({MAOI1_NOR2_NAND2, bool_op_cost(MAOI1_NOR2_NAND2)}); + if(gate_andn2) + { + b.push_back({MAOI1_NOR2_ANDN2L, bool_op_cost(MAOI1_NOR2_ANDN2L)}); + b.push_back({MAOI1_NOR2_ANDN2R, bool_op_cost(MAOI1_NOR2_ANDN2R)}); + } + if(gate_orn2) + { + b.push_back({MAOI1_NOR2_ORN2L, bool_op_cost(MAOI1_NOR2_ORN2L)}); + b.push_back({MAOI1_NOR2_ORN2R, bool_op_cost(MAOI1_NOR2_ORN2R)}); + } + } + } + if(gate_and2) + { + b.push_back({MAOI1_AND2, bool_op_cost(MAOI1_AND2)}); + if (N >= 4) + { + b.push_back({MAOI1_AND2_AND2, bool_op_cost(MAOI1_AND2_AND2)}); + if(gate_nand2) b.push_back({MAOI1_AND2_NAND2, bool_op_cost(MAOI1_AND2_NAND2)}); + if(gate_nor2) b.push_back({MAOI1_AND2_NOR2, bool_op_cost(MAOI1_AND2_NOR2)}); + if(gate_or2) b.push_back({MAOI1_AND2_OR2, bool_op_cost(MAOI1_AND2_OR2)}); + if(gate_andn2) + { + b.push_back({MAOI1_AND2_ANDN2L, bool_op_cost(MAOI1_AND2_ANDN2L)}); + b.push_back({MAOI1_AND2_ANDN2R, bool_op_cost(MAOI1_AND2_ANDN2R)}); + } + if(gate_orn2) + { + b.push_back({MAOI1_AND2_ORN2L, bool_op_cost(MAOI1_AND2_ORN2L)}); + b.push_back({MAOI1_AND2_ORN2R, bool_op_cost(MAOI1_AND2_ORN2R)}); + } + } + } + if(gate_or2) + { + b.push_back({MAOI1_OR2, bool_op_cost(MAOI1_OR2)}); + if (N >= 4) + { + b.push_back({MAOI1_OR2_OR2, bool_op_cost(MAOI1_OR2_OR2)}); + if(gate_and2) b.push_back({MAOI1_OR2_AND2, bool_op_cost(MAOI1_OR2_AND2)}); + if(gate_nor2) b.push_back({MAOI1_OR2_NOR2, bool_op_cost(MAOI1_OR2_NOR2)}); + if(gate_nand2) b.push_back({MAOI1_OR2_NAND2, bool_op_cost(MAOI1_OR2_NAND2)}); + if(gate_andn2) + { + b.push_back({MAOI1_OR2_ANDN2L, bool_op_cost(MAOI1_OR2_ANDN2L)}); + b.push_back({MAOI1_OR2_ANDN2R, bool_op_cost(MAOI1_OR2_ANDN2R)}); + } + if(gate_orn2) + { + b.push_back({MAOI1_OR2_ORN2L, bool_op_cost(MAOI1_OR2_ORN2L)}); + b.push_back({MAOI1_OR2_ORN2R, bool_op_cost(MAOI1_OR2_ORN2R)}); + } + } + } + if(gate_andn2) + { + b.push_back({MAOI1_ANDN2L, bool_op_cost(MAOI1_ANDN2L)}); + b.push_back({MAOI1_ANDN2R, bool_op_cost(MAOI1_ANDN2R)}); + if (N >= 4) + { + b.push_back({MAOI1_ANDN2L_ANDN2L, bool_op_cost(MAOI1_ANDN2L_ANDN2L)}); + b.push_back({MAOI1_ANDN2L_ANDN2R, bool_op_cost(MAOI1_ANDN2L_ANDN2R)}); + b.push_back({MAOI1_ANDN2R_ANDN2L, bool_op_cost(MAOI1_ANDN2R_ANDN2L)}); + b.push_back({MAOI1_ANDN2R_ANDN2R, bool_op_cost(MAOI1_ANDN2R_ANDN2R)}); + + if(gate_nor2) + { + b.push_back({MAOI1_ANDN2L_NOR2, bool_op_cost(MAOI1_ANDN2L_NOR2)}); + b.push_back({MAOI1_ANDN2R_NOR2, bool_op_cost(MAOI1_ANDN2R_NOR2)}); + } + if(gate_nand2) + { + b.push_back({MAOI1_ANDN2L_NAND2, bool_op_cost(MAOI1_ANDN2L_NAND2)}); + b.push_back({MAOI1_ANDN2R_NAND2, bool_op_cost(MAOI1_ANDN2R_NAND2)}); + } + if(gate_or2) + { + b.push_back({MAOI1_ANDN2L_OR2, bool_op_cost(MAOI1_ANDN2L_OR2)}); + b.push_back({MAOI1_ANDN2R_OR2, bool_op_cost(MAOI1_ANDN2R_OR2)}); + } + if(gate_and2) + { + b.push_back({MAOI1_ANDN2L_AND2, bool_op_cost(MAOI1_ANDN2L_AND2)}); + b.push_back({MAOI1_ANDN2R_AND2, bool_op_cost(MAOI1_ANDN2R_AND2)}); + } + if(gate_orn2) + { + b.push_back({MAOI1_ANDN2L_ORN2L, bool_op_cost(MAOI1_ANDN2L_ORN2L)}); + b.push_back({MAOI1_ANDN2L_ORN2R, bool_op_cost(MAOI1_ANDN2L_ORN2R)}); + b.push_back({MAOI1_ANDN2R_ORN2L, bool_op_cost(MAOI1_ANDN2R_ORN2L)}); + b.push_back({MAOI1_ANDN2R_ORN2R, bool_op_cost(MAOI1_ANDN2R_ORN2R)}); + } + } + } + if(gate_orn2) + { + b.push_back({MAOI1_ORN2L, bool_op_cost(MAOI1_ORN2L)}); + b.push_back({MAOI1_ORN2R, bool_op_cost(MAOI1_ORN2R)}); + if (N >= 4) + { + b.push_back({MAOI1_ORN2L_ORN2L, bool_op_cost(MAOI1_ORN2L_ORN2L)}); + b.push_back({MAOI1_ORN2L_ORN2R, bool_op_cost(MAOI1_ORN2L_ORN2R)}); + b.push_back({MAOI1_ORN2R_ORN2L, bool_op_cost(MAOI1_ORN2R_ORN2L)}); + b.push_back({MAOI1_ORN2R_ORN2R, bool_op_cost(MAOI1_ORN2R_ORN2R)}); + + if(gate_nor2) + { + b.push_back({MAOI1_ORN2L_NOR2, bool_op_cost(MAOI1_ORN2L_NOR2)}); + b.push_back({MAOI1_ORN2R_NOR2, bool_op_cost(MAOI1_ORN2R_NOR2)}); + } + if(gate_nand2) + { + b.push_back({MAOI1_ORN2L_NAND2, bool_op_cost(MAOI1_ORN2L_NAND2)}); + b.push_back({MAOI1_ORN2R_NAND2, bool_op_cost(MAOI1_ORN2R_NAND2)}); + } + if(gate_or2) + { + b.push_back({MAOI1_ORN2L_OR2, bool_op_cost(MAOI1_ORN2L_OR2)}); + b.push_back({MAOI1_ORN2R_OR2, bool_op_cost(MAOI1_ORN2R_OR2)}); + } + if(gate_and2) + { + b.push_back({MAOI1_ORN2L_AND2, bool_op_cost(MAOI1_ORN2L_AND2)}); + b.push_back({MAOI1_ORN2R_AND2, bool_op_cost(MAOI1_ORN2R_AND2)}); + } + if(gate_andn2) + { + b.push_back({MAOI1_ORN2L_ANDN2L, bool_op_cost(MAOI1_ORN2L_ANDN2L)}); + b.push_back({MAOI1_ORN2L_ANDN2R, bool_op_cost(MAOI1_ORN2L_ANDN2R)}); + b.push_back({MAOI1_ORN2R_ANDN2L, bool_op_cost(MAOI1_ORN2R_ANDN2L)}); + b.push_back({MAOI1_ORN2R_ANDN2R, bool_op_cost(MAOI1_ORN2R_ANDN2R)}); + } + } + } + } + if(gate_moai1) + { + b.push_back({MOAI1, bool_op_cost(MOAI1)}); + if (N >= 4) + { + if(gate_and3) b.push_back({MOAI1_AND3, bool_op_cost(MOAI1_AND3)}); + if(gate_or3) b.push_back({MOAI1_OR3, bool_op_cost(MOAI1_OR3)}); + if(gate_nor3) b.push_back({MOAI1_NOR3, bool_op_cost(MOAI1_NOR3)}); + if(gate_nand3) b.push_back({MOAI1_NAND3, bool_op_cost(MOAI1_NAND3)}); + } + if(gate_nand2) + { + b.push_back({MOAI1_NAND2, bool_op_cost(MOAI1_NAND2)}); + if (N >= 4) + { + b.push_back({MOAI1_NAND2_NAND2, bool_op_cost(MOAI1_NAND2_NAND2)}); + if(gate_nor2) b.push_back({MOAI1_NAND2_NOR2, bool_op_cost(MOAI1_NAND2_NOR2)}); + if(gate_and2) b.push_back({MOAI1_NAND2_AND2, bool_op_cost(MOAI1_NAND2_AND2)}); + if(gate_or2) b.push_back({MOAI1_NAND2_OR2, bool_op_cost(MOAI1_NAND2_OR2)}); + if(gate_andn2) + { + b.push_back({MOAI1_NAND2_ANDN2L, bool_op_cost(MOAI1_NAND2_ANDN2L)}); + b.push_back({MOAI1_NAND2_ANDN2R, bool_op_cost(MOAI1_NAND2_ANDN2R)}); + } + if(gate_orn2) + { + b.push_back({MOAI1_NAND2_ORN2L, bool_op_cost(MOAI1_NAND2_ORN2L)}); + b.push_back({MOAI1_NAND2_ORN2R, bool_op_cost(MOAI1_NAND2_ORN2R)}); + } + } + } + if(gate_nor2) + { + b.push_back({MOAI1_NOR2, bool_op_cost(MOAI1_NOR2)}); + if (N >= 4) + { + b.push_back({MOAI1_NOR2_NOR2, bool_op_cost(MOAI1_NOR2_NOR2)}); + if(gate_nand2) b.push_back({MOAI1_NOR2_NAND2, bool_op_cost(MOAI1_NOR2_NAND2)}); + if(gate_and2) b.push_back({MOAI1_NOR2_AND2, bool_op_cost(MOAI1_NOR2_AND2)}); + if(gate_or2) b.push_back({MOAI1_NOR2_OR2, bool_op_cost(MOAI1_NOR2_OR2)}); + if(gate_andn2) + { + b.push_back({MOAI1_NOR2_ANDN2L, bool_op_cost(MOAI1_NOR2_ANDN2L)}); + b.push_back({MOAI1_NOR2_ANDN2R, bool_op_cost(MOAI1_NOR2_ANDN2R)}); + } + if(gate_orn2) + { + b.push_back({MOAI1_NOR2_ORN2L, bool_op_cost(MOAI1_NOR2_ORN2L)}); + b.push_back({MOAI1_NOR2_ORN2R, bool_op_cost(MOAI1_NOR2_ORN2R)}); + } + } + } + if(gate_and2) + { + b.push_back({MOAI1_AND2, bool_op_cost(MOAI1_AND2)}); + if (N >= 4) + { + b.push_back({MOAI1_AND2_AND2, bool_op_cost(MOAI1_AND2_AND2)}); + if(gate_nand2) b.push_back({MOAI1_AND2_NAND2, bool_op_cost(MOAI1_AND2_NAND2)}); + if(gate_nor2) b.push_back({MOAI1_AND2_NOR2, bool_op_cost(MOAI1_AND2_NOR2)}); + if(gate_or2) b.push_back({MOAI1_AND2_OR2, bool_op_cost(MOAI1_AND2_OR2)}); + if(gate_andn2) + { + b.push_back({MOAI1_AND2_ANDN2L, bool_op_cost(MOAI1_AND2_ANDN2L)}); + b.push_back({MOAI1_AND2_ANDN2R, bool_op_cost(MOAI1_AND2_ANDN2R)}); + } + if(gate_orn2) + { + b.push_back({MOAI1_AND2_ORN2L, bool_op_cost(MOAI1_AND2_ORN2L)}); + b.push_back({MOAI1_AND2_ORN2R, bool_op_cost(MOAI1_AND2_ORN2R)}); + } + } + } + if(gate_or2) + { + b.push_back({MOAI1_OR2, bool_op_cost(MOAI1_OR2)}); + if (N >= 4) + { + b.push_back({MOAI1_OR2_OR2, bool_op_cost(MOAI1_OR2_OR2)}); + if(gate_nand2) b.push_back({MOAI1_OR2_NAND2, bool_op_cost(MOAI1_OR2_NAND2)}); + if(gate_nor2) b.push_back({MOAI1_OR2_NOR2, bool_op_cost(MOAI1_OR2_NOR2)}); + if(gate_and2) b.push_back({MOAI1_OR2_AND2, bool_op_cost(MOAI1_OR2_AND2)}); + if(gate_andn2) + { + b.push_back({MOAI1_OR2_ANDN2L, bool_op_cost(MOAI1_OR2_ANDN2L)}); + b.push_back({MOAI1_OR2_ANDN2R, bool_op_cost(MOAI1_OR2_ANDN2R)}); + } + if(gate_orn2) + { + b.push_back({MOAI1_OR2_ORN2L, bool_op_cost(MOAI1_OR2_ORN2L)}); + b.push_back({MOAI1_OR2_ORN2R, bool_op_cost(MOAI1_OR2_ORN2R)}); + } + } + } + if(gate_andn2) + { + b.push_back({MOAI1_ANDN2L, bool_op_cost(MOAI1_ANDN2L)}); + b.push_back({MOAI1_ANDN2R, bool_op_cost(MOAI1_ANDN2R)}); + if (N >= 4) + { + b.push_back({MOAI1_ANDN2L_ANDN2L, bool_op_cost(MOAI1_ANDN2L_ANDN2L)}); + b.push_back({MOAI1_ANDN2L_ANDN2R, bool_op_cost(MOAI1_ANDN2L_ANDN2R)}); + b.push_back({MOAI1_ANDN2R_ANDN2L, bool_op_cost(MOAI1_ANDN2R_ANDN2L)}); + b.push_back({MOAI1_ANDN2R_ANDN2R, bool_op_cost(MOAI1_ANDN2R_ANDN2R)}); + + if(gate_nor2) + { + b.push_back({MOAI1_ANDN2L_NOR2, bool_op_cost(MOAI1_ANDN2L_NOR2)}); + b.push_back({MOAI1_ANDN2R_NOR2, bool_op_cost(MOAI1_ANDN2R_NOR2)}); + } + if(gate_nand2) + { + b.push_back({MOAI1_ANDN2L_NAND2, bool_op_cost(MOAI1_ANDN2L_NAND2)}); + b.push_back({MOAI1_ANDN2R_NAND2, bool_op_cost(MOAI1_ANDN2R_NAND2)}); + } + if(gate_or2) + { + b.push_back({MOAI1_ANDN2L_OR2, bool_op_cost(MOAI1_ANDN2L_OR2)}); + b.push_back({MOAI1_ANDN2R_OR2, bool_op_cost(MOAI1_ANDN2R_OR2)}); + } + if(gate_and2) + { + b.push_back({MOAI1_ANDN2L_AND2, bool_op_cost(MOAI1_ANDN2L_AND2)}); + b.push_back({MOAI1_ANDN2R_AND2, bool_op_cost(MOAI1_ANDN2R_AND2)}); + } + if(gate_orn2) + { + b.push_back({MOAI1_ANDN2L_ORN2L, bool_op_cost(MOAI1_ANDN2L_ORN2L)}); + b.push_back({MOAI1_ANDN2L_ORN2R, bool_op_cost(MOAI1_ANDN2L_ORN2R)}); + b.push_back({MOAI1_ANDN2R_ORN2L, bool_op_cost(MOAI1_ANDN2R_ORN2L)}); + b.push_back({MOAI1_ANDN2R_ORN2R, bool_op_cost(MOAI1_ANDN2R_ORN2R)}); + } + } + } + if(gate_orn2) + { + b.push_back({MOAI1_ORN2L, bool_op_cost(MOAI1_ORN2L)}); + b.push_back({MOAI1_ORN2R, bool_op_cost(MOAI1_ORN2R)}); + if (N >= 4) + { + b.push_back({MOAI1_ORN2L_ORN2L, bool_op_cost(MOAI1_ORN2L_ORN2L)}); + b.push_back({MOAI1_ORN2L_ORN2R, bool_op_cost(MOAI1_ORN2L_ORN2R)}); + b.push_back({MOAI1_ORN2R_ORN2L, bool_op_cost(MOAI1_ORN2R_ORN2L)}); + b.push_back({MOAI1_ORN2R_ORN2R, bool_op_cost(MOAI1_ORN2R_ORN2R)}); + + if(gate_nor2) + { + b.push_back({MOAI1_ORN2L_NOR2, bool_op_cost(MOAI1_ORN2L_NOR2)}); + b.push_back({MOAI1_ORN2R_NOR2, bool_op_cost(MOAI1_ORN2R_NOR2)}); + } + if(gate_nand2) + { + b.push_back({MOAI1_ORN2L_NAND2, bool_op_cost(MOAI1_ORN2L_NAND2)}); + b.push_back({MOAI1_ORN2R_NAND2, bool_op_cost(MOAI1_ORN2R_NAND2)}); + } + if(gate_or2) + { + b.push_back({MOAI1_ORN2L_OR2, bool_op_cost(MOAI1_ORN2L_OR2)}); + b.push_back({MOAI1_ORN2R_OR2, bool_op_cost(MOAI1_ORN2R_OR2)}); + } + if(gate_and2) + { + b.push_back({MOAI1_ORN2L_AND2, bool_op_cost(MOAI1_ORN2L_AND2)}); + b.push_back({MOAI1_ORN2R_AND2, bool_op_cost(MOAI1_ORN2R_AND2)}); + } + if(gate_andn2) + { + b.push_back({MOAI1_ORN2L_ANDN2L, bool_op_cost(MOAI1_ORN2L_ANDN2L)}); + b.push_back({MOAI1_ORN2L_ANDN2R, bool_op_cost(MOAI1_ORN2L_ANDN2R)}); + b.push_back({MOAI1_ORN2R_ANDN2L, bool_op_cost(MOAI1_ORN2R_ANDN2L)}); + b.push_back({MOAI1_ORN2R_ANDN2R, bool_op_cost(MOAI1_ORN2R_ANDN2R)}); + } + } + } + } + + sort(b.begin(), b.end()); + + b.erase(remove_if(b.begin(), b.end(), + [this](bool_op_t bool_op) -> bool + { + const function_t f1 = function_t::INPUT_DEFAULT(); + vector > f; + bool_op_fun(bool_op.op_id, f1, &f); + sort(f.begin(), f.end()); + auto check = check_tt.insert(f); + return !check.second; + }), b.end()); + + check_tt.clear(); + + for(auto bool_op : b) + { + if(bool_op.op_cost > max_GE) max_GE = bool_op.op_cost; + if(bool_op.op_cost < min_GE) min_GE = bool_op.op_cost; + } +} + +template +void lighter::init_critiera(string criteria_file) +{ + ifstream input(criteria_file); + string key, value; + for(string line; getline( input, line ); ) + { + istringstream is_line(line); + if(getline(is_line, key, '=')) + { + if(getline(is_line, value)) + { + if(key == "Cost") Cost_Criteria = stod(value)*100; + if(key == "Involution") Involution_Criteria = stoi(value); + if(key == "Diff") Diff_Criteria = stoi(value); + if(key == "DiffFreq") DiffFreq_Criteria = stoi(value); + if(key == "Diff1") Diff1_Criteria = stoi(value); + if(key == "CardD1") CardD1_Criteria = stoi(value); + if(key == "Lin") Lin_Criteria = stoi(value); + if(key == "LinFreq") LinFreq_Criteria = stoi(value); + if(key == "Lin1") Lin1_Criteria = stoi(value); + if(key == "CardL1") CardL1_Criteria = stoi(value); + if(key == "MaxDeg") MaxDeg_Criteria = stoi(value); + if(key == "MinDeg") MinDeg_Criteria = stoi(value); + if(key == "MaxDegFreq") MaxDegFreq_Criteria = stoi(value); + if(key == "MinDegFreq") MinDegFreq_Criteria = stoi(value); + } + } + } +} + +template +void lighter::bool_op_simple_gates(function_t f, vector > *v, int operation) +{ + function_t fun_tmp = f; + + if(operation == NOT1) + { + for (int i = 0; i < N; i++) + { + fun_tmp.bit_slice[i] = ~(f.bit_slice[i]); + fun_tmp.info_line = (uint8_t)i; + fun_tmp.info_op = (uint8_t)operation; + fun_tmp.prev = f.bit_slice[i]; + fun_tmp.sort(); + v->push_back(fun_tmp); + fun_tmp = f; + } + } + else + { + for(int i = 0; i < N; i++) + { + for(int j = 0; j < N; j++) + { + if(i != j) + { + switch (operation) + { + case MAOI1 : + case XOR2 : fun_tmp.bit_slice[i] = f.bit_slice[i] ^ f.bit_slice[j]; break; + case MOAI1 : + case XNOR2 : fun_tmp.bit_slice[i] = ~(f.bit_slice[i] ^ f.bit_slice[j]); break; + } + fun_tmp.prev = f.bit_slice[i]; + fun_tmp.info_line = (uint8_t)i; + fun_tmp.info_op = (uint8_t)operation; + fun_tmp.sort(); + v->push_back(fun_tmp); + fun_tmp = f; + } + } + fun_tmp = f; + } + } +} + +template +void lighter::bool_op_double_gates(function_t f, vector > *v, int operation) +{ + auto two_inputs = [](const function_t f, int i, int a, int b, int operation) + { + auto fun_tmp = f; + switch (operation) + { + case MAOI1_NAND2 : + case MOAI1_AND2 : fun_tmp.bit_slice[i] = ~(f.bit_slice[(i+a)%N] & f.bit_slice[(i+b)%N]) ^ f.bit_slice[i];break; + case MAOI1_NOR2 : + case MOAI1_OR2 : fun_tmp.bit_slice[i] = ~(f.bit_slice[(i+a)%N] | f.bit_slice[(i+b)%N]) ^ f.bit_slice[i];break; + case XOR2_AND2 : + case MAOI1_AND2 : + case MOAI1_NAND2 : fun_tmp.bit_slice[i] = (f.bit_slice[(i+a)%N] & f.bit_slice[(i+b)%N]) ^ f.bit_slice[i];break; + case XOR2_OR2 : + case MAOI1_OR2 : + case MOAI1_NOR2 : fun_tmp.bit_slice[i] = ( f.bit_slice[(i+a)%N] | f.bit_slice[(i+b)%N]) ^ f.bit_slice[i];break; + + case MOAI1_ORN2R : + case MAOI1_ANDN2L : + case XOR2_ANDN2L : fun_tmp.bit_slice[i] = (~f.bit_slice[(i+a)%N] & f.bit_slice[(i+b)%N]) ^ f.bit_slice[i];break; + + case MOAI1_ORN2L : + case MAOI1_ANDN2R : + case XOR2_ANDN2R : fun_tmp.bit_slice[i] = ( f.bit_slice[(i+a)%N] &~f.bit_slice[(i+b)%N]) ^ f.bit_slice[i];break; + + case MOAI1_ANDN2R : + case MAOI1_ORN2L : + case XOR2_ORN2L : fun_tmp.bit_slice[i] = (~f.bit_slice[(i+a)%N] | f.bit_slice[(i+b)%N]) ^ f.bit_slice[i];break; + + case MOAI1_ANDN2L : + case MAOI1_ORN2R : + case XOR2_ORN2R : fun_tmp.bit_slice[i] = ( f.bit_slice[(i+a)%N] |~f.bit_slice[(i+b)%N]) ^ f.bit_slice[i];break; + } + fun_tmp.prev = f.bit_slice[i]; + fun_tmp.info_line = i; + fun_tmp.info_op = operation; + fun_tmp.sort(); + return fun_tmp; + }; + + auto three_inputs = [](const function_t f, int i, int a, int b, int c, int operation) + { + auto fun_tmp = f; + switch (operation) + { + case XOR2_AND2_AND2 : + case MAOI1_AND3 : + case MOAI1_NAND2_AND2 : + case MAOI1_AND2_AND2 : + case MOAI1_ORN2L_NAND2 : + case MAOI1_ANDN2R_NAND2 : + case MOAI1_NAND3 : fun_tmp.bit_slice[i] = (f.bit_slice[(i+a)%N] & f.bit_slice[(i+b)%N] & f.bit_slice[(i+c)%N]) ^ f.bit_slice[i];break; + + case XOR2_OR2_OR2 : + case MAOI1_OR3 : + case MOAI1_NOR2_OR2 : + case MAOI1_OR2_OR2 : + case MOAI1_ANDN2L_NOR2 : + case MAOI1_ORN2R_NOR2 : + case MOAI1_NOR3 : fun_tmp.bit_slice[i] = (f.bit_slice[(i+a)%N] | f.bit_slice[(i+b)%N] | f.bit_slice[(i+c)%N]) ^ f.bit_slice[i];break; + + case MOAI1_AND3 : + case MAOI1_NAND2_AND2 : + case MOAI1_AND2_AND2 : + case MOAI1_ANDN2R_NAND2 : + case MAOI1_ORN2L_NAND2 : + case MAOI1_NAND3 : fun_tmp.bit_slice[i] = ~(f.bit_slice[(i+a)%N] & f.bit_slice[(i+b)%N] & f.bit_slice[(i+c)%N]) ^ f.bit_slice[i];break; + + case MOAI1_OR3 : + case MAOI1_NOR2_OR2 : + case MOAI1_OR2_OR2 : + case MAOI1_ANDN2L_NOR2 : + case MOAI1_ORN2R_NOR2 : + case MAOI1_NOR3 : fun_tmp.bit_slice[i] = ~(f.bit_slice[(i+a)%N] | f.bit_slice[(i+b)%N] | f.bit_slice[(i+c)%N]) ^ f.bit_slice[i];break; + } + fun_tmp.prev = f.bit_slice[i]; + fun_tmp.info_line = (uint8_t)i; + fun_tmp.info_op = (uint8_t)operation; + fun_tmp.sort(); + return fun_tmp; + }; + + switch (operation) + { + case MAOI1_AND3 : + case MOAI1_NAND2_AND2 : + case MAOI1_AND2_AND2 : + case MAOI1_OR3 : + case MOAI1_NOR2_OR2 : + case MAOI1_OR2_OR2 : + case MOAI1_AND3 : + case MAOI1_NAND2_AND2 : + case MOAI1_AND2_AND2 : + case MOAI1_OR3 : + case MAOI1_NOR2_OR2 : + case MOAI1_OR2_OR2 : + case MOAI1_NAND3 : + case MOAI1_NOR3 : + case MAOI1_NAND3 : + case MAOI1_NOR3 : + case XOR2_AND2_AND2 : + case XOR2_OR2_OR2 : + case MOAI1_ANDN2L_NOR2 : + case MAOI1_ORN2R_NOR2 : + case MOAI1_ORN2R_NOR2 : + case MAOI1_ANDN2L_NOR2 : + case MOAI1_ORN2L_NAND2 : + case MAOI1_ANDN2R_NAND2: + case MOAI1_ANDN2R_NAND2: + case MAOI1_ORN2L_NAND2 : + for (int i = 0; i < N; i++) + { + for (int j = 1; j < N - 2; j++) + { + for (int k = j + 1; k < N - 1; k++) + { + for (int u = k + 1; u < N; u++) + { + v->push_back(three_inputs(f, i, j, k, u, operation)); + } + } + } + } + break; + case MAOI1_NAND2 : + case MAOI1_NOR2 : + case MAOI1_AND2 : + case MAOI1_OR2 : + case MOAI1_NAND2 : + case MOAI1_NOR2 : + case MOAI1_AND2 : + case MOAI1_OR2 : + case XOR2_AND2 : + case XOR2_OR2 : + case MAOI1_ANDN2L : + case MAOI1_ANDN2R : + case MAOI1_ORN2L : + case MAOI1_ORN2R : + case MOAI1_ANDN2L : + case MOAI1_ANDN2R : + case MOAI1_ORN2L : + case MOAI1_ORN2R : + case XOR2_ANDN2L : + case XOR2_ANDN2R : + case XOR2_ORN2L : + case XOR2_ORN2R : + for (int i = 0; i < N; i++) + { + for (int j = 1; j < N - 1; j++) + { + for (int k = j + 1; k < N; k++) + { + v->push_back(two_inputs(f, i, j, k, operation)); + } + } + } + break; + } +} + +template +void lighter::bool_op_triple_gates(function_t f, vector > *v, int operation) +{ + auto triple_gates = [](const function_t f, int i, int a, int b, int c, int operation) + { + auto fun_tmp = f; + switch (operation) + { + case MOAI1_NAND2_OR2 : + case MAOI1_AND2_OR2 : + case MOAI1_ORN2L_NOR2 : + case MAOI1_ANDN2R_NOR2 : + case XOR2_AND2_OR2 : fun_tmp.bit_slice[i] = ((f.bit_slice[(i+b)%N]|f.bit_slice[(i+c)%N])&f.bit_slice[(i+a)%N])^f.bit_slice[i];break; + + case MOAI1_NOR2_AND2 : + case MAOI1_OR2_AND2 : + case MOAI1_ANDN2L_NAND2 : + case MAOI1_ORN2R_NAND2 : + case XOR2_OR2_AND2 : fun_tmp.bit_slice[i] = ((f.bit_slice[(i+b)%N]&f.bit_slice[(i+c)%N])|f.bit_slice[(i+a)%N])^f.bit_slice[i];break; + + case MOAI1_ANDN2R_NOR2 : + case MAOI1_ORN2L_NOR2 : + case MAOI1_NAND2_OR2 : + case MOAI1_AND2_OR2 : fun_tmp.bit_slice[i] = ~((f.bit_slice[(i+b)%N]|f.bit_slice[(i+c)%N])&f.bit_slice[(i+a)%N])^f.bit_slice[i];break; + + case MOAI1_OR2_AND2 : + case MAOI1_NOR2_AND2 : + case MOAI1_ORN2R_NAND2 : + case MAOI1_ANDN2L_NAND2 : fun_tmp.bit_slice[i] = ~((f.bit_slice[(i+b)%N]&f.bit_slice[(i+c)%N])|f.bit_slice[(i+a)%N])^f.bit_slice[i];break; + + case MOAI1_NAND2_ANDN2R : + case MOAI1_ORN2L_ORN2L : + case MAOI1_AND2_ANDN2R : + case MAOI1_ANDN2R_ORN2L : + case XOR2_AND2_ANDN2R : + case XOR2_ANDN2R_ORN2L : fun_tmp.bit_slice[i] = ( f.bit_slice[(i+a)%N] & ( f.bit_slice[(i+b)%N] & ~f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + case MOAI1_NAND2_ORN2R : + case MOAI1_ORN2L_ANDN2L : + case MAOI1_AND2_ORN2R : + case MAOI1_ANDN2R_ANDN2L : + case XOR2_AND2_ORN2R : + case XOR2_ANDN2R_ANDN2L : fun_tmp.bit_slice[i] = ( f.bit_slice[(i+a)%N] & ( f.bit_slice[(i+b)%N] | ~f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + case MOAI1_NOR2_ANDN2L : + case MOAI1_ANDN2L_ORN2R : + case MAOI1_OR2_ANDN2L : + case MAOI1_ORN2R_ORN2R : + case XOR2_OR2_ANDN2L : + case XOR2_ORN2R_ORN2R : fun_tmp.bit_slice[i] = ( f.bit_slice[(i+a)%N] | (~f.bit_slice[(i+b)%N] & f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + case MOAI1_NOR2_ANDN2R : + case MOAI1_ANDN2L_ORN2L : + case MAOI1_OR2_ANDN2R : + case MAOI1_ORN2R_ORN2L : + case XOR2_OR2_ANDN2R : + case XOR2_ORN2R_ORN2L : fun_tmp.bit_slice[i] = ( f.bit_slice[(i+a)%N] | ( f.bit_slice[(i+b)%N] & ~f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + case MOAI1_NOR2_ORN2L : + case MOAI1_ANDN2L_ANDN2R : + case MAOI1_OR2_ORN2L : + case MAOI1_ORN2R_ANDN2R : + case XOR2_OR2_ORN2L : + case XOR2_ORN2R_ANDN2R : fun_tmp.bit_slice[i] = ( f.bit_slice[(i+a)%N] | (~f.bit_slice[(i+b)%N] | f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + case MOAI1_NOR2_ORN2R : + case MOAI1_ANDN2L_ANDN2L : + case MAOI1_OR2_ORN2R : + case MAOI1_ORN2R_ANDN2L : + case XOR2_OR2_ORN2R : + case XOR2_ORN2R_ANDN2L : fun_tmp.bit_slice[i] = ( f.bit_slice[(i+a)%N] | ( f.bit_slice[(i+b)%N] | ~f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + ///////////NOR2_NAND2 ~(A | ~(B & C)) = (~A & (B & C)) ANDN2L_AND2 + case MAOI1_NOR2_NAND2 : + case MAOI1_ANDN2L_AND2 : + case MOAI1_OR2_NAND2 : + case MOAI1_ORN2R_AND2 : + case XOR2_ANDN2L_AND2 : fun_tmp.bit_slice[i] = ( ~f.bit_slice[(i+a)%N] & ( f.bit_slice[(i+b)%N] & f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + ///////////NOR2_NOR2 ~(A | ~(B | C)) = (~A & (B | C)) ANDN2L_OR2 + case MAOI1_NOR2_NOR2 : + case MAOI1_ANDN2L_OR2 : + case MOAI1_OR2_NOR2 : + case MOAI1_ORN2R_OR2 : + case XOR2_ANDN2L_OR2 : fun_tmp.bit_slice[i] = ( ~f.bit_slice[(i+a)%N] & ( f.bit_slice[(i+b)%N] | f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + ///////////NOR2_ORN2R ~(A | (B | ~C)) = ~A & (~B & C) ANDN2L_ANDN2L + case MAOI1_NOR2_ORN2R : + case MAOI1_ANDN2L_ANDN2L : + case MOAI1_OR2_ORN2R : + case MOAI1_ORN2R_ANDN2L : + case XOR2_ANDN2L_ANDN2L : fun_tmp.bit_slice[i] = ( ~f.bit_slice[(i+a)%N] & (~f.bit_slice[(i+b)%N] & f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + ///////////NOR2_ORN2L ~(A | (~B | C)) = ~A & (B & ~C) ANDN2L_ANDN2R + case MAOI1_NOR2_ORN2L : + case MAOI1_ANDN2L_ANDN2R : + case MOAI1_OR2_ORN2L : + case MOAI1_ORN2R_ANDN2R : + case XOR2_ANDN2L_ANDN2R : fun_tmp.bit_slice[i] = ( ~f.bit_slice[(i+a)%N] & ( f.bit_slice[(i+b)%N] & ~f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + ///////////NOR2_ANDN2R ~(A | (B & ~C)) = ~A & (~B | C) ANDN2L_ORN2L + case MAOI1_NOR2_ANDN2R : + case MAOI1_ANDN2L_ORN2L : + case MOAI1_OR2_ANDN2R : + case MOAI1_ORN2R_ORN2L : + case XOR2_ANDN2L_ORN2L : fun_tmp.bit_slice[i] = ( ~f.bit_slice[(i+a)%N] & (~f.bit_slice[(i+b)%N] | f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + ///////////NOR2_ANDN2L ~(A | (~B & C)) = ~A & (B | ~C) ANDN2L_ORN2R + case MAOI1_NOR2_ANDN2L : + case MAOI1_ANDN2L_ORN2R : + case MOAI1_OR2_ANDN2L : + case MOAI1_ORN2R_ORN2R : + case XOR2_ANDN2L_ORN2R : fun_tmp.bit_slice[i] = ( ~f.bit_slice[(i+a)%N] & ( f.bit_slice[(i+b)%N] | ~f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + ///////////AND2_NAND2 (A & ~(B & C)) = ~(~A | (B & C)) + case MAOI1_AND2_NAND2 : + case MAOI1_ANDN2R_AND2 : + case MOAI1_NAND2_NAND2 : + case MOAI1_ORN2L_AND2 : + case XOR2_ANDN2R_AND2 : fun_tmp.bit_slice[i] = ( f.bit_slice[(i+a)%N] & ~( f.bit_slice[(i+b)%N] & f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + ///////////AND2_NAND2 (A & ~(B | C)) = ~(~A | (B | C)) + case MAOI1_AND2_NOR2 : + case MAOI1_ANDN2R_OR2 : + case MOAI1_NAND2_NOR2 : + case MOAI1_ORN2L_OR2 : + case XOR2_ANDN2R_OR2 : fun_tmp.bit_slice[i] = ( f.bit_slice[(i+a)%N] & ~( f.bit_slice[(i+b)%N] | f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + case MAOI1_AND2_ORN2L : + case MAOI1_ANDN2R_ANDN2R : + case MOAI1_NAND2_ORN2L : + case MOAI1_ORN2L_ANDN2R : + case XOR2_AND2_ORN2L : + case XOR2_ANDN2R_ANDN2R : fun_tmp.bit_slice[i] = ( f.bit_slice[(i+a)%N] & ~( f.bit_slice[(i+b)%N] & ~f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + case MAOI1_AND2_ANDN2L : + case MAOI1_ANDN2R_ORN2R : + case MOAI1_NAND2_ANDN2L : + case MOAI1_ORN2L_ORN2R : + case XOR2_AND2_ANDN2L : + case XOR2_ANDN2R_ORN2R : fun_tmp.bit_slice[i] = ( f.bit_slice[(i+a)%N] & ~( f.bit_slice[(i+b)%N] | ~f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + ///////////NAND2_NAND2 ~(A & ~(B & C)) = ~A | (B & C) ORN2L_AND2 + case MAOI1_NAND2_NAND2 : + case MAOI1_ORN2L_AND2 : + case MOAI1_AND2_NAND2 : + case MOAI1_ANDN2R_AND2 : + case XOR2_ORN2L_AND2 : fun_tmp.bit_slice[i] = ( ~f.bit_slice[(i+a)%N] | ( f.bit_slice[(i+b)%N] & f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + case MOAI1_AND2_NOR2 : + case MAOI1_NAND2_NOR2 : + case MOAI1_ANDN2R_OR2 : + case MAOI1_ORN2L_OR2 : + case XOR2_ORN2L_OR2 : fun_tmp.bit_slice[i] = ( ~f.bit_slice[(i+a)%N] | ( f.bit_slice[(i+b)%N] | f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + ////NAND2_ORN2R ~(A & (B | ~C)) = ~A | (~B & C) ORN2L_ANDN2L + case MAOI1_NAND2_ORN2R : + case MAOI1_ORN2L_ANDN2L : + case MOAI1_AND2_ORN2R : + case MOAI1_ANDN2R_ANDN2L : + case XOR2_ORN2L_ANDN2L : fun_tmp.bit_slice[i] = ( ~f.bit_slice[(i+a)%N] | (~f.bit_slice[(i+b)%N] & f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + ////NAND2_ORN2L ~(A & (~B | C)) = ~A | (B & ~C) ORN2L_ANDN2R + case MAOI1_NAND2_ORN2L : + case MAOI1_ORN2L_ANDN2R : + case MOAI1_AND2_ORN2L : + case MOAI1_ANDN2R_ANDN2R : + case XOR2_ORN2L_ANDN2R : fun_tmp.bit_slice[i] = ( ~f.bit_slice[(i+a)%N] | ( f.bit_slice[(i+b)%N] & ~f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + ////NAND2_ANDN2R ~(A & (B & ~C)) = ~A | (~B | C) ORN2L_ORN2L + case MAOI1_NAND2_ANDN2R : + case MAOI1_ORN2L_ORN2L : + case MOAI1_AND2_ANDN2R : + case MOAI1_ANDN2R_ORN2L : + case XOR2_ORN2L_ORN2L : fun_tmp.bit_slice[i] = ( ~f.bit_slice[(i+a)%N] | (~f.bit_slice[(i+b)%N] | f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + ////NAND2_ANDN2L ~(A & (~B & C)) = ~A | (B | ~C) ORN2L_ORN2R + case MOAI1_AND2_ANDN2L : + case MOAI1_ANDN2R_ORN2R : + case MAOI1_NAND2_ANDN2L : + case MAOI1_ORN2L_ORN2R : + case XOR2_ORN2L_ORN2R : fun_tmp.bit_slice[i] = ( ~f.bit_slice[(i+a)%N] | ( f.bit_slice[(i+b)%N] | ~f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + case MAOI1_OR2_NAND2 : + case MAOI1_ORN2R_AND2 : + case MOAI1_NOR2_NAND2 : + case MOAI1_ANDN2L_AND2 : + case XOR2_ORN2R_AND2 : fun_tmp.bit_slice[i] = ( f.bit_slice[(i+a)%N] | ~( f.bit_slice[(i+b)%N] & f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + + case MOAI1_NOR2_NOR2 : + case MAOI1_OR2_NOR2 : + case MOAI1_ANDN2L_OR2 : + case MAOI1_ORN2R_OR2 : + case XOR2_ORN2R_OR2 : fun_tmp.bit_slice[i] = ( f.bit_slice[(i+a)%N] | ~( f.bit_slice[(i+b)%N] | f.bit_slice[(i+c)%N]) )^f.bit_slice[i];break; + } + fun_tmp.prev = f.bit_slice[i]; + fun_tmp.info_line = (uint8_t)i; + fun_tmp.info_op = (uint8_t)operation; + fun_tmp.sort(); + return fun_tmp; + }; + + switch (operation) + { + case MOAI1_NAND2_OR2 : + case MAOI1_AND2_OR2 : + case MOAI1_ORN2L_NOR2 : + case MAOI1_ANDN2R_NOR2 : + case XOR2_AND2_OR2 : + + case MOAI1_NOR2_AND2 : + case MAOI1_OR2_AND2 : + case MOAI1_ANDN2L_NAND2 : + case MAOI1_ORN2R_NAND2 : + case XOR2_OR2_AND2 : + + case MOAI1_AND2_OR2 : + case MAOI1_NAND2_OR2 : + case MOAI1_ANDN2R_NOR2 : + case MAOI1_ORN2L_NOR2 : + + case MOAI1_OR2_AND2 : + case MAOI1_NOR2_AND2 : + case MOAI1_ORN2R_NAND2 : + case MAOI1_ANDN2L_NAND2 : + + case MOAI1_AND2_NOR2 : + case MAOI1_NAND2_NOR2 : + case MOAI1_ANDN2R_OR2 : + case MAOI1_ORN2L_OR2 : + case XOR2_ORN2L_OR2 : + + case MOAI1_NAND2_ANDN2R : + case MOAI1_ORN2L_ORN2L : + case MAOI1_AND2_ANDN2R : + case MAOI1_ANDN2R_ORN2L : + case XOR2_AND2_ANDN2R : + case XOR2_ANDN2R_ORN2L : + + case MOAI1_NAND2_ORN2L : + case MOAI1_ORN2L_ANDN2R : + case MAOI1_AND2_ORN2L : + case MAOI1_ANDN2R_ANDN2R : + case XOR2_AND2_ORN2L : + case XOR2_ANDN2R_ANDN2R : + + case MOAI1_NAND2_ORN2R : + case MOAI1_ORN2L_ANDN2L : + case MAOI1_AND2_ORN2R : + case MAOI1_ANDN2R_ANDN2L : + case XOR2_AND2_ORN2R : + case XOR2_ANDN2R_ANDN2L : + + case MOAI1_NOR2_ANDN2L : + case MOAI1_ANDN2L_ORN2R : + case MAOI1_OR2_ANDN2L : + case MAOI1_ORN2R_ORN2R : + case XOR2_OR2_ANDN2L : + case XOR2_ORN2R_ORN2R : + + case MOAI1_NOR2_ANDN2R : + case MOAI1_ANDN2L_ORN2L : + case MAOI1_OR2_ANDN2R : + case MAOI1_ORN2R_ORN2L : + case XOR2_OR2_ANDN2R : + case XOR2_ORN2R_ORN2L : + + case MOAI1_NOR2_ORN2L : + case MOAI1_ANDN2L_ANDN2R : + case MAOI1_OR2_ORN2L : + case MAOI1_ORN2R_ANDN2R : + case XOR2_OR2_ORN2L : + case XOR2_ORN2R_ANDN2R : + + case MOAI1_NOR2_ORN2R : + case MOAI1_ANDN2L_ANDN2L : + case MAOI1_OR2_ORN2R : + case MAOI1_ORN2R_ANDN2L : + case XOR2_OR2_ORN2R : + case XOR2_ORN2R_ANDN2L : + + case MOAI1_OR2_NAND2 : + case MAOI1_NOR2_NAND2 : + case MOAI1_ORN2R_AND2 : + case MAOI1_ANDN2L_AND2 : + case XOR2_ANDN2L_AND2 : + + case MOAI1_OR2_NOR2 : + case MAOI1_NOR2_NOR2 : + case MOAI1_ORN2R_OR2 : + case MAOI1_ANDN2L_OR2 : + case XOR2_ANDN2L_OR2 : + + case MOAI1_OR2_ORN2R : + case MOAI1_ORN2R_ANDN2L : + case MAOI1_NOR2_ORN2R : + case MAOI1_ANDN2L_ANDN2L : + case XOR2_ANDN2L_ANDN2L : + + case MOAI1_OR2_ORN2L : + case MOAI1_ORN2R_ANDN2R : + case MAOI1_NOR2_ORN2L : + case MAOI1_ANDN2L_ANDN2R : + case XOR2_ANDN2L_ANDN2R : + + case MOAI1_OR2_ANDN2R : + case MOAI1_ORN2R_ORN2L : + case MAOI1_NOR2_ANDN2R : + case MAOI1_ANDN2L_ORN2L : + case XOR2_ANDN2L_ORN2L : + + case MOAI1_OR2_ANDN2L : + case MOAI1_ORN2R_ORN2R : + case MAOI1_NOR2_ANDN2L : + case MAOI1_ANDN2L_ORN2R : + case XOR2_ANDN2L_ORN2R : + + case MOAI1_NAND2_NAND2 : + case MAOI1_AND2_NAND2 : + case MOAI1_ORN2L_AND2 : + case MAOI1_ANDN2R_AND2 : + case XOR2_ANDN2R_AND2 : + + case MOAI1_NAND2_NOR2 : + case MAOI1_AND2_NOR2 : + case MOAI1_ORN2L_OR2 : + case MAOI1_ANDN2R_OR2 : + case XOR2_ANDN2R_OR2 : + + case MOAI1_NAND2_ANDN2L : + case MOAI1_ORN2L_ORN2R : + case MAOI1_AND2_ANDN2L : + case MAOI1_ANDN2R_ORN2R : + case XOR2_AND2_ANDN2L : + case XOR2_ANDN2R_ORN2R : + + case MOAI1_AND2_NAND2 : + case MAOI1_NAND2_NAND2 : + case MOAI1_ANDN2R_AND2 : + case MAOI1_ORN2L_AND2 : + case XOR2_ORN2L_AND2 : + + case MOAI1_AND2_ORN2R : + case MOAI1_ANDN2R_ANDN2L : + case MAOI1_NAND2_ORN2R : + case MAOI1_ORN2L_ANDN2L : + case XOR2_ORN2L_ANDN2L : + + case MOAI1_AND2_ORN2L : + case MOAI1_ANDN2R_ANDN2R : + case MAOI1_NAND2_ORN2L : + case MAOI1_ORN2L_ANDN2R : + case XOR2_ORN2L_ANDN2R : + + case MOAI1_AND2_ANDN2R : + case MOAI1_ANDN2R_ORN2L : + case MAOI1_NAND2_ANDN2R : + case MAOI1_ORN2L_ORN2L : + case XOR2_ORN2L_ORN2L : + + case MOAI1_AND2_ANDN2L : + case MOAI1_ANDN2R_ORN2R : + case MAOI1_NAND2_ANDN2L : + case MAOI1_ORN2L_ORN2R : + case XOR2_ORN2L_ORN2R : + + case MOAI1_NOR2_NAND2 : + case MAOI1_OR2_NAND2 : + case MOAI1_ANDN2L_AND2 : + case MAOI1_ORN2R_AND2 : + case XOR2_ORN2R_AND2 : + + case MOAI1_NOR2_NOR2 : + case MAOI1_OR2_NOR2 : + case MOAI1_ANDN2L_OR2 : + case MAOI1_ORN2R_OR2 : + case XOR2_ORN2R_OR2 : + for (int i = 0; i < N; i++) + { + for (int j = 1; j < N; j++ ) + { + for (int k = j + 1; k < j + N - 1; k++) + { + if(k != N) + { + for (int u = k + 1; u < j + N; u++) + { + if (u != N) + { + v->push_back(triple_gates(f, i, j, k, u, operation)); + } + } + } + } + } + } + break; + } +} + + +template +void lighter::bool_op_fun(int op_id, function_t f, vector > *v) +{ + switch (op_id) + { + case NOT1 : + case XOR2 : + case XNOR2 : + case MAOI1 : + case MOAI1 : bool_op_simple_gates(f, v, op_id); break; + + case MOAI1_NAND2 : + case MOAI1_NOR2 : + case MOAI1_AND2 : + case MOAI1_OR2 : + case MAOI1_NAND3 : + case MAOI1_NOR3 : + case MOAI1_NAND3 : + case MOAI1_NOR3 : + case XOR2_AND2 : + case XOR2_OR2 : + case MAOI1_ANDN2L : + case MAOI1_ANDN2R : + case MAOI1_ORN2L : + case MAOI1_ORN2R : + case MOAI1_ANDN2L : + case MOAI1_ANDN2R : + case MOAI1_ORN2L : + case MOAI1_ORN2R : + case XOR2_ANDN2L : + case XOR2_ANDN2R : + case XOR2_ORN2L : + case XOR2_ORN2R : + case XOR2_OR2_OR2 : + + case MOAI1_NAND2_AND2 : + case MAOI1_AND2_AND2 : + case MOAI1_ORN2L_NAND2 : + case MAOI1_ANDN2R_NAND2 : + case XOR2_AND2_AND2 : + + case MAOI1_NAND2 : + case MAOI1_NOR2 : + case MAOI1_AND2 : + case MAOI1_OR2 : + case MAOI1_AND3 : + + case MAOI1_OR3 : + case MOAI1_NOR2_OR2 : + case MAOI1_OR2_OR2 : + case MOAI1_AND3 : + case MAOI1_NAND2_AND2 : + case MOAI1_AND2_AND2 : + case MOAI1_OR3 : + case MAOI1_NOR2_OR2 : + case MOAI1_OR2_OR2 : + case MOAI1_ANDN2L_NOR2 : + case MAOI1_ORN2R_NOR2 : + case MOAI1_ORN2R_NOR2 : + case MAOI1_ANDN2L_NOR2 : + case MOAI1_ANDN2R_NAND2: + case MAOI1_ORN2L_NAND2 : bool_op_double_gates(f, v, op_id); break; + + case MOAI1_AND2_OR2 : + case MAOI1_NAND2_OR2 : + case MOAI1_ANDN2R_NOR2 : + case MAOI1_ORN2L_NOR2 : + + case MOAI1_OR2_AND2 : + case MAOI1_NOR2_AND2 : + case MOAI1_ORN2R_NAND2 : + case MAOI1_ANDN2L_NAND2 : + + case MOAI1_AND2_NOR2 : + case MAOI1_NAND2_NOR2 : + case MOAI1_ANDN2R_OR2 : + case MAOI1_ORN2L_OR2 : + case XOR2_ORN2L_OR2 : + + case MOAI1_NAND2_OR2 : + case MAOI1_AND2_OR2 : + case MOAI1_ORN2L_NOR2 : + case MAOI1_ANDN2R_NOR2 : + case XOR2_AND2_OR2 : + + case MOAI1_NOR2_AND2 : + case MAOI1_OR2_AND2 : + case MOAI1_ANDN2L_NAND2 : + case MAOI1_ORN2R_NAND2 : + case XOR2_OR2_AND2 : + + case MOAI1_NAND2_ANDN2R : + case MOAI1_ORN2L_ORN2L : + case MAOI1_AND2_ANDN2R : + case MAOI1_ANDN2R_ORN2L : + case XOR2_AND2_ANDN2R : + case XOR2_ANDN2R_ORN2L : + + case MOAI1_NAND2_ORN2L : + case MOAI1_ORN2L_ANDN2R : + case MAOI1_AND2_ORN2L : + case MAOI1_ANDN2R_ANDN2R : + case XOR2_AND2_ORN2L : + case XOR2_ANDN2R_ANDN2R : + + case MOAI1_NAND2_ORN2R : + case MOAI1_ORN2L_ANDN2L : + case MAOI1_AND2_ORN2R : + case MAOI1_ANDN2R_ANDN2L : + case XOR2_AND2_ORN2R : + case XOR2_ANDN2R_ANDN2L : + + case MOAI1_NOR2_ANDN2L : + case MOAI1_ANDN2L_ORN2R : + case MAOI1_OR2_ANDN2L : + case MAOI1_ORN2R_ORN2R : + case XOR2_OR2_ANDN2L : + case XOR2_ORN2R_ORN2R : + + case MOAI1_NOR2_ANDN2R : + case MOAI1_ANDN2L_ORN2L : + case MAOI1_OR2_ANDN2R : + case MAOI1_ORN2R_ORN2L : + case XOR2_OR2_ANDN2R : + case XOR2_ORN2R_ORN2L : + + case MOAI1_NOR2_ORN2L : + case MOAI1_ANDN2L_ANDN2R : + case MAOI1_OR2_ORN2L : + case MAOI1_ORN2R_ANDN2R : + case XOR2_OR2_ORN2L : + case XOR2_ORN2R_ANDN2R : + + case MOAI1_NOR2_ORN2R : + case MOAI1_ANDN2L_ANDN2L : + case MAOI1_OR2_ORN2R : + case MAOI1_ORN2R_ANDN2L : + case XOR2_OR2_ORN2R : + case XOR2_ORN2R_ANDN2L : + + case MOAI1_OR2_NAND2 : + case MAOI1_NOR2_NAND2 : + case MOAI1_ORN2R_AND2 : + case MAOI1_ANDN2L_AND2 : + case XOR2_ANDN2L_AND2 : + + case MOAI1_OR2_NOR2 : + case MAOI1_NOR2_NOR2 : + case MOAI1_ORN2R_OR2 : + case MAOI1_ANDN2L_OR2 : + case XOR2_ANDN2L_OR2 : + + case MOAI1_OR2_ORN2R : + case MOAI1_ORN2R_ANDN2L : + case MAOI1_NOR2_ORN2R : + case MAOI1_ANDN2L_ANDN2L : + case XOR2_ANDN2L_ANDN2L : + + case MOAI1_OR2_ORN2L : + case MOAI1_ORN2R_ANDN2R : + case MAOI1_NOR2_ORN2L : + case MAOI1_ANDN2L_ANDN2R : + case XOR2_ANDN2L_ANDN2R : + + case MOAI1_OR2_ANDN2R : + case MOAI1_ORN2R_ORN2L : + case MAOI1_NOR2_ANDN2R : + case MAOI1_ANDN2L_ORN2L : + case XOR2_ANDN2L_ORN2L : + + case MOAI1_OR2_ANDN2L : + case MOAI1_ORN2R_ORN2R : + case MAOI1_NOR2_ANDN2L : + case MAOI1_ANDN2L_ORN2R : + case XOR2_ANDN2L_ORN2R : + + case MOAI1_NAND2_NAND2 : + case MAOI1_AND2_NAND2 : + case MOAI1_ORN2L_AND2 : + case MAOI1_ANDN2R_AND2 : + case XOR2_ANDN2R_AND2 : + + case MOAI1_NAND2_NOR2 : + case MAOI1_AND2_NOR2 : + case MOAI1_ORN2L_OR2 : + case MAOI1_ANDN2R_OR2 : + case XOR2_ANDN2R_OR2 : + + case MOAI1_NAND2_ANDN2L : + case MOAI1_ORN2L_ORN2R : + case MAOI1_AND2_ANDN2L : + case MAOI1_ANDN2R_ORN2R : + case XOR2_AND2_ANDN2L : + case XOR2_ANDN2R_ORN2R : + + case MOAI1_AND2_NAND2 : + case MAOI1_NAND2_NAND2 : + case MOAI1_ANDN2R_AND2 : + case MAOI1_ORN2L_AND2 : + case XOR2_ORN2L_AND2 : + + case MOAI1_AND2_ORN2R : + case MOAI1_ANDN2R_ANDN2L : + case MAOI1_NAND2_ORN2R : + case MAOI1_ORN2L_ANDN2L : + case XOR2_ORN2L_ANDN2L : + + case MOAI1_AND2_ORN2L : + case MOAI1_ANDN2R_ANDN2R : + case MAOI1_NAND2_ORN2L : + case MAOI1_ORN2L_ANDN2R : + case XOR2_ORN2L_ANDN2R : + + case MOAI1_AND2_ANDN2R : + case MOAI1_ANDN2R_ORN2L : + case MAOI1_NAND2_ANDN2R : + case MAOI1_ORN2L_ORN2L : + case XOR2_ORN2L_ORN2L : + + case MOAI1_AND2_ANDN2L : + case MOAI1_ANDN2R_ORN2R : + case MAOI1_NAND2_ANDN2L : + case MAOI1_ORN2L_ORN2R : + case XOR2_ORN2L_ORN2R : + + case MOAI1_NOR2_NAND2 : + case MAOI1_OR2_NAND2 : + case MOAI1_ANDN2L_AND2 : + case MAOI1_ORN2R_AND2 : + case XOR2_ORN2R_AND2 : + + case MOAI1_NOR2_NOR2 : + case MAOI1_OR2_NOR2 : + case MOAI1_ANDN2L_OR2 : + case MAOI1_ORN2R_OR2 : + case XOR2_ORN2R_OR2 : + bool_op_triple_gates(f, v, op_id); break; + } +} + +#endif // LIGHTER_BOOL_OP_H__ \ No newline at end of file diff --git a/lighter_impl_info.hpp b/lighter_impl_info.hpp new file mode 100644 index 0000000..1392c8d --- /dev/null +++ b/lighter_impl_info.hpp @@ -0,0 +1,510 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#ifndef LIGHTER_IMPL_INFO_H__ +#define LIGHTER_IMPL_INFO_H__ + +#include "func.hpp" + +using namespace Peigen; +using namespace Peigen::weight; +using namespace std; + +template +void lighter::update_unsorted_function(function_t f) +{ + auto get_diff_index = [](function_t f1, function_t f2) + { + int i = 0; + int j = 0; + bool found = false; + while(i < N) + { + while(j < N) + { + if(f1.bit_slice[i] == f2.bit_slice[j]) + { + found = true; + break; + } + j++; + } + if(found == false) return i; + found = false; + i++; + j = 0; + } + return -1; + }; + + int i = get_diff_index(f, unsorted_function); + int j = get_diff_index(unsorted_function, f); + + unsorted_function.bit_slice[j] = f.bit_slice[i]; + + unsorted_function.prev = f.prev; + unsorted_function.info_line = f.info_line; + unsorted_function.info_op = f.info_op; + + bit_slice_l_t mod = f.bit_slice[f.info_line]; + + for(int i = 0; i < N; i++) + if(unsorted_function.bit_slice[i] == mod) unsorted_function.info_line = i; +} + +template +string lighter::get_permut(function_t f1, function_t f2, char a, char b) +{ + auto get_index = [](int n, function_t f1, function_t f2) + { + int i = 0; + while( i < N ) + { + if(f1.bit_slice[n] == f2.bit_slice[i]) return i; + i++; + } + return -1; + }; + + string s; + int i = 0; + while( i < N ) + { + s = s + a + "[" + to_string(i) + "] = "; + s = s + b + "[" + to_string(get_index(i, f2, f1)) + "];\n"; + i++; + } + return s; +} + +template +void lighter::get_string_impl(string *s, function_t f, bool reverse) +{ + if( !reverse ) s->insert(0, bool_op_to_string(f, f.info_op, reverse)); + else s->append(bool_op_to_string(f, f.info_op, reverse)); +} + +template +int lighter::get_semi_impl(function_t f, + int count_vlist, + map > > *g, + string *s, + bool reverse) +{ + int cost = bool_op_cost(f.info_op); + auto found = g->at(0).find(f); + unsorted_function = f; + while(count_vlist >= 0) + { + update_unsorted_function(f); + get_string_impl(s, unsorted_function, reverse); + f.bit_slice[f.info_line] = f.prev; + f.sort(); + count_vlist -= bool_op_cost(f.info_op); + if( count_vlist == 0) break; + found = g->at(count_vlist).find(f); + f = *found; + cost += bool_op_cost(f.info_op); + } + + unsorted_function.bit_slice[unsorted_function.info_line] = unsorted_function.prev; + + if( !reverse ) + { + s->insert(0, "\n" + get_permut(start, unsorted_function, 'F', 'X') + "\n"); + s->insert(0, "// from : " + start.to_string() + "\n"); + } + else + { + s->append("\n" + get_permut(unsorted_function, unsorted_arrival, 'X', 'F') + "\n"); + s->append("// to : " + unsorted_arrival.to_string() + "\n"); + } + + return cost; +} + +template +void lighter::get_implementation(function_t f1, + function_t f2, + int v, + map > > *g1, + map > > *g2) +{ + static int impl_number = 0; + if(instance_name != old_instance_name) + { + impl_number = 0; + old_instance_name = instance_name; + } + cost_1 = 0; + cost_2 = 0; + + string s1, s2; + if(g1 == &f1_succ) + { + cost_1 = get_semi_impl(f1, count_list+bool_op_cost(f1.info_op), g1, &s1, false); + cost_2 = get_semi_impl(f2, v, g2, &s2, true); + } + else + { + cost_1 = get_semi_impl(f1, count_list+bool_op_cost(f1.info_op), g1, &s1, true); + cost_2 = get_semi_impl(f2, v, g2, &s2, false); + } + unsorted_function.sort(); + if(cost_1+cost_2 < shortest_path) + { + cout << "Generating implementation " << instance_name << " " << impl_number << endl; + shortest_path = cost_1+cost_2; + ofstream impl(imp_info + "_" + instance_name + "_" + to_string(impl_number) + ".c"); + impl << "// Implementation info.: " << imp_info << endl; + if(unsorted_function == start) impl << s2 << s1; + else if(unsorted_function == arrival) impl << s1 << s2; + impl << "// Cost : " << cost_1 << " + " << cost_2 << " -> " << (double)(cost_1 + cost_2)/100 << "GE" << endl; + cout << "(cost : " << cost_1 << " + " << cost_2 << " -> " << (double)(cost_1 + cost_2)/100 << "GE)" << endl; + + impl.close(); + impl_number ++; + } +} + +template +int lighter::get_semi_impl_concatenate(function_t f, + int count_vlist, string *s, + bool reverse, int PE[]) +{ + int cost = bool_op_cost(f.info_op); + auto found = f1_succ.at(0).find(f); + unsorted_function = f; + while(count_vlist >= 0) + { + update_unsorted_function(f); + get_string_impl(s, unsorted_function, reverse); + f.bit_slice[f.info_line] = f.prev; + f.sort(); + count_vlist -= bool_op_cost(f.info_op); + if( count_vlist == 0) break; + found = f1_succ.at(count_vlist).find(f); + f = *found; + cost += bool_op_cost(f.info_op); + } + + unsorted_function.bit_slice[unsorted_function.info_line] = unsorted_function.prev; + + if( !reverse ) + { + s->insert(0, "\n" + get_permut(start, unsorted_function, 'F', 'X') + "\n"); + s->insert(0, "// from : " + start.to_string() + "\n"); + + for (int i = 0; i < N; i++) s->append("X[" + to_string(i) + "] = F[" + to_string(PE[i]) + "];\n"); + for (int i = 0; i < N; i++) s->append("F[" + to_string(i) + "] = X[" + to_string(i) + "];\n"); + } + else + { + s->append("\n" + get_permut(unsorted_function, start, 'X', 'F') + "\n"); + for (int i = 0; i < N; i++) s->append("F[" + to_string(i) + "] = X[" + to_string(PE[i]) + "];\n"); + for (int i = 0; i < N; i++) s->append("X[" + to_string(i) + "] = F[" + to_string(i) + "];\n"); + + s->append("// to : " + unsorted_arrival.to_string() + "\n"); + } + + return cost; +} + +template +void lighter::get_implementation_concatenate(function_t f1, int v1, function_t f2, int v2, int PEm[]) +{ + static int impl_number = 0; + if(instance_name != old_instance_name) + { + impl_number = 0; + old_instance_name = instance_name; + } + cost_1 = 0; + cost_2 = 0; + + string s1, s2; + cost_1 = get_semi_impl_concatenate(f1, v1, &s1, false, PEm); + cost_2 = get_semi_impl_concatenate(f2, v2, &s2, true, PEo); + + unsorted_function.sort(); + + cout << "Generating implementation " << instance_name << " " << impl_number << endl; + if (shortest_path != cost_1+cost_2) + { + cout << "There must be wrong!!!" << endl; + } + ofstream impl(imp_info + "_" + instance_name + "_" + to_string(impl_number) + ".c"); + impl << "// Implementation info.: " << imp_info << endl; + impl << s1 << s2; + impl << "// Cost : " << cost_1 << " + " << cost_2 << " -> " << (double)(cost_1 + cost_2)/100 << "GE" << endl; + cout << "(cost : " << cost_1 << " + " << cost_2 << " -> " << (double)(cost_1 + cost_2)/100 << "GE)" << endl; + + impl.close(); + impl_number ++; +} + +template +int lighter::get_semi_impl_good( + function_t f, + int count_vlist, + string *s) +{ + function_t f_org = f; + int cost = bool_op_cost(f.info_op); + auto found = f1_succ.at(0).find(f); + unsorted_function = f; + while(count_vlist >= 0) + { + update_unsorted_function(f); + get_string_impl(s, unsorted_function, false); + f.bit_slice[f.info_line] = f.prev; + f.sort(); + count_vlist -= bool_op_cost(f.info_op); + if( count_vlist == 0) break; + found = f1_succ.at(count_vlist).find(f); + f = *found; + cost += bool_op_cost(f.info_op); + } + + unsorted_function.bit_slice[unsorted_function.info_line] = unsorted_function.prev; + + s->insert(0, "\n" + get_permut(start, unsorted_function, 'F', 'X') + "\n"); + s->insert(0, "// from : " + start.to_string() + "\n"); + + for (int i = 0; i < N; i++) s->append("X[" + to_string(i) + "] = F[" + to_string(i) + "];\n"); + s->append("// to : " + f_org.to_string() + "\n"); + + return cost; +} + +template +int lighter::get_semi_impl_good( + function_t f, + int count_vlist, string *s, + const function_t & f_p, int pi) +{ + function_t f_org = f; + int cost = bool_op_cost(f.info_op); + auto found = f1_succ.at(0).find(f); + unsorted_function = f; + while(count_vlist >= 0) + { + update_unsorted_function(f); + get_string_impl(s, unsorted_function, false); + f.bit_slice[f.info_line] = f.prev; + f.sort(); + count_vlist -= bool_op_cost(f.info_op); + if( count_vlist == 0) break; + found = f1_succ.at(count_vlist).find(f); + f = *found; + cost += bool_op_cost(f.info_op); + } + + unsorted_function.bit_slice[unsorted_function.info_line] = unsorted_function.prev; + + s->insert(0, "\n" + get_permut(start, unsorted_function, 'F', 'X') + "\n"); + s->insert(0, "// from : " + start.to_string() + "\n"); + s->append("// to : " + f_org.to_string() + "\n"); + for (int i = 0; i < N; i++) s->append("X[" + to_string(i) + "] = F[" + to_string(PE.idx[pi][i]) + "];\n"); + s->append("// perm : " + f_p.to_string() + "\n"); + + return cost; +} + +template +void lighter::get_implementation_good( + function_t f1, int v1, + const function_t & fp, int pi, + function_t f2, int v2, + function_t f3, int good_idx) +{ + int tid = omp_get_thread_num(); + string outfn = imp_info + "__" + "statistics_Part" + to_string(tid) + ".csv"; + ifstream inf(outfn.c_str()); + string thisSbox = f1.to_string(); + string existSbox; + for (string line; getline(inf, line);) + { + istringstream is_line(line); + getline(is_line, existSbox, ','); + getline(is_line, existSbox, ','); + getline(is_line, existSbox, ','); + if (existSbox == thisSbox) + { + inf.close(); + return; // A duplicate S-box + } + } + inf.close(); + + string name = to_string(good_idx); + string is_best = "-"; + int v = v1 + v2; + if (v <= (2*(l+min_GE) - max_GE)) is_best = "Best"; + else is_best = "Unknown"; + evaluator Eva(f3, name, v, is_best); + + ofstream outf(outfn.c_str(), ios::app); + outf << Eva.show(); + outf.close(); + + string fn_prefix = Eva.hash(); + + #pragma omp critical + { + struct stat dirstate; + if (stat(fn_prefix.c_str(), &dirstate) != 0 ) + { + const int dir_err = mkdir(fn_prefix.c_str(), 0777); + if (-1 == dir_err) cout << "Error creating directory!" << endl; + } + + string subSboxesfilen = fn_prefix + "/" + sboxesfile; + ofstream sboxf(subSboxesfilen, ios::app); + sboxf << name << "," << f3.LUT_to_string() << endl; + sboxf.close(); + + function_t f3PErep = f3.PE_representative(); + string f3PErep_str = fn_prefix + "/" + f3PErep.to_string() + ".c"; + struct stat filestate; + if (stat(f3PErep_str.c_str(), &filestate) != 0) + { + good_PE_number++; + string PEsboxfn = imp_info + "_PEsboxes.txt"; + ofstream PEsboxf(PEsboxfn.c_str(), ios::app); + PEsboxf << good_PE_number << "," << f3.LUT_to_string() << endl; + PEsboxf.close(); + + cost_1 = 0; + cost_2 = 0; + string s1, s2; + + cost_1 = get_semi_impl_good(f1, v1, &s1, fp, pi); + cost_2 = get_semi_impl_good(f2, v2, &s2); + s2.append("// final : " + f3.to_string() + "\n"); + + ofstream impl(f3PErep_str); + impl << "// Implementation info.: " << imp_info << endl; + impl << s1 << s2; + impl << "// Cost : " << cost_1 << " + " << cost_2 << " -> " << (double)(cost_1 + cost_2)/100 << "GE" << endl; + impl.close(); + + cout << "Generating implementation " << name << endl; + cout << "(cost : " << cost_1 << " + " << cost_2 << " -> " << (double)(cost_1 + cost_2)/100 << "GE)" << endl; + } + } +} + +template +void lighter::get_implementation_good( + function_t f1, int v, int good_idx) +{ + int tid = omp_get_thread_num(); + string outfn = imp_info + "__" + "statistics_Part" + to_string(tid) + ".csv"; + ifstream inf(outfn.c_str()); + string thisSbox = f1.to_string(); + string existSbox; + for (string line; getline(inf, line);) + { + istringstream is_line(line); + getline(is_line, existSbox, ','); + getline(is_line, existSbox, ','); + getline(is_line, existSbox, ','); + if (existSbox == thisSbox) + { + inf.close(); + return; // A duplicate S-box + } + } + inf.close(); + + string name = to_string(good_idx); + string is_best = "-"; + if (v <= l+min_GE) is_best = "Best"; + else is_best = "Unknown"; + evaluator Eva(f1, name, v, is_best); + + ofstream outf(outfn.c_str(), ios::app); + outf << Eva.show(); + outf.close(); + + string fn_prefix = Eva.hash(); + + #pragma omp critical + { + struct stat dirstate; + if (stat(fn_prefix.c_str(), &dirstate) != 0 ) + { + const int dir_err = mkdir(fn_prefix.c_str(), 0777); + if (-1 == dir_err) cout << "Error creating directory!" << endl; + } + + string subSboxesfilen = fn_prefix + "/" + sboxesfile; + ofstream sboxf(subSboxesfilen, ios::app); + sboxf << name << "," << f1.LUT_to_string() << endl; + sboxf.close(); + + function_t f1PErep = f1.PE_representative(); + string f1PErep_str = fn_prefix + "/" + f1PErep.to_string() + ".c"; + struct stat filestate; + if (stat(f1PErep_str.c_str(), &filestate) != 0) + { + good_PE_number++; + string PEsboxfn = imp_info + "_PEsboxes.txt"; + ofstream PEsboxf(PEsboxfn.c_str(), ios::app); + PEsboxf << good_PE_number << "," << f1.LUT_to_string() << endl; + PEsboxf.close(); + + cost_1 = 0; + cost_2 = 0; + string s1, s2; + + cost_1 = get_semi_impl_good(f1, v, &s1); + + ofstream impl(f1PErep_str); + impl << "// Implementation info.: " << imp_info << endl; + impl << s1; + impl << "// Cost : " << cost_1 << " + " << cost_2 << " -> " << (double)(cost_1 + cost_2)/100 << "GE" << endl; + impl.close(); + + cout << "Generating implementation " << name << endl; + cout << "(cost : " << cost_1 << " + " << cost_2 << " -> " << (double)(cost_1 + cost_2)/100 << "GE)" << endl; + } + } +} + +#endif // LIGHTER_IMPL_INFO_H__ diff --git a/lighter_mitm.hpp b/lighter_mitm.hpp new file mode 100644 index 0000000..54b57dd --- /dev/null +++ b/lighter_mitm.hpp @@ -0,0 +1,1416 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#ifndef LIGHTER_MITM_H__ +#define LIGHTER_MITM_H__ + +using namespace Peigen; +using namespace Peigen::weight; +using namespace std; + +#define MAX_VEC_NODES (max_nodes>>1UL) + +#define pre_parse_args() \ + int myargc = 0; \ + char* myargv[50]; \ + string args_tmp = "x " + args; \ + char * args_str = new char [args_tmp.length() + 1]; \ + strcpy(args_str, args_tmp.c_str()); \ + char * p = strtok(args_str," "); \ + while( p != 0) \ + { \ + myargv[myargc] = p; \ + p = strtok(NULL, " "); \ + myargc++; \ + } \ + myargv[myargc] = NULL; \ + int opt; \ + optind = 0; + + +#define post_parse_args() \ + delete [] args_str; \ + max_nodes = max_ram * 100000000UL / sizeof(function_t); \ + if (gate_maoi1 == 1) gate_xor2 = 0; \ + if (gate_moai1 == 1) gate_xnor2 = 0; + + +template +void lighter::pre_compute(string args) +{ + clock_t t1 = clock(); + + pre_parse_args(); + while ((opt = getopt_long(myargc, myargv, "c:z:vap:f:r:", longopts, NULL)) != EOF) + { + switch(opt) + { + case 'a': all_gates(); break; + case 'v': verbose = true; break; + case 'c': pre_l = abs(atoi(optarg))*100; cout << "Precompute Limit : "<< optarg << endl; break; + case 'z': cntpoint = abs(atoi(optarg))*100; cout << "Continue from breakpoint : "<< optarg << endl; break; + case 'p': omp_nb_threads = (atoi(optarg)); cout << "Thread number : "<< omp_nb_threads< +void lighter::pre_computing() +{ + function_t f1 = function_t::INPUT_DEFAULT(); + f1.sort(); f1.bit_slice_to_LUT(); + f1_succ.clear(); + f2_succ.clear(); + + if (cntpoint != -1) + { + read_pre_bin(cntpoint); + count_list = cntpoint; + while (count_list <= pre_l) + { + expand(&f1_succ, &f2_succ, count_list); + if (verbose) cout << count_list << endl; + write_pre_bin(count_list+min_GE); + count_list++; + } + } + else + { + f1_succ.insert(pair > >(0, {f1})); + write_pre_bin(0); + count_list = 0; + while (count_list <= pre_l) + { + expand(&f1_succ, &f2_succ, count_list); + if (verbose) cout << count_list << endl; + write_pre_bin(count_list+min_GE); + count_list++; + } + } +} + +template +void lighter::write_pre_bin(int lambda) +{ + ofstream fout; + size_t function_s = sizeof(function_t); + auto map_it = f1_succ.find(lambda); + while (map_it != f1_succ.end()) + { + string pre_filen = "pre_" + imp_info + "_" + to_string(map_it->first) + ".bin"; + fout.open(pre_filen, ios::binary); + for (auto set_it = map_it->second.begin(); set_it != map_it->second.end(); set_it++) + { + fout.write((char *)&(*set_it), function_s); + } + fout.close(); + map_it++; + } +} + +template +void lighter::read_pre_bin(int completed) +{ + ifstream fin; + function_t f_t; + int lambda = 0; + size_t function_s = sizeof(function_t); + + f1_succ.clear(); + while (lambda <= (completed + max_GE)) + { + string pre_filen = "pre_" + imp_info + "_" + to_string(lambda) + ".bin"; + fin.open(pre_filen, ios::binary); + if (fin) + { + set > new_list; + f1_succ.insert(pair > >(lambda, new_list)); + auto to_insert = &(f1_succ[lambda]); + while (fin.read((char*)(&f_t), function_s)) + { + (*to_insert).insert(f_t); + } + } + fin.close(); + lambda++; + } + if(verbose) print_graphe_info(); +} + +template +void lighter::search_batch(string args) +{ + function_t f1 = function_t::INPUT_DEFAULT(); + function_t f2 = function_t::INPUT_DEFAULT(); + string sboxesfile = "sboxes" + to_string(N) + ".txt"; + + pre_parse_args(); + while ((opt = getopt_long(myargc, myargv, "i:o:l:c:vuwap:f:r:", longopts, NULL)) != EOF) + { + switch(opt) + { + case 'a': all_gates(); break; + case 'v': verbose = true; break; + case 'c': pre_l = atoi(optarg)*100; cout << "Precomputed Limit : "<< optarg << endl; break; + case 'u': one_expand = true; break; + case 'w': write_in_file = true; break; + case 'i': f1.parse_function(optarg); break; + case 'o': sboxesfile = optarg; break; + case 'l': l = abs(atoi(optarg))*100; cout << "Lambda : "<< optarg << endl; break; + case 'p': omp_nb_threads = (atoi(optarg)); cout << "Thread number : "<< omp_nb_threads< +double lighter::search_single(string args) +{ + clock_t t1 = clock(); + + function_t f1 = function_t::INPUT_DEFAULT(); + function_t f2 = function_t::INPUT_DEFAULT(); + string instance_LUT; + instance_name = ""; + + pre_parse_args(); + while ((opt = getopt_long(myargc, myargv, "i:o:c:l:vuwap:f:r:n:", longopts, NULL)) != EOF) + { + switch(opt) + { + case 'c': pre_l = atoi(optarg)*100; cout << "Precomputed Limit : "<< optarg << endl; break; + case 'a': all_gates(); break; + case 'v': verbose = true; break; + case 'u': one_expand = true; break; + case 'w': write_in_file = true; break; + case 'i': f1.parse_function(optarg); break; + case 'o': f2.parse_function(optarg); instance_LUT = optarg; break; + case 'n': instance_name = optarg; break; + case 'l': l = abs(atoi(optarg))*100; cout << "Lambda : "<< optarg << endl; break; + case 'p': omp_nb_threads = (atoi(optarg)); cout << "Thread number : "<< omp_nb_threads< +void lighter::mitm(function_t f1, function_t f2) +{ + /* + initilisation of the two graphs + we start by adding the function f and the identity + */ + unsorted_arrival = f2; + f1.sort(); + f1.bit_slice_to_LUT(); + f2.sort(); + f2.bit_slice_to_LUT(); + + if (pre_l < 0) f1_succ.insert(pair > >(0, {f1})); + f2_succ.insert(pair > >(0, {f2})); + start = f1; + arrival = f2; + + /* + we expand the two graphs + */ + count_list = 0; + + while(count_list <= l) + { + if (count_list > pre_l) + { + expand(&f1_succ, &f2_succ, count_list); + pre_l = count_list; + } + else if (one_expand || (count_list == 0)) + { + function_t fun_found; + int vect_found; + + if(is_in_graphe_collision(count_list, 0, f2, &fun_found, &vect_found, &f1_succ)) + { + get_implementation(f2, fun_found, vect_found, &f2_succ, &f1_succ); + } + } + + if(!one_expand) + { + expand(&f2_succ, &f1_succ, count_list); + } + + if(verbose && (count_list%100) == 0) cout << "\033[0;32m" << count_list << "\033[0m" << endl; + count_list++; + if(2*count_list > shortest_path) + { + if (count_list > pre_l) write_pre_bin(0); + exit_m(); + return; + } + } + if(verbose) print_graphe_info(); + if (count_list > pre_l) + { + write_pre_bin(0); + } + exit_m(); + return; +} + +template +void lighter::expand(map > > *current, + map > > *opposite, + int lambda) +{ + auto to_expand = current->find(lambda); + if(to_expand != current->end()) + { + for(auto bool_op : b) + { + int bool_op_cost = bool_op.op_cost; + vector > successors; + + for(auto fun : (*to_expand).second) + { + bool_op_fun(bool_op.op_id, fun, &successors); + if (successors.size() >= MAX_VEC_NODES) + { + sort(successors.begin(), successors.end()); + successors.erase(unique(successors.begin(), successors.end()), successors.end()); + } + } + + if(successors.size() != 0) + { + sort(successors.begin(), successors.end()); + successors.erase(unique(successors.begin(), successors.end()), successors.end()); + + auto s = current->find(lambda+bool_op.op_cost); + set > new_list; + v_list_process(lambda, bool_op_cost, &successors, current, &new_list, opposite); + if(s != current->end()) + { + s->second.insert(new_list.begin(), new_list.end()); + } + else + { + current->insert(pair > >(lambda+bool_op.op_cost, new_list)); + } + } + } + } +} + +template +void lighter::v_list_process(int lambda, int op_cost, vector > *tmp, + map > > *current, + set > *to_insert, map > > *opposite) +{ + function_t fun_found; + int vect_found; + + #pragma omp parallel for private(fun_found, vect_found) num_threads(omp_nb_threads) + for(auto it_tmp = tmp->begin(); it_tmp < tmp->end(); it_tmp++) + { + if(!is_in_graphe(lambda, op_cost, (*it_tmp), current)) + { + #pragma omp critical + { + (*it_tmp).bit_slice_to_LUT(); + to_insert->insert(*it_tmp); + nodes_cmp++; + if(nodes_cmp == max_nodes) + { + exit_m(); + } + } + if(is_in_graphe_collision(lambda, op_cost, (*it_tmp), &fun_found, &vect_found, opposite)) + { + #pragma omp critical + { + //system("notify-send 'Collision'"); + get_implementation((*it_tmp), fun_found, vect_found, current, opposite); + } + } + } + } +} + +template +bool lighter::is_in_graphe(int lambda, int op_cost, function_t f, map > > *g) +{ + for(auto it = g->begin(); it != g->end() ; it++) + { + if((*it).second.find(f) != (*it).second.end()) + { + if((*it).first > lambda+op_cost) + { + #pragma omp critical + { + (*it).second.erase(f); + } + return false; + } + return true; + } + } + return false; +} + +template +bool lighter::is_in_graphe_collision(int lambda, int op_cost, function_t f, + function_t *fun_found, + int *vect_found, + map > > *g) +{ + for(auto it = g->begin(); it != g->end(); it++) + { + if (it->first >= lambda - max_GE) + { + if( lambda + (*it).first + op_cost >= shortest_path ) return false; + if((*it).second.find(f) != (*it).second.end()) + { + *vect_found = (*it).first; + *fun_found = *((*it).second.find(f)); + return true; + } + } + } + return false; +} + +template +double lighter::search_single_concatenate(string args) +{ + clock_t t1 = clock(); + + function_t f1 = function_t::INPUT_DEFAULT(); + function_t f2 = function_t::INPUT_DEFAULT(); + string instance_LUT; + instance_name = ""; + + pre_parse_args(); + while ((opt = getopt_long(myargc, myargv, "i:o:c:l:vuwap:f:r:n:", longopts, NULL)) != EOF) + { + switch(opt) + { + case 'c': pre_l = atoi(optarg)*100; cout << "Precomputed Limit : "<< optarg << endl; break; + case 'a': all_gates(); break; + case 'v': verbose = true; break; + case 'u': one_expand = true; break; + case 'w': write_in_file = true; break; + case 'i': f1.parse_function(optarg); break; + case 'o': f2.parse_function(optarg); instance_LUT = optarg; break; + case 'n': instance_name = optarg; break; + case 'l': l = abs(atoi(optarg))*100; cout << "Lambda : "<< optarg << endl; break; + case 'p': omp_nb_threads = (atoi(optarg)); cout << "Thread number : "<< omp_nb_threads< +void lighter::search_batch_concatenate(string args) +{ + function_t f1 = function_t::INPUT_DEFAULT(); + function_t f2 = function_t::INPUT_DEFAULT(); + string sboxesfile = "sboxes" + to_string(N) + ".txt"; + + pre_parse_args(); + while ((opt = getopt_long(myargc, myargv, "i:o:l:c:vuwap:f:r:", longopts, NULL)) != EOF) + { + switch(opt) + { + case 'c': pre_l = atoi(optarg)*100; cout << "Precomputed Limit : "<< optarg << endl; break; + case 'a': all_gates(); break; + case 'v': verbose = true; break; + case 'u': one_expand = true; break; + case 'w': write_in_file = true; break; + case 'i': f1.parse_function(optarg); break; + case 'o': sboxesfile = optarg; break; + case 'l': l = abs(atoi(optarg))*100; cout << "Lambda : "<< optarg << endl; break; + case 'p': omp_nb_threads = (atoi(optarg)); cout << "Thread number : "<< omp_nb_threads< +void lighter::concatenate(function_t f1, function_t f2) +{ + unsorted_arrival = f2; + + f1.sort(); f1.bit_slice_to_LUT(); + f2.sort(PEo); f2.bit_slice_to_LUT(); + start = f1; + arrival = f2; + + if (pre_l < 0) f1_succ.insert(pair > >(0, {f1})); + + count_list = 0; + while (count_list <= l + max_GE) + { + if ((count_list > pre_l) && (count_list <= l)) + { + expand(count_list); + match(count_list); + write_pre_bin(count_list+min_GE); + pre_l = count_list; + } + else + { + match(count_list); + } + + if(verbose && (count_list%100) == 0) cout << count_list << endl; + count_list++; + + if(2*count_list - max_GE + 1 > shortest_path) + { + exit_m(); + return; + } + } + + if (verbose) print_graphe_info(); + exit_m(); + return; +} + +template +void lighter::expand(int lambda) +{ + auto to_expand = f1_succ.find(lambda); + if(to_expand != f1_succ.end()) + { + for(auto bool_op : b) + { + int bool_op_cost = bool_op.op_cost; + vector > successors; + + for(auto fun : (*to_expand).second) + { + bool_op_fun(bool_op.op_id, fun, &successors); + if (successors.size() >= MAX_VEC_NODES) + { + sort(successors.begin(), successors.end()); + successors.erase(unique(successors.begin(), successors.end()), successors.end()); + } + } + + if(successors.size() != 0) + { + sort(successors.begin(), successors.end()); + successors.erase(unique(successors.begin(), successors.end()), successors.end()); + + auto s = f1_succ.find(lambda+bool_op.op_cost); + set > new_list; + v_list_process(lambda, bool_op_cost, &successors, &new_list); + if(s != f1_succ.end()) + { + s->second.insert(new_list.begin(), new_list.end()); + } + else + { + f1_succ.insert(pair > >(lambda+bool_op.op_cost, new_list)); + } + } + } + } +} + +template +void lighter::v_list_process(int lambda, int op_cost, vector > *tmp, + set > *to_insert) +{ + function_t fun_found; + int vect_found; + + #pragma omp parallel for private(fun_found, vect_found) num_threads(omp_nb_threads) + for(auto it_tmp = tmp->begin(); it_tmp < tmp->end(); it_tmp++) + { + if(!is_in_graphe(lambda, op_cost, (*it_tmp))) + { + #pragma omp critical + { + (*it_tmp).bit_slice_to_LUT(); + to_insert->insert(*it_tmp); + nodes_cmp++; + if(nodes_cmp == max_nodes) + { + exit_m(); + } + } + } + } +} + +template +bool lighter::is_in_graphe(int lambda, int op_cost, function_t f) +{ + for(auto it = f1_succ.begin(); it != f1_succ.end() ; it++) + { + if((*it).second.find(f) != (*it).second.end()) + { + if((*it).first > lambda+op_cost) + { + #pragma omp critical + { + (*it).second.erase(f); + } + return false; + } + return true; + } + } + return false; +} + +template +void lighter::match(int c1) +{ + auto f1_it = f1_succ.find(c1); + if (f1_it != f1_succ.end()) + { + vector* > tmp_vec; + for (auto set1_it = f1_it->second.begin(); set1_it != f1_it->second.end(); set1_it++) + { + tmp_vec.push_back( &(*set1_it) ); + } + + //for (auto set1_it = f1_it->second.begin(); set1_it != f1_it->second.end(); set1_it++) + #pragma omp parallel for num_threads(omp_nb_threads) + for (auto vec1_it = tmp_vec.begin(); vec1_it < tmp_vec.end(); vec1_it++) + { + #pragma omp flush + if (2 * c1 - max_GE + 1 < shortest_path) + { + //auto func1 = *set1_it; + auto func1 = **vec1_it; + + bit_slice_t cp_bit_slice = OPs.composite(func1.LUT, arrival.LUT); + function_t func_comp(cp_bit_slice); + + int PEm[N]; + func_comp.sort(PEm); + + auto f2_it = f1_succ.begin(); + while ((f2_it != f1_succ.end()) && (f2_it->first <= (c1 + max_GE))) + { + #pragma omp flush + if ((c1 + f2_it->first) < shortest_path) + { + int c2; + c2 = f2_it->first; + if (c2 > (c1 - max_GE)) + { + auto vec2_it = f2_it->second.find(func_comp); + if (vec2_it != f2_it->second.end()) + { + auto func2 = *vec2_it; + + #pragma omp flush + #pragma omp critical + { + if (shortest_path > (c1 + c2)) + { + shortest_path = c1 + c2; + get_implementation_concatenate(func2, c2, func1, c1, PEm); + } + } + } + } + f2_it++; + } + else + { + break; + } + } + } + } + } +} + +template +void lighter::generate(string args) +{ + clock_t t1 = clock(); + + pre_parse_args(); + while ((opt = getopt_long(myargc, myargv, "o:l:c:vwap:f:r:s:", longopts, NULL)) != EOF) + { + switch(opt) + { + case 'c': pre_l = atoi(optarg)*100; cout << "Precomputed Limit : "<< optarg << endl; break; + case 'a': all_gates(); break; + case 'v': verbose = true; break; + case 'w': write_in_file = true; break; + case 'o': sboxesfile = optarg; break; + case 'l': l = abs(atoi(optarg))*100; cout << "Lambda : "<< optarg << endl; break; + case 'p': omp_nb_threads = (atoi(optarg)); cout << "Thread number : "<< omp_nb_threads<::INPUT_DEFAULT(); + + generate(); + + reset(); + + t1 = clock() - t1; + cout << "Takes time (mins): " << (((double)t1) / (CLOCKS_PER_SEC * 60.0)) << endl; +} + +#if 0 +template +void lighter::generate() +{ + function_t f1 = function_t::INPUT_DEFAULT(); + f1.sort(); + f1.bit_slice_to_LUT(); + + if (pre_l < 0) f1_succ.insert(pair > >(0, {f1})); + + count_list = 0; + while (count_list <= l + max_GE) + { + if ((count_list > pre_l) && (count_list <= l)) + { + expand(count_list); + filter(count_list); + write_pre_bin(count_list+min_GE); + pre_l = count_list; + } + else + { + filter(count_list); + } + + if(verbose && (count_list%100) == 0) cout << count_list << endl; + count_list++; + + if(2*count_list - max_GE + 1 > Cost_Criteria) + { + exit_m(); + return; + } + } + + if(verbose) print_graphe_info(); + exit_m(); + return; +} +#endif + +template +void lighter::generate() +{ + function_t f1 = function_t::INPUT_DEFAULT(); + f1.sort(); + f1.bit_slice_to_LUT(); + + if (pre_l < 0) + { + f1_succ.insert(pair > >(0, {f1})); + } + + count_list = 0; + while (count_list <= l + max_GE) + { + if(verbose && (count_list%100) == 0) cout << count_list << endl; + + if ((count_list > pre_l) && (count_list <= l)) + { + expand(count_list); + filter_pre(count_list); + } + else + { + filter_pre(count_list); + } + + count_list++; + + // All S-boxes which can be implemented with <= l + min_GE + // have been stored in the precomputed graph + // Thus, if (l + min_GE >= Cost_Criteria) and all precomputed + // S-boxes has been checked, no need to expand the graph further. + if((count_list <= l + min_GE) && (count_list > Cost_Criteria)) + { + exit_m(); + return; + } + else if ((count_list > l) && (count_list > Cost_Criteria)) + { + break; + } + } + + #if 0 + // c1 + c2 <= c1 + c1 + max_GE; c1 + c2 > l + min_GE; + // c1 + c1 + max_GE > l + min_GE + int c1LowerBound = (l + min_GE >= max_GE ? (((l + min_GE - max_GE) / 2) + 1) : 0); + count_list = c1LowerBound; + while ((count_list <= l + max_GE) && + (count_list + count_list - max_GE + 1 <= Cost_Criteria)) // c1 + c2 >= c1 + c1 - max_GE + 1 + { + filter(count_list); + + if(verbose && (count_list%100) == 0) + { + cout + << (count_list + count_list - max_GE + 1) << " ~ " + << (count_list + count_list + max_GE) << endl; + } + count_list++; + } + #endif + + // c1 + c2 <= c1 + c1 + max_GE; c1 + c2 > l + min_GE; + // c1 + c1 + max_GE > l + min_GE + int c1LowerBound = (l + min_GE >= max_GE ? (((l + min_GE - max_GE) / 2) + 1) : 0); + count_list = l + max_GE; + while (count_list >= c1LowerBound) // c1 + c2 >= c1 + c1 - max_GE + 1 + { + if(verbose && (count_list%100) == 0) + { + cout + << (count_list + count_list - max_GE + 1) << " ~ " + << (count_list + count_list + max_GE) << endl; + } + + if (count_list + count_list - max_GE + 1 > Cost_Criteria) + { + count_list--; + continue; + } + + filter(count_list); + + + count_list--; + } + + + if(verbose) print_graphe_info(); + exit_m(); + return; +} + +template +void lighter::filter_pre(int c1) +{ + auto f1_it = f1_succ.find(c1); + if (f1_it != f1_succ.end()) + { + vector* > tmp_vec; + for (auto set1_it = f1_it->second.begin(); set1_it != f1_it->second.end(); set1_it++) + { + tmp_vec.push_back( &(*set1_it) ); + } + + #pragma omp parallel for num_threads(omp_nb_threads) + for (auto vec1_it = tmp_vec.begin(); vec1_it < tmp_vec.end(); vec1_it++) + { + int good_idx = 0; + int good_idx_old = -1; + + #pragma omp flush + if (c1 <= Cost_Criteria) // for parallel; Cost_Criteria may reduced by other threads + { + auto func1 = **vec1_it; + if (is_good(func1)) + { + #pragma omp critical + { + if (c1 <= Cost_Criteria) + { + if (c1 < Cost_Criteria) Cost_Criteria = c1; + good_idx = good_number; + good_number++; + } + } + if (good_idx != good_idx_old) + { + get_implementation_good(func1, c1, good_idx); + good_idx_old = good_idx; + } + } + } + } + } +} + +template +void lighter::filter(int c1) +{ // Any Implementation can be decomposed into two parts. + // Suppose Imp = Imp1 || Imp2 + // Use the following decomposed, we can find + // There exist Imp1 and Imp2, such that cost of Imp1 = C1 and cost of Imp2 = C2, s.t. + // C1 <= C2 and C2 - C1 <= 2 * max_GE; + // Init: C1 = C; C2 = 0 + // While C1 > C2: C1 -= deltai; C2 += deltai; move one Instruction in Imp1 to Imp2; + // Finished: C1 <= C2; C1 + delta_last > C2 - delta_last; => C2 - C1 < 2*delta; + // If C2 - C1 < delta: this case is covered by testing c1 || c2, and c1 <= c2 <= c1 + max_GE; + // If C2 - C1 >= delta: we adjust again by setting C1 += delta; C2 -= delta; i.e. move the last instruction back to Imp1 + // then C1 - C2 < 2*delta - delta = delta + // this case is covered by testing c1 || c2, and c1 - max_GE <= c2 < c1 + // Thus, any implementation can be decomposed into two parts, the larger part costs no more max_GE than the smaller part + // We do not plainly considered c2 in [c1, c1 + 2*max_GE] for the sake of efficiency. + // For fixed c1, we considered c2 in [c1 - max_GE + 1, c1 + max_GE] + int c2LowerBound = (c1 - max_GE + 1) > (l + min_GE- c1 + 1 ) ? (c1 - max_GE + 1) : (l + min_GE - c1 + 1); + // S-boxes which cost less than $l + min_GE$ has already been examed in filter_pre() + // Thus, only need to exam c1 + c2 > l + min_GE => c2 > l + min_GE - c1 + // Combination c1 + (c1 - max_GE) is already examed under the + // combination c1' + (c1' + max_GE), where c1' = c1 - max_GE + int c2UpperBound = c1 + max_GE; + int curCostLowerBound = c1 + c2LowerBound; + + auto f1_it = f1_succ.find(c1); + if (f1_it != f1_succ.end()) + { + vector* > tmp_vec; + for (auto set1_it = f1_it->second.begin(); set1_it != f1_it->second.end(); set1_it++) + { + tmp_vec.push_back( &(*set1_it) ); + } + + #pragma omp parallel for num_threads(omp_nb_threads) + for (auto vec1_it = tmp_vec.begin(); vec1_it < tmp_vec.end(); vec1_it++) + { + int good_idx = 0; + int good_idx_old = -1; + + #pragma omp flush + if (curCostLowerBound <= Cost_Criteria) // for parallel; Cost_Criteria may reduced by other threads + { + auto func1 = **vec1_it; + vector > func_perms; + func_perms.reserve(FACT_(N)); + for (int i = 0; i < FACT_(N); i++) + { + function_t func_perm; + OPs.composite(func_perm.LUT, PE.func[i].LUT, func1.LUT); + func_perm.LUT_to_bit_slice(); + func_perms.push_back(func_perm); + } + + auto f2_it = f1_succ.begin(); + while ((f2_it != f1_succ.end()) && (f2_it->first <= c2UpperBound)) + { + int c2 = f2_it->first; + int curCost = c1 + c2; + + if (c2 < c2LowerBound) + { + f2_it++; + continue; + } + + #pragma omp flush + if (curCost > Cost_Criteria) + { + break; + } + + auto vec2_it = f2_it->second.begin(); + while (vec2_it != f2_it->second.end()) + { + auto func2 = *vec2_it; + + function_t func_comp; + for (int i = 0; i < FACT_(N); i++) + { + OPs.composite(func_comp.LUT, func2.LUT, func_perms[i].LUT); + func_comp.LUT_to_bit_slice(); + if (is_good(func_comp)) + { + #pragma omp critical + { + if (curCost <= Cost_Criteria) + { + Cost_Criteria = curCost; + good_idx = good_number; + good_number++; + } + } + if (good_idx != good_idx_old) + { + get_implementation_good(func1, c1, func_perms[i], i, func2, c2, func_comp, good_idx); + good_idx_old = good_idx; + } + } + } + vec2_it++; + } + f2_it++; + } + func_perms.clear(); + func_perms.shrink_to_fit(); + } + } + } +} + +template +void lighter::evaluate_filter(string args) +{ + clock_t t1 = clock(); + + function_t f1 = function_t::INPUT_DEFAULT(); + function_t f2 = function_t::INPUT_DEFAULT(); + string sboxesfile = "sboxes" + to_string(N) + ".txt"; + + pre_parse_args(); + while ((opt = getopt_long(myargc, myargv, "o:l:c:vwap:f:r:s:", longopts, NULL)) != EOF) + { + switch(opt) + { + case 'c': pre_l = atoi(optarg)*100; cout << "Precomputed Limit : "<< optarg << endl; break; + case 'a': all_gates(); break; + case 'v': verbose = true; break; + case 'w': write_in_file = true; break; + case 'o': sboxesfile = optarg; break; + case 'l': l = abs(atoi(optarg))*100; cout << "Lambda : "<< optarg << endl; break; + case 'p': omp_nb_threads = (atoi(optarg)); cout << "Thread number : "<< omp_nb_threads<::INPUT_DEFAULT(); + + init_b(conf_file); + init_critiera(criteria_file); + + if (Cost_Criteria != -1) + { + genImpInfo(); + read_pre_bin(pre_l); + } + string statisticianfn = imp_info + "-c" + criteria_file.substr(0, criteria_file.rfind('.')) + "-o" + sboxesfile.substr(0, sboxesfile.rfind('.')) + "__" + "statistics.csv"; + statistician.open(statisticianfn.c_str(), ios::app); + evaluate_writeTitleLine(statistician); + statistician.close(); + string is_best = "-"; + + ifstream sboxes(sboxesfile); + string instance_LUT; + for(string line; getline(sboxes, line); ) + { + istringstream instance_line(line); + if(getline(instance_line, instance_name, ',')) + { + if(getline(instance_line, instance_LUT)) + { + f2.parse_function(instance_LUT); + + if (is_good(f2)) + { + if (Cost_Criteria != -1) + { + init_varbles(); + concatenate(f1, f2); + if (shortest_path <= (2*(l+min_GE) - max_GE)) is_best = "Best"; + else is_best = "Unknown"; + if (shortest_path <= Cost_Criteria) + { + statistician.open(statisticianfn.c_str(), ios::app); + evaluate(instance_name, f2, shortest_path, is_best, statistician); + statistician.close(); + } + } + else + { + statistician.open(statisticianfn.c_str(), ios::app); + evaluate(instance_name, f2, 0, is_best, statistician); + statistician.close(); + } + } + } + } + } + + reset(); + + t1 = clock() - t1; + cout << "Takes time (mins): " << (((double)t1) / (CLOCKS_PER_SEC * 60.0)) << endl; +} + +template +void lighter::evaluate_writeTitleLine(ofstream & outf) +{ + string conf = conf_file.substr(0, conf_file.rfind('.')); + outf + << "Cipher" << "," + << "LUT" << "," + << "bit_slice" << "," + << "Permutation" << "," + << "Involution" << "," + << "Diff" << "," + << "DiffFreq" << "," + << "Diff1" << "," + << "CardD1" << "," + << "Lin" << "," + << "LinFreq" << "," + << "Lin1" << "," + << "CardL1" << "," + << "max_degree" << "," + << "min_degree" << "," + << "MaxDegreeFreq" << "," + << "MinDegreeFreq" << "," + << "Max_ProductDegrees" << "," + << "LS_number" << "," + << "\"max_v (v, w)-linear\"" << "," + << "\"max_w (v, w)-linear\"" << "," + << "Optimal_Class" << "," + << "Cost (GE) " + conf << "," + << "Cost is Best " << "," + << "inv_LUT" << "," + << "inv_bit_slice" << "," + << "inv_max_degree" << "," + << "inv_min_degree" << "," + << "inv_MaxDegreeFreq" << "," + << "inv_MinDegreeFreq" << "," + << "inv_Max_ProductDegrees"<< "," + << "inv_LS_number" << "," + << "\"inv_max_v (v, w)-linear\"" << "," + << "\"inv_max_w (v, w)-linear\"" << "," + << "inv_Optimal_Class" + << endl; +} + + +template +void lighter::evaluate(string sboxesfile, string outputfile) +{ + ifstream sboxes(sboxesfile); + + vector > sboxes_vec; + for(string line; getline(sboxes, line); ) + { + istringstream instance_line(line); + string name_str; + string LUT_str; + if(getline(instance_line, name_str, ',')) + { + if(getline(instance_line, LUT_str)) + { + sboxes_vec.push_back(pair(name_str, LUT_str)); + } + } + } + sboxes.close(); + + ofstream *outfs = new ofstream[omp_nb_threads]; + for (int tid = 0; tid < omp_nb_threads; tid++) + { + string outfn = outputfile; + string outfn_sub = "_Part" + to_string(tid); + outfn.insert(outfn.rfind('.'), outfn_sub); + outfs[tid].open(outfn.c_str(), ios::app); + evaluate_writeTitleLine(outfs[tid]); + } + + #pragma omp parallel for num_threads(omp_nb_threads) + for (auto vec_it = sboxes_vec.begin(); vec_it < sboxes_vec.end(); vec_it++) + { + string name_str = (*vec_it).first; + string LUT_str = (*vec_it).second; + function_t f(LUT_str); + int tid = omp_get_thread_num(); + evaluate(name_str, f, 0, "-", outfs[tid]); + } + + for (int tid = 0; tid < omp_nb_threads; tid++) + { + outfs[tid].close(); + } + + delete [] outfs; +} + +template +void lighter::evaluate_verbose(string sboxesfile, string outputfile_prefix) +{ + ifstream sboxes(sboxesfile); + + vector > sboxes_vec; + for(string line; getline(sboxes, line); ) + { + istringstream instance_line(line); + string name_str; + string LUT_str; + if(getline(instance_line, name_str, ',')) + { + if(getline(instance_line, LUT_str)) + { + sboxes_vec.push_back(pair(name_str, LUT_str)); + } + } + } + sboxes.close(); + + #pragma omp parallel for num_threads(omp_nb_threads) + for (auto vec_it = sboxes_vec.begin(); vec_it < sboxes_vec.end(); vec_it++) + { + string name_str = (*vec_it).first; + string LUT_str = (*vec_it).second; + string file_str = outputfile_prefix + "_" + name_str + ".txt"; + ofstream fout(file_str.c_str()); + function_t f(LUT_str); + fout << name_str << " Sbox:" << endl; + fout << f.show_all_properties(); + if (f.is_permutation()) + { + function_t f_inv; + f_inv = f.inverse(); + fout << name_str << " Inverse Sbox: " << endl; + fout << f_inv.show_all_properties(); + } + fout.close(); + } +} + +#endif // LIGHTER_MITM_H__ \ No newline at end of file diff --git a/lighter_string_bool_op.hpp b/lighter_string_bool_op.hpp new file mode 100644 index 0000000..f9b3ae3 --- /dev/null +++ b/lighter_string_bool_op.hpp @@ -0,0 +1,928 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#ifndef LIGHTER_STRING_BOOL_OP_H__ +#define LIGHTER_STRING_BOOL_OP_H__ + +using namespace Peigen; +using namespace Peigen::weight; +using namespace std; + +template +void lighter::write_c(string *s, vector tmp_tab) +{ + int i = 0; + auto pos = s->find_first_of("["); + s->insert(pos+1, to_string(tmp_tab[0])); + while((pos = s->find_first_of("[", pos+1)) != string::npos) + { + s->insert(pos+1, to_string(tmp_tab[i%(tmp_tab.size())])); + i++; + } +} + +template +string lighter::bool_op_not(const function_t f, string s, bool reverse) +{ + int tmp; + function_t fun_prev = f; + if(!reverse) + { + fun_prev.bit_slice[f.info_line] = f.prev; + for(int i = 0; i < N; i++) if(fun_prev.bit_slice[i] == f.prev) tmp = i; + } + else tmp = f.info_line; + write_c(&s, {tmp}); + return s; +} + +template +string lighter::bool_op_two_inputs(const function_t f, int operation, string s, bool reverse) +{ + int tmp; + function_t fun_prev = f; + if(!reverse) + { + fun_prev.bit_slice[f.info_line] = f.prev; + for(int i = 0; i < N; i++) if(fun_prev.bit_slice[i] == f.prev) tmp = i; + } + else tmp = f.info_line; + + bit_slice_l_t a, b; + + int j; + for(j = 0; j < N; j++) + { + switch (operation) + { + case MAOI1 : + case XOR2 : a = fun_prev.bit_slice[tmp] ^ fun_prev.bit_slice[j]; break; + case MOAI1 : + case XNOR2 : a = ~(fun_prev.bit_slice[tmp] ^ fun_prev.bit_slice[j]); break; + } + if(!reverse) b = f.bit_slice[f.info_line]; + else b = f.prev; + if(a == b) break; + } + write_c(&s, {tmp, j}); + return s; +} + +template +string lighter::bool_op_super_gates(const function_t f, int operation, string s, bool reverse) +{ + int tmp; + function_t fun_prev = f; + if(!reverse) + { + fun_prev.bit_slice[f.info_line] = f.prev; + for(int i = 0; i < N; i++) if(fun_prev.bit_slice[i] == f.prev) tmp = i; + } + else tmp = f.info_line; + + bit_slice_l_t a, b; + switch(operation) + { + case MOAI1_AND2 : + case MOAI1_OR2 : + case MOAI1_NAND2 : + case MOAI1_NOR2 : + case XOR2_AND2 : + case XOR2_OR2 : + case MOAI1_ORN2R : + case MAOI1_ANDN2L : + case MOAI1_ORN2L : + case MAOI1_ANDN2R : + case MOAI1_ANDN2R : + case MAOI1_ORN2L : + case MOAI1_ANDN2L : + case MAOI1_ORN2R : + case XOR2_ANDN2L : + case XOR2_ANDN2R : + case XOR2_ORN2L : + case XOR2_ORN2R : + case MAOI1_NAND2 : + case MAOI1_NOR2 : + case MAOI1_AND2 : + case MAOI1_OR2 : + for(int k = 0; k < N; k++) + { + for(int j = 0; j < N; j++) + { + int op1, op2; + switch(operation) + { + case MAOI1_NAND2 : + case MOAI1_AND2 : a = ~( fun_prev.bit_slice[k] & fun_prev.bit_slice[j]) ^ fun_prev.bit_slice[tmp]; op1 = k; op2 = j; break; + case MAOI1_NOR2 : + case MOAI1_OR2 : a = ~( fun_prev.bit_slice[k] | fun_prev.bit_slice[j]) ^ fun_prev.bit_slice[tmp]; op1 = k; op2 = j; break; + case XOR2_AND2 : + case MAOI1_AND2 : + case MOAI1_NAND2 : a = ( fun_prev.bit_slice[k] & fun_prev.bit_slice[j]) ^ fun_prev.bit_slice[tmp]; op1 = k; op2 = j; break; + case XOR2_OR2 : + case MAOI1_OR2 : + case MOAI1_NOR2 : a = ( fun_prev.bit_slice[k] | fun_prev.bit_slice[j]) ^ fun_prev.bit_slice[tmp]; op1 = k; op2 = j; break; + + case MOAI1_ORN2R : a = (~fun_prev.bit_slice[k] & fun_prev.bit_slice[j]) ^ fun_prev.bit_slice[tmp]; op1 = j; op2 = k; break; + case MAOI1_ANDN2L : + case XOR2_ANDN2L : a = (~fun_prev.bit_slice[k] & fun_prev.bit_slice[j]) ^ fun_prev.bit_slice[tmp]; op1 = k; op2 = j; break; + + case MOAI1_ORN2L : a = ( fun_prev.bit_slice[k] & ~fun_prev.bit_slice[j]) ^ fun_prev.bit_slice[tmp]; op1 = k; op2 = j; break; + case MAOI1_ANDN2R : + case XOR2_ANDN2R : a = ( fun_prev.bit_slice[k] & ~fun_prev.bit_slice[j]) ^ fun_prev.bit_slice[tmp]; op1 = j; op2 = k; break; + + case MOAI1_ANDN2R : a = (~fun_prev.bit_slice[k] | fun_prev.bit_slice[j]) ^ fun_prev.bit_slice[tmp]; op1 = j; op2 = k; break; + case MAOI1_ORN2L : + case XOR2_ORN2L : a = (~fun_prev.bit_slice[k] | fun_prev.bit_slice[j]) ^ fun_prev.bit_slice[tmp]; op1 = k; op2 = j; break; + + case MOAI1_ANDN2L : a = ( fun_prev.bit_slice[k] | ~fun_prev.bit_slice[j]) ^ fun_prev.bit_slice[tmp]; op1 = k; op2 = j; break; + case MAOI1_ORN2R : + case XOR2_ORN2R : a = ( fun_prev.bit_slice[k] | ~fun_prev.bit_slice[j]) ^ fun_prev.bit_slice[tmp]; op1 = j; op2 = k; break; + } + if(!reverse) b = f.bit_slice[f.info_line]; + else b = f.prev; + if( a == b ) + { + write_c(&s, {tmp, op1, op2}); + return s; + } + } + } + case XOR2_AND2_AND2 : + case MAOI1_AND3 : + case MOAI1_NAND2_AND2 : + case MAOI1_AND2_AND2 : + case MOAI1_ORN2L_NAND2 : + case MAOI1_ANDN2R_NAND2 : + case MOAI1_NAND3 : + case XOR2_OR2_OR2 : + case MAOI1_OR3 : + case MOAI1_NOR2_OR2 : + case MAOI1_OR2_OR2 : + case MOAI1_ANDN2L_NOR2 : + case MAOI1_ORN2R_NOR2 : + case MOAI1_NOR3 : + case MOAI1_AND3 : + case MAOI1_NAND2_AND2 : + case MOAI1_AND2_AND2 : + case MOAI1_ANDN2R_NAND2 : + case MAOI1_ORN2L_NAND2 : + case MAOI1_NAND3 : + case MOAI1_OR3 : + case MAOI1_NOR2_OR2 : + case MOAI1_OR2_OR2 : + case MAOI1_NOR3 : + case MOAI1_ORN2R_NOR2 : + case MAOI1_ANDN2L_NOR2 : + for(int k = 0; k < N; k++) + { + for(int j = 0; j < N; j++) + { + for(int l = 0; l < N; l++) + { + switch(operation) + { + case XOR2_AND2_AND2 : + case MAOI1_AND3 : + case MOAI1_NAND2_AND2 : + case MAOI1_AND2_AND2 : + case MOAI1_ORN2L_NAND2 : + case MAOI1_ANDN2R_NAND2 : + case MOAI1_NAND3 : a = (fun_prev.bit_slice[k] & fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ^ fun_prev.bit_slice[tmp]; break; + + case XOR2_OR2_OR2 : + case MAOI1_OR3 : + case MOAI1_NOR2_OR2 : + case MAOI1_OR2_OR2 : + case MOAI1_ANDN2L_NOR2 : + case MAOI1_ORN2R_NOR2 : + case MOAI1_NOR3 : a = (fun_prev.bit_slice[k] | fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ^ fun_prev.bit_slice[tmp]; break; + + case MOAI1_AND3 : + case MAOI1_NAND2_AND2 : + case MOAI1_AND2_AND2 : + case MOAI1_ANDN2R_NAND2 : + case MAOI1_ORN2L_NAND2 : + case MAOI1_NAND3 : a = ~(fun_prev.bit_slice[k] & fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ^ fun_prev.bit_slice[tmp]; break; + + case MOAI1_OR3 : + case MAOI1_NOR2_OR2 : + case MOAI1_OR2_OR2 : + case MAOI1_ANDN2L_NOR2 : + case MOAI1_ORN2R_NOR2 : + case MAOI1_NOR3 : a = ~(fun_prev.bit_slice[k] | fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ^ fun_prev.bit_slice[tmp]; break; + } + if(!reverse) b = f.bit_slice[f.info_line]; + else b = f.prev; + if( a == b ) + { + write_c(&s, {tmp, l, j, k}); + return s; + } + } + } + } + } + return s; +} + +template +string lighter::bool_op_super_super_gates(const function_t f, int operation, string s, bool reverse) +{ + int tmp; + function_t fun_prev = f; + if(!reverse) + { + fun_prev.bit_slice[f.info_line] = f.prev; + for(int i = 0; i < N; i++) if(fun_prev.bit_slice[i] == f.prev) tmp = i; + } + else tmp = f.info_line; + + bit_slice_l_t a, b; + int op1, op2, op3; + for(int k = 0; k < N; k++) + { + for(int j = 0; j < N; j++) + { + for(int l = 0; l < N; l++) + { + switch(operation) + { + case MAOI1_NAND2_NAND2 : + case MOAI1_AND2_NAND2 : a = ~( fun_prev.bit_slice[k] & ~( fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MAOI1_NOR2_NOR2 : + case MOAI1_OR2_NOR2 : a = ~( fun_prev.bit_slice[k] | ~( fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MAOI1_AND2_NAND2 : + case MOAI1_NAND2_NAND2 : a = ( fun_prev.bit_slice[k] & ~( fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MAOI1_OR2_NOR2 : + case MOAI1_NOR2_NOR2 : a = ( fun_prev.bit_slice[k] | ~( fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case XOR2_AND2_OR2 : + case MAOI1_AND2_OR2 : + case MOAI1_NAND2_OR2 : a = ( fun_prev.bit_slice[k] & ( fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case XOR2_OR2_AND2 : + case MAOI1_OR2_AND2 : + case MOAI1_NOR2_AND2 : a = ( fun_prev.bit_slice[k] | ( fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MAOI1_AND2_NOR2 : + case MOAI1_NAND2_NOR2 : a = ( fun_prev.bit_slice[k] & ~( fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MAOI1_OR2_NAND2 : + case MOAI1_NOR2_NAND2 : a = ( fun_prev.bit_slice[k] | ~( fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MAOI1_NAND2_OR2 : + case MOAI1_AND2_OR2 : a = ~( fun_prev.bit_slice[k] & ( fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MAOI1_NOR2_AND2 : + case MOAI1_OR2_AND2 : a = ~( fun_prev.bit_slice[k] | ( fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MAOI1_NAND2_NOR2 : + case MOAI1_AND2_NOR2 : a = ~( fun_prev.bit_slice[k] & ~( fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MAOI1_NOR2_NAND2 : + case MOAI1_OR2_NAND2 : a = ~( fun_prev.bit_slice[k] | ~( fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + + case MOAI1_NAND2_ANDN2L : + case MAOI1_AND2_ANDN2L : + case XOR2_AND2_ANDN2L : a = ( fun_prev.bit_slice[k] & (~fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = l; op1 = j; break; + case MOAI1_NAND2_ANDN2R : + case MAOI1_AND2_ANDN2R : + case XOR2_AND2_ANDN2R : a = ( fun_prev.bit_slice[k] & ( fun_prev.bit_slice[j] & ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MOAI1_NAND2_ORN2L : + case MAOI1_AND2_ORN2L : + case XOR2_AND2_ORN2L : a = ( fun_prev.bit_slice[k] & (~fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = l; op1 = j; break; + case MOAI1_NAND2_ORN2R : + case MAOI1_AND2_ORN2R : + case XOR2_AND2_ORN2R : a = ( fun_prev.bit_slice[k] & ( fun_prev.bit_slice[j] | ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + + case MOAI1_AND2_ANDN2L : + case MAOI1_NAND2_ANDN2L : a = ~( fun_prev.bit_slice[k] & (~fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = l; op1 = j; break; + case MOAI1_AND2_ANDN2R : + case MAOI1_NAND2_ANDN2R : a = ~( fun_prev.bit_slice[k] & ( fun_prev.bit_slice[j] & ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MOAI1_AND2_ORN2L : + case MAOI1_NAND2_ORN2L : a = ~( fun_prev.bit_slice[k] & (~fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = l; op1 = j; break; + case MOAI1_AND2_ORN2R : + case MAOI1_NAND2_ORN2R : a = ~( fun_prev.bit_slice[k] & ( fun_prev.bit_slice[j] | ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + + case MOAI1_NOR2_ANDN2L : + case MAOI1_OR2_ANDN2L : + case XOR2_OR2_ANDN2L : a = ( fun_prev.bit_slice[k] | (~fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = l; op1 = j; break; + case MOAI1_NOR2_ANDN2R : + case MAOI1_OR2_ANDN2R : + case XOR2_OR2_ANDN2R : a = ( fun_prev.bit_slice[k] | ( fun_prev.bit_slice[j] & ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MOAI1_NOR2_ORN2L : + case MAOI1_OR2_ORN2L : + case XOR2_OR2_ORN2L : a = ( fun_prev.bit_slice[k] | (~fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = l; op1 = j; break; + case MOAI1_NOR2_ORN2R : + case MAOI1_OR2_ORN2R : + case XOR2_OR2_ORN2R : a = ( fun_prev.bit_slice[k] | ( fun_prev.bit_slice[j] | ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = l; op1 = j; break; + + case MOAI1_OR2_ANDN2L : + case MAOI1_NOR2_ANDN2L : a = ~( fun_prev.bit_slice[k] | (~fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = l; op1 = j; break; + case MOAI1_OR2_ANDN2R : + case MAOI1_NOR2_ANDN2R : a = ~( fun_prev.bit_slice[k] | ( fun_prev.bit_slice[j] & ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MOAI1_OR2_ORN2L : + case MAOI1_NOR2_ORN2L : a = ~( fun_prev.bit_slice[k] | (~fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = l; op1 = j; break; + case MOAI1_OR2_ORN2R : + case MAOI1_NOR2_ORN2R : a = ~( fun_prev.bit_slice[k] | ( fun_prev.bit_slice[j] | ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = l; op1 = j; break; + + case MAOI1_ANDN2L_AND2 : + case XOR2_ANDN2L_AND2 : a = ( ~fun_prev.bit_slice[k] & ( fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = j; op2 = l; op1 = k; break; + case MAOI1_ANDN2L_OR2 : + case XOR2_ANDN2L_OR2 : a = ( ~fun_prev.bit_slice[k] & ( fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = j; op2 = l; op1 = k; break; + case MAOI1_ANDN2L_ANDN2L : + case XOR2_ANDN2L_ANDN2L : a = ( ~fun_prev.bit_slice[k] & (~fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = l; op2 = j; op1 = k; break; + case MAOI1_ANDN2L_ANDN2R : + case XOR2_ANDN2L_ANDN2R : a = ( ~fun_prev.bit_slice[k] & ( fun_prev.bit_slice[j] & ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = j; op2 = l; op1 = k; break; + case MAOI1_ANDN2L_ORN2L : + case XOR2_ANDN2L_ORN2L : a = ( ~fun_prev.bit_slice[k] & (~fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = l; op2 = j; op1 = k; break; + case MAOI1_ANDN2L_ORN2R : + case XOR2_ANDN2L_ORN2R : a = ( ~fun_prev.bit_slice[k] & ( fun_prev.bit_slice[j] | ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = j; op2 = l; op1 = k; break; + case MAOI1_ANDN2R_AND2 : + case XOR2_ANDN2R_AND2 : a = ( fun_prev.bit_slice[k] & ~( fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MAOI1_ANDN2R_OR2 : + case XOR2_ANDN2R_OR2 : a = ( fun_prev.bit_slice[k] & ~( fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MAOI1_ANDN2R_ANDN2L : + case XOR2_ANDN2R_ANDN2L : a = ( fun_prev.bit_slice[k] & ~(~fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = l; op1 = j; break; + case MAOI1_ANDN2R_ANDN2R : + case XOR2_ANDN2R_ANDN2R : a = ( fun_prev.bit_slice[k] & ~( fun_prev.bit_slice[j] & ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MAOI1_ANDN2R_ORN2L : + case XOR2_ANDN2R_ORN2L : a = ( fun_prev.bit_slice[k] & ~(~fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = l; op1 = j; break; + case MAOI1_ANDN2R_ORN2R : + case XOR2_ANDN2R_ORN2R : a = ( fun_prev.bit_slice[k] & ~( fun_prev.bit_slice[j] | ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + + case MAOI1_ANDN2L_NAND2 : a = ( ~fun_prev.bit_slice[k] & ~( fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = j; op2 = l; op1 = k; break; + case MAOI1_ANDN2R_NOR2 : a = ( fun_prev.bit_slice[k] & ( fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + + case MAOI1_ORN2L_AND2 : + case XOR2_ORN2L_AND2 : a = ( ~fun_prev.bit_slice[k] | ( fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = j; op2 = l; op1 = k; break; + case MAOI1_ORN2L_OR2 : + case XOR2_ORN2L_OR2 : a = ( ~fun_prev.bit_slice[k] | ( fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = j; op2 = l; op1 = k; break; + case MAOI1_ORN2L_ANDN2L : + case XOR2_ORN2L_ANDN2L : a = ( ~fun_prev.bit_slice[k] | (~fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = l; op2 = j; op1 = k; break; + case MAOI1_ORN2L_ANDN2R : + case XOR2_ORN2L_ANDN2R : a = ( ~fun_prev.bit_slice[k] | ( fun_prev.bit_slice[j] & ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = j; op2 = l; op1 = k; break; + case MAOI1_ORN2L_ORN2L : + case XOR2_ORN2L_ORN2L : a = ( ~fun_prev.bit_slice[k] | (~fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = l; op2 = j; op1 = k; break; + case MAOI1_ORN2L_ORN2R : + case XOR2_ORN2L_ORN2R : a = ( ~fun_prev.bit_slice[k] | ( fun_prev.bit_slice[j] | ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = j; op2 = l; op1 = k; break; + case MAOI1_ORN2R_AND2 : + case XOR2_ORN2R_AND2 : a = ( fun_prev.bit_slice[k] | ~( fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MAOI1_ORN2R_OR2 : + case XOR2_ORN2R_OR2 : a = ( fun_prev.bit_slice[k] | ~( fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MAOI1_ORN2R_ANDN2L : + case XOR2_ORN2R_ANDN2L : a = ( fun_prev.bit_slice[k] | ~(~fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = l; op1 = j; break; + case MAOI1_ORN2R_ANDN2R : + case XOR2_ORN2R_ANDN2R : a = ( fun_prev.bit_slice[k] | ~( fun_prev.bit_slice[j] & ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MAOI1_ORN2R_ORN2L : + case XOR2_ORN2R_ORN2L : a = ( fun_prev.bit_slice[k] | ~(~fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = l; op1 = j; break; + case MAOI1_ORN2R_ORN2R : + case XOR2_ORN2R_ORN2R : a = ( fun_prev.bit_slice[k] | ~( fun_prev.bit_slice[j] | ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + + case MAOI1_ORN2L_NOR2 : a = ( ~fun_prev.bit_slice[k] | ~( fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = j; op2 = l; op1 = k; break; + case MAOI1_ORN2R_NAND2 : a = ( fun_prev.bit_slice[k] | ( fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + + case MOAI1_ANDN2L_AND2 : a = ~( ~fun_prev.bit_slice[k] & ( fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = j; op2 = l; op1 = k; break; + case MOAI1_ANDN2L_OR2 : a = ~( ~fun_prev.bit_slice[k] & ( fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = j; op2 = l; op1 = k; break; + case MOAI1_ANDN2L_NAND2 : a = ~( ~fun_prev.bit_slice[k] & ~( fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = j; op2 = l; op1 = k; break; + case MOAI1_ANDN2L_ANDN2L : a = ~( ~fun_prev.bit_slice[k] & (~fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = l; op2 = j; op1 = k; break; + case MOAI1_ANDN2L_ANDN2R : a = ~( ~fun_prev.bit_slice[k] & ( fun_prev.bit_slice[j] & ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = j; op2 = l; op1 = k; break; + case MOAI1_ANDN2L_ORN2L : a = ~( ~fun_prev.bit_slice[k] & (~fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = l; op2 = j; op1 = k; break; + case MOAI1_ANDN2L_ORN2R : a = ~( ~fun_prev.bit_slice[k] & ( fun_prev.bit_slice[j] | ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = j; op2 = l; op1 = k; break; + case MOAI1_ANDN2R_AND2 : a = ~( fun_prev.bit_slice[k] & ~( fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MOAI1_ANDN2R_OR2 : a = ~( fun_prev.bit_slice[k] & ~( fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MOAI1_ANDN2R_NOR2 : a = ~( fun_prev.bit_slice[k] & ( fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MOAI1_ANDN2R_ANDN2L : a = ~( fun_prev.bit_slice[k] & ~(~fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = l; op1 = j; break; + case MOAI1_ANDN2R_ANDN2R : a = ~( fun_prev.bit_slice[k] & ~( fun_prev.bit_slice[j] & ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MOAI1_ANDN2R_ORN2L : a = ~( fun_prev.bit_slice[k] & ~(~fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = l; op1 = j; break; + case MOAI1_ANDN2R_ORN2R : a = ~( fun_prev.bit_slice[k] & ~( fun_prev.bit_slice[j] | ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MOAI1_ORN2L_AND2 : a = ~( ~fun_prev.bit_slice[k] | ( fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = j; op2 = l; op1 = k; break; + case MOAI1_ORN2L_OR2 : a = ~( ~fun_prev.bit_slice[k] | ( fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = j; op2 = l; op1 = k; break; + case MOAI1_ORN2L_NOR2 : a = ~( ~fun_prev.bit_slice[k] | ~( fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = j; op2 = l; op1 = k; break; + case MOAI1_ORN2L_ANDN2L : a = ~( ~fun_prev.bit_slice[k] | (~fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = l; op2 = j; op1 = k; break; + case MOAI1_ORN2L_ANDN2R : a = ~( ~fun_prev.bit_slice[k] | ( fun_prev.bit_slice[j] & ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = j; op2 = l; op1 = k; break; + case MOAI1_ORN2L_ORN2L : a = ~( ~fun_prev.bit_slice[k] | (~fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = l; op2 = j; op1 = k; break; + case MOAI1_ORN2L_ORN2R : a = ~( ~fun_prev.bit_slice[k] | ( fun_prev.bit_slice[j] | ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = j; op2 = l; op1 = k; break; + case MOAI1_ORN2R_AND2 : a = ~( fun_prev.bit_slice[k] | ~( fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MOAI1_ORN2R_OR2 : a = ~( fun_prev.bit_slice[k] | ~( fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MOAI1_ORN2R_NAND2 : a = ~( fun_prev.bit_slice[k] | ( fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MOAI1_ORN2R_ANDN2L : a = ~( fun_prev.bit_slice[k] | ~(~fun_prev.bit_slice[j] & fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = l; op1 = j; break; + case MOAI1_ORN2R_ANDN2R : a = ~( fun_prev.bit_slice[k] | ~( fun_prev.bit_slice[j] & ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + case MOAI1_ORN2R_ORN2L : a = ~( fun_prev.bit_slice[k] | ~(~fun_prev.bit_slice[j] | fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = l; op1 = j; break; + case MOAI1_ORN2R_ORN2R : a = ~( fun_prev.bit_slice[k] | ~( fun_prev.bit_slice[j] | ~fun_prev.bit_slice[l]) ) ^ fun_prev.bit_slice[tmp]; op3 = k; op2 = j; op1 = l; break; + } + if(!reverse) b = f.bit_slice[f.info_line]; + else b = f.prev; + if( a == b ) + { + write_c(&s, {tmp, op1, op2, op3}); + return s; + } + } + } + } + return s; +} + +template +string lighter::bool_op_base_string(uint8_t op) +{ + string feq = "F[] = "; + string ff = "F[], F[])"; + string f = ", F[], "; + string and2 = "AND2 ("; + string nand2 = "NAND2("; + string or2 = "OR2 ("; + string nor2 = "NOR2 ("; + string nor3 = "NOR3 (F[], F[], F[])"; + string nand3 = "NAND3(F[], F[], F[])"; + string or3 = "OR3 (F[], F[], F[])"; + string and3 = "AND3 (F[], F[], F[])"; + string maoi1 = "MAOI1(F[], "; + string moai1 = "MOAI1(F[], "; + string xor2 = "XOR2 (F[], "; + string andn2 = "ANDN2("; + string orn2 = "ORN2 ("; + string tmp1, tmp2, tmp3; + + switch (op) + { + case NOT1 : return feq+"NOT1(F[]);\n"; + case XOR2 : return feq+"XOR2 (F[], F[]);\n"; + case XNOR2 : return feq+"XNOR2(F[], F[]);\n"; + case MAOI1 : return feq+"MAOI1(F[], F[] , F[] , F[]);\n"; + case MOAI1 : return feq+"MOAI1(F[], F[] , F[] , F[]);\n"; + case MOAI1_NAND2 : return feq+moai1+nand2+ff+f+nand2+ff+");\n"; + case MOAI1_NOR2 : return feq+moai1+nor2+ff+f+nor2+ff+");\n"; + case MOAI1_AND2 : return feq+moai1+and2+ff+f+and2+ff+");\n"; + case MOAI1_OR2 : return feq+moai1+or2+ff+f+or2+ff+");\n"; + case MOAI1_NAND3 : return feq+moai1+nand3+f+nand3+");\n"; + case MOAI1_NOR3 : return feq+moai1+nor3+f+nor3+");\n"; + case MAOI1_NAND3 : return feq+maoi1+nand3+f+nand3+");\n"; + case MAOI1_NOR3 : return feq+maoi1+nor3+f+nor3+");\n"; + case MOAI1_ANDN2L : + case MOAI1_ANDN2R : return feq+moai1+andn2+ff+f+andn2+ff+");\n"; + case MOAI1_ORN2L : + case MOAI1_ORN2R : return feq+moai1+orn2+ff+f+orn2+ff+");\n"; + case MAOI1_ANDN2L : + case MAOI1_ANDN2R : return feq+maoi1+andn2+ff+f+andn2+ff+");\n"; + case MAOI1_ORN2L : + case MAOI1_ORN2R : return feq+maoi1+orn2+ff+f+orn2+ff+");\n"; + case XOR2_AND2 : return feq+xor2+and2+ff+");\n"; + case XOR2_OR2 : return feq+xor2+or2+ff+");\n"; + case XOR2_ANDN2L : + case XOR2_ANDN2R : return feq+xor2+andn2+ff+");\n"; + case XOR2_ORN2L : + case XOR2_ORN2R : return feq+xor2+orn2+ff+");\n"; + case XOR2_AND2_AND2 : return feq+xor2+and2+and2+ff+", F[]));\n"; break; + case XOR2_AND2_OR2 : return feq+xor2+and2+or2+ff+", F[]));\n"; break; + case XOR2_AND2_ANDN2L : + case XOR2_AND2_ANDN2R : return feq+xor2+and2+andn2+ff+", F[]));\n"; break; + case XOR2_AND2_ORN2L : + case XOR2_AND2_ORN2R : return feq+xor2+and2+orn2+ff+", F[]));\n"; break; + case XOR2_OR2_AND2 : return feq+xor2+or2+and2+ff+", F[]));\n"; break; + case XOR2_OR2_OR2 : return feq+xor2+or2+or2+ff+", F[]));\n"; break; + case XOR2_OR2_ANDN2L : + case XOR2_OR2_ANDN2R : return feq+xor2+or2+andn2+ff+", F[]));\n"; break; + case XOR2_OR2_ORN2L : + case XOR2_OR2_ORN2R : return feq+xor2+or2+orn2+ff+", F[]));\n"; break; + case XOR2_ANDN2L_AND2 : return feq+xor2+andn2+"F[], "+and2+ff+"));\n"; break; + case XOR2_ANDN2R_AND2 : return feq+xor2+andn2+and2+ff+", F[]));\n"; break; + case XOR2_ANDN2L_OR2 : return feq+xor2+andn2+"F[], "+or2+ff+"));\n"; break; + case XOR2_ANDN2R_OR2 : return feq+xor2+andn2+or2+ff+", F[]));\n"; break; + case XOR2_ANDN2L_ANDN2L : + case XOR2_ANDN2L_ANDN2R : return feq+xor2+andn2+"F[], "+andn2+ff+"));\n"; break; + case XOR2_ANDN2R_ANDN2L : + case XOR2_ANDN2R_ANDN2R : return feq+xor2+andn2+andn2+ff+", F[]));\n"; break; + case XOR2_ANDN2L_ORN2L : + case XOR2_ANDN2L_ORN2R : return feq+xor2+andn2+"F[], "+orn2+ff+"));\n"; break; + case XOR2_ANDN2R_ORN2L : + case XOR2_ANDN2R_ORN2R : return feq+xor2+andn2+orn2+ff+", F[]));\n"; break; + case XOR2_ORN2L_AND2 : return feq+xor2+orn2+"F[], "+and2+ff+"));\n"; break; + case XOR2_ORN2R_AND2 : return feq+xor2+orn2+and2+ff+", F[]));\n"; break; + case XOR2_ORN2L_OR2 : return feq+xor2+orn2+"F[], "+or2+ff+"));\n"; break; + case XOR2_ORN2R_OR2 : return feq+xor2+orn2+or2+ff+", F[]));\n"; break; + case XOR2_ORN2L_ANDN2L : + case XOR2_ORN2L_ANDN2R : return feq+xor2+orn2+"F[], "+andn2+ff+"));\n"; break; + case XOR2_ORN2R_ANDN2L : + case XOR2_ORN2R_ANDN2R : return feq+xor2+orn2+andn2+ff+", F[]));\n"; break; + case XOR2_ORN2L_ORN2L : + case XOR2_ORN2L_ORN2R : return feq+xor2+orn2+"F[], "+orn2+ff+"));\n"; break; + case XOR2_ORN2R_ORN2L : + case XOR2_ORN2R_ORN2R : return feq+xor2+orn2+orn2+ff+", F[]));\n"; break; + case MAOI1_NAND2 : return feq+maoi1+nand2+ff+f+nand2+ff+");\n"; + case MAOI1_NOR2 : return feq+maoi1+nor2+ff+f+nor2+ff+");\n"; + case MAOI1_AND2 : return feq+maoi1+and2+ff+f+and2+ff+");\n"; + case MAOI1_OR2 : return feq+maoi1+or2+ff+f+or2+ff+");\n"; + case MAOI1_AND3 : return feq+maoi1+and3+f+and3+");\n"; + case MAOI1_OR3 : return feq+maoi1+or3+f+or3+");\n"; + case MOAI1_AND3 : return feq+moai1+and3+f+and3+");\n"; + case MOAI1_OR3 : return feq+moai1+or3+f+or3+");\n"; + case MOAI1_AND2_NAND2 : tmp1 = and2; tmp2 = nand2; tmp3 = moai1;break; + case MOAI1_OR2_NOR2 : tmp1 = or2; tmp2 = nor2; tmp3 = moai1;break; + case MOAI1_NAND2_NAND2 : tmp1 = nand2; tmp2 = nand2; tmp3 = moai1;break; + case MOAI1_NOR2_NOR2 : tmp1 = nor2; tmp2 = nor2; tmp3 = moai1;break; + case MOAI1_NAND2_OR2 : tmp1 = nand2; tmp2 = or2; tmp3 = moai1;break; + case MOAI1_NOR2_AND2 : tmp1 = nor2; tmp2 = and2; tmp3 = moai1; break; + case MOAI1_NAND2_NOR2 : tmp1 = nand2; tmp2 = nor2; tmp3 = moai1; break; + case MOAI1_NOR2_NAND2 : tmp1 = nor2; tmp2 = nand2; tmp3 = moai1; break; + case MOAI1_AND2_OR2 : tmp1 = and2; tmp2 = or2; tmp3 = moai1; break; + case MOAI1_OR2_AND2 : tmp1 = or2; tmp2 = and2; tmp3 = moai1; break; + case MOAI1_AND2_NOR2 : tmp1 = and2; tmp2 = nor2; tmp3 = moai1; break; + case MOAI1_OR2_NAND2 : tmp1 = or2; tmp2 = nand2; tmp3 = moai1; break; + case MAOI1_NAND2_NAND2 : tmp1 = nand2 ; tmp2 = nand2; tmp3 = maoi1;break; + case MAOI1_NOR2_NOR2 : tmp1 = nor2 ; tmp2 = nor2; tmp3 = maoi1; break; + case MAOI1_AND2_NAND2 : tmp1 = and2; tmp2 = nand2; tmp3 = maoi1; break; + case MAOI1_OR2_NOR2 : tmp1 = or2; tmp2 = nor2; tmp3 = maoi1; break; + case MAOI1_AND2_OR2 : tmp1 = and2; tmp2 = or2; tmp3 = maoi1; break; + case MAOI1_OR2_AND2 : tmp1 = or2; tmp2 = and2; tmp3 = maoi1;break; + case MAOI1_AND2_NOR2 : tmp1 = and2; tmp2 = nor2; tmp3 = maoi1; break; + case MAOI1_OR2_NAND2 : tmp1 = or2; tmp2 = nand2; tmp3 = maoi1; break; + case MAOI1_NAND2_OR2 : tmp1 = nand2; tmp2 = or2; tmp3 = maoi1;break; + case MAOI1_NOR2_AND2 : tmp1 = nor2; tmp2 = and2; tmp3 = maoi1;break; + case MAOI1_NAND2_NOR2 : tmp1 = nand2; tmp2 = nor2; tmp3 = maoi1;break; + case MAOI1_NOR2_NAND2 : tmp1 = nor2; tmp2 = nand2; tmp3 = maoi1;break; + case MOAI1_NAND2_AND2 : tmp1 = nand2; tmp2 = and2; tmp3 = moai1; break; + case MAOI1_AND2_AND2 : tmp1 = and2; tmp2 = and2; tmp3 = maoi1; break; + case MOAI1_NOR2_OR2 : tmp1 = nor2; tmp2 = or2; tmp3 = moai1; break; + case MAOI1_OR2_OR2 : tmp1 = or2; tmp2 = or2; tmp3 = maoi1; break; + case MAOI1_NAND2_AND2 : tmp1 = nand2; tmp2 = and2; tmp3 = maoi1; break; + case MOAI1_AND2_AND2 : tmp1 = and2; tmp2 = and2; tmp3 = moai1; break; + case MAOI1_NOR2_OR2 : tmp1 = nor2; tmp2 = or2; tmp3 = maoi1; break; + case MOAI1_OR2_OR2 : tmp1 = or2; tmp2 = or2; tmp3 = moai1; break; + + case MOAI1_AND2_ANDN2L : return feq + moai1 + and2 + andn2 + ff + ", F[])" + f + and2 + andn2 + ff + ", F[]));\n"; + case MOAI1_AND2_ANDN2R : return feq + moai1 + and2 + andn2 + ff + ", F[])" + f + and2 + andn2 + ff + ", F[]));\n"; + case MOAI1_AND2_ORN2L : return feq + moai1 + and2 + orn2 + ff + ", F[])" + f + and2 + orn2 + ff + ", F[]));\n"; + case MOAI1_AND2_ORN2R : return feq + moai1 + and2 + orn2 + ff + ", F[])" + f + and2 + orn2 + ff + ", F[]));\n"; + case MOAI1_OR2_ANDN2L : return feq + moai1 + or2 + andn2 + ff + ", F[])" + f + or2 + andn2 + ff + ", F[]));\n"; + case MOAI1_OR2_ANDN2R : return feq + moai1 + or2 + andn2 + ff + ", F[])" + f + or2 + andn2 + ff + ", F[]));\n"; + case MOAI1_OR2_ORN2L : return feq + moai1 + or2 + orn2 + ff + ", F[])" + f + or2 + orn2 + ff + ", F[]));\n"; + case MOAI1_OR2_ORN2R : return feq + moai1 + or2 + orn2 + ff + ", F[])" + f + or2 + orn2 + ff + ", F[]));\n"; + case MOAI1_NAND2_ANDN2L : return feq + moai1 + nand2 + andn2 + ff + ", F[])" + f + nand2 + andn2 + ff + ", F[]));\n"; + case MOAI1_NAND2_ANDN2R : return feq + moai1 + nand2 + andn2 + ff + ", F[])" + f + nand2 + andn2 + ff + ", F[]));\n"; + case MOAI1_NAND2_ORN2L : return feq + moai1 + nand2 + orn2 + ff + ", F[])" + f + nand2 + orn2 + ff + ", F[]));\n"; + case MOAI1_NAND2_ORN2R : return feq + moai1 + nand2 + orn2 + ff + ", F[])" + f + nand2 + orn2 + ff + ", F[]));\n"; + case MOAI1_NOR2_ANDN2L : return feq + moai1 + nor2 + andn2 + ff + ", F[])" + f + nor2 + andn2 + ff + ", F[]));\n"; + case MOAI1_NOR2_ANDN2R : return feq + moai1 + nor2 + andn2 + ff + ", F[])" + f + nor2 + andn2 + ff + ", F[]));\n"; + case MOAI1_NOR2_ORN2L : return feq + moai1 + nor2 + orn2 + ff + ", F[])" + f + nor2 + orn2 + ff + ", F[]));\n"; + case MOAI1_NOR2_ORN2R : return feq + moai1 + nor2 + orn2 + ff + ", F[])" + f + nor2 + orn2 + ff + ", F[]));\n"; + case MOAI1_ANDN2R_AND2 : return feq + moai1 + andn2 + and2 + ff + ", F[])" + f + andn2 + and2 + ff + ", F[]));\n"; + case MOAI1_ANDN2R_OR2 : return feq + moai1 + andn2 + or2 + ff + ", F[])" + f + andn2 + or2 + ff + ", F[]));\n"; + case MOAI1_ANDN2R_NAND2 : return feq + moai1 + andn2 + nand2 + ff + ", F[])" + f + andn2 + nand2 + ff + ", F[]));\n"; + case MOAI1_ANDN2R_NOR2 : return feq + moai1 + andn2 + nor2 + ff + ", F[])" + f + andn2 + nor2 + ff + ", F[]));\n"; + case MOAI1_ANDN2R_ANDN2L : return feq + moai1 + andn2 + andn2 + ff + ", F[])" + f + andn2 + andn2 + ff + ", F[]));\n"; + case MOAI1_ANDN2R_ANDN2R : return feq + moai1 + andn2 + andn2 + ff + ", F[])" + f + andn2 + andn2 + ff + ", F[]));\n"; + case MOAI1_ANDN2R_ORN2L : return feq + moai1 + andn2 + orn2 + ff + ", F[])" + f + andn2 + orn2 + ff + ", F[]));\n"; + case MOAI1_ANDN2R_ORN2R : return feq + moai1 + andn2 + orn2 + ff + ", F[])" + f + andn2 + orn2 + ff + ", F[]));\n"; + case MOAI1_ORN2R_AND2 : return feq + moai1 + orn2 + and2 + ff + ", F[])" + f + orn2 + and2 + ff + ", F[]));\n"; + case MOAI1_ORN2R_OR2 : return feq + moai1 + orn2 + or2 + ff + ", F[])" + f + orn2 + or2 + ff + ", F[]));\n"; + case MOAI1_ORN2R_NAND2 : return feq + moai1 + orn2 + nand2 + ff + ", F[])" + f + orn2 + nand2 + ff + ", F[]));\n"; + case MOAI1_ORN2R_NOR2 : return feq + moai1 + orn2 + nor2 + ff + ", F[])" + f + orn2 + nor2 + ff + ", F[]));\n"; + case MOAI1_ORN2R_ANDN2L : return feq + moai1 + orn2 + andn2 + ff + ", F[])" + f + orn2 + andn2 + ff + ", F[]));\n"; + case MOAI1_ORN2R_ANDN2R : return feq + moai1 + orn2 + andn2 + ff + ", F[])" + f + orn2 + andn2 + ff + ", F[]));\n"; + case MOAI1_ORN2R_ORN2L : return feq + moai1 + orn2 + orn2 + ff + ", F[])" + f + orn2 + orn2 + ff + ", F[]));\n"; + case MOAI1_ORN2R_ORN2R : return feq + moai1 + orn2 + orn2 + ff + ", F[])" + f + orn2 + orn2 + ff + ", F[]));\n"; + case MAOI1_AND2_ANDN2L : return feq + maoi1 + and2 + andn2 + ff + ", F[])" + f + and2 + andn2 + ff + ", F[]));\n"; + case MAOI1_AND2_ANDN2R : return feq + maoi1 + and2 + andn2 + ff + ", F[])" + f + and2 + andn2 + ff + ", F[]));\n"; + case MAOI1_AND2_ORN2L : return feq + maoi1 + and2 + orn2 + ff + ", F[])" + f + and2 + orn2 + ff + ", F[]));\n"; + case MAOI1_AND2_ORN2R : return feq + maoi1 + and2 + orn2 + ff + ", F[])" + f + and2 + orn2 + ff + ", F[]));\n"; + case MAOI1_OR2_ANDN2L : return feq + maoi1 + or2 + andn2 + ff + ", F[])" + f + or2 + andn2 + ff + ", F[]));\n"; + case MAOI1_OR2_ANDN2R : return feq + maoi1 + or2 + andn2 + ff + ", F[])" + f + or2 + andn2 + ff + ", F[]));\n"; + case MAOI1_OR2_ORN2L : return feq + maoi1 + or2 + orn2 + ff + ", F[])" + f + or2 + orn2 + ff + ", F[]));\n"; + case MAOI1_OR2_ORN2R : return feq + maoi1 + or2 + orn2 + ff + ", F[])" + f + or2 + orn2 + ff + ", F[]));\n"; + case MAOI1_NAND2_ANDN2L : return feq + maoi1 + nand2 + andn2 + ff + ", F[])" + f + nand2 + andn2 + ff + ", F[]));\n"; + case MAOI1_NAND2_ANDN2R : return feq + maoi1 + nand2 + andn2 + ff + ", F[])" + f + nand2 + andn2 + ff + ", F[]));\n"; + case MAOI1_NAND2_ORN2L : return feq + maoi1 + nand2 + orn2 + ff + ", F[])" + f + nand2 + orn2 + ff + ", F[]));\n"; + case MAOI1_NAND2_ORN2R : return feq + maoi1 + nand2 + orn2 + ff + ", F[])" + f + nand2 + orn2 + ff + ", F[]));\n"; + case MAOI1_NOR2_ANDN2L : return feq + maoi1 + nor2 + andn2 + ff + ", F[])" + f + nor2 + andn2 + ff + ", F[]));\n"; + case MAOI1_NOR2_ANDN2R : return feq + maoi1 + nor2 + andn2 + ff + ", F[])" + f + nor2 + andn2 + ff + ", F[]));\n"; + case MAOI1_NOR2_ORN2L : return feq + maoi1 + nor2 + orn2 + ff + ", F[])" + f + nor2 + orn2 + ff + ", F[]));\n"; + case MAOI1_NOR2_ORN2R : return feq + maoi1 + nor2 + orn2 + ff + ", F[])" + f + nor2 + orn2 + ff + ", F[]));\n"; + case MAOI1_ANDN2R_AND2 : return feq + maoi1 + andn2 + and2 + ff + ", F[])" + f + andn2 + and2 + ff + ", F[]));\n"; + case MAOI1_ANDN2R_OR2 : return feq + maoi1 + andn2 + or2 + ff + ", F[])" + f + andn2 + or2 + ff + ", F[]));\n"; + case MAOI1_ANDN2R_NAND2 : return feq + maoi1 + andn2 + nand2 + ff + ", F[])" + f + andn2 + nand2 + ff + ", F[]));\n"; + case MAOI1_ANDN2R_NOR2 : return feq + maoi1 + andn2 + nor2 + ff + ", F[])" + f + andn2 + nor2 + ff + ", F[]));\n"; + case MAOI1_ANDN2R_ANDN2L : return feq + maoi1 + andn2 + andn2 + ff + ", F[])" + f + andn2 + andn2 + ff + ", F[]));\n"; + case MAOI1_ANDN2R_ANDN2R : return feq + maoi1 + andn2 + andn2 + ff + ", F[])" + f + andn2 + andn2 + ff + ", F[]));\n"; + case MAOI1_ANDN2R_ORN2L : return feq + maoi1 + andn2 + orn2 + ff + ", F[])" + f + andn2 + orn2 + ff + ", F[]));\n"; + case MAOI1_ANDN2R_ORN2R : return feq + maoi1 + andn2 + orn2 + ff + ", F[])" + f + andn2 + orn2 + ff + ", F[]));\n"; + case MAOI1_ORN2R_AND2 : return feq + maoi1 + orn2 + and2 + ff + ", F[])" + f + orn2 + and2 + ff + ", F[]));\n"; + case MAOI1_ORN2R_OR2 : return feq + maoi1 + orn2 + or2 + ff + ", F[])" + f + orn2 + or2 + ff + ", F[]));\n"; + case MAOI1_ORN2R_NAND2 : return feq + maoi1 + orn2 + nand2 + ff + ", F[])" + f + orn2 + nand2 + ff + ", F[]));\n"; + case MAOI1_ORN2R_NOR2 : return feq + maoi1 + orn2 + nor2 + ff + ", F[])" + f + orn2 + nor2 + ff + ", F[]));\n"; + case MAOI1_ORN2R_ANDN2L : return feq + maoi1 + orn2 + andn2 + ff + ", F[])" + f + orn2 + andn2 + ff + ", F[]));\n"; + case MAOI1_ORN2R_ANDN2R : return feq + maoi1 + orn2 + andn2 + ff + ", F[])" + f + orn2 + andn2 + ff + ", F[]));\n"; + case MAOI1_ORN2R_ORN2L : return feq + maoi1 + orn2 + orn2 + ff + ", F[])" + f + orn2 + orn2 + ff + ", F[]));\n"; + case MAOI1_ORN2R_ORN2R : return feq + maoi1 + orn2 + orn2 + ff + ", F[])" + f + orn2 + orn2 + ff + ", F[]));\n"; + case MOAI1_ANDN2L_AND2 : return feq + moai1 + andn2 + "F[], " + and2 + ff + ")" + f + andn2 + "F[], " + and2 + ff + "));\n"; + case MOAI1_ANDN2L_OR2 : return feq + moai1 + andn2 + "F[], " + or2 + ff + ")" + f + andn2 + "F[], " + or2 + ff + "));\n"; + case MOAI1_ANDN2L_NAND2 : return feq + moai1 + andn2 + "F[], " + nand2 + ff + ")" + f + andn2 + "F[], " + nand2 + ff + "));\n"; + case MOAI1_ANDN2L_NOR2 : return feq + moai1 + andn2 + "F[], " + nor2 + ff + ")" + f + andn2 + "F[], " + nor2 + ff + "));\n"; + case MOAI1_ANDN2L_ANDN2L : return feq + moai1 + andn2 + "F[], " + andn2 + ff + ")" + f + andn2 + "F[], " + andn2 + ff + "));\n"; + case MOAI1_ANDN2L_ANDN2R : return feq + moai1 + andn2 + "F[], " + andn2 + ff + ")" + f + andn2 + "F[], " + andn2 + ff + "));\n"; + case MOAI1_ANDN2L_ORN2L : return feq + moai1 + andn2 + "F[], " + orn2 + ff + ")" + f + andn2 + "F[], " + orn2 + ff + "));\n"; + case MOAI1_ANDN2L_ORN2R : return feq + moai1 + andn2 + "F[], " + orn2 + ff + ")" + f + andn2 + "F[], " + orn2 + ff + "));\n"; + case MOAI1_ORN2L_AND2 : return feq + moai1 + orn2 + "F[], " + and2 + ff + ")" + f + orn2 + "F[], " + and2 + ff + "));\n"; + case MOAI1_ORN2L_OR2 : return feq + moai1 + orn2 + "F[], " + or2 + ff + ")" + f + orn2 + "F[], " + or2 + ff + "));\n"; + case MOAI1_ORN2L_NAND2 : return feq + moai1 + orn2 + "F[], " + nand2 + ff + ")" + f + orn2 + "F[], " + nand2 + ff + "));\n"; + case MOAI1_ORN2L_NOR2 : return feq + moai1 + orn2 + "F[], " + nor2 + ff + ")" + f + orn2 + "F[], " + nor2 + ff + "));\n"; + case MOAI1_ORN2L_ANDN2L : return feq + moai1 + orn2 + "F[], " + andn2 + ff + ")" + f + orn2 + "F[], " + andn2 + ff + "));\n"; + case MOAI1_ORN2L_ANDN2R : return feq + moai1 + orn2 + "F[], " + andn2 + ff + ")" + f + orn2 + "F[], " + andn2 + ff + "));\n"; + case MOAI1_ORN2L_ORN2L : return feq + moai1 + orn2 + "F[], " + orn2 + ff + ")" + f + orn2 + "F[], " + orn2 + ff + "));\n"; + case MOAI1_ORN2L_ORN2R : return feq + moai1 + orn2 + "F[], " + orn2 + ff + ")" + f + orn2 + "F[], " + orn2 + ff + "));\n"; + case MAOI1_ANDN2L_AND2 : return feq + maoi1 + andn2 + "F[], " + and2 + ff + ")" + f + andn2 + "F[], " + and2 + ff + "));\n"; + case MAOI1_ANDN2L_OR2 : return feq + maoi1 + andn2 + "F[], " + or2 + ff + ")" + f + andn2 + "F[], " + or2 + ff + "));\n"; + case MAOI1_ANDN2L_NAND2 : return feq + maoi1 + andn2 + "F[], " + nand2 + ff + ")" + f + andn2 + "F[], " + nand2 + ff + "));\n"; + case MAOI1_ANDN2L_NOR2 : return feq + maoi1 + andn2 + "F[], " + nor2 + ff + ")" + f + andn2 + "F[], " + nor2 + ff + "));\n"; + case MAOI1_ANDN2L_ANDN2L : return feq + maoi1 + andn2 + "F[], " + andn2 + ff + ")" + f + andn2 + "F[], " + andn2 + ff + "));\n"; + case MAOI1_ANDN2L_ANDN2R : return feq + maoi1 + andn2 + "F[], " + andn2 + ff + ")" + f + andn2 + "F[], " + andn2 + ff + "));\n"; + case MAOI1_ANDN2L_ORN2L : return feq + maoi1 + andn2 + "F[], " + orn2 + ff + ")" + f + andn2 + "F[], " + orn2 + ff + "));\n"; + case MAOI1_ANDN2L_ORN2R : return feq + maoi1 + andn2 + "F[], " + orn2 + ff + ")" + f + andn2 + "F[], " + orn2 + ff + "));\n"; + case MAOI1_ORN2L_AND2 : return feq + maoi1 + orn2 + "F[], " + and2 + ff + ")" + f + orn2 + "F[], " + and2 + ff + "));\n"; + case MAOI1_ORN2L_OR2 : return feq + maoi1 + orn2 + "F[], " + or2 + ff + ")" + f + orn2 + "F[], " + or2 + ff + "));\n"; + case MAOI1_ORN2L_NAND2 : return feq + maoi1 + orn2 + "F[], " + nand2 + ff + ")" + f + orn2 + "F[], " + nand2 + ff + "));\n"; + case MAOI1_ORN2L_NOR2 : return feq + maoi1 + orn2 + "F[], " + nor2 + ff + ")" + f + orn2 + "F[], " + nor2 + ff + "));\n"; + case MAOI1_ORN2L_ANDN2L : return feq + maoi1 + orn2 + "F[], " + andn2 + ff + ")" + f + orn2 + "F[], " + andn2 + ff + "));\n"; + case MAOI1_ORN2L_ANDN2R : return feq + maoi1 + orn2 + "F[], " + andn2 + ff + ")" + f + orn2 + "F[], " + andn2 + ff + "));\n"; + case MAOI1_ORN2L_ORN2L : return feq + maoi1 + orn2 + "F[], " + orn2 + ff + ")" + f + orn2 + "F[], " + orn2 + ff + "));\n"; + case MAOI1_ORN2L_ORN2R : return feq + maoi1 + orn2 + "F[], " + orn2 + ff + ")" + f + orn2 + "F[], " + orn2 + ff + "));\n"; + } + return feq+tmp3+tmp1+tmp2+ff+", F[])"+f+ tmp1+tmp2+ff+", F[]));\n"; +} + +template +string lighter::bool_op_to_string(const function_t f, uint8_t op, bool reverse) +{ + switch (op) + { + case NOT1 : + return bool_op_not(f, bool_op_base_string(op), reverse); + + case XOR2 : + case XNOR2 : + case MAOI1 : + case MOAI1 : + return bool_op_two_inputs(f, op, bool_op_base_string(op), reverse); + + case MOAI1_NAND2 : + case MOAI1_NOR2 : + case MOAI1_AND2 : + case MOAI1_OR2 : + case MAOI1_NAND3 : + case MAOI1_NOR3 : + case MOAI1_NAND3 : + case MOAI1_NOR3 : + case XOR2_AND2 : + case XOR2_OR2 : + case MAOI1_ANDN2L : + case MAOI1_ANDN2R : + case MAOI1_ORN2L : + case MAOI1_ORN2R : + case MOAI1_ANDN2L : + case MOAI1_ANDN2R : + case MOAI1_ORN2L : + case MOAI1_ORN2R : + case XOR2_ANDN2L : + case XOR2_ANDN2R : + case XOR2_ORN2L : + case XOR2_ORN2R : + case XOR2_OR2_OR2 : + + case MOAI1_NAND2_AND2 : + case MAOI1_AND2_AND2 : + case MOAI1_ORN2L_NAND2 : + case MAOI1_ANDN2R_NAND2 : + case XOR2_AND2_AND2 : + + case MAOI1_NAND2 : + case MAOI1_NOR2 : + case MAOI1_AND2 : + case MAOI1_OR2 : + case MAOI1_AND3 : + + case MAOI1_OR3 : + case MOAI1_NOR2_OR2 : + case MAOI1_OR2_OR2 : + case MOAI1_AND3 : + case MAOI1_NAND2_AND2 : + case MOAI1_AND2_AND2 : + case MOAI1_OR3 : + case MAOI1_NOR2_OR2 : + case MOAI1_OR2_OR2 : + case MOAI1_ANDN2L_NOR2 : + case MAOI1_ORN2R_NOR2 : + case MOAI1_ORN2R_NOR2 : + case MAOI1_ANDN2L_NOR2 : + case MOAI1_ANDN2R_NAND2 : + case MAOI1_ORN2L_NAND2 : + return bool_op_super_gates(f, op, bool_op_base_string(op), reverse); + + case MOAI1_AND2_OR2 : + case MAOI1_NAND2_OR2 : + case MOAI1_ANDN2R_NOR2 : + case MAOI1_ORN2L_NOR2 : + + case MOAI1_OR2_AND2 : + case MAOI1_NOR2_AND2 : + case MOAI1_ORN2R_NAND2 : + case MAOI1_ANDN2L_NAND2 : + + case MOAI1_AND2_NOR2 : + case MAOI1_NAND2_NOR2 : + case MOAI1_ANDN2R_OR2 : + case MAOI1_ORN2L_OR2 : + case XOR2_ORN2L_OR2 : + + case MOAI1_NAND2_OR2 : + case MAOI1_AND2_OR2 : + case MOAI1_ORN2L_NOR2 : + case MAOI1_ANDN2R_NOR2 : + case XOR2_AND2_OR2 : + + case MOAI1_NOR2_AND2 : + case MAOI1_OR2_AND2 : + case MOAI1_ANDN2L_NAND2 : + case MAOI1_ORN2R_NAND2 : + case XOR2_OR2_AND2 : + + case MOAI1_NAND2_ANDN2R : + case MOAI1_ORN2L_ORN2L : + case MAOI1_AND2_ANDN2R : + case MAOI1_ANDN2R_ORN2L : + case XOR2_AND2_ANDN2R : + case XOR2_ANDN2R_ORN2L : + + case MOAI1_NAND2_ORN2L : + case MOAI1_ORN2L_ANDN2R : + case MAOI1_AND2_ORN2L : + case MAOI1_ANDN2R_ANDN2R : + case XOR2_AND2_ORN2L : + case XOR2_ANDN2R_ANDN2R : + + case MOAI1_NAND2_ORN2R : + case MOAI1_ORN2L_ANDN2L : + case MAOI1_AND2_ORN2R : + case MAOI1_ANDN2R_ANDN2L : + case XOR2_AND2_ORN2R : + case XOR2_ANDN2R_ANDN2L : + + case MOAI1_NOR2_ANDN2L : + case MOAI1_ANDN2L_ORN2R : + case MAOI1_OR2_ANDN2L : + case MAOI1_ORN2R_ORN2R : + case XOR2_OR2_ANDN2L : + case XOR2_ORN2R_ORN2R : + + case MOAI1_NOR2_ANDN2R : + case MOAI1_ANDN2L_ORN2L : + case MAOI1_OR2_ANDN2R : + case MAOI1_ORN2R_ORN2L : + case XOR2_OR2_ANDN2R : + case XOR2_ORN2R_ORN2L : + + case MOAI1_NOR2_ORN2L : + case MOAI1_ANDN2L_ANDN2R : + case MAOI1_OR2_ORN2L : + case MAOI1_ORN2R_ANDN2R : + case XOR2_OR2_ORN2L : + case XOR2_ORN2R_ANDN2R : + + case MOAI1_NOR2_ORN2R : + case MOAI1_ANDN2L_ANDN2L : + case MAOI1_OR2_ORN2R : + case MAOI1_ORN2R_ANDN2L : + case XOR2_OR2_ORN2R : + case XOR2_ORN2R_ANDN2L : + + case MOAI1_OR2_NAND2 : + case MAOI1_NOR2_NAND2 : + case MOAI1_ORN2R_AND2 : + case MAOI1_ANDN2L_AND2 : + case XOR2_ANDN2L_AND2 : + + case MOAI1_OR2_NOR2 : + case MAOI1_NOR2_NOR2 : + case MOAI1_ORN2R_OR2 : + case MAOI1_ANDN2L_OR2 : + case XOR2_ANDN2L_OR2 : + + case MOAI1_OR2_ORN2R : + case MOAI1_ORN2R_ANDN2L : + case MAOI1_NOR2_ORN2R : + case MAOI1_ANDN2L_ANDN2L : + case XOR2_ANDN2L_ANDN2L : + + case MOAI1_OR2_ORN2L : + case MOAI1_ORN2R_ANDN2R : + case MAOI1_NOR2_ORN2L : + case MAOI1_ANDN2L_ANDN2R : + case XOR2_ANDN2L_ANDN2R : + + case MOAI1_OR2_ANDN2R : + case MOAI1_ORN2R_ORN2L : + case MAOI1_NOR2_ANDN2R : + case MAOI1_ANDN2L_ORN2L : + case XOR2_ANDN2L_ORN2L : + + case MOAI1_OR2_ANDN2L : + case MOAI1_ORN2R_ORN2R : + case MAOI1_NOR2_ANDN2L : + case MAOI1_ANDN2L_ORN2R : + case XOR2_ANDN2L_ORN2R : + + case MOAI1_NAND2_NAND2 : + case MAOI1_AND2_NAND2 : + case MOAI1_ORN2L_AND2 : + case MAOI1_ANDN2R_AND2 : + case XOR2_ANDN2R_AND2 : + + case MOAI1_NAND2_NOR2 : + case MAOI1_AND2_NOR2 : + case MOAI1_ORN2L_OR2 : + case MAOI1_ANDN2R_OR2 : + case XOR2_ANDN2R_OR2 : + + case MOAI1_NAND2_ANDN2L : + case MOAI1_ORN2L_ORN2R : + case MAOI1_AND2_ANDN2L : + case MAOI1_ANDN2R_ORN2R : + case XOR2_AND2_ANDN2L : + case XOR2_ANDN2R_ORN2R : + + case MOAI1_AND2_NAND2 : + case MAOI1_NAND2_NAND2 : + case MOAI1_ANDN2R_AND2 : + case MAOI1_ORN2L_AND2 : + case XOR2_ORN2L_AND2 : + + case MOAI1_AND2_ORN2R : + case MOAI1_ANDN2R_ANDN2L : + case MAOI1_NAND2_ORN2R : + case MAOI1_ORN2L_ANDN2L : + case XOR2_ORN2L_ANDN2L : + + case MOAI1_AND2_ORN2L : + case MOAI1_ANDN2R_ANDN2R : + case MAOI1_NAND2_ORN2L : + case MAOI1_ORN2L_ANDN2R : + case XOR2_ORN2L_ANDN2R : + + case MOAI1_AND2_ANDN2R : + case MOAI1_ANDN2R_ORN2L : + case MAOI1_NAND2_ANDN2R : + case MAOI1_ORN2L_ORN2L : + case XOR2_ORN2L_ORN2L : + + case MOAI1_AND2_ANDN2L : + case MOAI1_ANDN2R_ORN2R : + case MAOI1_NAND2_ANDN2L : + case MAOI1_ORN2L_ORN2R : + case XOR2_ORN2L_ORN2R : + + case MOAI1_NOR2_NAND2 : + case MAOI1_OR2_NAND2 : + case MOAI1_ANDN2L_AND2 : + case MAOI1_ORN2R_AND2 : + case XOR2_ORN2R_AND2 : + + case MOAI1_NOR2_NOR2 : + case MAOI1_OR2_NOR2 : + case MOAI1_ANDN2L_OR2 : + case MAOI1_ORN2R_OR2 : + case XOR2_ORN2R_OR2 : + return bool_op_super_super_gates(f, op, bool_op_base_string(op), reverse); + } + return ""; +} + + +#endif // LIGHTER_STRING_BOOL_OP_H__ + diff --git a/lighter_test.cpp b/lighter_test.cpp new file mode 100644 index 0000000..b8d6325 --- /dev/null +++ b/lighter_test.cpp @@ -0,0 +1,152 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#include +#include +#include +#include +#include +#include "func.hpp" + +using namespace Peigen; +using namespace Peigen::weight; +using namespace std; + +template +inline T MAOI1(T x, T y, T z, T t) +{ + return (~((x & y) | (~(z | t)))); +} + +template +inline T MOAI1(T x, T y, T z, T t) +{ + return (~((x | y) & (~(z & t)))); +} + +template +inline T NAND2(T x, T y) +{ + return ~(x & y); +} + +template +inline T NOR2(T x, T y) +{ + return ~(x | y); +} + +template +inline T NAND3(T x, T y, T z) +{ + return ~(x & y & z); +} + +template +inline T NOR3(T x, T y, T z) +{ + return ~(x | y | z); +} + +template +inline T AND3(T x, T y, T z) +{ + return (x & y & z); +} + +template +inline T OR3(T x, T y, T z) +{ + return (x | y | z); +} + +template +inline T AND2(T x, T y) +{ + return (x & y); +} + +template +inline T ANDN2(T x, T y) +{ + return (~x & y); +} + +template +inline T OR2(T x, T y) +{ + return (x | y); +} + +template +inline T ORN2(T x, T y) +{ + return ((~x) | y); +} + +template +inline T XOR2(T x, T y) +{ + return (x ^ y); +} + +template +inline T XNOR2(T x, T y) +{ + return ~(x ^ y); +} + +template +inline T NOT1(T x) +{ + return ~x; +} + +int main() +{ + ofstream fout("results_test.txt"); + +#define n 4 + function_t I = function_t::INPUT_DEFAULT(); + bit_slice_t X; + bit_slice_t F; + + #include "testcode.cpp" + + fout.close(); + +} diff --git a/lighter_utils.hpp b/lighter_utils.hpp new file mode 100644 index 0000000..0efbf65 --- /dev/null +++ b/lighter_utils.hpp @@ -0,0 +1,191 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#ifndef LIGHTER_UTILS_H__ +#define LIGHTER_UTILS_H__ + +#include "func.hpp" + +using namespace Peigen; +using namespace Peigen::weight; +using namespace std; + +template +void lighter::print_graphe_info() +{ + int tot_f1 = 0; int tot_f2 = 0; + + cout << "\n\033[0;31mf1_succ : \033[0m"; + + auto it = f1_succ.begin(); + + while (it != f1_succ.end()) + { + cout << (*it).first << " (" << (*it).second.size() << ") "; + tot_f1 += ((*it).second.size()); + it++; + } + cout << "\n\033[1;33mTotal = " << tot_f1; + cout << "\n\033[0;31mf2_succ : \033[0m"; + it = f2_succ.begin(); + while (it != f2_succ.end()) + { + cout << (*it).first << " (" << (*it).second.size() << ") "; + tot_f2 += ((*it).second.size()); + it++; + } + cout << "\n\033[1;33mTotal = " << tot_f2 << "\033[0m\n"; +} + +template +void lighter::exit_m() +{ + if(write_in_file) + { + graphe_to_file(&f1_succ, "f1"); + graphe_to_file(&f2_succ, "f2"); + } + //system("notify-send 'EXIT'"); + //exit(0); +} + +template +void lighter::graphe_to_file(map > > *graphe, + string graphe_name) +{ + string name; + int tot_number = 0; + /* + Generating file_info + */ + ofstream file_info(graphe_name + "_infos.txt"); + file_info << "Number of lists in graph : " + << graphe->size() + << endl; + auto it_graphe = graphe->begin(); + while(it_graphe != graphe->end()) + { + if((*it_graphe).second.size() != 0) + { + tot_number += (*it_graphe).second.size(); + file_info << "Number of nodes in list " + << (*it_graphe).first + << " : " + << (*it_graphe).second.size() + << endl; + } + if((*it_graphe).second.size() != 0) + { + name = graphe_name + + string("_list_") + + to_string((*it_graphe).first) + + string(".txt"); + + ofstream file_list(name); + /* + Generating *_list_n.txt + */ + if(file_list.is_open()) + { + auto it_curr = (*it_graphe).second.begin(); + while(it_curr != (*it_graphe).second.end()) + { + file_list << (*it_curr).to_string()< +void lighter::print_uint16(uint16_t n){ + int i = 16; + while(i != 0) + { + printf("%c", (n & 0x8000) == 0 ? '0' : '1'); + n <<= 1; + i--; + } + printf("\n"); +} + +template +void lighter::print_uint8(uint8_t n){ + int i = 8; + while(i != 0) + { + printf("%c", (n & 0x80) == 0 ? '0' : '1'); + n <<= 1; + i--; + } + printf("\n"); +} + +template +string lighter::uint8_to_string(uint8_t n) +{ + string s; + int i = 8; + while(i != 0) + { + (n & 0x80) == 0 ? s+='0' : s+='1'; + n <<= 1; + i--; + } + return s; +} + +template +void lighter::print_uint32(uint32_t n){ + int i = 32; + while(i != 0) + { + printf("%c", (n & 0x80000000) == 0 ? '0' : '1'); + n <<= 1; + i--; + } + printf("\n"); +} + +#endif // LIGHTER_UTILS_H__ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..53fd5ef --- /dev/null +++ b/main.cpp @@ -0,0 +1,409 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "faster.hpp" +#include "func.hpp" +#include "evaluator.hpp" +#include "lighter.hpp" + +using namespace std; +using namespace Peigen; +using namespace Peigen::depth; +using namespace Peigen::weight; + +int main(int argc, char *argv[]) +{ + clock_t t0, t1; + std::string mainargs; + if (argc > 1) + { + for (int i = 1; i < argc; i++) + { + mainargs += argv[i]; + mainargs += " "; + } + mainargs.pop_back(); + } + string args = mainargs; + +#if defined(EVA) + // Evaluate Cryptographic Properties: evaluate() and evaluate_verbose() + cout << "Evaluating Cryptographic Properties" << endl; + t0 = clock(); + Peigen::weight::lighter<3> sbox3_Eva; + sbox3_Eva.omp_nb_threads = 1; + sbox3_Eva.evaluate("sboxes3.txt", "properties_sboxes3.csv"); + sbox3_Eva.evaluate_verbose("sboxes3.txt", "properties"); + t1 = clock() - t0; + cout << "Total time (mins): " << (((double)t1) / (CLOCKS_PER_SEC * 60.0)) << endl; + + t0 = clock(); + Peigen::weight::lighter<4> sbox4_Eva; + sbox4_Eva.omp_nb_threads = 1; + sbox4_Eva.evaluate("sboxes4.txt", "properties_sboxes4.csv"); + sbox4_Eva.evaluate_verbose("sboxes4.txt", "properties"); + t1 = clock() - t0; + cout << "Total time (mins): " << (((double)t1) / (CLOCKS_PER_SEC * 60.0)) << endl; + + t0 = clock(); + Peigen::weight::lighter<5> sbox5_Eva; + sbox5_Eva.omp_nb_threads = 1; + sbox5_Eva.evaluate("sboxes5.txt", "properties_sboxes5.csv"); + sbox5_Eva.evaluate_verbose("sboxes5.txt", "properties"); + t1 = clock() - t0; + cout << "Total time (mins): " << (((double)t1) / (CLOCKS_PER_SEC * 60.0)) << endl; + + t0 = clock(); + Peigen::weight::lighter<6> sbox6_Eva; + sbox6_Eva.omp_nb_threads = 1; + sbox6_Eva.evaluate("sboxes6.txt", "properties_sboxes6.csv"); + sbox6_Eva.evaluate_verbose("sboxes6.txt", "properties"); + t1 = clock() - t0; + cout << "Total time (mins): " << (((double)t1) / (CLOCKS_PER_SEC * 60.0)) << endl; + + t0 = clock(); + Peigen::weight::lighter<7> sbox7_Eva; + sbox7_Eva.omp_nb_threads = 1; + sbox7_Eva.evaluate("sboxes7.txt", "properties_sboxes7.csv"); + sbox7_Eva.evaluate_verbose("sboxes7.txt", "properties"); + t1 = clock() - t0; + cout << "Total time (mins): " << (((double)t1) / (CLOCKS_PER_SEC * 60.0)) << endl; + + t0 = clock(); + Peigen::weight::lighter<8> sbox8_Eva; + sbox8_Eva.omp_nb_threads = 1; + sbox8_Eva.evaluate("sboxes8.txt", "properties_sboxes8.csv"); + sbox8_Eva.evaluate_verbose("sboxes8.txt", "properties"); + t1 = clock() - t0; + cout << "Total time (mins): " << (((double)t1) / (CLOCKS_PER_SEC * 60.0)) << endl; +#endif + +#if defined(EVA_Single) + string SboxName; + string SboxLUTstr; + if (argc <= 2) + { + //SboxName = "SERPENT_S0"; + //SboxLUTstr = "03080f010a06050b0e0d04020700090c"; + SboxName = "PRESENT"; + SboxLUTstr = "0c05060b09000a0d030e0f0804070102"; + } + else + { + SboxName = argv[1]; + SboxLUTstr = argv[2]; + } + Peigen::function_t<4> S(SboxLUTstr); + Peigen::function_t<4> iS; + // Or initilized by: + // uint8_t S_LUT[16] = {3,8,15,1,10,6,5,11,14,13,4,2,7,0,9,12}; + // function_t<4> S(S_LUT); + string fn = "properties_" + SboxName + ".txt"; + ofstream fout(fn.c_str()); + fout << SboxName << ", " << S.to_string() << endl << endl; + // Call the following for evaluating concerned properties + fout << S.show_LUT() << endl; + fout << S.show_coordinates_ANF() << endl; + fout << S.show_components_ANF() << endl; + fout << S.show_difference_distribution_matrix() << endl; + fout << S.show_boomerang_connection_table() << endl; + fout << S.show_linear_approximation_matrix() << endl; + fout << S.show_autocorrelation_matrix() << endl; + fout << S.show_k_product_degree_table() << endl; + fout << S.show_V_S_table() << endl; + fout << S.show_linear_structures() << endl; + fout << S.show_v_w_linear() << endl; + // Or call the following to show all properties + // S.show_all_properties(fout); + if (S.is_permutation()) + { + iS = S.inverse(); + fout << SboxName << " inverse, " << iS.to_string() << endl << endl; + fout << iS.show_LUT() << endl; + fout << iS.show_coordinates_ANF() << endl; + fout << iS.show_components_ANF() << endl; + fout << iS.show_difference_distribution_matrix() << endl; + fout << iS.show_boomerang_connection_table() << endl; + fout << iS.show_linear_approximation_matrix() << endl; + fout << iS.show_autocorrelation_matrix() << endl; + fout << iS.show_k_product_degree_table() << endl; + fout << iS.show_V_S_table() << endl; + fout << iS.show_linear_structures() << endl; + fout << iS.show_v_w_linear() << endl; + // Or call the following to show all properties + // iS.show_all_properties(fout); + } + + // Evaluating equvalence classes for a single S-box + Peigen::function_t<4> Gi; + Peigen::function_t<4> A; + Peigen::function_t<4> B; + int gi; + uint8_t a; + uint8_t b; + if(S.is_optimal(gi, Gi, A, B, a, b)) + { + fout << "Belongs to optimal class: G" << gi << endl; + fout << "G: " << Gi.to_string() << ", " << Gi.LUT_to_string() << endl; + fout << "A: " << A.to_string() << ", " << A.LUT_to_string() << endl; + fout << "B: " << B.to_string() << ", " << B.LUT_to_string() << endl; + fout << "a: " << a + '\0' << endl; + fout << "b: " << b + '\0' << endl; + fout << endl; + iS.is_optimal(gi, Gi, A, B, a, b); + fout << "Inverse S-box belongs to optimal class: G" << gi << endl; + fout << "G: " << Gi.to_string() << ", " << Gi.LUT_to_string() << endl; + fout << "A: " << A.to_string() << ", " << A.LUT_to_string() << endl; + fout << "B: " << B.to_string() << ", " << B.LUT_to_string() << endl; + fout << "a: " << a + '\0' << endl; + fout << "b: " << b + '\0' << endl; + } + else + { + fout << "Is not optimal." << endl; + } + fout << endl; + fout.close(); + + S.get_PE((SboxName + "_PE.txt").c_str()); + S.get_XE((SboxName + "_XE.txt").c_str()); + S.get_PXE((SboxName + "_PXE.txt").c_str()); + // Partition the affine equvalent class will take about 15 mins, and only support for 4-bit S-box + // so is commented here, uncomment it if need + // S.get_AE_PXEreps((SboxName + "_AE_PXEreps.txt").c_str()); + + // Testing two S-boxes for Affine Equivalence + string S1str = "5473_f322_131f_62c7"; /* PRINCE */ + string S2str = "6f48_16f8_e2e4_b86a"; /* G13 */ + Peigen::function_t<4> S1(S1str); + Peigen::function_t<4> S2(S2str); + Peigen::function_t<4> RS; + Peigen::function_t<4> S1_L1, S1_L2; + Peigen::function_t<4> S2_L1, S2_L2; + uint8_t S1_c; + uint8_t S2_c; + + fout.open((S1str + "_AE_" + S2str + ".txt").c_str()); + fout << "S1: " << S1.to_string() << ", " << S1.LUT_to_string() << endl; + fout << "S2: " << S2.to_string() << ", " << S2.LUT_to_string() << endl; + if(S1.is_AE(S2, RS, S1_L1, S1_L2, S1_c, S2_L1, S2_L2, S2_c)) + { + fout << "RS: " << RS.to_string() << ", " << RS.LUT_to_string() << endl; + fout << "S1_L1: " << S1_L1.to_string() << ", " << S1_L1.LUT_to_string() << endl; + fout << "S1_L2: " << S1_L2.to_string() << ", " << S1_L2.LUT_to_string() << endl; + fout << "S1_c: " << S1_c + '\0' << endl; + fout << "S2_L1: " << S2_L1.to_string() << ", " << S2_L1.LUT_to_string() << endl; + fout << "S2_L2: " << S2_L2.to_string() << ", " << S2_L2.LUT_to_string() << endl; + fout << "S2_c: " << S2_c + '\0' << endl; + } + else + { + fout << "Is not AE." << endl; + } + fout.close(); +#endif + + +#if defined(GC) + cout << "Finding BGC/GEC/MC implementations" << endl; +#if(SIZEINBIT == 3) + t0 = clock(); + Peigen::weight::lighter<3> sbox3_GC; + if (argc <= 1) + { + args = "-v -c 6 -l 6 -p 4 -r 160 --not1 --and2 --andn2 --or2 --xor2 -f software.conf"; + //args = "-v -c 7 -l 7 -p 4 -r 160 --not1 --nand2 --nor2 --and2 --or2 --xor2 --xnor2 --andn2 --orn2 --nand3 --nor3 --maoi1 --moai1 -f UMC180nm.conf"; + //args = "-av -c 7 -l 7 -p 4 -r 160 -f TSMC65nm.conf"; + } + sbox3_GC.pre_compute(args); + sbox3_GC.search_batch_concatenate(args); + t1 = clock() - t0; + cout << "Total time (mins): " << (((double)t1) / (CLOCKS_PER_SEC * 60.0)) << endl; +#endif +#if(SIZEINBIT == 4) + t0 = clock(); + Peigen::weight::lighter<4> sbox4_GC; + if (argc <= 1) + { + // Here, the limit (-c) for pre-compute and the limit (-l) for finding implementations + // are set to be a little bit smaller than that set to get the results in the paper. + // In the paper, -c and -l for software is set to be 7, for TSMC65nm and UMC180nm they are set to be 12. + // That will cost reletively longer time and large memory. + // So, to run as a demon on a PC, we recommend to set them smaller + // and set them larger to find better solutions when running on a server + // The following settings for ``generation'' are similiar. + args = "-v -c 6 -l 6 -p 4 -r 160 --not1 --and2 --andn2 --or2 --xor2 -f software.conf"; + //args = "-v -c 9 -l 9 -p 4 -r 160 --not1 --nand2 --nor2 --and2 --or2 --xor2 --xnor2 --andn2 --orn2 --nand3 --nor3 --maoi1 --moai1 -f UMC180nm.conf"; + //args = "-av -c 9 -l 9 -p 4 -r 160 -f TSMC65nm.conf"; + } + sbox4_GC.pre_compute(args); + sbox4_GC.search_batch_concatenate(args); + t1 = clock() - t0; + cout << "Total time (mins): " << (((double)t1) / (CLOCKS_PER_SEC * 60.0)) << endl; +#endif +#endif + +#if defined(DEPTH) + // Find Depth-optimal implementation + cout << "Finding Depth-optimal implementation" << endl; + +#if(SIZEINBIT == 3) + // Depth for 3-bit S-boxes; + t0 = clock(); + Peigen::depth::faster<3> sbox3_Depth; + if (argc <= 1) + { + args = "-v -c 4 -p 4 -r 160 --not1 --and2 --andn2 --or2 --xor2 -f software.conf"; + //args = "-v -c 4 -p 4 -r 160 --not1 --nand2 --nor2 --and2 --or2 --xor2 --xnor2 --andn2 --orn2 --nand3 --nor3 --maoi1 --moai1 -f UMC180nm.conf"; + //args = "-av -c 4 -p 4 -r 160 -f TSMC65nm.conf"; + //args = "-v -c 4 -p 4 -r 160 --not1 --and2 --nand2 --or2 --nor2 --xor2 --xnor2 -f STM65nm.conf"; + //args = "-v -c 4 -p 4 -r 160 --not1 --and2 --nand2 --or2 --nor2 --xor2 --xnor2 -f gatec.conf"; + } + sbox3_Depth.pre_compute(args); + sbox3_Depth.search_batch(args); + t1 = clock() - t0; + cout << "Total time (mins): " << (((double)t1) / (CLOCKS_PER_SEC * 60.0)) << endl; +#endif +#if(SIZEINBIT == 4) + // Depth for 4-bit S-boxes; + t0 = clock(); + Peigen::depth::faster<4> sbox4_Depth; + if (argc <= 1) + { + args = "-v -c 4 -p 4 -r 160 --not1 --and2 --andn2 --or2 --xor2 -f software.conf"; + //args = "-v -c 3 -l 3 -p 4 -r 160 --not1 --nand2 --nor2 --and2 --or2 --xor2 --xnor2 --andn2 --orn2 --nand3 --nor3 --maoi1 --moai1 -f UMC180nm.conf"; + //args = "-av -c 3 -l 3 -p 4 -r 160 -f TSMC65nm.conf"; + //args = "-v -c 4 -l 4 -p 4 -r 160 --not1 --and2 --nand2 --or2 --nor2 --xor2 --xnor2 -f STM65nm.conf"; + //args = "-v -c 4 -l 4 -p 4 -r 160 --not1 --and2 --nand2 --or2 --nor2 --xor2 --xnor2 -f gatec.conf"; + } + sbox4_Depth.pre_compute(args); + sbox4_Depth.search_batch(args); + t1 = clock() - t0; + cout << "Total time (mins): " << (((double)t1) / (CLOCKS_PER_SEC * 60.0)) << endl; +#endif +#endif + +#if defined(FILTER) + cout << "Evaluating and filtering " << endl; + +#if(SIZEINBIT == 3) + t0 = clock(); + Peigen::weight::lighter<3> sbox3_FILTER; + if (argc <= 1) + { + args = "-v -c 6 -l 6 -p 4 -r 160 --not1 --and2 --andn2 --or2 --xor2 -f software.conf -s criteria.conf -o sboxes3.txt"; + //args = "-v -c 7 -l 7 -p 4 -r 160 --not1 --nand2 --nor2 --and2 --or2 --xor2 --xnor2 --andn2 --orn2 --nand3 --nor3 --maoi1 --moai1 -f UMC180nm.conf -s criteria.conf -o sboxes3.txt"; + //args = "-av -c 7 -l 7 -p 4 -r 160 -f TSMC65nm.conf -s criteria.conf -o sboxes3.txt"; + } + // Note, if the graph under a same configuration has already been pre computed and stored, + // or if ``Cost'' is not set in ``criteria.conf'', i.e., ``Cost'' is not a criterion for filtering, + // The line ``pre_compute(args)'' can be commented. + sbox3_FILTER.pre_compute(args); + sbox3_FILTER.evaluate_filter(args); + t1 = clock() - t0; + cout << "Total time (mins): " << (((double)t1) / (CLOCKS_PER_SEC * 60.0)) << endl; +#endif +#if(SIZEINBIT == 4) + // Provide the set of criteria in a file, e.g., ``criteria_filter.conf'' + // An example is provided in ``criteria_filter.conf'': + t0 = clock(); + Peigen::weight::lighter<4> sbox4_FILTER; + if (argc <= 1) + { + args = "-v -c 6 -l 6 -p 4 -r 160 --not1 --and2 --andn2 --or2 --xor2 -f software.conf -s criteria_filter.conf -o sboxes4.txt"; + //args = "-v -c 9 -l 9 -p 4 -r 160 --not1 --nand2 --nor2 --and2 --or2 --xor2 --xnor2 --andn2 --orn2 --nand3 --nor3 --maoi1 --moai1 -f UMC180nm.conf -s criteria_filter.conf -o sboxes4.txt"; + //args = "-av -c 9 -l 9 -p 4 -r 160 -f TSMC65nm.conf -s criteria_filter.conf -o sboxes4.txt"; + } + // Note, if ``Cost'' is set in ``criteria_filter.conf'', i.e., ``Cost'' is a criterion for filtering, + // and if the graph under a same configuration has not been pre computed, + // The following line ``pre_compute(args)'' should be uncommented. + // sbox4_FILTER.pre_compute(args); + sbox4_FILTER.evaluate_filter(args); + t1 = clock() - t0; + cout << "Total time (mins): " << (((double)t1) / (CLOCKS_PER_SEC * 60.0)) << endl; +#endif +#endif + +#if defined(GEN) + cout << "Generating " << endl; + +#if(SIZEINBIT == 3) + t0 = clock(); + Peigen::weight::lighter<3> sbox3_GEN; + if (argc <= 1) + { + args = "-v -c 6 -l 6 -p 4 -r 160 --not1 --and2 --andn2 --or2 --xor2 -f software.conf -s criteria.conf"; + //args = "-v -c 7 -l 7 -p 4 -r 160 --not1 --nand2 --nor2 --and2 --or2 --xor2 --xnor2 --andn2 --orn2 --nand3 --nor3 --maoi1 --moai1 -f UMC180nm.conf -s criteria.conf"; + //args = "-av -c 7 -l 7 -p 4 -r 160 -f TSMC65nm.conf -s criteria.conf"; + } + // Note, if the graph under a same configuration has already been pre computed and stored, + // The line ``pre_compute(args)'' can be commented. + sbox3_GEN.pre_compute(args); + sbox3_GEN.generate(args); + t1 = clock() - t0; + cout << "Total time (mins): " << (((double)t1) / (CLOCKS_PER_SEC * 60.0)) << endl; +#endif +#if(SIZEINBIT == 4) + t0 = clock(); + Peigen::weight::lighter<4> sbox4_GEN; + if (argc <= 1) + { + args = "-v -c 6 -l 6 -p 4 -r 160 --not1 --and2 --andn2 --or2 --xor2 -f software.conf -s criteria.conf"; + //args = "-v -c 9 -l 9 -p 4 -r 160 --not1 --nand2 --nor2 --and2 --or2 --xor2 --xnor2 --andn2 --orn2 --nand3 --nor3 --maoi1 --moai1 -f UMC180nm.conf -s criteria.conf"; + //args = "-av -c 9 -l 9 -p 4 -r 160 -f TSMC65nm.conf -s criteria.conf"; + } + // Note, if the graph under a same configuration has already been pre computed and stored, + // The line ``pre_compute(args)'' can be commented. + sbox4_GEN.pre_compute(args); + sbox4_GEN.generate(args); + t1 = clock() - t0; + cout << "Total time (mins): " << (((double)t1) / (CLOCKS_PER_SEC * 60.0)) << endl; +#endif +#endif + + return 0; +} \ No newline at end of file diff --git a/peigen.pxd b/peigen.pxd new file mode 100644 index 0000000..0bc35d6 --- /dev/null +++ b/peigen.pxd @@ -0,0 +1,157 @@ +########################################################## +# PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes +# +# Copyright 2019 by +# Zhenzhen Bao +# Jian Guo +# San Ling +# Yu Sasaki +# +# This platform is developed based on the open source application +# +# Optimizing Implementations of Lightweight Building Blocks +# +# Copyright 2017 by +# Jade Tourteaux +# Jérémy Jean +# +# We follow the same copyright policy. +# +# This file is part of some open source application. +# +# Some open source application is free software: you can redistribute +# it and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation, either +# version 3 of the License, or (at your option) any later version. +# +# Some open source application is distributed in the hope that it will +# be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Foobar. If not, see . +# +# @license GPL-3.0+ +########################################################## + +#include 'libcpp/string.pxd' + +from cython.parallel cimport parallel +cimport openmp +from libcpp.string cimport string + +cdef extern from 'lighter.hpp' namespace "Peigen::weight": + + cdef cppclass CppLighter3 "lighter<3>": + CppLighter3() + void evaluate(string sboxesfile, string outputfile) + void evaluate_verbose(string sboxesfile, string outputfile_prefix) + void pre_compute(string argvs) + void search_batch(string argvs) + double search_single(string argvs) + void search_batch_concatenate(string argvs) + double search_single_concatenate(string argvs) + void evaluate_filter(string args) + void generate(string args) + + cdef cppclass CppLighter4 "lighter<4>": + CppLighter4() + void evaluate(string sboxesfile, string outputfile) + void evaluate_verbose(string sboxesfile, string outputfile_prefix) + void pre_compute(string argvs) + void search_batch(string argvs) + double search_single(string argvs) + void search_batch_concatenate(string argvs) + double search_single_concatenate(string argvs) + void evaluate_filter(string args) + void generate(string args) + + cdef cppclass CppLighter5 "lighter<5>": + CppLighter5() + void evaluate(string sboxesfile, string outputfile) + void evaluate_verbose(string sboxesfile, string outputfile_prefix) + void pre_compute(string argvs) + void search_batch(string argvs) + double search_single(string argvs) + void search_batch_concatenate(string argvs) + double search_single_concatenate(string argvs) + void evaluate_filter(string args) + void generate(string args) + + cdef cppclass CppLighter6 "lighter<6>": + CppLighter6() + void evaluate(string sboxesfile, string outputfile) + void evaluate_verbose(string sboxesfile, string outputfile_prefix) + void pre_compute(string argvs) + void search_batch(string argvs) + double search_single(string argvs) + void search_batch_concatenate(string argvs) + double search_single_concatenate(string argvs) + void evaluate_filter(string args) + void generate(string args) + + cdef cppclass CppLighter7 "lighter<7>": + CppLighter7() + void evaluate(string sboxesfile, string outputfile) + void evaluate_verbose(string sboxesfile, string outputfile_prefix) + void pre_compute(string argvs) + void search_batch(string argvs) + double search_single(string argvs) + void search_batch_concatenate(string argvs) + double search_single_concatenate(string argvs) + void evaluate_filter(string args) + void generate(string args) + + cdef cppclass CppLighter8 "lighter<8>": + CppLighter8() + void evaluate(string sboxesfile, string outputfile) + void evaluate_verbose(string sboxesfile, string outputfile_prefix) + void pre_compute(string argvs) + void search_batch(string argvs) + double search_single(string argvs) + void search_batch_concatenate(string argvs) + double search_single_concatenate(string argvs) + void evaluate_filter(string args) + void generate(string args) + + + +cdef extern from 'faster.hpp' namespace "Peigen::depth": + + cdef cppclass CppFaster3 "faster<3>": + CppFaster3() + void pre_compute(string argvs) + void search_batch(string argvs) + double search_single(string argvs) + + + cdef cppclass CppFaster4 "faster<4>": + CppFaster4() + void pre_compute(string argvs) + void search_batch(string argvs) + double search_single(string argvs) + + cdef cppclass CppFaster5 "faster<5>": + CppFaster5() + void pre_compute(string argvs) + void search_batch(string argvs) + double search_single(string argvs) + + cdef cppclass CppFaster6 "faster<6>": + CppFaster6() + void pre_compute(string argvs) + void search_batch(string argvs) + double search_single(string argvs) + + cdef cppclass CppFaster7 "faster<7>": + CppFaster7() + void pre_compute(string argvs) + void search_batch(string argvs) + double search_single(string argvs) + + cdef cppclass CppFaster8 "faster<8>": + CppFaster8() + void pre_compute(string argvs) + void search_batch(string argvs) + double search_single(string argvs) \ No newline at end of file diff --git a/peigen.pyx b/peigen.pyx new file mode 100644 index 0000000..b696464 --- /dev/null +++ b/peigen.pyx @@ -0,0 +1,376 @@ +########################################################## +# PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes +# +# Copyright 2019 by +# Zhenzhen Bao +# Jian Guo +# San Ling +# Yu Sasaki +# +# This platform is developed based on the open source application +# +# Optimizing Implementations of Lightweight Building Blocks +# +# Copyright 2017 by +# Jade Tourteaux +# Jérémy Jean +# +# We follow the same copyright policy. +# +# This file is part of some open source application. +# +# Some open source application is free software: you can redistribute +# it and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation, either +# version 3 of the License, or (at your option) any later version. +# +# Some open source application is distributed in the hope that it will +# be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Foobar. If not, see . +# +# @license GPL-3.0+ +########################################################## + +from peigen cimport CppLighter3 +from peigen cimport CppLighter4 +from peigen cimport CppLighter5 +from peigen cimport CppLighter6 +from peigen cimport CppLighter7 +from peigen cimport CppLighter8 + +from peigen cimport CppFaster3 +from peigen cimport CppFaster4 +from peigen cimport CppFaster5 +from peigen cimport CppFaster6 +from peigen cimport CppFaster7 +from peigen cimport CppFaster8 + +cdef class Lighter(object): + + cdef CppLighter3* lighter3 + cdef CppLighter4* lighter4 + cdef CppLighter5* lighter5 + cdef CppLighter6* lighter6 + cdef CppLighter7* lighter7 + cdef CppLighter8* lighter8 + + def __cinit__(self): + self.lighter3 = NULL + self.lighter4 = NULL + self.lighter5 = NULL + self.lighter6 = NULL + self.lighter7 = NULL + self.lighter8 = NULL + + def __dealloc__(self): + del self.lighter3 + del self.lighter4 + del self.lighter5 + del self.lighter6 + del self.lighter7 + del self.lighter8 + + def __init__(self): + self.lighter3 = new CppLighter3() + self.lighter4 = new CppLighter4() + self.lighter5 = new CppLighter5() + self.lighter6 = new CppLighter6() + self.lighter7 = new CppLighter7() + self.lighter8 = new CppLighter8() + + ########################################################## + # void evaluate(string sboxesfile, string outputfile) + ########################################################## + def evaluate_3bit(self, str1='sboxes3.txt', str2='sboxes3_eva.csv'): + self.lighter3.evaluate(str1, str2) + + def evaluate_4bit(self, str1='sboxes4.txt', str2='sboxes4_eva.csv'): + self.lighter4.evaluate(str1, str2) + + def evaluate_5bit(self, str1='sboxes5.txt', str2='sboxes5_eva.csv'): + self.lighter5.evaluate(str1, str2) + + def evaluate_6bit(self, str1='sboxes6.txt', str2='sboxes6_eva.csv'): + self.lighter6.evaluate(str1, str2) + + def evaluate_7bit(self, str1='sboxes7.txt', str2='sboxes7_eva.csv'): + self.lighter7.evaluate(str1, str2) + + def evaluate_8bit(self, str1='sboxes8.txt', str2='sboxes8_eva.csv'): + self.lighter8.evaluate(str1, str2) + + ########################################################## + # void evaluate_verbose(string sboxesfile, string outputfile_prefix) + ########################################################## + def evaluate_verbose_3bit(self, str1='sboxes3.txt', str2='sboxes3_evav_'): + self.lighter3.evaluate_verbose(str1, str2) + + def evaluate_verbose_4bit(self, str1='sboxes4.txt', str2='sboxes4_evav_'): + self.lighter4.evaluate_verbose(str1, str2) + + def evaluate_verbose_5bit(self, str1='sboxes5.txt', str2='sboxes5_evav_'): + self.lighter5.evaluate_verbose(str1, str2) + + def evaluate_verbose_6bit(self, str1='sboxes6.txt', str2='sboxes6_evav_'): + self.lighter6.evaluate_verbose(str1, str2) + + def evaluate_verbose_7bit(self, str1='sboxes7.txt', str2='sboxes7_evav_'): + self.lighter7.evaluate_verbose(str1, str2) + + def evaluate_verbose_8bit(self, str1='sboxes8.txt', str2='sboxes8_evav_'): + self.lighter8.evaluate_verbose(str1, str2) + + ########################################################## + # pre_computation + ########################################################## + def pre_compute_3bit(self, str='-av'): + self.lighter3.pre_compute(str) + + def pre_compute_4bit(self, str='-av'): + self.lighter4.pre_compute(str) + + def pre_compute_5bit(self, str='-av'): + self.lighter5.pre_compute(str) + + def pre_compute_6bit(self, str='-av'): + self.lighter6.pre_compute(str) + + def pre_compute_7bit(self, str='-av'): + self.lighter7.pre_compute(str) + + def pre_compute_8bit(self, str='-av'): + self.lighter8.pre_compute(str) + + ########################################################## + # search in batch mode (a set of S-boxes) + ########################################################## + + def search_batch_3bit(self, str='-av'): + self.lighter3.search_batch(str) + + def search_batch_4bit(self, str='-av'): + self.lighter4.search_batch(str) + + def search_batch_5bit(self, str='-av'): + self.lighter5.search_batch(str) + + def search_batch_6bit(self, str='-av'): + self.lighter6.search_batch(str) + + def search_batch_7bit(self, str='-av'): + self.lighter7.search_batch(str) + + def search_batch_8bit(self, str='-av'): + self.lighter8.search_batch(str) + + ########################################################## + # search in single mode (one S-boxes) + ########################################################## + + def search_single_3bit(self, str='-av'): + return self.lighter3.search_single(str) + + def search_single_4bit(self, str='-av'): + return self.lighter4.search_single(str) + + def search_single_5bit(self, str='-av'): + return self.lighter5.search_single(str) + + def search_single_6bit(self, str='-av'): + return self.lighter6.search_single(str) + + def search_single_7bit(self, str='-av'): + return self.lighter7.search_single(str) + + def search_single_8bit(self, str='-av'): + return self.lighter8.search_single(str) + + ########################################################## + # search in batch mode using concatnation method (a set of S-boxes) + ########################################################## + + def search_batch_concatenate_3bit(self, str='-av'): + self.lighter3.search_batch_concatenate(str) + + def search_batch_concatenate_4bit(self, str='-av'): + self.lighter4.search_batch_concatenate(str) + + def search_batch_concatenate_5bit(self, str='-av'): + self.lighter5.search_batch_concatenate(str) + + def search_batch_concatenate_6bit(self, str='-av'): + self.lighter6.search_batch_concatenate(str) + + def search_batch_concatenate_7bit(self, str='-av'): + self.lighter7.search_batch_concatenate(str) + + def search_batch_concatenate_8bit(self, str='-av'): + self.lighter8.search_batch_concatenate(str) + + ########################################################## + # search in single mode using concatnation method (one S-boxes) + ########################################################## + + def search_single_concatenate_3bit(self, str='-av'): + return self.lighter3.search_single_concatenate(str) + + def search_single_concatenate_4bit(self, str='-av'): + return self.lighter4.search_single_concatenate(str) + + def search_single_concatenate_5bit(self, str='-av'): + return self.lighter5.search_single_concatenate(str) + + def search_single_concatenate_6bit(self, str='-av'): + return self.lighter6.search_single_concatenate(str) + + def search_single_concatenate_7bit(self, str='-av'): + return self.lighter7.search_single_concatenate(str) + + def search_single_concatenate_8bit(self, str='-av'): + return self.lighter8.search_single_concatenate(str) + + ########################################################## + # void evaluate_filter(string args) + ########################################################## + + def evaluate_filter_3bit(self, str='-av'): + return self.lighter3.evaluate_filter(str) + + def evaluate_filter_4bit(self, str='-av'): + return self.lighter4.evaluate_filter(str) + + def evaluate_filter_5bit(self, str='-av'): + return self.lighter5.evaluate_filter(str) + + def evaluate_filter_6bit(self, str='-av'): + return self.lighter6.evaluate_filter(str) + + def evaluate_filter_7bit(self, str='-av'): + return self.lighter7.evaluate_filter(str) + + def evaluate_filter_8bit(self, str='-av'): + return self.lighter8.evaluate_filter(str) + + ########################################################## + # void generate(string args) + ########################################################## + + def generate_3bit(self, str='-av'): + return self.lighter3.generate(str) + + def generate_4bit(self, str='-av'): + return self.lighter4.generate(str) + + def generate_5bit(self, str='-av'): + return self.lighter5.generate(str) + + def generate_6bit(self, str='-av'): + return self.lighter6.generate(str) + + def generate_7bit(self, str='-av'): + return self.lighter7.generate(str) + + def generate_8bit(self, str='-av'): + return self.lighter8.generate(str) + +cdef class Faster(object): + + cdef CppFaster3* faster3 + cdef CppFaster4* faster4 + cdef CppFaster5* faster5 + cdef CppFaster6* faster6 + cdef CppFaster7* faster7 + cdef CppFaster8* faster8 + + def __cinit__(self): + self.faster3 = NULL + self.faster4 = NULL + self.faster5 = NULL + self.faster6 = NULL + self.faster7 = NULL + self.faster8 = NULL + + def __dealloc__(self): + del self.faster3 + del self.faster4 + del self.faster5 + del self.faster6 + del self.faster7 + del self.faster8 + + def __init__(self): + self.faster3 = new CppFaster3() + self.faster4 = new CppFaster4() + self.faster5 = new CppFaster5() + self.faster6 = new CppFaster6() + self.faster7 = new CppFaster7() + self.faster8 = new CppFaster8() + + ########################################### + # pre_computation + ########################################### + def pre_compute_3bit(self, str='-av'): + self.faster3.pre_compute(str) + + def pre_compute_4bit(self, str='-av'): + self.faster4.pre_compute(str) + + def pre_compute_5bit(self, str='-av'): + self.faster5.pre_compute(str) + + def pre_compute_6bit(self, str='-av'): + self.faster6.pre_compute(str) + + def pre_compute_7bit(self, str='-av'): + self.faster7.pre_compute(str) + + def pre_compute_8bit(self, str='-av'): + self.faster8.pre_compute(str) + + ########################################### + # search in batch mode (a set of S-boxes) + ########################################### + + def search_batch_3bit(self, str='-av'): + self.faster3.search_batch(str) + + def search_batch_4bit(self, str='-av'): + self.faster4.search_batch(str) + + def search_batch_5bit(self, str='-av'): + self.faster5.search_batch(str) + + def search_batch_6bit(self, str='-av'): + self.faster6.search_batch(str) + + def search_batch_7bit(self, str='-av'): + self.faster7.search_batch(str) + + def search_batch_8bit(self, str='-av'): + self.faster8.search_batch(str) + + ########################################### + # search in single mode (one S-boxes) + ########################################### + + def search_single_3bit(self, str='-av'): + return self.faster3.search_single(str) + + def search_single_4bit(self, str='-av'): + return self.faster4.search_single(str) + + def search_single_5bit(self, str='-av'): + return self.faster5.search_single(str) + + def search_single_6bit(self, str='-av'): + return self.faster6.search_single(str) + + def search_single_7bit(self, str='-av'): + return self.faster7.search_single(str) + + def search_single_8bit(self, str='-av'): + return self.faster8.search_single(str) \ No newline at end of file diff --git a/peigen.so b/peigen.so new file mode 100644 index 0000000..022b762 Binary files /dev/null and b/peigen.so differ diff --git a/sboxes3.txt b/sboxes3.txt new file mode 100644 index 0000000..e7dca64 --- /dev/null +++ b/sboxes3.txt @@ -0,0 +1,4 @@ +PRINTcipher,0001030607040502 +SEA,0005060704030102 +ctc2,0706000402050103 +3-way,0702040501060300 diff --git a/sboxes4.txt b/sboxes4.txt new file mode 100644 index 0000000..5934679 --- /dev/null +++ b/sboxes4.txt @@ -0,0 +1,206 @@ +Piccolo,0e040b0203080009010a070f060c050d +Pride,0004080f01050e0902070a0c0b0d0603 +Prost,0004080f01050e0902070a0c0b0d0603 +Rectangle,06050c0a010e07090b00030d080f0402 +REC_0,09040f0a0e0100060c070308020b050d +LBlock_8,08070e050f0d00060b0c090a02040103 +LBlock_9,0b050f000702090d0408010c0e0a0306 +LBlock_6,0b09040e000f0a0d060c050703080102 +LBlock_7,0d0a0f000e04090b0201080307050c06 +LBlock_4,0e050f0007020c0d010804090b0a0603 +LBlock_1,040b0e090f0d000a070c050602080103 +LBlock_5,020d0b0c0f0e0009070a060301080405 +LBlock_2,010e070c0f0d00060b0509030204080a +LBlock_3,0706080b000f030e090a0c0d05020401 +LBlock_0,0e090f000d040a0b0102080307060c05 +SKINNY_4,0c060900010a020b0308050d040e070f +SC2000_4,02050a0c070f010b0d0600090408030e +TWINE,0c000f0a020b090508030d07010e0604 +MIBS,040f03080d0a0c000b05070e02060109 +KLEIN,07040a09010f0b000c030206080e0d05 +Qarma_sigma1,0a0d0e060f0703050908000c0b010204 +Panda,000103020f0c090b0a060807050e0d04 +Qarma_sigma2,0b06080f0c00090e030704050d02010a +PRINCE,0b0f03020a0c0901060708000e050d04 +Midori_Sb1,010005030e020f070d0a090b0c080406 +SERPENT_S5,0f05020b040a090c00030e080d060701 +SERPENT_S4,010f08030c000b060205040a090e070d +SERPENT_S2,08060709030c0a0f0d010e04000b0502 +SERPENT_S6,07020c050804060b0e09010f0d030a00 +SERPENT_S3,000f0b080c0906030d0102040a07050e +PRESENT,0c05060b09000a0d030e0f0804070102 +SERPENT_S7,010d0f000e08020b07040c0a09030506 +SERPENT_S1,0f0c02070900050a010b0e08060d0304 +SERPENT_S0,03080f010a06050b0e0d04020700090c +Noekeon,070a020c04080f000509010e030d0b06 +Qarma_sigma0,000e020a090f080b060403070d0c0105 +Midori_Sb0,0c0a0d030e0b0f070809010500020406 +MANTIS,0c0a0d030e0b0f070809010500020406 +DES_S6_1,0c010a0f09020608000d03040e07050b +GIFT,010a040c060f0309020d0b070500080e +DES_S2_4,0d080a01030f04020b06070c00050e09 +DES_S6_3,090e0f0502080c030700040a010d0b06 +DES_S7_3,01040b0d0c03070e0a0f060800050902 +DES_S4_4,030f00060a010d080904050b0c07020e +DES_S4_3,0a0609000c0b070d0f01030e05020804 +DES_S4_2,0d080b05060f00030407020c010a0e09 +DES_S4_1,070d0e030006090a010208050b0c040f +DES_S5_3,0402010b0a0d07080f090c050603000e +DES_S2_1,0f01080e060b03040907020d0c00050a +DES_S6_4,0403020c09050f0a0b0e01070600080d +DES_S8_3,070b0401090c0e0200060a0d0f030508 +DES_S8_1,0d020804060f0b010a09030e05000c07 +DES_S5_2,0e0b020c04070d0105000f0a03090806 +DES_S5_4,0b080c07010e020d060f00090a040503 +DES_S6_2,0a0f0402070c090506010d0e000b0308 +DES_S2_2,030d04070f02080e0c00010a06090b05 +DES_S3_4,010a0d0006090807040f0e030b05020c +DES_S3_2,0d0700090304060a0208050e0c0b0f01 +DES_S3_3,0d060409080f03000b01020c050a0e07 +DES_S7_1,040b020e0f00080d030c0907050a0601 +DES_S8_4,02010e07040a080d0f0c09000305060b +DES_S7_4,060b0d0801040a070905000f0e02030c +DES_S2_3,000e070b0a040d0105080c060903020f +DES_S1_1,0e040d01020f0b08030a060c05090007 +DES_S1_4,0f0c080204090107050b030e0a00060d +DES_S7_2,0d000b070409010a0e03050c020f0806 +DES_S1_3,04010e080d06020b0f0c0907030a0500 +DES_S3_1,0a00090e06030f05010d0c070b040208 +DES_S1_2,000f07040e020d010a060c0b09050308 +DES_S5_1,020c0401070a0b060805030f0d000e09 +DES_S8_2,010f0d080a0307040c05060b000e0902 +Iceberg_S0,0d070302090a0c010f04050e06000b08 +Anubis_S0,0d070302090a0c010f04050e06000b08 +Khazad_P,030f0e0005040b0c0d0a090607080201 +Khazad_Q,090e05060a02030c0f00040d070b0108 +Iceberg_S1,040a0f0c000d090b0e06010703050802 +Anubis_S1,040a0f0c000d090b0e06010703050802 +Minalpher,0b03040102080c0f050d0e0006090a07 +Fox_S3,0d0a0b01040308090507020c0f00060e +Whirlpool_E,010b090c0d060f030e0807040a020500 +JH_S0,0900040b0d0c030f010a02060705080e +Whirlpool_R,070c0b0d0e04090f0603080a02050100 +JH_S1,030c060d050701090f0200040b0a0e08 +SMASH_256_S2,010b06000e0d050a0c02090703080f04 +SMASH_256_S3,0402090c08010e070f05000b060a030d +Hummingbird_2_S4,0f040508090702010a03000e060c0d0b +Hummingbird_2_S3,020f0c0105060a0d0e080304000b0907 +SMASH_256_S1,060d0c070f01030a080b050002040e09 +Hummingbird_2_S1,070c0e090201050f0b060d0004080a03 +Hummingbird_2_S2,040a0106080f070c03000e0d05090b02 +Fox_S2,0b04010f00030e0d0a0807050c020906 +LAC,0e090f000d040a0b0102080307060c05 +UDCIKMP11,0008060d050f070c040e020309010b0a +Luffa_v1,070d0b0a0c040803050f06000901020e +Magma_7,080e02050609010c0f040b000d0a0307 +Magma_6,050d0f0609020c0a0b07080104030e00 +Luffa,0d0e0001050a07060b03090c0f080204 +GOST_IETF_4,0e070a0c0d01030900020b040f080506 +CLEFIA_SS2,0b08050e0a06040c0f07020301000d09 +Magma_5,070f050a0801060d0009030e0b04020c +Magma_4,0c0802010d040f0607000a05030e090b +Enocoro_S4,0103090a050e07020d000c0f0408060b +Twofish_Q0_T2,0b0a050e060d09000c080f0302040701 +GOST2_2,0e000801070a05060d020409030f0c0b +CLEFIA_SS0,0e060c0a0807020f0b01040005090d03 +Magma_2,06080203090a050c010e04070b0d000f +CLEFIA_SS3,0a02060d0304050e000708090b0f0c01 +CLEFIA_SS1,0604000d020b0a03090c0e0f08070501 +GOST2_1,060a0f04030805000d0e0701020b0c09 +Magma_8,01070e0d00050803040f0a06090c0b02 +Magma_3,0b030508020f0a0d0e0107040c090600 +CS_cipher_G,0a0600020b0e01080d0405030f0c0709 +Fox_S1,020501090e0a0c080604070f0d0b0003 +Magma_1,0c0406020a050b090e080d0700030f01 +CS_cipher_F,0f0d0b0b070507070e0d0a0b0e0d0e0f +GOST_5,060c0701050f0d08040a090e00030b02 +Lucifer_S1,07020e09030b00040c0d010a060f0805 +Kuznyechik_nu0,0205030b06090e0a00040f01080d0c07 +BLAKE_7,0d0b070e0c01030905000f040806020a +GOST_IETF_6,030a0d0c0102000b07050904080f0e06 +GOST_1,040a09020d08000e060b010c070f0503 +GOST_6,040b0a000702010d03060805090c0f0e +Lucifer_S0,0c0f070a0e0d0b000206030109040508 +Kuznyechik_sigma,0c0d0004080b0a0e030905020f010607 +GOST_IETF_8,0b0a0f05000c0e080602030901070d04 +BLAKE_5,020c060a000b0803040d07050f0e0109 +GOST_2,0e0b040c060d0f0a0203080100070509 +GOST_3,0508010d0a0304020e0f0c070600090b +BLAKE_4,0900050702040a0f0e010b0c0608030d +BLAKE_9,0a020804070601050f0b090e030c0d00 +Twofish_Q0_T1,0e0c0b08010203050f040a060700090d +GOST_IETF_1,09060302080b01070a040e0f0c000d05 +GOST_4,070d0a010008090f0e04060c0b020503 +BLAKE_8,060f0e090b0300080c020d0701040a05 +Twofish_Q1_T0,02080b0d0f07060e03010904000a0c05 +Twofish_Q0_T3,0d070f040102060e090b030008050c0a +BLAKE_2,0b080c0005020f0d0a0e030607010904 +Twofish_Q1_T1,010e020b040c0307060d0a050f090008 +Twofish_Q1_T2,040c07050106090a000e0d08020b030f +Twofish_Q0_T0,0801070d060f0302000b05090e0c0a04 +BLAKE_3,070903010d0c0b0e0206050a04000f08 +GOST_IETF_3,0e0406020b030d080c0f050a00070109 +Kuznyechik_phi,0b020b080c04010c060305080e03060b +GOST_IETF_7,010d0209070a0600080c04050f030b0e +Twofish_Q1_T3,0b0905010c030d0e0604070f0200080a +GOST_7,0d0b0401030f0509000a0e070608020c +BLAKE_6,0c05010f0e0d040a000706030902080b +GOST_IETF_2,03070e09080a0f000502060c0b040d01 +GOST_8,010f0d0005070a040902030e060b080c +GOST_IETF_5,0b050109080d0f000e0402030c070a06 +BLAKE_1,0e0a0408090f0d06010c00020b070503 +Kuznyechik_nu1,07060c09000f080104050b0e0d02030a +Optimal_S11,0001020d04070f06080e0b0a05090c03 +Optimal_S7,0001020d04070f06080c0e0b0a090305 +Optimal_S12,0001020d04070f06080e0b0a09030c05 +Optimal_S6,0001020d04070f06080c0b090a0e0503 +Optimal_S3,0001020d04070f06080c05030a0e0b09 +Optimal_S13,0001020d04070f06080e0c09050b0a03 +Optimal_S4,0001020d04070f06080c090b0a0e0503 +Optimal_S5,0001020d04070f06080c0b090a0e0305 +Serpent_type_S16,00030508090c0e070a0d0f04060b0102 +Serpent_type_S7,00030508060c0b070d0a0e04010f0209 +Serpent_type_S11,00030508060d0f0207040e0b0a01090c +Serpent_type_S12,00030508060d0f020c090a040b0e0107 +Serpent_type_S15,0003050807090b0e0a0d0f040c020601 +Serpent_type_S17,000305080a0d09040f0602010c0b070e +Serpent_type_S1,0003050806090a070b0c0e02010f0d04 +Serpent_type_S2,0003050806090b020d040e010a0f070c +Serpent_type_S6,00030508060c0b070a0d090e0f010204 +Serpent_type_S8,00030508060c0f010a04090e0d0b0207 +Serpent_type_S10,00030508060d0f01090c020b0a07040e +Serpent_type_S9,00030508060c0f020e090b070d0a0401 +Serpent_type_S19,0003050a070c0b060d0402090e01080f +Serpent_type_S18,000305080b0c060f0e090207040a0d01 +Serpent_type_S14,000305080704090e0f06020b0a0d0c01 +Serpent_type_S13,00030508060f0a0107090e040b0c0d02 +Serpent_type_S0,00030506070a0b0c0d040e090801020f +Serpent_type_S3,00030508060a0f040e0d090201070c0b +Serpent_type_S5,00030508060c0b070a04090e0f01020d +Serpent_type_S4,00030508060c0b07090e0a0d0f020104 +Golden_S2,00030508060a0f040e0d090201070c0b +Golden_S3,00030508060c0b070a04090e0f01020d +Golden_S0,0003050806090c070d0a0e04010f0b02 +Golden_S1,00030508060c0b07090e0a0d0f020104 +Optimal_S8,0001020d04070f06080e09050a0b030c +Optimal_S2,0001020d04070f06080b0e030a0c0509 +Optimal_S0,0001020d04070f06080b0c09030e0a05 +Optimal_S10,0001020d04070f06080e0b050a09030c +Optimal_S14,0001020d04070f06080e0c0b0309050a +Optimal_S9,0001020d04070f06080e0b0305090a0c +Optimal_S1,0001020d04070f06080b0e0305090a0c +Optimal_S15,0001020d04070f06080e0c0b09030a05 +Num1_DL_22_3,000E09050F080A07030B060C04010D02 +Num1_DL_13_1,000C09070F020601030B040E0A050D08 +Num1_DL_22_2,000B06090C05030E0D070804020A0F01 +Num1_DL_13_0,000C090706010F02030B040E0D080A05 +Num1_DL_22_1,000B0E010A070D04060C090F05080302 +Num1_DL_13_3,000D040B070E0902060A030508010F0C +Num1_DL_04_0,000B0C050601090A030E0F080D040207 +Num1_DL_22_0,000D08020E0B07050F06030C0401090A +Num1_DL_04_1,000C0D0A050B0E070F06020103080904 +Num1_DL_13_2,000B08050F0C03060E04070902010D0A +mCrypton_S0,040F03080D0A0C000B05070E02060109 +mCrypton_S1,010C070A060D05030F0B02000804090E +mCrypton_S2,070E0C0200090D0A030F050806040B01 +mCrypton_S3,0B000A070D0604020C0E030901050F08 \ No newline at end of file diff --git a/sboxes5.txt b/sboxes5.txt new file mode 100644 index 0000000..bb3d68c --- /dev/null +++ b/sboxes5.txt @@ -0,0 +1,10 @@ +keccak,00050A0B14111617090C03020D080F0E1215181B060104071A1D10131E191C1F +Ascon,040B1F141A1509021B0508121D03061C1E13070E000D1118100C0119160A0F17 +Fides_5,0100191A111D151B140504170E12021C0F0806030D0718101E091F0A160C0B13 +SC2000_5,141A071F130C0A0F161E0D0E041809121B0B01150610021C1705080300111D19 +icepole,1F050A0B14111617090C03020D080F0E1215181B060104071A1D10131E191C00 +APN_S0,0001020403060810050A0F1B131D1F14071219150C0E181C1A0B170D1E091116 +APN_S1,0001020403080D10050B151F170F131E061C1D09181B0E120A110C1A07191416 +APN_S2,0001020403080D1005111C1B1E0E180A06130B141F1D0C15121A0F1907161709 +APN_S3,000102040308101C050A191112171F1D06140D18130B09161B070E151A0C1E0F +APN_S4,000102040308101C050A1A1211141F1D0615180C160F19070E130D17091E1B0B \ No newline at end of file diff --git a/sboxes6.txt b/sboxes6.txt new file mode 100644 index 0000000..9058713 --- /dev/null +++ b/sboxes6.txt @@ -0,0 +1,3 @@ +Fides_6,3600300D0F1223353F192D3403142129080A39253B2422021A323A183C130E2A2E3D05311F0B1C040C1E3716090620171B271511101D3E01282F3338072B262C +APN_6,0036300D0F123523193F2D34031429213B2402220A0839253C132A0E321A3A18271B1511101D013E2F283338072B2C261F0B041C3D2E0531090617201E0C3716 +SC2000_6,2F3B192A0F171C271A2624133C181D38253F143D37021E2C090A06163530330B3E3423120E2E003611281B041F08050C0310292221072D31323A01152B39200D diff --git a/sboxes7.txt b/sboxes7.txt new file mode 100644 index 0000000..548bec5 --- /dev/null +++ b/sboxes7.txt @@ -0,0 +1 @@ +MISTY,1b32335a3b1017545b1a72736b2c66491f24136c372e3f4a5d0f405625511c040b46200d7b3544422b1e41144b79156f0e550936740c6753280a7e38020760291912652f303908685f782a4c6445753d594803577c4f623c1d215e276a704d3a016d6e6318772305267600312d7a7f61502211064716524e713e6943345c587d \ No newline at end of file diff --git a/sboxes8.txt b/sboxes8.txt new file mode 100644 index 0000000..76f22eb --- /dev/null +++ b/sboxes8.txt @@ -0,0 +1,53 @@ +Safer,012DE293BE4515AE780387A4B838CF3F08670994EB26A86BBD18341BBBBF72F74035489C512F3B55E3C09FD8D3F38DB1FFA73EDC8677D7A611FBF4BA92916483F133EFDA2CB5B22B88D199CB8C841D14819771CA5FA38B573C82C4525C1CE8A004B4854AF61354B6DF0C1A8EDEE039FC209B244EA9989EABF260D06CEAFAC7D900D41F6E43BCEC5389FE7A5D49C932C2F99AF86D16DB599644E9CDE646428F0AC1CCB965B0D2C6AC1E4162292E0E7450025AC3257B8A2A5BF0060D476F709D7E10CE1227D54C4FD679306836757DE4ED806A9037A25E76AAC57F3DAFA5E51961FD4D7CB70BEEAD4B22F5E7732321C805E166DDB3586963560FA1319517073A28 +Kalyna_pi1,CEBBEB92EACB13C1E93AD6B2D29017F8421556B4651C8843C55C36BAF557678D31F664589EF422AA750F02B1DF6D734D7C262EF7085D443E9F14C8AE5410D8BC1A6B69F3BD33ABFAD19B684E169591EE4C638E5BCC3C19A181497BD96F3760CAE72B48FD9645FC41120D79E5898CE32030DCB76C4AB53F97D4622D06A4A5835F2ADAC9007EA255BF11D59CCF0E0A3D517D931BFEC44709860B8F9D6A07B9B0981832714BEF3B70A0E440FFC3A9E678F98B46801E38E1B8A8E00C23761D252405F16E94289A84E8A34F77D385E252F282507A2F7453B361AF3935DECD1F99ACAD722CDDD087BE5EA6EC04C60334FBDB59B6C201F05AEDA766217F8A27C7C029D7 +Crypton_0_5,F0124C7A4716033AE69D447753CA3B0F9B9854903DAC74569EDE5CF386397CC491A9975F9C0D78CCFD43BF024B92603E7D1D50CBB8B97027AA96488838D76842A8D0A62E25F42C6E0CB7CEE0BE0B24678CECC552D9D809B4CF8F8D8B592351E3D3B118F8D405A2DB826C00468AAFDABC991AADB31F0E714FC72BE52AE2582906F6FEF9196BEABBC2A355A1DF6F4583698E7B723CEEFF07A5E8F10A1C75E12F21D2B63FF773B25D7935801741947E15EDB5D5931420617631C96AAB34A0A41EBAE7134EC6D6877FBD846226956D4D572804644A1101406508B0E932CD81662D5BEFA7FBDDF2335A63C1E4C3AEDCFC2210FA9FD1859A1B5E30EBC8894937C036F5 +MD2,292E43C9A2D87C013D3654A1ECF0061362A705F3C0C7738C98932BD9BC4C82CA1E9B573CFDD4E01667426F188A17E512BE4EC4D6DA9EDE49A0FBF58EBB2FEE7AA968799115B2073F94C210890B225F21807F5D9A5A903227353ECCE7BFF79703FF1930B348A5B5D1D75E922AAC56AAC64FB838D296A47DB676FC6BE29C7404F1459D705964718720865BCF65E62DA8021B6025ADAEB0B9F61C46616934407E0F5547A323DD51AF3AC35CF9CEBAC5EA262C530D6E85288409D3DFCDF441814D526ADC37C86CC1ABFA24E17B080CBDB14A7888958BE363E86DE9CBD5FE3B001D39F2EFB70E6658D0E4A67772F8EB754B0A314450B48FED1F1ADB998D339F118314 +Chiasmus,6533CFB93764CDF3263AC1A2728A8FE3FD56B30F102B3EA0BD1EAB1D9CE28798A8D3B4DF92753B3920A5FA1BBE90F609E561C4C906C2A61CF9947B537301259A1AFFE95A76134B95AC0BC7B2B8D617A927EBD15CC39B22158E40115E5716D0B05D7931BBEA4FD9DE000AD7AD3F99683466F04435895481B1842A8B6FC043FE9648820CDA74BC21F1672EDB49E4D5715929E0A130DD916BB7B669C580AA6DA32C0578BA511407D4EC7ECC24629EDC8CD81F46E89F4EA48532CEA7FCE197AE2D527D0E6C835FBF187C36630DEFC85B55124AF27038F8AF8677470423026E4C5803507A3D28F5E741F445606A08887F9D934DD22FEEE6CBEDFBCAF719B5428DC63C +Kalyna_pi2,93D99AB5982245FCBA6ADF029FDC51594A172BC294F4BBA362E471D4CD7016E1493CC0D85C9BAD8553A17AC82DE0D172A62CC4E37678B7B4093B0E414CDEB29025A5D7031100C32E92EF4E129D7DCB3510D54F9E4DA955C6D07B1897D336E64856818F77CC9CB9E2ACB82F15A47CDA381E0B05D6146E6C7E66FDB1E560AF5E3387C9F05D6D3F888DC7F71DE9ECED802927CF99A8500F3724283095D23E5B4083B369571F071C8ABC20EBCE8EABEE31A273F9CA3A1AFB0DC1FEFAF26FBD96DD4352B608F3AEBE19893226B0EA4B6484826BF579BF015F75631B233D682A65E891F6FF1358F1470A7FC5A7E7615A0646444204A0DB398654AA8C34218BF80C7467 +FLY,009BC2155D844CD16738EFB07E2BF6A3B9AA36782F6EE3F7125C9AD489CD01452C6344DE02963970BAE41857A1F58BCE5187EDFFB5A8CA1BDF906C3246037D29D5F2205BCC3104BDA6418E79EA9F681C48E6698A13779EAFF305CB2DB4D03752C43E93AC40E922567B8DF1061762BFDA1D7F07B1DBFA65882EC9A543583CE0947621ABFD6A3FB7E2DD4F538CC019950883C54E091450D89CF4EE27613B7AA2B6FEA981C6E8BC1F5A3572990AD347246D0B4D752397D26034C816A0BBFCE15E8FE7981A64AE4B71850CB33DCF5528D9F0B2DC5F30F90D26C391A7741E82664AEB6F10B8D78673FB0E592A42E59DA433C73A54EC92C125AD49806BD6F80FBE7C11 +CLEFIA_S1,6CDAC3E94E9D0A3DB836B43813340CD9BF74948FB79CE5DC9E07494F982CB09312EBCDB392E74160E321273BE619D20E9111C73F2A8EA1BC2BC8C50F5BF3878BFBF5DE20C6A784CED86551C9A4EF4353255D9B31E83E0DD780FF698ABA0B735C6E541562F6353052A316D32832FAAA5ECFEAED783358097B63C0C1461EDFA9995504C486397782EC4018909759DD831F9A370624647CA556480885D06126CA6F7E6AB671A07005D1458C231CF0EE89AD7A4BC22FDB5A4D7667172DF4CBB14AA8B522473AD5104C72CC00F9E0FDE2FEAEF85FABF11B4281D6BE4429A657B9AFF2D47566BB689F5002013C7F8D1A88BDACF7E47996A2FC6DB26B03E12E7D14951D +Crypton_1_0_S0,63EC59AADB8E66C0373C14FF1344A9913B788DEFC22AF0D7619EA5BC48151247ED421A3338C81790A6D55D656AFE8FA193CA2F0C6858DFF44511A0A72296FB7D1DB484E0BF57E90A4E83CC7A7139C732743DDE5085066F53E8AD8219E1BA36CB0E28F39B4A62941FBDF66741D8D12DA486B701C5B07502F92C296ED25F8BFC5AE47FDD0755B12B8972183A4CB6E380CE49CF6BB9F20DDC649546F7109A20A23FD687703E21FD4D7BC3AE098A04B354F8300056D4E725BBAC9873EAC99D4F7E03AB92A8430FFA245C1E603197CDC679F55EE534761C81B2AF0B5BD9E2276DD088C151E69C77BE9923DAEB522EB508056CB81BA3698CD34026F1C49F35EE7C4B16 +Streebog,FCEEDD11CF6E3116FBC4FADA23C5044DE977F0DB932E99BA1736F1BB14CD5FC1F918655AE25CEF21811C3C428B018E4F058402AEE36A8FA0060BED987FD4D31FEB342C51EAC848ABF22A68A2FD3ACECCB5700E56080C7612BF7213479CB75D8715A19629107B9AC7F391786F9D9EB2B13275193DFF358A7E6D54C680C3BD0D57DFF524A93EA843C9D779D6F67C22B903E00FECDE7A94B0BCDCE828504E330A4AA79760731E0062441AB83882649F2641AD454692275E552F8CA3A57D69D5953B0758B34086AC1DF730376BE488D9E789E11B83494C3FF8FE8D53AA90CAD88561207167A42D2B095BCB9B25D0BEE56C5259A674D2E6F4B4C0D166AFC2394B63B6 +Iraqi,AD54F0430135FE2429AC736DDFC798BD5A2E95C1DA82FA28CB0423EDECF6D58FA9B030173DCE4522619B046DB7DC2A40157B1DE9FD69B7D101BF710C2E0708B7A6C7A6074E2587FCAE548CA4985E16B93B44B53CB04333191CBE8AC62C5A5CDD95AFBA1931D232ED29CF1FE27279E60F3A198E3A62E83B03BD1C087483B94EFAEF2174AD5E2D683E7AB31296F6FA11084F9DE1EE2F0A853A087E5244998D029ECC3282353B20F3A0AC23186B2373E48F1CE04D37191C7859BA98315475B41E8A864DB69D3DE61695360F6E20D59B6A4E1017598C9EA96088BA681EC74323DA9FD26D1CEE2196ADB4F7C9539669A4E43BCF65DD633478C71F0690CAD7D1312AC3 +ZUC_S1,55C263713BC847869F3CDA5B29AAFD778CC5940CA61A1300E3A8167240F9F8424426689681D9453E1076C6A78B3943E13AB5562AC06DB3052266BFDC0BFA6248DD20110636C9C1CFF62752BB69F5D4877F844CD29C57A4BC4F9ADFFED68D7AEB2B53D85CA11417FB23D57D3067730809EEB7703F61B2198E4EE54B938F5DDBA9ADF1AE2ECB0DFCF42D466E1D97E8D1E94D37A5755E839EAB829DB91CE0CD498901B6BD5824A25F387899159050B895E4D091C7CEED0FB46FA0CCF0024A79C3DEA3EFEA51E66B18EC1B2C80F774E7FF215A6A541E41319235C433070ABA7E0E3488B1987CF33D606C7BCAD31F3265042864BE859B2F598AD7B025ACAF1203E2F2 +Stribog,FCEEDD11CF6E3116FBC4FADA23C5044DE977F0DB932E99BA1736F1BB14CD5FC1F918655AE25CEF21811C3C428B018E4F058402AEE36A8FA0060BED987FD4D31FEB342C51EAC848ABF22A68A2FD3ACECCB5700E56080C7612BF7213479CB75D8715A19629107B9AC7F391786F9D9EB2B13275193DFF358A7E6D54C680C3BD0D57DFF524A93EA843C9D779D6F67C22B903E00FECDE7A94B0BCDCE828504E330A4AA79760731E0062441AB83882649F2641AD454692275E552F8CA3A57D69D5953B0758B34086AC1DF730376BE488D9E789E11B83494C3FF8FE8D53AA90CAD88561207167A42D2B095BCB9B25D0BEE56C5259A674D2E6F4B4C0D166AFC2394B63B6 +Enocoro,63521ADF8AF6AE5589E7D02DBD0124781BD9E354C8A4EC7EAB009C2E916737534E6B6C11B2C082FD3945FE9B34D7A708B89A33C64C1D69A16E3EC50A57F4F183F5471F7AA5293C42D6738DF08E18AAC120BFE693510EF798DDBA6A0548236DD41E607543972A31DB8419AFBCCCF3E84688AC8BE47BD5583602B10772E1DC5F2F5DE5D10C2699B56FE04A3BDEA2689217CAEEA9B6035ED325FB9D61590690742C2795A0B97CED04D250E24977CB3A0F9E70165CEF21B39F0DA6C92294FA4BD865853D9628145B66EA7FCEF94013ADC3B0F2C23880CF710B874D3556E964BE1CBBB730C42BFF6241A8158C12C7798F5AFCCD094F7DF886DA103276B4A33F4481EB +ZUC_S0,3E725B47CAE0003304D1549809B96DCB7B1BF932AF9D6AA5B82DFC1D085303904D4E8499E4CED991DDB685488B296EACCDC1F81E734369C6B5BDFD396320D438767DB2A7CFED57C5F32CBB142106559BE3EF5E314F7F5AA40D8251495FBA581C4A16D517A892241F8CFFD8AE2E01D3AD3B4BDA46EBC9DE9A8F87D73A806F2FC8B1B437F70A2213287CCC3C89C7C3965607BF7EF00B2B975235417961A64C10FEBC2695888AB0A3FBC01894F2E1E5E95DD0DC1166645CEC59427512F5749CAA230E86ABBE2A02E767E644A26CC2939FF1F6FA36D250689E6271153DD640C4E20F8E83776B25053F0C30EA70B7A1E8A9658D271ADB81B3A0F4457A19DFEE783460 +Anubis,A7D3E671D0AC4D793AC991FC1E4754BD8CA57AFB63B8DDD4E5B3C5BEA9880CA239DF29DA2BA8CB4C4B22AA244170A6F95AE2B0367DE433FF6020088B5EAB7F787C2C57D2DC6D7E0D5394C32827065FAD675C55480E52EA425B5D305851593C4E388A7214E7C6DE508E92D17793459ACE2D0362B6B9BF966B3F0712AE4034463EDBCFECCCC1A1C0D61DF4613B10D868A0B10A696C49FA76C49E9B6E99C2B798BC8F851FB4F8112E00251C2A3D054F7BB23290AF19A3F7739D1574EECA9F0F1B7586849C4A971A65F6ED09BB2683EB6F81046A430117E187F58DE3238044166621FED531D935180264F2F156CD82C8BAF0EFE9E8FD89D7C7B5A42F95130BF3E037 +Iceberg,24C13830E757DF203E991A34CAD652FD406CD33D4A59F877FB610A56B9D2FCF107F593CD00B662A763FE44BD5F926B68034EA2970B6083A302E54567F413088B10CEBEB42A3A9684C89F14C0C46F31D9ABAE0E647CDA1B05A815A5909485712C3519262853E27F3B2FA9CC2E1176ED4D875EC2C780B06D17B2FFE4B7549DB866749CDB36475DDE70D591AA3FC9D8F3F25B892D225CE14633E609BCE8817DE949E0B13237EA5AF62758698A50BADD51F975A178D043F7257B7E1CACD49A2B42E34B0172D74CFAEB73488C0CF06A2341ECB3EF1D12BB880DC38D4F5582EEAD8606A09565BF7A3998049B9EA4C6CF6EDCD1CB1F8F8E3C21A6B516AFC5181E0F2979 +Zorro,B2E55EFD5FC550BCDC4AFA8828D8E0D1B5D03CB099C1E8E21359A7FB713431F19F3ACE6EA8A4B47E1FB7511D389D4669530E421B0F1168CAAA06F0BD266F00D962F31560F23D7F35632D67931C91F99C662A812095F8E34D5A6D247BB9EFDFDA58A992762EB3390C29CD43FEABF594231680C0124CE9481908AE41708414A2D5B83365BAED17CF961E3B0BC2C8B6BB8BA15475C4105DD62597E6FC49F75218868DCBE1BFD78E37BE82CC64907C328F4BAC1AEAD3F46B2CFF550A45098901302BD2778772EB36DE9E8CDB6C9B05024EAF04AD74C3EEA6F6C77D40D40D3E5BEC78A0B14473475C982122613FC67A56DDE785C98A5727079A03A383E46AA52F794F +SNOW_3G_sq,25247367D7AE5C30A4EE6ECB7DB582DBE48E48494F5D6A787088E85F5E8465E2D8E9CCED402F112857D2ACE34A151BB9B28085A62E024729074B0EC151AA89D4CA0146B3EFDD447BC27FBEC39F204C6483A2684213B441CDBAC6BB6D4D7121F48DB0E593FE8FE6CF43453122373696FABC0F08521D551AC54E23697A92FF5B5AEB9A1CA9D17E0DFC508AB662F50AF8DC033C0C39F1B8F33DF2D597668132A00006CEF6EAB717F78C79D6A7BF8B3F1F536375352C60FD27D394A57CA105582DBDD9C7AF6B540BE03804C89DE714B1879CDF6FF9DA2AC459167491AB266176342BAD99FB72EC3312DE983BC09B3E18103A56E177C91E9E95A39019A86C09D0F086 +SKINNY_8,654C6A424B63436B55755A7A53735B7B358C3A818933803B9525982A9023992BE5CCE8C1C9E0C0E9D5F5D8F8D0F0D9F9A51CA8121BA013A905B50AB803B00BB932883C858D34843D91229C2C94249D2D624A6C454D64446D52725C7C54745D7DA11AAC151DA414AD02B10CBC04B40DBDE1C8ECC5CDE4C4EDD1F1DCFCD4F4DDFD368E38828B30833996269A2893209B29664E6841496040695676587850705979A61EAA1119A310AB06B608BA00B309BBE6CEEAC2CBE3C3EBD6F6DAFAD3F3DBFB318A3E868F37873F92219E2E97279F2F61486E464F67476F51715E7E57775F7FA218AE161FA717AF01B20EBE07B70FBFE2CAEEC6CFE7C7EFD2F2DEFED7F7DFFF +Khazad,BA542F7453D3D24D50AC8DBF70529A4CEAD597D133515BA6DE48A899DB32B7FCE39E919BE2BB416EA5CB6B95A1F3B102CCC41D14C363DA5D5FDC7DCD7F5A6C5CF726FFEDE89D6F8E19A0F0890F07AFFB08150D040164DF7679DD3D163F376D38B973E93555717B8C7288F62A3E5E27460C65686103C157D6D958D866D73AC83CFA96A798ECB8C7AE694BABA9670A47F2B522E5EEBE2B8112831B0E23F54521CE492CF9E6B62817821A8BFE8A09C9874EE12EE4E0EB90A41E85600025F4F1940BE775EF3431D4D0867EADFD29303B9FF8C6130605C511777C7A78361C39591856B3B02420B292A3C0446210B4844393C24ABD8F2DBC9C6A40CFA2804F1FCAAA42 +Fox,5DDE00B7D3CA3C0DC3F8CB8D7689AA1288224FDB6D47E44C789A4993C4C08613A920531C4ECF3539B4A1546403C7855C5BCDD8729642B8E1A260EFBD02AF8C737C7F5EF965E6EBAD5AA5798E1530ECA4C23EE07451FB2D6E944D5534AE527E9D4AF780F0D090A7E89F50D5D198CCA017F4B6C1285F2601AB2538827D48FC1BCE3F6BE26766435919843DF52FC9BCD9952941DA1AB0E969D27BD7119B338A2309D47144686FF20EDF87DC83186AEE998162362E7AFE459C75910C0FE7F614631D0B8BB3F3B23B084B10A632B9A892F156DD21BF04BED6FD77EA3AC88F571EFA2B58C527ACE3ED97BB46054031E5372C9E0AB1B5066C1FA32A70FFBA072416C661 +newDES,2089EFBC667DDD48D444512556ED939546E5117C73CF21147A8F19D733B78A8E92D36EAD01E4BD0E674EA224FDA774FF9E2DB93262A8FAEB368DC3F7F03F9402E0A9D6B43E16756C13ACA19FA02F2BABC2AFB238C47017DC5915A4829D0855FBD82C5EB3E2265A7728CA22CE2345E7F61D6D4A47B0063C91410D4D970C7F5FC7396505E896D28118B50A79BB30C18BFCDB4058E960805035BF90DA0B6A849B685B881F2AF3427E871E1A57BAB69AF27B52A6D02798BE71CD7269E15449A3636FCC3DC8D9AA0FC61CC0FE86EADE07ECF8C929B19C5C8343F9F5B8CB09F1001B2E85AE4B125DD164784CD51053046B8C343A3703F461C5EEE376314FE6DFA5993B +Skipjack,A3D70983F848F6F4B321157899B1AFF9E72D4D8ACE4CCA2E5295D91E4E3844280ADF02A017F1606812B77AC3E9FA3D5396846BBAF2639A197CAEE5F5F7166AA239B67B0FC193811BEEB41AEAD0912FB855B9DA853F41BFE05A58805F660BD89035D5C0A733066569450094566D989B7697FCB2C2B0FEDB20E1EBD6E4DD474A1D42ED9E6E493CCD4327D207D4DEC7671889CB301F8DC68FAAC874DCC95D5C31A47088612C9F0D2B8750825464267D0340344B1C73D1C4FD3BCCFB7FABE63E5BA5AD04239C145122F02979717EFF8C0EE20CEFBC72756F37A1ECD38E628B8610E8087711BE924F24C532369DCFF3A6BBAC5E6CA9135725B5E3BDA83A0105592A46 +AES,637C777BF26B6FC53001672BFED7AB76CA82C97DFA5947F0ADD4A2AF9CA472C0B7FD9326363FF7CC34A5E5F171D8311504C723C31896059A071280E2EB27B27509832C1A1B6E5AA0523BD6B329E32F8453D100ED20FCB15B6ACBBE394A4C58CFD0EFAAFB434D338545F9027F503C9FA851A3408F929D38F5BCB6DA2110FFF3D2CD0C13EC5F974417C4A77E3D645D197360814FDC222A908846EEB814DE5E0BDBE0323A0A4906245CC2D3AC629195E479E7C8376D8DD54EA96C56F4EA657AAE08BA78252E1CA6B4C6E8DD741F4BBD8B8A703EB5664803F60E613557B986C11D9EE1F8981169D98E949B1E87E9CE5528DF8CA1890DBFE6426841992D0FB054BB16 +BelT,B194BAC80A08F53B366D008E584A5DE48504FA9D1BB6C7AC252E72C202FDCE0D5BE3D61217B96181FE6786AD716B890B5CB0C0FF33C356B835C405AED8E07F99E12BDC1AE28257EC703FCCF095EE8DF1C1AB76389FE678CAF7C6F860D5BB9C4FF33C657B637C306ADD4EA7799EB23D313E98B56E27D3BCCF591E181F4C5AB793E9DEE72C8F0C0FA62DDB49F46F73964706075316ED247A3739CBA38303A98BF692BD9B1CE5D141015445FBC95E4D0EF2682080AA227D642F2687F93490405511BE32971343FC9A48A02A885F194B09A17ECDA4D01544AF8CA58450BF66D2E88AA2D7465242A8DFB36974C551EB232921D4EFD9B43A622875911410EA776CDA1D +Kalyna_pi3,688DCA4D734B4E2AD45226B3541E191F2203463D2D4A5383138AB7D52579F5BD582F0D02ED519E11F23E555ED1163C66705DF34540CCE8945608CE1A3AD2E1DFB5386E0EE5F4F986E94FD68523CF32993114AEEEC848D330A19241B118C42C71724415FD37BE5FAA9B88D8AB899CFA60EABC620C24A6A8EC6720DB7C28DDAC5B347E10F17B8F63A0059A437721BF2709C39FB6D729C2EBC0A48B8C1DFBFFC1B2972EF865F67507044933E4D9B9D042C76C90008E6F5001C5DA473FCD69A2E27AA7C6930F0A06E62B96A31CAF6A128439E7B082F7FE9D875C8135DEB4A5FC80EFCBBB6B76BA5A7D780B95E3AD74983B36646DDCF059A94C177F91B8C9571BE061 +Crypton_1_0_S3,B1F68E07726BD5E076215A14BFC349A8AC0D42F9EE385473559970CD831FA14EED1CDF25AA9087BB4764AB31D8FE7D5F338BF44A95A612CC6048058FC4BD2E919B5327DE39E10F6D1EEAC17B0C5730F50AAE66B31D849829FFB23DA02645CB178935B86C5B02E6DA227F9CE8F1D96304D4C7E396402ABC82C8D01952677CFA369DC93A43A4182F5C3C659EDBE700F28DC6976F80B52B1AD1F70628E2DC6A3BB46134C25879F30E46152C03BA8692C0E978EFB7016EDD5920EB7AA9FC3256D713B0A27416CA4C85F84F88D69423B9AD62D2504137FB75ECCF5ED38C6908E4719A2411F0AF4DCE93778A4B5DC510A7B63E09FD1B7E513F68A5A3BEE52D9F81440B +SEED_S0,A985D6D3541DAC255D43181E51FCCA632844209DE0E2C817A58F037BBB13D2EE708C3FA832DDF674EC950B575C5BBD01241C739810CCF2D92CE772839BD186C96050A3EB0DB69E4FB75AC678A612AFD561C3B441527D8D081F9900190453F7E1FD762F27B08B0EABA26E934D697C090ABFEFF3C58714FE64DE2E4B1A06216B6602F5928A0CB37ED07A4796E52680ADDFA13037AE36152238F4A7454C81E9849735CBCE3C7111C78975FBDAF8945982C4FF493967C0CFD7B80F8E4223916CDBA434F148C26F3D2D40BE3EBCC1AABA4E553BDC687F9CD84A5677A0ED46B52B65FAE3B9B19F5EF9E6B231EA6D5FE4F0CD88163A58D462290733E81B0579906A2A9A +Kalyna_pi0,A8435F066B756C5971DF879517F0D8096DF31DCBC94D2CAF79E097FD6F4B45393EDDA34FB4B69A0E1FBF15E149D293C692729E61D163FAEEF419D5AD58A4BBA1DCF2833742E47A329CCCAB4A8F6E04272EE7E25A9616232BC265660FBCA947413448FCB76A88A55386F95BDB387BC31E2233242836C7B23B8E77BAF5149F08559B4CFE605CDA1846CD7D21B03F1B89FFEB84693A9DD7D3706740B5DE5D3091B1781101E5006898A0C502A6742D0BA276B3BECEBDAEE98A311CECF19994AAF6262FEFE88C3503D47FFB05C15E90203D82F7EA0A0D7EF8501AC40757B83C62E3C8AC526410D0D9130C122951B9CFD6738D8154C0ED4E44A72A8525E6CA7C8B5680 +CS_cipher,290D61409CEB9E8F1F855F585B013986972ED7D635AE171621B6694EA57287083C18E6E7FAADB889B700F76F738411633F967F6EBF149DACA40E7EF6204A623003C54B5A46A344657D4D3D4279491B5CF56CB59454FF56570BF4430C4F706D0AE4023E2FA247E0C1D51A95A7515E332B5DD41D2CEE75ECDD7C4CA6B478483A3298AFC0E12D090F1EB9278AE9BDE39F07B1EA9293536A311080F2D89B0436068EBEA96445381C7A6BF3A1F0CD37251581FB90E8D97B5219282688FCD1E28CA0348267DACBC741E5C4C8EFDBC3CCABCEEDD0BBD3D2716813129AB3C2CADE77DCDF6683BC8D60C62223B28B910576CF74C9AAF199A859503B2AFEF924B0BAFDF855 +Crypton_1_0_S1,8DB365AA6F3A9903DCF050FF4C11A646ECE136BF0BA8C35F857A96F22154481DB70968CCE0235C429A577595A9FB3E864E2BBC30A1617FD31544829E885AEFF574D21283FE5DA728390E33E9C5E41FC8D1F47B411618BD4DA3B60A6487EAD82F38A0CF6E2989527CF6DB9D056347B4921ADE0417C2D508E7B0A4B94B7D2EF36993FD771C55C6AC26C960E831DA8F023B253FADE6CB3473915619DF406A808AFC5B1EC1F884F735ED0FBA242A10CE51E3C00059539F94EEB262CDAB27763DF90CAE4AA20D3CEB90717881C45E371BE5D77997D0D97006CABE2C6D678B9CB5432207459B72DDFA668C6BAF49B8D62014B1E26C8EA5324F0198C7137ED4BBF12D58 +Whirlpool,1823C6E887B8014F36A6D2F5796F915260BC9B8EA30C7B351DE0D7C22E4BFE57157737E59FF04ADA58C9290AB1A06B85BD5D10F4CB3E0567E427418BA77D95D8FBEE7C66DD17479ECA2DBF07AD5A83336302AA71C81949D9F2E35B889A2632B0E90FD580BECD3448FF7A905F20681AAEB454932264F173124008C3ECDBA18D3D9700CF2B7682D61BB5AF6A5045F330EF3F55A2EA65BA2FC0DE1CFD4D9275068AB2E60E1F62D4A896F9C525598472394C5E78388CD1A5E261B3219C1E43C7FC0451996D0DFADF7E243BABCE118F4EB7EB3C8194F7B9132CD3E76EC40356447FA92ABBC153DC0B9D6C3174F646AC8914E1163A690970B6D0EDCC4298A4285CF886 +DBlock,51369353D94AFC58E42E0D14DA9D9169EF7203C6158D5C623FB9457013A3956F84DBB8898A6ED47B40DC9B0C508EEE6A883B0F6B85D354A820DFB51B327C566474FAC72D9617AECDB4F5578CF1BCD8FE2706E1A91A0E5B08F49F4BED73B7AC7623CA16BAA7008B4641D57EF205F66367618F3DC81C5AB0793881AA3397E62C0122874FBE2471359CB1ADC51D803E75B328682AA0BF2FB2C4CE19D7CFAF02A4A57A39D204ABF7602B4CEC4D109012FB78824E3747D6A2D186B6C1E9DDA1F855DE987DE530FDE2CC3AEAD00A29E8E3EBF09A5D3C21C0486D1EE71FC944341883F9595F42926C11A652FF9E49260743BDC399F3770B5ECB0931E0C2657F2594BB66 +CSS,33733B2663236B763E7E362B6E2E667BD393DB0643034B96DE9ED60B4E0E469B57175F82C787CF125A1A528FCA8AC21FD999D10049094190D898D001480840913D7D35246D2D65743C7C34256C2C6475DD9DD5044D0D4594DC9CD4054C0C449559195180C989C11058185081C888C011D797DF0247074F92DA9AD20F4A0A429F53135B86C383CB165E1E568BCE8EC61BB3F3BBA6E3A3EBF6BEFEB6ABEEAEE6FB37773F2267276F723A7A322F6A2A627FB9F9B1A0E9A9E1F0B8F8B0A1E8A8E0F15D1D5584CD8DC5145C1C5485CC8CC415BDFDB5A4EDADE5F4BCFCB4A5ECACE4F539793120692961703878302168286071B7F7BFA2E7A7EFF2BAFAB2AFEAAAE2FF +Crypton_1_0_S2,B17276BFACEE5583EDAA47D8339560C49B391E0C0A1DFF26895B22F1D440C8679DA43CE7C6B5F7DC61791586786EEB32B0CA4F23D2FB5E08244D8A100951A39FF66B21C30D38991F1C9064FE8BA648BD53E1EA57AE84B24535027FD9C72AD07CC9186500972B066A34F32C92EFDD7A56A24C88B95075D3E411CE4BA7FD3FBE818ED55A49425470A1DF87AB7DF412052E270FC13066983DCBB8E69C63E3BC19FA3A2F9EF26F1A283BC20E03C0B759A9D77485D6AD41EC8C71F0935DB61B68E54407E014A8F973CD4E25BB315F4ACC8F91DE6D7BF5B329A0176CDAE80496825236435CDB8D80D1E2B45846BAE90120FC1316F8946237CF699AAF77C53E7EA52D0B +CMEA,D9235FE6CA6897B07BF20C3411A58D4E0A46778D109F5E62F134ECA5C9B3D82B5947E3D2FFAE64CA158B7D3821BC96004956231597E4CB6FF2703C88BAD10DAEE238BA449F835D1CDEABC765F176092086BD0AF13CA72993CB455FE8107462DEB87780D11226AC6DE9CFF3543A0B954EB130A496F857498E051F627CC32BDAEDBB860D7A97136C4E5130E5F22FD8C4A99176F01743382984A2DBEF655ECA0DBCE7FAD8816F001442257C5DC99EB633AB5A6F9BD9FE7144C537A2882D00B613EC4E96A85AB5D7C38D3FF2EC0460711B290479E3C71B66814A259DDC5F3EB0F8A29134F65C6789730522AACBEEBF18D04DF536AE012F94C3498BBD5812E0776CDA +Twofish_p0,A967B3E804FDA3769A928078E4DDD1380DC6359818F7EC6C43753726FA139448F2D08B308454DF23195B3D59F3AEA2826301832ED9519B7CA6EBA5BE160CE361C08C3AF5732C250BBB4E896B536AB4F1E1E6BD45E2F4B666CC950356D41C1ED7FBC38EB5E9CFBFBAEA7739AF33C96271817909AD24CDF9D8E5C5B94D440886E7A11DAAED0670B2D2417BA01131C2279020F660FF965CB1AB9E9C521B5F930AEF918549EE2D4F8F3B47876D46D63E69642ACECB2FFC97057AAC7FD51A4B0EA75A28143F29883C4C02B8DAB017551F8A7D57C78D74B7C49F727E152212580799346E50DE6865BCDBF8C8A82B40DCFE32A4CA1021F0D35D0F006F9D36424A5EC1E0 +Picaro,080C03060604050605040C0C040305030A1F293B4B55627B8295AFBFC5D9E2F9012D456A8AACCFEA9FBCDDFD1C355F750F346152C2FBA392132B7444DBE1B3810F4481C292DB1352B3FB34742B61A3E10E59A4F8D8877C283C6799C9E7B44C140263CAAD1D71D7BD2741E383315AF79A0F74E192522BB3C2A3DB4434FB81136102839A1DBD3127ADF77163E341CAD75A0E99B428F8674CD87CE7C95987143CA40AAFD97B3B95E24B62C5BF1F55F982290ABFF94B7BC5823BE2551FAF952962D90EC914D828E73CF84C87599967A47CB401DD35EA6ABC5F8ACF1CFD2DAC759F4502E35ABDAD41F71DD7318363719A27CA01FD758AEA1C9F6A5FAC2DDDBC45CF35 +Kuznechik,FCEEDD11CF6E3116FBC4FADA23C5044DE977F0DB932E99BA1736F1BB14CD5FC1F918655AE25CEF21811C3C428B018E4F058402AEE36A8FA0060BED987FD4D31FEB342C51EAC848ABF22A68A2FD3ACECCB5700E56080C7612BF7213479CB75D8715A19629107B9AC7F391786F9D9EB2B13275193DFF358A7E6D54C680C3BD0D57DFF524A93EA843C9D779D6F67C22B903E00FECDE7A94B0BCDCE828504E330A4AA79760731E0062441AB83882649F2641AD454692275E552F8CA3A57D69D5953B0758B34086AC1DF730376BE488D9E789E11B83494C3FF8FE8D53AA90CAD88561207167A42D2B095BCB9B25D0BEE56C5259A674D2E6F4B4C0D166AFC2394B63B6 +CSA,3AEA68FE33E9881A83CFE17FBAE23812E82761950C36E570A206827C17A32649BE7A6D47C1518FF3CC5B67BDCD1808C9FF69EF034E484A843FB41004DCF55CC616ABAC4CF16A2F3C3BD4D594D0C4636271A1F94F2EAAC556E33993CE6564E4586C194279DDEE96F68AEC1E855345DEBB7E0A9A132A9DC25E5A1F32359CA87330293DE792871B2B4BA557974015E6BC0EEBC3342DB84425A41CC723ED906E5000999E4DD9DA8D6F5F3ED7217486DF6B058E5D3711D22875D6A77724BFF0B002B7F8FC8109B10176917D0FC8A0F2CB7860D1F7E0B59822B3201DA6DB7B599FAE31FBD3B6CA437207F4D84114550D548BB9AD460BAF80522CFA8C8966FDB2A99BC0 +iScream,008565D25BFF7ACE4DE22C369215BDAD57F3372D880DACBC189F7ECA41EE61D659EC78D447F926A3908BBF300A136FC02BAE918AD8740B12CC63FD43B23DE85DB61C833BC8459D2452DDE4F4AB08776DF5E548C56C76BA109920A704873FD05FA51E9B39B002EA67C6DF71F6544F8D2EE76AC7DE3597554E228106B47CFB1AA1D579FC428401E95C14933329C16EA8B828320C89B9A9D975ED58CD62F8469E19CB7FA227D760FE5A8E95E34C160F31BE64D33CB37BCF40EF8F9456F2170EAF2A2F8CF1E1DC53687244C91BA0389A07B55ED103B123801FA43496E0F0C4497369DAC309AA4A51F7703E8666EB21981DB7DBC2BB114B506BE69C25FA7D823AA605 +SMS4,D690E9FECCE13DB716B614C228FB2C052B679A762ABE04C3AA441326498606999C4250F491EF987A33540B43EDCFAC62E4B31CA9C908E89580DF94FA758F3FA64707A7FCF37317BA83593C19E6854FA8686B81B27164DA8BF8EB0F4B70569D351E240E5E6358D1A225227C3B01217887D40046579FD327524C3602E7A0C4C89EEABF8AD240C738B5A3F7F2CEF96115A1E0AE5DA49B341A55AD933230F58CB1E31DF6E22E8266CA60C02923AB0D534E6FD5DB3745DEFD8E2F03FF6A726D6C5B518D1BAF92BBDDBC7F11D95C411F105AD80AC13188A5CD7BBD2D74D012B8E5B4B08969974A0C96777E65B9F109C56EC68418F07DEC3ADC4D2079EE5F3ED7CB3948 +Turing,6151EB19B95D60387CB20612C45B163B2B1883B07F75FAA0E9DD6D7A6B682D49B51C90F7ED9FE8CEAE77C213FDCD3ECF376AD4DB8E651F1A87CB4015880D35B3110FD03048F9A8AC85270E8AE05064A7CCE4F198FFA104DAD5BC1BBBD1FE31CABAD92EF31D474A3D714CAB7D8DC759B8C1961EFC44C87BDC5C782A9DA5F073228905F4072152A6289A92698FC5C3F5E1DEEC09F2D3AF3423AADF7E8229C0241403324E396FC6B19BEA727941D8266C5E2CB4A25357E29C865495B6808C3667BD08932F995AF83AD75684D201F6664D558B0C0B46B73C4591A4E370D6FBE610A9C9009EE74F76253F5FA3332002EF6274EE178142580A4B63E5BE6EADBF439497 +Scream,208DB2DA3335A6FF7A526AC6A4A85123A29630ABC817149EE8F3F8DD85E24BD86C010E3DB6394A836FAA866E6840985F371305870482318924389D54227B63BD752C47E9C26043AC57A11F27E7AD5CD20F77FD08793A495DED90657C564F2E69CD443F625B886BC45E2D670B9F21292AD67E74E04173507655973C097D5A927084B926341D81322B3664AEC000EE8FA7BE58DC7FEC9B7810CC2F94F13B9C6D1648B5CA11FA0D8E07B10C12284C46F48BA9CFBB03A0FCEF2580F6B3BA3EF7D591C38AC145DE66F50AC915D9A36199B0E4D1FBD34EBFD4D771CB1EDB021A93EAC5EB72F91CE5CE4DF24219E1DF5995B78C9AF018E6C7AFBCB8E31BD0A553B406FE +Kuznyechik,FCEEDD11CF6E3116FBC4FADA23C5044DE977F0DB932E99BA1736F1BB14CD5FC1F918655AE25CEF21811C3C428B018E4F058402AEE36A8FA0060BED987FD4D31FEB342C51EAC848ABF22A68A2FD3ACECCB5700E56080C7612BF7213479CB75D8715A19629107B9AC7F391786F9D9EB2B13275193DFF358A7E6D54C680C3BD0D57DFF524A93EA843C9D779D6F67C22B903E00FECDE7A94B0BCDCE828504E330A4AA79760731E0062441AB83882649F2641AD454692275E552F8CA3A57D69D5953B0758B34086AC1DF730376BE488D9E789E11B83494C3FF8FE8D53AA90CAD88561207167A42D2B095BCB9B25D0BEE56C5259A674D2E6F4B4C0D166AFC2394B63B6 +CLEFIA_S0,5749D1C62F3374FB956D82EA0EB0A81C28D04B925CEE85B1C40A763D63F917AFBFA11965F77A322006CEE4839D5B4CD8425D2EE8D49B0F133C8967C071AAB6F5A4BEFD8C120097DA78E1CF6B394355263098CCDDEB54B38F4E16FA22A5770961D62A533745C16CAEEF7008998B1DF2B4E9C79F4A3125FE7CD3A2BD561488600BCDE234509EDC11052BB7A948FF668A73037586F16AA740C2B92CDB1F58943EEDFC1BA004B88DE6596293357ECA21DF4715F3BA7FA669C84D873B9C01E0DE24527B0C681E80B25AE7ADD523F4463F91C96E8472BB0D18D996F05F41AC27C5E33A816F07A379F62D381A445EB5D2ECCB909A36E529C34FAB6451F810D7BC027D8E +Fantomas,1E755FE199FC892F86EEF17B235210940CB74D67D842C8D6C46BAABA3DA50033532D0BB8DAA8C56CCAB6A42260075DD74FF41532811B9C8E913FE6F970E9437E8DF3CC65087A18AB166A77FDA7C082049F31DEE349D0594654EF2E3CBB2192B5553E0FA9DCB9C17FCEA6B43072035BD14BE41320851D9A8A972CF6E862F8476D294168D5ACCBBE1AB0DBC74E176426A039837851ED76FFE2F25C9D8F0A93340525587CCDAFDFB319BDC2D25614712AA33A806144F56EEBFBE748908C069E370998E5D9731F6F0DBC027D63EAB1D496128827C9F75EC64C5040FA3B2BAE3584A101695AFE8BEC95289BF0E06624570E871CB24574D34ACFDDC379A2BF36AD1138 +ARIA_s2,E24E54FC94C24ACC620D6A463C4D8BD15EFA64CBB497BE2BBC772E03D31959C11D06416B55F09969EA9C18AE63DFE7BB007366FB964C85E43A0945AA0FEE10EB2D7FF429ACCFAD918D78C895F92FCECD087A88385C832A2847DBB8C793A41253FF870E3136215848018E377432CAE9B1B7AB0CD7C4564226079860D9B6B91140EC208CBDA0C984044923F14F501F13DCD8C09E57E3C37B653B028F3EE82592E515DDFD17A9BFD49A7EC53967FE769D43A7E1D0F568F21B347005A38AD57986A830C6514B1EA627F635D26E2416825FDAE675A2EF2CB21C9F5D6F800A72449B6C900B5B337D5A52F361A1F7B0D63F7C6DED14E0A53D22B3F889DE711AAFBAB581 +Twofish_p1,75F3C6F4DB7BFBC84AD3E66B457DE84BD632D8FD3771F1E1300FF81B87FA063F5EBAAE5B8A00BC9D6DC1B10E805DD2D5A0840714B5902CA3B2734C549274365138B0BD5AFC6062966C42F7107C28278C13959CC724463B70CAE385CB11D093B8A68320FF9F77C3CC036F08BF40E72BE2790CAA82413AEAB9E49AA4977EDA7A176694A11D3DF0DEB30B72A71CEFD1533E8F33265FEC762A498188EE21C41AEBD9C53999CDAD318B011823DD1F4E2DF9484FF2658E785C58198DE59857677F0564AF63B6FEF5B73CA5CEE96844E04D4369292EAC1559A80A9E6E47DF34356ACFDC22C9C09B89D4EDAB12A20D52BB022FA9D7611EB45004F6C2162586565509BE91 +Camellia,70822CECB327C0E5E4855735EA0CAE4123EF6B934519A521ED0E4F4E1D6592BD86B8AF8F7CEB1FCE3E30DC5F5EC50B1AA6E139CAD5475D3DD9015AD651566C4D8B0D9A66FBCCB02D74122B20F0B18499DF4CCBC2347E76056DB7A931D11704D714583A61DE1B111C320F9C165318F222FE44CFB2C3B57A912408E8A860FC6950AAD0A07DA1896297545B1E95E0FF64D210C40048A3F775DB8A03E6DA093FDD94875C8302CD4A90337367F6F39D7FBFE2529BD826C837C63B81966F4B13BE632EE979A78C9F6EBC8E29F5F9B62FFDB4597898066AE74671BAD425AB4288A28DFA7207B955F8EEAC0A36492A683C38F1A44028D37BBBC943C115E3ADF477C7809E +SEED_S1,38E82DA6CFDEB3B8AF6055C7446F6B5BC36233B529A0E2A7D39111061CBC364BEF886CA817C416F4C245E1D63F3D8E98284EF63EA5F90DDFD82B667A272FF17242D441C07367AC8BF7AD801FCA2CAA34D20BEEE95D9418F857AE08C513CD86B9FF7DC131F58A6AB1D120D7022204687107DB9D9961BEE659DD5190DC9AA3ABD0810F471AE3EC8DBF967B5CA2A163234DC89E9C3A0C2EBA6E9F5AF292F34978CC15FB70757F351003646DC674D5B4EA097619FE4012E0BD05FA01F02A5EA956438514899BB0E5487997FC1E82218C1B5F7754B21D254F0046ED5852EB7EDAC9FD3095653CB6E4BB7C0E5039263284699337E724A4CB530A87D94C838FCE3B4AB7 +E2,E1423E814E179EFDB43F2CDA311EE041CCF3827D7C128EBBE45815D56FE94C4B357B5A9A9045BCF879D61B8802ABCF64090CF001A4B0F693436386DC11A5838BC9D019956AA15C246E5021802FE7530F912204EDA6484967ECF7C039CEF22DBE5D1CE387070D7AF4FB32F58CDB8F2596A8EACD336554068D890A5ED9160E716C0BFF60D22ED3C855C223B774E29BDF772BB93C6213E59434B127849FD7510061AD8573030840EF68FE971FDEAF66E8B8AEBDB3EBC66B47A9D8A772EE1D7EAAB675CBD43069207F375B9D78A3F176FA053D3A44573BCAC78A18469CBFBA38561A924D2629A298109970A0C528C16D14ACF95F4FC4C3D1FCDDB259E6B536524A2A diff --git a/software.conf b/software.conf new file mode 100644 index 0000000..03e84df --- /dev/null +++ b/software.conf @@ -0,0 +1,15 @@ +not1_cost=1.00 +and2_cost=1.00 +nand2_cost=1.00 +or2_cost=1.00 +nor2_cost=1.00 +xor2_cost=1.00 +xnor2_cost=1.00 +maoi1_cost=1.00 +moai1_cost=1.00 +nand3_cost=1.00 +nor3_cost=1.00 +and3_cost=1.00 +or3_cost=1.00 +andn2_cost=1.00 +orn2_cost=1.00 diff --git a/static_sort.h b/static_sort.h new file mode 100644 index 0000000..2235536 --- /dev/null +++ b/static_sort.h @@ -0,0 +1,115 @@ +/* + Copyright (c) 2016 Kang Yue Sheng Benjamin + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef static_sort_h +#define static_sort_h + +/* + Adapted from the Bose-Nelson Sorting network code from: + https://github.com/atinm/bose-nelson/blob/master/bose-nelson.c + */ + +/** + * A Functor class to create a sort for fixed sized arrays/containers with a + * compile time generated Bose-Nelson sorting network. + * \tparam NumElements The number of elements in the array or container to sort. + * \tparam T The element type. + * \tparam Compare A comparator functor class that returns true if lhs < rhs. + */ +template class StaticSort +{ + template struct Swap + { + template inline void s(T &v0, T &v1) + { + T t = Compare()(v0, v1) ? v0 : v1; // Min + v1 = Compare()(v0, v1) ? v1 : v0; // Max + v0 = t; + } + + inline Swap(A &a, const int &i0, const int &i1) { s(a[i0], a[i1]); } + }; + + template struct Swap + { + template inline void s(T &v0, T &v1) + { + // Explicitly code out the Min and Max to nudge the compiler + // to generate branchless code. + T t = v0 < v1 ? v0 : v1; // Min + v1 = v0 < v1 ? v1 : v0; // Max + v0 = t; + } + + inline Swap(A &a, const int &i0, const int &i1) { s(a[i0], a[i1]); } + }; + + template struct PB + { + inline PB(A &a) + { + enum { L = X >> 1, M = (X & 1 ? Y : Y + 1) >> 1, IAddL = I + L, XSubL = X - L }; + PB p0(a); + PB p1(a); + PB p2(a); + } + }; + + template struct PB + { + inline PB(A &a) { Swap s(a, I - 1, J - 1); } + }; + + template struct PB + { + inline PB(A &a) { Swap s0(a, I - 1, J); Swap s1(a, I - 1, J - 1); } + }; + + template struct PB + { + inline PB(A &a) { Swap s0(a, I - 1, J - 1); Swap s1(a, I, J - 1); } + }; + + template struct PS + { + inline PS(A &a) + { + enum { L = M >> 1, IAddL = I + L, MSubL = M - L}; + PS ps0(a); + PS ps1(a); + PB pb(a); + } + }; + + template struct PS + { + inline PS(A &a) {} + }; + +public: + /** + * Sorts the array/container arr. + * \param arr The array/container to be sorted. + */ + template inline void operator() (Container &arr) const + { + PS ps(arr); + } + + /** + * Sorts the array arr. + * \param arr The array to be sorted. + */ + template inline void operator() (T *arr) const + { + PS ps(arr); + } +}; +#endif diff --git a/subspaces.hpp b/subspaces.hpp new file mode 100644 index 0000000..c78cf46 --- /dev/null +++ b/subspaces.hpp @@ -0,0 +1,160 @@ +/** + * PEIGEN: a Platform for Evaluation, Implementation, and Generation of S-boxes + * + * Copyright 2019 by + * Zhenzhen Bao + * Jian Guo + * San Ling + * Yu Sasaki + * + * This platform is developed based on the open source application + * + * Optimizing Implementations of Lightweight Building Blocks + * + * Copyright 2017 by + * Jade Tourteaux + * Jérémy Jean + * + * We follow the same copyright policy. + * + * This file is part of some open source application. + * + * Some open source application is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Some open source application is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see . + * + * @license GPL-3.0+ + */ + +#ifndef SUBSPACES_H__ +#define SUBSPACES_H__ + +#include +#include +#include +#include +#include + +using namespace std; + +/* + generating the echelon matrices over finite fields to generate the basis of subspaces + according to the method used in sagemath reduced_echelon_matrix_iterator() method +*/ +template +const vector > > SubSpaces = [&] +{ + if (N <= 8) + { + vector > > subspaces; + #define N0 N + #define Ci(x) (C0[x+1]-1) + + int A0[N0 + 1], T0[N0 + 1], F0[N0 + 1], H0[N0 + 1], C0[N0 + 1], X0, Y0, I0, L0, Z0; + int M0; + + array, N> m; + vector > free_positions; + + vector > newdim_subspaces; + vector anew_subspace; + + for (int dim = 1; dim < N; dim++) + { + newdim_subspaces.clear(); + + M0 = dim; + + for (int i=0; i<=(N0-M0); i++) A0[i] = 0; for (int i=N0-M0+1; i<=N0; i++) A0[i] = 1; + for (int i = 1; i<=M0; i++) { C0[i] = N0 - M0 + i; H0[N0-M0+i] = i; } + T0[N0-M0] = -1; T0[1] = 0; F0[N0] = N0 - M0 + 1; I0 = N0 - M0; L0 = N0; + + do + { + for (int i = 0; i < dim; i++) m[i].fill(0); + free_positions.clear(); + for (int i = 0; i < dim; i++) + { + m[i][Ci(i)] = 1; + for (int j = Ci(i) + 1; j < N; j++) + { + if (A0[j+1] != 1) + { + free_positions.push_back(pair(i, j)); + } + } + } + + int num_free_pos = free_positions.size(); + for (uint64_t v = 0; v < (1<> (uint64_t)fi) & 1ULL); + } + anew_subspace.clear(); + for (int ri = 0; ri < dim; ri++) + { + uint8_t vec = 0; + for (int ci = 0; ci < N; ci++) vec |= (m[ri][ci] << ci); + anew_subspace.push_back(vec); + } + anew_subspace.shrink_to_fit(); + newdim_subspaces.push_back(anew_subspace); + } + + if (I0 == 0) + { + break; + } + else + { + if (T0[I0] < 0) { if ((-T0[I0]) != (I0-1)){ T0[I0-1] = T0[I0]; } T0[I0] = I0-1; } + if ( A0[I0]==0 ) + { + X0 = I0; Y0 = F0[L0]; if (A0[I0-1] == 1){ F0[I0] = F0[I0 - 1]; } else { F0[I0] = I0; } + if (F0[L0] == L0) { L0 = I0; I0 = T0[I0]; goto CHANGE0; } + if (L0 == N0) { T0[F0[N0]] = -I0 - 1; T0[I0 + 1] = T0[I0]; I0 = F0[N0]; F0[N0] = F0[N0] + 1; goto CHANGE0; } + T0[L0] = -I0-1; T0[I0+1] = T0[I0]; F0[L0] = F0[L0] + 1; I0 = L0; goto CHANGE0; + } + Y0 = I0; + if (I0 != L0) + { + F0[L0] = X0 = F0[L0] - 1; F0[I0 - 1] = F0[I0]; + if (L0 == N0) + { + if (I0 == (F0[N0] - 1)) { I0 = T0[I0]; goto CHANGE0; } + T0[F0[N0]-1] = -I0-1; T0[I0+1] = T0[I0]; I0 = F0[N0] - 1; goto CHANGE0; + } + T0[L0] = -I0 -1; T0[I0 + 1] = T0[I0]; I0 = L0; goto CHANGE0; + } + X0 = N0; F0[L0 - 1] = F0[L0]; F0[N0] = N0; L0 = N0; + if (I0 == N0 - 1) { I0 = T0[N0 - 1]; goto CHANGE0; } + T0[N0 - 1] = -I0 - 1; T0[I0 + 1] = T0[I0]; I0 = N0 - 1; + CHANGE0: + A0[X0] = 1; A0[Y0] = 0; H0[X0] = Z0 = H0[Y0]; C0[Z0] = X0; + } + } while (true); + + newdim_subspaces.shrink_to_fit(); + subspaces.push_back(newdim_subspaces); + } + #undef Ci + #undef N0 + return subspaces; + } +} (); + +#endif \ No newline at end of file