text:
subgroupdict = Dict((generators = 1,) => [[1, 9], [1, 11], [1, 13], [1, 16], [1, 17], [1, 20], [1, 21], [1, 22], [1, 25], [1, 27], [1, 28], [1, 29], [1, 31], [1, 33], [1, 35], [1, 37], [1, 45], [1, 47], [1, 51], [1, 54], [1, 56], [1, 59], [1, 71], [1, 74], [1, 77], [1, 79], [1, 85], [1, 104], [1, 106], [1, 109], [1, 151], [1, 152], [1, 158], [1, 165], [1, 169], [1, 174], [1, 176], [1, 178], [1, 179], [1, 184], [1, 186], [1, 188], [1, 189], [1, 4, 23], [1, 6, 34], [1, 7, 39], [1, 10, 46], [1, 12, 48], [1, 18, 58], [1, 70, 122], [1, 78, 124], [1, 84, 137], [1, 86, 140], [1, 88, 125], [1, 94, 108], [1, 98, 121], [1, 101, 192], [1, 102, 135], [1, 116, 145], [1, 2, 9, 44], [1, 3, 16, 55], [1, 5, 27, 62], [1, 43, 146, 165], [1, 61, 74, 156], [1, 64, 128, 165], [1, 66, 104, 154], [1, 74, 75, 123], [1, 87, 104, 138], [1, 91, 109, 166], [1, 92, 112, 165], [1, 95, 109, 111], [1, 9, 110, 173], [1, 16, 113, 144], [1, 27, 127, 148], [1, 142, 165, 172], [1, 157, 165, 181], [1, 160, 165, 180], [1, 15, 53, 101, 165, 192], [1, 30, 88, 105, 125, 165], [1, 12, 38, 48, 165, 183], [1, 41, 81, 102, 135, 165], [1, 49, 98, 121, 130, 165], [1, 4, 23, 60, 165, 187], [1, 65, 84, 119, 137, 165], [1, 68, 94, 108, 155, 165], [1, 72, 76, 116, 145, 165], [1, 10, 46, 80, 97, 165], [1, 7, 39, 89, 165, 167], [1, 70, 96, 114, 122, 165], [1, 86, 99, 140, 165, 168], [1, 18, 58, 117, 132, 165], [1, 78, 124, 134, 164, 165], [1, 6, 34, 149, 153, 165], [1, 8, 43, 82, 136, 146, 165, 191], [1, 14, 64, 120, 128, 162, 165, 185], [1, 19, 26, 133, 157, 163, 165, 181], [1, 24, 57, 92, 112, 165, 177, 190], [1, 32, 36, 69, 160, 165, 180, 182], [1, 40, 90, 115, 139, 157, 165, 181], [1, 42, 63, 129, 147, 160, 165, 180], [1, 50, 52, 100, 126, 142, 165, 172], [1, 67, 93, 103, 131, 142, 165, 172], [1, 43, 73, 118, 146, 161, 165, 170], [1, 83, 92, 112, 141, 165, 171, 175], [1, 64, 107, 128, 143, 150, 159, 165]],
(generators = 0,) => [[1]],
(generators = 2,) => [[1, 9, 16, 74], [1, 9, 17, 51], [1, 9, 20, 85], [1, 9, 21, 54], [1, 9, 27, 104], [1, 9, 28, 59], [1, 9, 109, 165], [1, 9, 178, 186], [1, 9, 179, 188], [1, 11, 27, 106], [1, 11, 74, 152], [1, 11, 165, 174], [1, 13, 16, 77], [1, 13, 104, 158], [1, 13, 165, 184], [1, 16, 22, 45], [1, 16, 25, 35], [1, 16, 27, 109], [1, 16, 29, 79], [1, 16, 104, 165], [1, 16, 151, 189], [1, 16, 158, 184], [1, 17, 28, 109], [1, 17, 59, 165], [1, 20, 109, 178], [1, 20, 165, 186], [1, 21, 35, 176], [1, 21, 56, 151], [1, 21, 109, 179], [1, 21, 165, 188], [1, 22, 29, 165], [1, 22, 79, 104], [1, 25, 33, 179], [1, 25, 54, 169], [1, 25, 104, 151], [1, 25, 165, 189], [1, 27, 31, 47], [1, 27, 33, 56], [1, 27, 37, 71], [1, 27, 74, 165], [1, 27, 152, 174], [1, 27, 169, 176], [1, 28, 51, 165], [1, 29, 45, 104], [1, 31, 37, 74], [1, 31, 71, 165], [1, 33, 54, 189], [1, 33, 74, 176], [1, 33, 165, 169], [1, 35, 56, 188], [1, 35, 104, 189], [1, 35, 151, 165], [1, 37, 47, 165], [1, 45, 79, 165], [1, 47, 71, 74], [1, 51, 59, 109], [1, 54, 109, 188], [1, 54, 165, 179], [1, 56, 74, 169], [1, 56, 165, 176], [1, 74, 104, 109], [1, 74, 106, 174], [1, 77, 104, 184], [1, 77, 158, 165], [1, 85, 109, 186], [1, 85, 165, 178], [1, 106, 152, 165], [1, 151, 176, 188], [1, 169, 179, 189], [1, 4, 23, 28, 79, 106], [1, 4, 23, 45, 51, 152], [1, 6, 17, 34, 37, 77], [1, 6, 34, 47, 59, 158], [1, 7, 31, 39, 45, 85], [1, 7, 39, 71, 79, 178], [1, 10, 17, 22, 46, 152], [1, 10, 29, 46, 59, 106], [1, 11, 13, 94, 108, 178], [1, 11, 17, 45, 88, 125], [1, 11, 20, 86, 140, 158], [1, 11, 22, 51, 70, 122], [1, 12, 28, 31, 48, 158], [1, 12, 48, 51, 71, 77], [1, 13, 20, 78, 124, 152], [1, 13, 28, 47, 116, 145], [1, 13, 31, 59, 84, 137], [1, 17, 71, 84, 137, 184], [1, 18, 22, 47, 58, 85], [1, 18, 29, 37, 58, 178], [1, 20, 29, 71, 98, 121], [1, 20, 37, 79, 102, 135], [1, 22, 31, 98, 121, 186], [1, 28, 29, 70, 122, 174], [1, 37, 51, 116, 145, 184], [1, 45, 47, 102, 135, 186], [1, 59, 79, 88, 125, 174], [1, 77, 85, 101, 106, 192], [1, 77, 86, 140, 174, 186], [1, 78, 106, 124, 184, 186], [1, 85, 94, 108, 174, 184], [1, 101, 152, 158, 178, 192], [1, 2, 9, 16, 17, 44, 51, 74], [1, 2, 9, 20, 21, 44, 54, 85], [1, 2, 9, 27, 28, 44, 59, 104], [1, 2, 9, 44, 109, 110, 165, 173], [1, 2, 9, 44, 178, 179, 186, 188], [1, 3, 9, 16, 22, 45, 55, 74], [1, 3, 13, 16, 25, 35, 55, 77], [1, 3, 16, 27, 29, 55, 79, 109], [1, 3, 16, 55, 104, 113, 144, 165], [1, 3, 16, 55, 151, 158, 184, 189], [1, 5, 9, 27, 31, 47, 62, 104], [1, 5, 11, 27, 33, 56, 62, 106], [1, 5, 16, 27, 37, 62, 71, 109], [1, 5, 27, 62, 74, 127, 148, 165], [1, 5, 27, 62, 152, 169, 174, 176], [1, 9, 11, 16, 74, 75, 123, 152], [1, 9, 13, 27, 87, 104, 138, 158], [1, 9, 25, 109, 142, 165, 172, 189], [1, 9, 16, 29, 74, 79, 113, 144], [1, 9, 33, 64, 109, 128, 165, 169], [1, 9, 35, 43, 109, 146, 151, 165], [1, 9, 27, 37, 71, 104, 127, 148], [1, 9, 56, 109, 157, 165, 176, 181], [1, 9, 16, 61, 74, 106, 156, 174], [1, 9, 27, 66, 77, 104, 154, 184], [1, 9, 91, 95, 109, 111, 165, 166], [1, 9, 92, 109, 112, 160, 165, 180], [1, 11, 31, 71, 157, 165, 174, 181], [1, 11, 37, 47, 64, 128, 165, 174], [1, 11, 61, 74, 104, 109, 152, 156], [1, 11, 27, 106, 127, 148, 169, 176], [1, 13, 22, 29, 43, 146, 165, 184], [1, 13, 45, 79, 142, 165, 172, 184], [1, 13, 66, 74, 104, 109, 154, 158], [1, 13, 16, 77, 113, 144, 151, 189], [1, 16, 20, 27, 95, 109, 111, 178], [1, 16, 21, 104, 160, 165, 180, 188], [1, 9, 16, 28, 59, 74, 110, 173], [1, 16, 27, 31, 47, 109, 127, 148], [1, 16, 33, 104, 157, 165, 169, 181], [1, 16, 43, 104, 142, 146, 165, 172], [1, 16, 54, 92, 104, 112, 165, 179], [1, 16, 56, 64, 104, 128, 165, 176], [1, 16, 66, 87, 104, 138, 154, 165], [1, 16, 27, 85, 91, 109, 166, 186], [1, 17, 20, 59, 92, 112, 165, 186], [1, 17, 21, 28, 95, 109, 111, 179], [1, 9, 17, 27, 51, 104, 110, 173], [1, 17, 28, 54, 91, 109, 166, 188], [1, 17, 59, 85, 160, 165, 178, 180], [1, 20, 28, 51, 160, 165, 180, 186], [1, 20, 74, 91, 104, 109, 166, 178], [1, 9, 20, 85, 110, 173, 179, 188], [1, 21, 25, 64, 128, 165, 188, 189], [1, 21, 27, 74, 92, 112, 165, 188], [1, 21, 33, 142, 165, 169, 172, 188], [1, 21, 43, 146, 157, 165, 181, 188], [1, 21, 51, 59, 91, 109, 166, 179], [1, 9, 21, 54, 110, 173, 178, 186], [1, 22, 25, 79, 87, 104, 138, 151], [1, 16, 22, 27, 45, 109, 113, 144], [1, 22, 35, 66, 79, 104, 154, 189], [1, 22, 29, 77, 142, 158, 165, 172], [1, 25, 27, 43, 74, 146, 165, 189], [1, 25, 29, 45, 66, 104, 151, 154], [1, 25, 56, 160, 165, 176, 180, 189], [1, 25, 92, 112, 157, 165, 181, 189], [1, 16, 25, 35, 113, 144, 158, 184], [1, 27, 35, 74, 142, 151, 165, 172], [1, 27, 54, 74, 160, 165, 179, 180], [1, 27, 61, 74, 75, 123, 156, 165], [1, 27, 64, 74, 128, 157, 165, 181], [1, 28, 51, 85, 92, 112, 165, 178], [1, 29, 35, 45, 87, 104, 138, 189], [1, 31, 33, 37, 74, 75, 123, 176], [1, 31, 37, 56, 61, 74, 156, 169], [1, 31, 64, 71, 106, 128, 152, 165], [1, 33, 35, 92, 112, 151, 165, 169], [1, 33, 43, 146, 160, 165, 169, 180], [1, 33, 47, 61, 71, 74, 156, 176], [1, 27, 33, 56, 127, 148, 152, 174], [1, 35, 54, 151, 157, 165, 179, 181], [1, 35, 64, 128, 151, 160, 165, 180], [1, 37, 47, 106, 152, 157, 165, 181], [1, 43, 45, 77, 79, 146, 158, 165], [1, 43, 54, 56, 146, 165, 176, 179], [1, 43, 64, 92, 112, 128, 146, 165], [1, 47, 56, 71, 74, 75, 123, 169], [1, 51, 54, 59, 95, 109, 111, 188], [1, 54, 64, 128, 142, 165, 172, 179], [1, 56, 92, 112, 142, 165, 172, 176], [1, 74, 77, 87, 104, 109, 138, 184], [1, 74, 85, 95, 104, 109, 111, 186], [1, 74, 75, 104, 106, 109, 123, 174], [1, 142, 157, 160, 165, 172, 180, 181], [1, 4, 6, 23, 25, 33, 34, 94, 98, 108, 121, 179], [1, 4, 9, 10, 16, 23, 46, 70, 74, 88, 122, 125], [1, 4, 18, 21, 23, 56, 58, 84, 86, 137, 140, 151], [1, 6, 7, 21, 34, 35, 39, 70, 78, 122, 124, 176], [1, 6, 9, 12, 27, 34, 48, 84, 104, 116, 137, 145], [1, 7, 10, 33, 39, 46, 54, 86, 116, 140, 145, 189], [1, 7, 16, 18, 27, 39, 58, 98, 102, 109, 121, 135], [1, 10, 12, 35, 46, 48, 56, 94, 102, 108, 135, 188], [1, 11, 22, 28, 29, 51, 70, 96, 114, 122, 165, 174], [1, 11, 17, 30, 45, 59, 79, 88, 105, 125, 165, 174], [1, 11, 13, 68, 85, 94, 108, 155, 165, 174, 178, 184], [1, 11, 20, 77, 86, 99, 140, 158, 165, 168, 174, 186], [1, 12, 18, 25, 48, 54, 58, 78, 88, 124, 125, 169], [1, 13, 17, 31, 59, 65, 71, 84, 119, 137, 165, 184], [1, 13, 28, 37, 47, 51, 72, 76, 116, 145, 165, 184], [1, 13, 20, 78, 106, 124, 134, 152, 164, 165, 184, 186], [1, 15, 53, 77, 85, 101, 106, 152, 158, 165, 178, 192], [1, 10, 17, 22, 29, 46, 59, 80, 97, 106, 152, 165], [1, 6, 17, 34, 37, 47, 59, 77, 149, 153, 158, 165], [1, 20, 22, 29, 31, 49, 71, 98, 121, 130, 165, 186], [1, 20, 37, 41, 45, 47, 79, 81, 102, 135, 165, 186], [1, 18, 22, 29, 37, 47, 58, 85, 117, 132, 165, 178], [1, 12, 28, 31, 38, 48, 51, 71, 77, 158, 165, 183], [1, 4, 23, 28, 45, 51, 60, 79, 106, 152, 165, 187], [1, 7, 31, 39, 45, 71, 79, 85, 89, 165, 167, 178], [1, 70, 84, 101, 102, 122, 135, 137, 169, 179, 189, 192], [1, 74, 78, 86, 94, 101, 104, 108, 109, 124, 140, 192], [1, 88, 98, 101, 116, 121, 125, 145, 151, 176, 188, 192], [1, 2, 9, 44, 91, 92, 95, 109, 110, 111, 112, 160, 165, 166, 173, 180], [1, 3, 16, 43, 55, 66, 87, 104, 113, 138, 142, 144, 146, 154, 165, 172], [1, 5, 27, 61, 62, 64, 74, 75, 123, 127, 128, 148, 156, 157, 165, 181], [1, 8, 13, 22, 29, 43, 64, 82, 92, 112, 128, 136, 146, 165, 184, 191], [1, 8, 16, 43, 73, 82, 104, 118, 136, 142, 146, 161, 165, 170, 172, 191], [1, 8, 43, 45, 54, 56, 77, 79, 82, 136, 146, 158, 165, 176, 179, 191], [1, 9, 24, 57, 83, 92, 109, 112, 141, 160, 165, 171, 175, 177, 180, 190], [1, 9, 32, 36, 42, 63, 69, 92, 109, 112, 129, 147, 160, 165, 180, 182], [1, 11, 14, 37, 43, 47, 64, 92, 112, 120, 128, 146, 162, 165, 174, 185], [1, 11, 19, 26, 31, 71, 133, 142, 157, 160, 163, 165, 172, 174, 180, 181], [1, 11, 21, 25, 37, 47, 64, 107, 128, 143, 150, 159, 165, 174, 188, 189], [1, 11, 31, 35, 40, 54, 71, 90, 115, 139, 151, 157, 165, 174, 179, 181], [1, 13, 21, 33, 45, 67, 79, 93, 103, 131, 142, 165, 169, 172, 184, 188], [1, 13, 45, 50, 52, 79, 100, 126, 142, 157, 160, 165, 172, 180, 181, 184], [1, 13, 22, 29, 43, 54, 56, 73, 118, 146, 161, 165, 170, 176, 179, 184], [1, 14, 21, 25, 31, 64, 71, 106, 120, 128, 152, 162, 165, 185, 188, 189], [1, 14, 27, 64, 74, 107, 120, 128, 143, 150, 157, 159, 162, 165, 181, 185], [1, 16, 43, 50, 52, 67, 93, 100, 103, 104, 126, 131, 142, 146, 165, 172], [1, 17, 20, 24, 43, 57, 59, 64, 92, 112, 128, 146, 165, 177, 186, 190], [1, 17, 25, 32, 36, 56, 59, 69, 85, 160, 165, 176, 178, 180, 182, 189], [1, 17, 20, 33, 35, 59, 83, 92, 112, 141, 151, 165, 169, 171, 175, 186], [1, 17, 42, 59, 63, 85, 129, 142, 147, 157, 160, 165, 172, 178, 180, 181], [1, 19, 26, 27, 40, 64, 74, 90, 115, 128, 133, 139, 157, 163, 165, 181], [1, 19, 26, 35, 37, 47, 54, 106, 133, 151, 152, 157, 163, 165, 179, 181], [1, 20, 25, 28, 42, 51, 56, 63, 129, 147, 160, 165, 176, 180, 186, 189], [1, 20, 28, 32, 36, 51, 69, 142, 157, 160, 165, 172, 180, 181, 182, 186], [1, 21, 22, 29, 33, 50, 52, 77, 100, 126, 142, 158, 165, 169, 172, 188], [1, 22, 29, 67, 77, 93, 103, 131, 142, 157, 158, 160, 165, 172, 180, 181], [1, 24, 28, 33, 35, 51, 57, 85, 92, 112, 151, 165, 169, 177, 178, 190], [1, 28, 43, 51, 64, 83, 85, 92, 112, 128, 141, 146, 165, 171, 175, 178], [1, 31, 43, 64, 71, 92, 106, 107, 112, 128, 143, 146, 150, 152, 159, 165], [1, 37, 40, 47, 90, 106, 115, 139, 142, 152, 157, 160, 165, 172, 180, 181], [1, 43, 45, 64, 73, 77, 79, 92, 112, 118, 128, 146, 158, 161, 165, 170], [1, 2, 3, 4, 9, 10, 11, 16, 17, 22, 23, 44, 45, 46, 51, 55, 70, 74, 75, 88, 122, 123, 125, 152], [1, 2, 5, 6, 9, 12, 13, 27, 28, 31, 34, 44, 47, 48, 59, 62, 84, 87, 104, 116, 137, 138, 145, 158], [1, 3, 5, 7, 16, 18, 20, 27, 29, 37, 39, 55, 58, 62, 71, 79, 95, 98, 102, 109, 111, 121, 135, 178], [1, 4, 7, 12, 15, 23, 38, 39, 43, 48, 53, 60, 64, 89, 92, 101, 112, 128, 146, 165, 167, 183, 187, 192], [1, 4, 9, 10, 16, 23, 27, 30, 46, 60, 70, 74, 80, 88, 96, 97, 104, 105, 109, 114, 122, 125, 165, 187], [1, 4, 9, 10, 16, 23, 28, 29, 46, 59, 61, 70, 74, 79, 88, 106, 110, 113, 122, 125, 144, 156, 173, 174], [1, 4, 18, 21, 23, 35, 56, 58, 60, 65, 84, 86, 99, 117, 119, 132, 137, 140, 151, 165, 168, 176, 187, 188], [1, 4, 23, 41, 60, 72, 76, 78, 81, 102, 116, 124, 134, 135, 142, 145, 157, 160, 164, 165, 172, 180, 181, 187], [1, 4, 6, 23, 25, 33, 34, 49, 54, 60, 68, 94, 98, 108, 121, 130, 149, 153, 155, 165, 169, 179, 187, 189], [1, 6, 10, 15, 18, 34, 46, 53, 58, 80, 97, 101, 117, 132, 142, 149, 153, 157, 160, 165, 172, 180, 181, 192], [1, 6, 9, 12, 16, 27, 34, 38, 48, 65, 72, 74, 76, 84, 104, 109, 116, 119, 137, 145, 149, 153, 165, 183], [1, 6, 30, 34, 41, 43, 64, 81, 86, 88, 92, 99, 102, 105, 112, 125, 128, 135, 140, 146, 149, 153, 165, 168], [1, 6, 9, 12, 17, 27, 34, 37, 48, 51, 66, 71, 77, 84, 104, 110, 116, 127, 137, 145, 148, 154, 173, 184], [1, 6, 7, 21, 34, 35, 39, 56, 70, 78, 89, 96, 114, 122, 124, 134, 149, 151, 153, 164, 165, 167, 176, 188], [1, 7, 9, 16, 18, 27, 39, 41, 49, 58, 74, 81, 89, 98, 102, 104, 109, 117, 121, 130, 132, 135, 165, 167], [1, 7, 16, 18, 22, 27, 31, 39, 45, 47, 58, 85, 91, 98, 102, 109, 113, 121, 127, 135, 144, 148, 166, 186], [1, 7, 10, 25, 33, 39, 46, 54, 72, 76, 80, 86, 89, 97, 99, 116, 140, 145, 165, 167, 168, 169, 179, 189], [1, 7, 30, 39, 65, 68, 84, 88, 89, 94, 105, 108, 119, 125, 137, 142, 155, 157, 160, 165, 167, 172, 180, 181], [1, 9, 15, 16, 27, 53, 68, 74, 78, 86, 94, 99, 101, 104, 108, 109, 124, 134, 140, 155, 164, 165, 168, 192], [1, 10, 12, 21, 35, 38, 41, 46, 48, 56, 68, 80, 81, 94, 97, 102, 108, 135, 151, 155, 165, 176, 183, 188], [1, 10, 43, 46, 49, 64, 65, 78, 80, 84, 92, 97, 98, 112, 119, 121, 124, 128, 130, 134, 137, 146, 164, 165], [1, 11, 13, 20, 61, 66, 74, 78, 86, 91, 94, 101, 104, 108, 109, 124, 140, 152, 154, 156, 158, 166, 178, 192], [1, 12, 18, 25, 30, 33, 38, 48, 54, 58, 78, 88, 105, 117, 124, 125, 132, 134, 164, 165, 169, 179, 183, 189], [1, 12, 38, 48, 49, 70, 86, 96, 98, 99, 114, 121, 122, 130, 140, 142, 157, 160, 165, 168, 172, 180, 181, 183], [1, 15, 21, 30, 35, 49, 53, 56, 72, 76, 88, 98, 101, 105, 116, 121, 125, 130, 145, 151, 165, 176, 188, 192], [1, 15, 25, 33, 41, 53, 54, 65, 70, 81, 84, 96, 101, 102, 114, 119, 122, 135, 137, 165, 169, 179, 189, 192], [1, 18, 43, 58, 64, 68, 70, 72, 76, 92, 94, 96, 108, 112, 114, 116, 117, 122, 128, 132, 145, 146, 155, 165], [1, 74, 75, 77, 78, 85, 86, 87, 94, 95, 101, 104, 106, 108, 109, 111, 123, 124, 138, 140, 174, 184, 186, 192], [1, 2, 9, 24, 25, 44, 56, 57, 83, 91, 92, 95, 109, 110, 111, 112, 141, 142, 157, 160, 165, 166, 171, 172, 173, 175, 176, 177, 180, 181, 189, 190], [1, 2, 9, 32, 33, 35, 36, 42, 43, 44, 63, 64, 69, 91, 92, 95, 109, 110, 111, 112, 128, 129, 146, 147, 151, 160, 165, 166, 169, 173, 180, 182], [1, 3, 8, 16, 21, 33, 43, 55, 66, 73, 82, 87, 104, 113, 118, 136, 138, 142, 144, 146, 154, 157, 160, 161, 165, 169, 170, 172, 180, 181, 188, 191], [1, 3, 16, 43, 50, 52, 54, 55, 56, 64, 66, 67, 87, 92, 93, 100, 103, 104, 112, 113, 126, 128, 131, 138, 142, 144, 146, 154, 165, 172, 176, 179], [1, 5, 14, 27, 35, 54, 61, 62, 64, 74, 75, 107, 120, 123, 127, 128, 142, 143, 148, 150, 151, 156, 157, 159, 160, 162, 165, 172, 179, 180, 181, 185], [1, 5, 19, 21, 25, 26, 27, 40, 43, 61, 62, 64, 74, 75, 90, 92, 112, 115, 123, 127, 128, 133, 139, 146, 148, 156, 157, 163, 165, 181, 188, 189], [1, 8, 9, 16, 25, 27, 35, 43, 50, 52, 67, 73, 74, 82, 93, 100, 103, 104, 109, 118, 126, 131, 136, 142, 146, 151, 161, 165, 170, 172, 189, 191], [1, 9, 14, 16, 19, 26, 27, 33, 40, 56, 64, 74, 90, 104, 107, 109, 115, 120, 128, 133, 139, 143, 150, 157, 159, 162, 163, 165, 169, 176, 181, 185], [1, 9, 16, 21, 24, 27, 32, 36, 42, 54, 57, 63, 69, 74, 83, 92, 104, 109, 112, 129, 141, 147, 160, 165, 171, 175, 177, 179, 180, 182, 188, 190], [1, 2, 3, 4, 9, 10, 11, 16, 17, 22, 23, 27, 28, 29, 30, 44, 45, 46, 51, 55, 59, 60, 61, 70, 74, 75, 79, 80, 88, 96, 97, 104, 105, 106, 109, 110, 113, 114, 122, 123, 125, 144, 152, 156, 165, 173, 174, 187], [1, 2, 5, 6, 9, 12, 13, 16, 17, 27, 28, 31, 34, 37, 38, 44, 47, 48, 51, 59, 62, 65, 66, 71, 72, 74, 76, 77, 84, 87, 104, 109, 110, 116, 119, 127, 137, 138, 145, 148, 149, 153, 154, 158, 165, 173, 183, 184], [1, 3, 5, 7, 9, 16, 18, 20, 22, 27, 29, 31, 37, 39, 41, 45, 47, 49, 55, 58, 62, 71, 74, 79, 81, 85, 89, 91, 95, 98, 102, 104, 109, 111, 113, 117, 121, 127, 130, 132, 135, 144, 148, 165, 166, 167, 178, 186], [1, 4, 13, 20, 23, 28, 32, 36, 37, 40, 41, 45, 47, 50, 51, 52, 60, 69, 72, 76, 78, 79, 81, 90, 100, 102, 106, 115, 116, 124, 126, 134, 135, 139, 142, 145, 152, 157, 160, 164, 165, 172, 180, 181, 182, 184, 186, 187], [1, 4, 7, 12, 15, 23, 28, 31, 38, 39, 43, 45, 48, 51, 53, 60, 64, 71, 73, 77, 79, 83, 85, 89, 92, 101, 106, 107, 112, 118, 128, 141, 143, 146, 150, 152, 158, 159, 161, 165, 167, 170, 171, 175, 178, 183, 187, 192], [1, 6, 11, 14, 17, 20, 24, 30, 34, 37, 41, 43, 45, 47, 57, 59, 64, 73, 77, 79, 81, 86, 88, 92, 99, 102, 105, 112, 118, 120, 125, 128, 135, 140, 146, 149, 153, 158, 161, 162, 165, 168, 170, 174, 177, 185, 186, 190], [1, 6, 10, 15, 17, 18, 22, 29, 34, 37, 40, 42, 46, 47, 53, 58, 59, 63, 67, 77, 80, 85, 90, 93, 97, 101, 103, 106, 115, 117, 129, 131, 132, 139, 142, 147, 149, 152, 153, 157, 158, 160, 165, 172, 178, 180, 181, 192], [1, 7, 11, 13, 17, 19, 26, 30, 31, 39, 42, 45, 50, 52, 59, 63, 65, 68, 71, 79, 84, 85, 88, 89, 94, 100, 105, 108, 119, 125, 126, 129, 133, 137, 142, 147, 155, 157, 160, 163, 165, 167, 172, 174, 178, 180, 181, 184], [1, 8, 10, 13, 17, 20, 22, 24, 29, 31, 43, 46, 49, 57, 59, 64, 65, 71, 78, 80, 82, 84, 92, 97, 98, 106, 107, 112, 119, 121, 124, 128, 130, 134, 136, 137, 143, 146, 150, 152, 159, 164, 165, 177, 184, 186, 190, 191], [1, 8, 11, 13, 14, 18, 22, 28, 29, 37, 43, 47, 51, 58, 64, 68, 70, 72, 76, 82, 83, 85, 92, 94, 96, 108, 112, 114, 116, 117, 120, 122, 128, 132, 136, 141, 145, 146, 155, 162, 165, 171, 174, 175, 178, 184, 185, 191], [1, 11, 12, 19, 20, 22, 26, 28, 29, 31, 32, 36, 38, 48, 49, 51, 67, 69, 70, 71, 77, 86, 93, 96, 98, 99, 103, 114, 121, 122, 130, 131, 133, 140, 142, 157, 158, 160, 163, 165, 168, 172, 174, 180, 181, 182, 183, 186], [1, 9, 11, 13, 15, 16, 20, 27, 53, 61, 66, 68, 74, 75, 77, 78, 85, 86, 87, 91, 94, 95, 99, 101, 104, 106, 108, 109, 111, 123, 124, 134, 138, 140, 152, 154, 155, 156, 158, 164, 165, 166, 168, 174, 178, 184, 186, 192], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192]],
allsubgroups => [[1], [1, 9], [1, 11], [1, 13], [1, 16], [1, 17], [1, 20], [1, 21], [1, 22], [1, 25], [1, 27], [1, 28], [1, 29], [1, 31], [1, 33], [1, 35], [1, 37], [1, 45], [1, 47], [1, 51], [1, 54], [1, 56], [1, 59], [1, 71], [1, 74], [1, 77], [1, 79], [1, 85], [1, 104], [1, 106], [1, 109], [1, 151], [1, 152], [1, 158], [1, 165], [1, 169], [1, 174], [1, 176], [1, 178], [1, 179], [1, 184], [1, 186], [1, 188], [1, 189], [1, 4, 23], [1, 6, 34], [1, 7, 39], [1, 10, 46], [1, 12, 48], [1, 18, 58], [1, 70, 122], [1, 78, 124], [1, 84, 137], [1, 86, 140], [1, 88, 125], [1, 94, 108], [1, 98, 121], [1, 101, 192], [1, 102, 135], [1, 116, 145], [1, 2, 9, 44], [1, 3, 16, 55], [1, 5, 27, 62], [1, 43, 146, 165], [1, 61, 74, 156], [1, 64, 128, 165], [1, 66, 104, 154], [1, 74, 75, 123], [1, 87, 104, 138], [1, 91, 109, 166], [1, 92, 112, 165], [1, 95, 109, 111], [1, 9, 110, 173], [1, 16, 113, 144], [1, 27, 127, 148], [1, 142, 165, 172], [1, 157, 165, 181], [1, 160, 165, 180], [1, 15, 53, 101, 165, 192], [1, 30, 88, 105, 125, 165], [1, 12, 38, 48, 165, 183], [1, 41, 81, 102, 135, 165], [1, 49, 98, 121, 130, 165], [1, 4, 23, 60, 165, 187], [1, 65, 84, 119, 137, 165], [1, 68, 94, 108, 155, 165], [1, 72, 76, 116, 145, 165], [1, 10, 46, 80, 97, 165], [1, 7, 39, 89, 165, 167], [1, 70, 96, 114, 122, 165], [1, 86, 99, 140, 165, 168], [1, 18, 58, 117, 132, 165], [1, 78, 124, 134, 164, 165], [1, 6, 34, 149, 153, 165], [1, 8, 43, 82, 136, 146, 165, 191], [1, 14, 64, 120, 128, 162, 165, 185], [1, 19, 26, 133, 157, 163, 165, 181], [1, 24, 57, 92, 112, 165, 177, 190], [1, 32, 36, 69, 160, 165, 180, 182], [1, 40, 90, 115, 139, 157, 165, 181], [1, 42, 63, 129, 147, 160, 165, 180], [1, 50, 52, 100, 126, 142, 165, 172], [1, 67, 93, 103, 131, 142, 165, 172], [1, 43, 73, 118, 146, 161, 165, 170], [1, 83, 92, 112, 141, 165, 171, 175], [1, 64, 107, 128, 143, 150, 159, 165], [1, 9, 16, 74], [1, 9, 17, 51], [1, 9, 20, 85], [1, 9, 21, 54], [1, 9, 27, 104], [1, 9, 28, 59], [1, 9, 109, 165], [1, 9, 178, 186], [1, 9, 179, 188], [1, 11, 27, 106], [1, 11, 74, 152], [1, 11, 165, 174], [1, 13, 16, 77], [1, 13, 104, 158], [1, 13, 165, 184], [1, 16, 22, 45], [1, 16, 25, 35], [1, 16, 27, 109], [1, 16, 29, 79], [1, 16, 104, 165], [1, 16, 151, 189], [1, 16, 158, 184], [1, 17, 28, 109], [1, 17, 59, 165], [1, 20, 109, 178], [1, 20, 165, 186], [1, 21, 35, 176], [1, 21, 56, 151], [1, 21, 109, 179], [1, 21, 165, 188], [1, 22, 29, 165], [1, 22, 79, 104], [1, 25, 33, 179], [1, 25, 54, 169], [1, 25, 104, 151], [1, 25, 165, 189], [1, 27, 31, 47], [1, 27, 33, 56], [1, 27, 37, 71], [1, 27, 74, 165], [1, 27, 152, 174], [1, 27, 169, 176], [1, 28, 51, 165], [1, 29, 45, 104], [1, 31, 37, 74], [1, 31, 71, 165], [1, 33, 54, 189], [1, 33, 74, 176], [1, 33, 165, 169], [1, 35, 56, 188], [1, 35, 104, 189], [1, 35, 151, 165], [1, 37, 47, 165], [1, 45, 79, 165], [1, 47, 71, 74], [1, 51, 59, 109], [1, 54, 109, 188], [1, 54, 165, 179], [1, 56, 74, 169], [1, 56, 165, 176], [1, 74, 104, 109], [1, 74, 106, 174], [1, 77, 104, 184], [1, 77, 158, 165], [1, 85, 109, 186], [1, 85, 165, 178], [1, 106, 152, 165], [1, 151, 176, 188], [1, 169, 179, 189], [1, 4, 23, 28, 79, 106], [1, 4, 23, 45, 51, 152], [1, 6, 17, 34, 37, 77], [1, 6, 34, 47, 59, 158], [1, 7, 31, 39, 45, 85], [1, 7, 39, 71, 79, 178], [1, 10, 17, 22, 46, 152], [1, 10, 29, 46, 59, 106], [1, 11, 13, 94, 108, 178], [1, 11, 17, 45, 88, 125], [1, 11, 20, 86, 140, 158], [1, 11, 22, 51, 70, 122], [1, 12, 28, 31, 48, 158], [1, 12, 48, 51, 71, 77], [1, 13, 20, 78, 124, 152], [1, 13, 28, 47, 116, 145], [1, 13, 31, 59, 84, 137], [1, 17, 71, 84, 137, 184], [1, 18, 22, 47, 58, 85], [1, 18, 29, 37, 58, 178], [1, 20, 29, 71, 98, 121], [1, 20, 37, 79, 102, 135], [1, 22, 31, 98, 121, 186], [1, 28, 29, 70, 122, 174], [1, 37, 51, 116, 145, 184], [1, 45, 47, 102, 135, 186], [1, 59, 79, 88, 125, 174], [1, 77, 85, 101, 106, 192], [1, 77, 86, 140, 174, 186], [1, 78, 106, 124, 184, 186], [1, 85, 94, 108, 174, 184], [1, 101, 152, 158, 178, 192], [1, 2, 9, 16, 17, 44, 51, 74], [1, 2, 9, 20, 21, 44, 54, 85], [1, 2, 9, 27, 28, 44, 59, 104], [1, 2, 9, 44, 109, 110, 165, 173], [1, 2, 9, 44, 178, 179, 186, 188], [1, 3, 9, 16, 22, 45, 55, 74], [1, 3, 13, 16, 25, 35, 55, 77], [1, 3, 16, 27, 29, 55, 79, 109], [1, 3, 16, 55, 104, 113, 144, 165], [1, 3, 16, 55, 151, 158, 184, 189], [1, 5, 9, 27, 31, 47, 62, 104], [1, 5, 11, 27, 33, 56, 62, 106], [1, 5, 16, 27, 37, 62, 71, 109], [1, 5, 27, 62, 74, 127, 148, 165], [1, 5, 27, 62, 152, 169, 174, 176], [1, 9, 11, 16, 74, 75, 123, 152], [1, 9, 13, 27, 87, 104, 138, 158], [1, 9, 25, 109, 142, 165, 172, 189], [1, 9, 16, 29, 74, 79, 113, 144], [1, 9, 33, 64, 109, 128, 165, 169], [1, 9, 35, 43, 109, 146, 151, 165], [1, 9, 27, 37, 71, 104, 127, 148], [1, 9, 56, 109, 157, 165, 176, 181], [1, 9, 16, 61, 74, 106, 156, 174], [1, 9, 27, 66, 77, 104, 154, 184], [1, 9, 91, 95, 109, 111, 165, 166], [1, 9, 92, 109, 112, 160, 165, 180], [1, 11, 31, 71, 157, 165, 174, 181], [1, 11, 37, 47, 64, 128, 165, 174], [1, 11, 61, 74, 104, 109, 152, 156], [1, 11, 27, 106, 127, 148, 169, 176], [1, 13, 22, 29, 43, 146, 165, 184], [1, 13, 45, 79, 142, 165, 172, 184], [1, 13, 66, 74, 104, 109, 154, 158], [1, 13, 16, 77, 113, 144, 151, 189], [1, 16, 20, 27, 95, 109, 111, 178], [1, 16, 21, 104, 160, 165, 180, 188], [1, 9, 16, 28, 59, 74, 110, 173], [1, 16, 27, 31, 47, 109, 127, 148], [1, 16, 33, 104, 157, 165, 169, 181], [1, 16, 43, 104, 142, 146, 165, 172], [1, 16, 54, 92, 104, 112, 165, 179], [1, 16, 56, 64, 104, 128, 165, 176], [1, 16, 66, 87, 104, 138, 154, 165], [1, 16, 27, 85, 91, 109, 166, 186], [1, 17, 20, 59, 92, 112, 165, 186], [1, 17, 21, 28, 95, 109, 111, 179], [1, 9, 17, 27, 51, 104, 110, 173], [1, 17, 28, 54, 91, 109, 166, 188], [1, 17, 59, 85, 160, 165, 178, 180], [1, 20, 28, 51, 160, 165, 180, 186], [1, 20, 74, 91, 104, 109, 166, 178], [1, 9, 20, 85, 110, 173, 179, 188], [1, 21, 25, 64, 128, 165, 188, 189], [1, 21, 27, 74, 92, 112, 165, 188], [1, 21, 33, 142, 165, 169, 172, 188], [1, 21, 43, 146, 157, 165, 181, 188], [1, 21, 51, 59, 91, 109, 166, 179], [1, 9, 21, 54, 110, 173, 178, 186], [1, 22, 25, 79, 87, 104, 138, 151], [1, 16, 22, 27, 45, 109, 113, 144], [1, 22, 35, 66, 79, 104, 154, 189], [1, 22, 29, 77, 142, 158, 165, 172], [1, 25, 27, 43, 74, 146, 165, 189], [1, 25, 29, 45, 66, 104, 151, 154], [1, 25, 56, 160, 165, 176, 180, 189], [1, 25, 92, 112, 157, 165, 181, 189], [1, 16, 25, 35, 113, 144, 158, 184], [1, 27, 35, 74, 142, 151, 165, 172], [1, 27, 54, 74, 160, 165, 179, 180], [1, 27, 61, 74, 75, 123, 156, 165], [1, 27, 64, 74, 128, 157, 165, 181], [1, 28, 51, 85, 92, 112, 165, 178], [1, 29, 35, 45, 87, 104, 138, 189], [1, 31, 33, 37, 74, 75, 123, 176], [1, 31, 37, 56, 61, 74, 156, 169], [1, 31, 64, 71, 106, 128, 152, 165], [1, 33, 35, 92, 112, 151, 165, 169], [1, 33, 43, 146, 160, 165, 169, 180], [1, 33, 47, 61, 71, 74, 156, 176], [1, 27, 33, 56, 127, 148, 152, 174], [1, 35, 54, 151, 157, 165, 179, 181], [1, 35, 64, 128, 151, 160, 165, 180], [1, 37, 47, 106, 152, 157, 165, 181], [1, 43, 45, 77, 79, 146, 158, 165], [1, 43, 54, 56, 146, 165, 176, 179], [1, 43, 64, 92, 112, 128, 146, 165], [1, 47, 56, 71, 74, 75, 123, 169], [1, 51, 54, 59, 95, 109, 111, 188], [1, 54, 64, 128, 142, 165, 172, 179], [1, 56, 92, 112, 142, 165, 172, 176], [1, 74, 77, 87, 104, 109, 138, 184], [1, 74, 85, 95, 104, 109, 111, 186], [1, 74, 75, 104, 106, 109, 123, 174], [1, 142, 157, 160, 165, 172, 180, 181], [1, 4, 6, 23, 25, 33, 34, 94, 98, 108, 121, 179], [1, 4, 9, 10, 16, 23, 46, 70, 74, 88, 122, 125], [1, 4, 18, 21, 23, 56, 58, 84, 86, 137, 140, 151], [1, 6, 7, 21, 34, 35, 39, 70, 78, 122, 124, 176], [1, 6, 9, 12, 27, 34, 48, 84, 104, 116, 137, 145], [1, 7, 10, 33, 39, 46, 54, 86, 116, 140, 145, 189], [1, 7, 16, 18, 27, 39, 58, 98, 102, 109, 121, 135], [1, 10, 12, 35, 46, 48, 56, 94, 102, 108, 135, 188], [1, 11, 22, 28, 29, 51, 70, 96, 114, 122, 165, 174], [1, 11, 17, 30, 45, 59, 79, 88, 105, 125, 165, 174], [1, 11, 13, 68, 85, 94, 108, 155, 165, 174, 178, 184], [1, 11, 20, 77, 86, 99, 140, 158, 165, 168, 174, 186], [1, 12, 18, 25, 48, 54, 58, 78, 88, 124, 125, 169], [1, 13, 17, 31, 59, 65, 71, 84, 119, 137, 165, 184], [1, 13, 28, 37, 47, 51, 72, 76, 116, 145, 165, 184], [1, 13, 20, 78, 106, 124, 134, 152, 164, 165, 184, 186], [1, 15, 53, 77, 85, 101, 106, 152, 158, 165, 178, 192], [1, 10, 17, 22, 29, 46, 59, 80, 97, 106, 152, 165], [1, 6, 17, 34, 37, 47, 59, 77, 149, 153, 158, 165], [1, 20, 22, 29, 31, 49, 71, 98, 121, 130, 165, 186], [1, 20, 37, 41, 45, 47, 79, 81, 102, 135, 165, 186], [1, 18, 22, 29, 37, 47, 58, 85, 117, 132, 165, 178], [1, 12, 28, 31, 38, 48, 51, 71, 77, 158, 165, 183], [1, 4, 23, 28, 45, 51, 60, 79, 106, 152, 165, 187], [1, 7, 31, 39, 45, 71, 79, 85, 89, 165, 167, 178], [1, 70, 84, 101, 102, 122, 135, 137, 169, 179, 189, 192], [1, 74, 78, 86, 94, 101, 104, 108, 109, 124, 140, 192], [1, 88, 98, 101, 116, 121, 125, 145, 151, 176, 188, 192], [1, 2, 9, 44, 91, 92, 95, 109, 110, 111, 112, 160, 165, 166, 173, 180], [1, 3, 16, 43, 55, 66, 87, 104, 113, 138, 142, 144, 146, 154, 165, 172], [1, 5, 27, 61, 62, 64, 74, 75, 123, 127, 128, 148, 156, 157, 165, 181], [1, 8, 13, 22, 29, 43, 64, 82, 92, 112, 128, 136, 146, 165, 184, 191], [1, 8, 16, 43, 73, 82, 104, 118, 136, 142, 146, 161, 165, 170, 172, 191], [1, 8, 43, 45, 54, 56, 77, 79, 82, 136, 146, 158, 165, 176, 179, 191], [1, 9, 24, 57, 83, 92, 109, 112, 141, 160, 165, 171, 175, 177, 180, 190], [1, 9, 32, 36, 42, 63, 69, 92, 109, 112, 129, 147, 160, 165, 180, 182], [1, 11, 14, 37, 43, 47, 64, 92, 112, 120, 128, 146, 162, 165, 174, 185], [1, 11, 19, 26, 31, 71, 133, 142, 157, 160, 163, 165, 172, 174, 180, 181], [1, 11, 21, 25, 37, 47, 64, 107, 128, 143, 150, 159, 165, 174, 188, 189], [1, 11, 31, 35, 40, 54, 71, 90, 115, 139, 151, 157, 165, 174, 179, 181], [1, 13, 21, 33, 45, 67, 79, 93, 103, 131, 142, 165, 169, 172, 184, 188], [1, 13, 45, 50, 52, 79, 100, 126, 142, 157, 160, 165, 172, 180, 181, 184], [1, 13, 22, 29, 43, 54, 56, 73, 118, 146, 161, 165, 170, 176, 179, 184], [1, 14, 21, 25, 31, 64, 71, 106, 120, 128, 152, 162, 165, 185, 188, 189], [1, 14, 27, 64, 74, 107, 120, 128, 143, 150, 157, 159, 162, 165, 181, 185], [1, 16, 43, 50, 52, 67, 93, 100, 103, 104, 126, 131, 142, 146, 165, 172], [1, 17, 20, 24, 43, 57, 59, 64, 92, 112, 128, 146, 165, 177, 186, 190], [1, 17, 25, 32, 36, 56, 59, 69, 85, 160, 165, 176, 178, 180, 182, 189], [1, 17, 20, 33, 35, 59, 83, 92, 112, 141, 151, 165, 169, 171, 175, 186], [1, 17, 42, 59, 63, 85, 129, 142, 147, 157, 160, 165, 172, 178, 180, 181], [1, 19, 26, 27, 40, 64, 74, 90, 115, 128, 133, 139, 157, 163, 165, 181], [1, 19, 26, 35, 37, 47, 54, 106, 133, 151, 152, 157, 163, 165, 179, 181], [1, 20, 25, 28, 42, 51, 56, 63, 129, 147, 160, 165, 176, 180, 186, 189], [1, 20, 28, 32, 36, 51, 69, 142, 157, 160, 165, 172, 180, 181, 182, 186], [1, 21, 22, 29, 33, 50, 52, 77, 100, 126, 142, 158, 165, 169, 172, 188], [1, 22, 29, 67, 77, 93, 103, 131, 142, 157, 158, 160, 165, 172, 180, 181], [1, 24, 28, 33, 35, 51, 57, 85, 92, 112, 151, 165, 169, 177, 178, 190], [1, 28, 43, 51, 64, 83, 85, 92, 112, 128, 141, 146, 165, 171, 175, 178], [1, 31, 43, 64, 71, 92, 106, 107, 112, 128, 143, 146, 150, 152, 159, 165], [1, 37, 40, 47, 90, 106, 115, 139, 142, 152, 157, 160, 165, 172, 180, 181], [1, 43, 45, 64, 73, 77, 79, 92, 112, 118, 128, 146, 158, 161, 165, 170], [1, 2, 3, 4, 9, 10, 11, 16, 17, 22, 23, 44, 45, 46, 51, 55, 70, 74, 75, 88, 122, 123, 125, 152], [1, 2, 5, 6, 9, 12, 13, 27, 28, 31, 34, 44, 47, 48, 59, 62, 84, 87, 104, 116, 137, 138, 145, 158], [1, 3, 5, 7, 16, 18, 20, 27, 29, 37, 39, 55, 58, 62, 71, 79, 95, 98, 102, 109, 111, 121, 135, 178], [1, 4, 7, 12, 15, 23, 38, 39, 43, 48, 53, 60, 64, 89, 92, 101, 112, 128, 146, 165, 167, 183, 187, 192], [1, 4, 9, 10, 16, 23, 27, 30, 46, 60, 70, 74, 80, 88, 96, 97, 104, 105, 109, 114, 122, 125, 165, 187], [1, 4, 9, 10, 16, 23, 28, 29, 46, 59, 61, 70, 74, 79, 88, 106, 110, 113, 122, 125, 144, 156, 173, 174], [1, 4, 18, 21, 23, 35, 56, 58, 60, 65, 84, 86, 99, 117, 119, 132, 137, 140, 151, 165, 168, 176, 187, 188], [1, 4, 23, 41, 60, 72, 76, 78, 81, 102, 116, 124, 134, 135, 142, 145, 157, 160, 164, 165, 172, 180, 181, 187], [1, 4, 6, 23, 25, 33, 34, 49, 54, 60, 68, 94, 98, 108, 121, 130, 149, 153, 155, 165, 169, 179, 187, 189], [1, 6, 10, 15, 18, 34, 46, 53, 58, 80, 97, 101, 117, 132, 142, 149, 153, 157, 160, 165, 172, 180, 181, 192], [1, 6, 9, 12, 16, 27, 34, 38, 48, 65, 72, 74, 76, 84, 104, 109, 116, 119, 137, 145, 149, 153, 165, 183], [1, 6, 30, 34, 41, 43, 64, 81, 86, 88, 92, 99, 102, 105, 112, 125, 128, 135, 140, 146, 149, 153, 165, 168], [1, 6, 9, 12, 17, 27, 34, 37, 48, 51, 66, 71, 77, 84, 104, 110, 116, 127, 137, 145, 148, 154, 173, 184], [1, 6, 7, 21, 34, 35, 39, 56, 70, 78, 89, 96, 114, 122, 124, 134, 149, 151, 153, 164, 165, 167, 176, 188], [1, 7, 9, 16, 18, 27, 39, 41, 49, 58, 74, 81, 89, 98, 102, 104, 109, 117, 121, 130, 132, 135, 165, 167], [1, 7, 16, 18, 22, 27, 31, 39, 45, 47, 58, 85, 91, 98, 102, 109, 113, 121, 127, 135, 144, 148, 166, 186], [1, 7, 10, 25, 33, 39, 46, 54, 72, 76, 80, 86, 89, 97, 99, 116, 140, 145, 165, 167, 168, 169, 179, 189], [1, 7, 30, 39, 65, 68, 84, 88, 89, 94, 105, 108, 119, 125, 137, 142, 155, 157, 160, 165, 167, 172, 180, 181], [1, 9, 15, 16, 27, 53, 68, 74, 78, 86, 94, 99, 101, 104, 108, 109, 124, 134, 140, 155, 164, 165, 168, 192], [1, 10, 12, 21, 35, 38, 41, 46, 48, 56, 68, 80, 81, 94, 97, 102, 108, 135, 151, 155, 165, 176, 183, 188], [1, 10, 43, 46, 49, 64, 65, 78, 80, 84, 92, 97, 98, 112, 119, 121, 124, 128, 130, 134, 137, 146, 164, 165], [1, 11, 13, 20, 61, 66, 74, 78, 86, 91, 94, 101, 104, 108, 109, 124, 140, 152, 154, 156, 158, 166, 178, 192], [1, 12, 18, 25, 30, 33, 38, 48, 54, 58, 78, 88, 105, 117, 124, 125, 132, 134, 164, 165, 169, 179, 183, 189], [1, 12, 38, 48, 49, 70, 86, 96, 98, 99, 114, 121, 122, 130, 140, 142, 157, 160, 165, 168, 172, 180, 181, 183], [1, 15, 21, 30, 35, 49, 53, 56, 72, 76, 88, 98, 101, 105, 116, 121, 125, 130, 145, 151, 165, 176, 188, 192], [1, 15, 25, 33, 41, 53, 54, 65, 70, 81, 84, 96, 101, 102, 114, 119, 122, 135, 137, 165, 169, 179, 189, 192], [1, 18, 43, 58, 64, 68, 70, 72, 76, 92, 94, 96, 108, 112, 114, 116, 117, 122, 128, 132, 145, 146, 155, 165], [1, 74, 75, 77, 78, 85, 86, 87, 94, 95, 101, 104, 106, 108, 109, 111, 123, 124, 138, 140, 174, 184, 186, 192], [1, 2, 9, 24, 25, 44, 56, 57, 83, 91, 92, 95, 109, 110, 111, 112, 141, 142, 157, 160, 165, 166, 171, 172, 173, 175, 176, 177, 180, 181, 189, 190], [1, 2, 9, 32, 33, 35, 36, 42, 43, 44, 63, 64, 69, 91, 92, 95, 109, 110, 111, 112, 128, 129, 146, 147, 151, 160, 165, 166, 169, 173, 180, 182], [1, 3, 8, 16, 21, 33, 43, 55, 66, 73, 82, 87, 104, 113, 118, 136, 138, 142, 144, 146, 154, 157, 160, 161, 165, 169, 170, 172, 180, 181, 188, 191], [1, 3, 16, 43, 50, 52, 54, 55, 56, 64, 66, 67, 87, 92, 93, 100, 103, 104, 112, 113, 126, 128, 131, 138, 142, 144, 146, 154, 165, 172, 176, 179], [1, 5, 14, 27, 35, 54, 61, 62, 64, 74, 75, 107, 120, 123, 127, 128, 142, 143, 148, 150, 151, 156, 157, 159, 160, 162, 165, 172, 179, 180, 181, 185], [1, 5, 19, 21, 25, 26, 27, 40, 43, 61, 62, 64, 74, 75, 90, 92, 112, 115, 123, 127, 128, 133, 139, 146, 148, 156, 157, 163, 165, 181, 188, 189], [1, 8, 9, 16, 25, 27, 35, 43, 50, 52, 67, 73, 74, 82, 93, 100, 103, 104, 109, 118, 126, 131, 136, 142, 146, 151, 161, 165, 170, 172, 189, 191], [1, 9, 14, 16, 19, 26, 27, 33, 40, 56, 64, 74, 90, 104, 107, 109, 115, 120, 128, 133, 139, 143, 150, 157, 159, 162, 163, 165, 169, 176, 181, 185], [1, 9, 16, 21, 24, 27, 32, 36, 42, 54, 57, 63, 69, 74, 83, 92, 104, 109, 112, 129, 141, 147, 160, 165, 171, 175, 177, 179, 180, 182, 188, 190], [1, 2, 3, 4, 9, 10, 11, 16, 17, 22, 23, 27, 28, 29, 30, 44, 45, 46, 51, 55, 59, 60, 61, 70, 74, 75, 79, 80, 88, 96, 97, 104, 105, 106, 109, 110, 113, 114, 122, 123, 125, 144, 152, 156, 165, 173, 174, 187], [1, 2, 5, 6, 9, 12, 13, 16, 17, 27, 28, 31, 34, 37, 38, 44, 47, 48, 51, 59, 62, 65, 66, 71, 72, 74, 76, 77, 84, 87, 104, 109, 110, 116, 119, 127, 137, 138, 145, 148, 149, 153, 154, 158, 165, 173, 183, 184], [1, 3, 5, 7, 9, 16, 18, 20, 22, 27, 29, 31, 37, 39, 41, 45, 47, 49, 55, 58, 62, 71, 74, 79, 81, 85, 89, 91, 95, 98, 102, 104, 109, 111, 113, 117, 121, 127, 130, 132, 135, 144, 148, 165, 166, 167, 178, 186], [1, 4, 13, 20, 23, 28, 32, 36, 37, 40, 41, 45, 47, 50, 51, 52, 60, 69, 72, 76, 78, 79, 81, 90, 100, 102, 106, 115, 116, 124, 126, 134, 135, 139, 142, 145, 152, 157, 160, 164, 165, 172, 180, 181, 182, 184, 186, 187], [1, 4, 7, 12, 15, 23, 28, 31, 38, 39, 43, 45, 48, 51, 53, 60, 64, 71, 73, 77, 79, 83, 85, 89, 92, 101, 106, 107, 112, 118, 128, 141, 143, 146, 150, 152, 158, 159, 161, 165, 167, 170, 171, 175, 178, 183, 187, 192], [1, 6, 11, 14, 17, 20, 24, 30, 34, 37, 41, 43, 45, 47, 57, 59, 64, 73, 77, 79, 81, 86, 88, 92, 99, 102, 105, 112, 118, 120, 125, 128, 135, 140, 146, 149, 153, 158, 161, 162, 165, 168, 170, 174, 177, 185, 186, 190], [1, 6, 10, 15, 17, 18, 22, 29, 34, 37, 40, 42, 46, 47, 53, 58, 59, 63, 67, 77, 80, 85, 90, 93, 97, 101, 103, 106, 115, 117, 129, 131, 132, 139, 142, 147, 149, 152, 153, 157, 158, 160, 165, 172, 178, 180, 181, 192], [1, 7, 11, 13, 17, 19, 26, 30, 31, 39, 42, 45, 50, 52, 59, 63, 65, 68, 71, 79, 84, 85, 88, 89, 94, 100, 105, 108, 119, 125, 126, 129, 133, 137, 142, 147, 155, 157, 160, 163, 165, 167, 172, 174, 178, 180, 181, 184], [1, 8, 10, 13, 17, 20, 22, 24, 29, 31, 43, 46, 49, 57, 59, 64, 65, 71, 78, 80, 82, 84, 92, 97, 98, 106, 107, 112, 119, 121, 124, 128, 130, 134, 136, 137, 143, 146, 150, 152, 159, 164, 165, 177, 184, 186, 190, 191], [1, 8, 11, 13, 14, 18, 22, 28, 29, 37, 43, 47, 51, 58, 64, 68, 70, 72, 76, 82, 83, 85, 92, 94, 96, 108, 112, 114, 116, 117, 120, 122, 128, 132, 136, 141, 145, 146, 155, 162, 165, 171, 174, 175, 178, 184, 185, 191], [1, 11, 12, 19, 20, 22, 26, 28, 29, 31, 32, 36, 38, 48, 49, 51, 67, 69, 70, 71, 77, 86, 93, 96, 98, 99, 103, 114, 121, 122, 130, 131, 133, 140, 142, 157, 158, 160, 163, 165, 168, 172, 174, 180, 181, 182, 183, 186], [1, 9, 11, 13, 15, 16, 20, 27, 53, 61, 66, 68, 74, 75, 77, 78, 85, 86, 87, 91, 94, 95, 99, 101, 104, 106, 108, 109, 111, 123, 124, 134, 138, 140, 152, 154, 155, 156, 158, 164, 165, 166, 168, 174, 178, 184, 186, 192], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192], [1, 9, 16, 27, 74, 104, 109, 165], [1, 9, 17, 28, 51, 59, 109, 165], [1, 9, 20, 85, 109, 165, 178, 186], [1, 9, 21, 54, 109, 165, 179, 188], [1, 11, 27, 74, 106, 152, 165, 174], [1, 13, 16, 77, 104, 158, 165, 184], [1, 16, 22, 29, 45, 79, 104, 165], [1, 16, 25, 35, 104, 151, 165, 189], [1, 21, 35, 56, 151, 165, 176, 188], [1, 25, 33, 54, 165, 169, 179, 189], [1, 27, 31, 37, 47, 71, 74, 165], [1, 27, 33, 56, 74, 165, 169, 176], [1, 2, 9, 16, 17, 27, 28, 44, 51, 59, 74, 104, 109, 110, 165, 173], [1, 2, 9, 20, 21, 44, 54, 85, 109, 110, 165, 173, 178, 179, 186, 188], [1, 3, 9, 16, 22, 27, 29, 45, 55, 74, 79, 104, 109, 113, 144, 165], [1, 3, 13, 16, 25, 35, 55, 77, 104, 113, 144, 151, 158, 165, 184, 189], [1, 5, 9, 16, 27, 31, 37, 47, 62, 71, 74, 104, 109, 127, 148, 165], [1, 5, 11, 27, 33, 56, 62, 74, 106, 127, 148, 152, 165, 169, 174, 176], [1, 9, 11, 16, 27, 61, 74, 75, 104, 106, 109, 123, 152, 156, 165, 174], [1, 9, 13, 16, 27, 66, 74, 77, 87, 104, 109, 138, 154, 158, 165, 184], [1, 9, 16, 20, 27, 74, 85, 91, 95, 104, 109, 111, 165, 166, 178, 186], [1, 9, 16, 21, 27, 54, 74, 92, 104, 109, 112, 160, 165, 179, 180, 188], [1, 9, 16, 25, 27, 35, 43, 74, 104, 109, 142, 146, 151, 165, 172, 189], [1, 9, 16, 27, 33, 56, 64, 74, 104, 109, 128, 157, 165, 169, 176, 181], [1, 9, 17, 20, 28, 51, 59, 85, 92, 109, 112, 160, 165, 178, 180, 186], [1, 9, 17, 21, 28, 51, 54, 59, 91, 95, 109, 111, 165, 166, 179, 188], [1, 9, 21, 25, 33, 54, 64, 109, 128, 142, 165, 169, 172, 179, 188, 189], [1, 9, 21, 35, 43, 54, 56, 109, 146, 151, 157, 165, 176, 179, 181, 188], [1, 9, 25, 56, 92, 109, 112, 142, 157, 160, 165, 172, 176, 180, 181, 189], [1, 9, 33, 35, 43, 64, 92, 109, 112, 128, 146, 151, 160, 165, 169, 180], [1, 11, 27, 31, 37, 47, 64, 71, 74, 106, 128, 152, 157, 165, 174, 181], [1, 13, 16, 22, 29, 43, 45, 77, 79, 104, 142, 146, 158, 165, 172, 184], [1, 16, 21, 25, 35, 56, 64, 104, 128, 151, 160, 165, 176, 180, 188, 189], [1, 16, 21, 33, 43, 104, 142, 146, 157, 160, 165, 169, 172, 180, 181, 188], [1, 16, 22, 25, 29, 35, 45, 66, 79, 87, 104, 138, 151, 154, 165, 189], [1, 16, 25, 33, 35, 54, 92, 104, 112, 151, 157, 165, 169, 179, 181, 189], [1, 16, 43, 54, 56, 64, 92, 104, 112, 128, 142, 146, 165, 172, 176, 179], [1, 21, 25, 27, 43, 64, 74, 92, 112, 128, 146, 157, 165, 181, 188, 189], [1, 21, 27, 33, 35, 56, 74, 92, 112, 142, 151, 165, 169, 172, 176, 188], [1, 25, 27, 33, 43, 54, 56, 74, 146, 160, 165, 169, 176, 179, 180, 189], [1, 27, 31, 33, 37, 47, 56, 61, 71, 74, 75, 123, 156, 165, 169, 176], [1, 27, 35, 54, 64, 74, 128, 142, 151, 157, 160, 165, 172, 179, 180, 181], [1, 2, 9, 16, 17, 20, 21, 27, 28, 44, 51, 54, 59, 74, 85, 91, 92, 95, 104, 109, 110, 111, 112, 160, 165, 166, 173, 178, 179, 180, 186, 188], [1, 3, 9, 13, 16, 22, 25, 27, 29, 35, 43, 45, 55, 66, 74, 77, 79, 87, 104, 109, 113, 138, 142, 144, 146, 151, 154, 158, 165, 172, 184, 189], [1, 5, 9, 11, 16, 27, 31, 33, 37, 47, 56, 61, 62, 64, 71, 74, 75, 104, 106, 109, 123, 127, 128, 148, 152, 156, 157, 165, 169, 174, 176, 181], [1, 8, 13, 16, 22, 29, 43, 45, 54, 56, 64, 73, 77, 79, 82, 92, 104, 112, 118, 128, 136, 142, 146, 158, 161, 165, 170, 172, 176, 179, 184, 191], [1, 9, 17, 20, 24, 28, 33, 35, 43, 51, 57, 59, 64, 83, 85, 92, 109, 112, 128, 141, 146, 151, 160, 165, 169, 171, 175, 177, 178, 180, 186, 190], [1, 9, 17, 20, 25, 28, 32, 36, 42, 51, 56, 59, 63, 69, 85, 92, 109, 112, 129, 142, 147, 157, 160, 165, 172, 176, 178, 180, 181, 182, 186, 189], [1, 11, 14, 21, 25, 27, 31, 37, 43, 47, 64, 71, 74, 92, 106, 107, 112, 120, 128, 143, 146, 150, 152, 157, 159, 162, 165, 174, 181, 185, 188, 189], [1, 11, 19, 26, 27, 31, 35, 37, 40, 47, 54, 64, 71, 74, 90, 106, 115, 128, 133, 139, 142, 151, 152, 157, 160, 163, 165, 172, 174, 179, 180, 181], [1, 13, 16, 21, 22, 29, 33, 43, 45, 50, 52, 67, 77, 79, 93, 100, 103, 104, 126, 131, 142, 146, 157, 158, 160, 165, 169, 172, 180, 181, 184, 188], [1, 2, 9, 16, 17, 20, 21, 24, 25, 27, 28, 32, 33, 35, 36, 42, 43, 44, 51, 54, 56, 57, 59, 63, 64, 69, 74, 83, 85, 91, 92, 95, 104, 109, 110, 111, 112, 128, 129, 141, 142, 146, 147, 151, 157, 160, 165, 166, 169, 171, 172, 173, 175, 176, 177, 178, 179, 180, 181, 182, 186, 188, 189, 190], [1, 3, 8, 9, 13, 16, 21, 22, 25, 27, 29, 33, 35, 43, 45, 50, 52, 54, 55, 56, 64, 66, 67, 73, 74, 77, 79, 82, 87, 92, 93, 100, 103, 104, 109, 112, 113, 118, 126, 128, 131, 136, 138, 142, 144, 146, 151, 154, 157, 158, 160, 161, 165, 169, 170, 172, 176, 179, 180, 181, 184, 188, 189, 191], [1, 5, 9, 11, 14, 16, 19, 21, 25, 26, 27, 31, 33, 35, 37, 40, 43, 47, 54, 56, 61, 62, 64, 71, 74, 75, 90, 92, 104, 106, 107, 109, 112, 115, 120, 123, 127, 128, 133, 139, 142, 143, 146, 148, 150, 151, 152, 156, 157, 159, 160, 162, 163, 165, 169, 172, 174, 176, 179, 180, 181, 185, 188, 189], [1, 4, 6, 7, 9, 10, 12, 15, 16, 18, 21, 23, 25, 27, 30, 33, 34, 35, 38, 39, 41, 43, 46, 48, 49, 53, 54, 56, 58, 60, 64, 65, 68, 70, 72, 74, 76, 78, 80, 81, 84, 86, 88, 89, 92, 94, 96, 97, 98, 99, 101, 102, 104, 105, 108, 109, 112, 114, 116, 117, 119, 121, 122, 124, 125, 128, 130, 132, 134, 135, 137, 140, 142, 145, 146, 149, 151, 153, 155, 157, 160, 164, 165, 167, 168, 169, 172, 176, 179, 180, 181, 183, 187, 188, 189, 192], [1, 9, 16, 21, 25, 27, 33, 35, 43, 54, 56, 64, 74, 92, 104, 109, 112, 128, 142, 146, 151, 157, 160, 165, 169, 172, 176, 179, 180, 181, 188, 189]],
(generators = 3,) => [[1, 9, 16, 27, 74, 104, 109, 165], [1, 9, 17, 28, 51, 59, 109, 165], [1, 9, 20, 85, 109, 165, 178, 186], [1, 9, 21, 54, 109, 165, 179, 188], [1, 11, 27, 74, 106, 152, 165, 174], [1, 13, 16, 77, 104, 158, 165, 184], [1, 16, 22, 29, 45, 79, 104, 165], [1, 16, 25, 35, 104, 151, 165, 189], [1, 21, 35, 56, 151, 165, 176, 188], [1, 25, 33, 54, 165, 169, 179, 189], [1, 27, 31, 37, 47, 71, 74, 165], [1, 27, 33, 56, 74, 165, 169, 176], [1, 2, 9, 16, 17, 27, 28, 44, 51, 59, 74, 104, 109, 110, 165, 173], [1, 2, 9, 20, 21, 44, 54, 85, 109, 110, 165, 173, 178, 179, 186, 188], [1, 3, 9, 16, 22, 27, 29, 45, 55, 74, 79, 104, 109, 113, 144, 165], [1, 3, 13, 16, 25, 35, 55, 77, 104, 113, 144, 151, 158, 165, 184, 189], [1, 5, 9, 16, 27, 31, 37, 47, 62, 71, 74, 104, 109, 127, 148, 165], [1, 5, 11, 27, 33, 56, 62, 74, 106, 127, 148, 152, 165, 169, 174, 176], [1, 9, 11, 16, 27, 61, 74, 75, 104, 106, 109, 123, 152, 156, 165, 174], [1, 9, 13, 16, 27, 66, 74, 77, 87, 104, 109, 138, 154, 158, 165, 184], [1, 9, 16, 20, 27, 74, 85, 91, 95, 104, 109, 111, 165, 166, 178, 186], [1, 9, 16, 21, 27, 54, 74, 92, 104, 109, 112, 160, 165, 179, 180, 188], [1, 9, 16, 25, 27, 35, 43, 74, 104, 109, 142, 146, 151, 165, 172, 189], [1, 9, 16, 27, 33, 56, 64, 74, 104, 109, 128, 157, 165, 169, 176, 181], [1, 9, 17, 20, 28, 51, 59, 85, 92, 109, 112, 160, 165, 178, 180, 186], [1, 9, 17, 21, 28, 51, 54, 59, 91, 95, 109, 111, 165, 166, 179, 188], [1, 9, 21, 25, 33, 54, 64, 109, 128, 142, 165, 169, 172, 179, 188, 189], [1, 9, 21, 35, 43, 54, 56, 109, 146, 151, 157, 165, 176, 179, 181, 188], [1, 9, 25, 56, 92, 109, 112, 142, 157, 160, 165, 172, 176, 180, 181, 189], [1, 9, 33, 35, 43, 64, 92, 109, 112, 128, 146, 151, 160, 165, 169, 180], [1, 11, 27, 31, 37, 47, 64, 71, 74, 106, 128, 152, 157, 165, 174, 181], [1, 13, 16, 22, 29, 43, 45, 77, 79, 104, 142, 146, 158, 165, 172, 184], [1, 16, 21, 25, 35, 56, 64, 104, 128, 151, 160, 165, 176, 180, 188, 189], [1, 16, 21, 33, 43, 104, 142, 146, 157, 160, 165, 169, 172, 180, 181, 188], [1, 16, 22, 25, 29, 35, 45, 66, 79, 87, 104, 138, 151, 154, 165, 189], [1, 16, 25, 33, 35, 54, 92, 104, 112, 151, 157, 165, 169, 179, 181, 189], [1, 16, 43, 54, 56, 64, 92, 104, 112, 128, 142, 146, 165, 172, 176, 179], [1, 21, 25, 27, 43, 64, 74, 92, 112, 128, 146, 157, 165, 181, 188, 189], [1, 21, 27, 33, 35, 56, 74, 92, 112, 142, 151, 165, 169, 172, 176, 188], [1, 25, 27, 33, 43, 54, 56, 74, 146, 160, 165, 169, 176, 179, 180, 189], [1, 27, 31, 33, 37, 47, 56, 61, 71, 74, 75, 123, 156, 165, 169, 176], [1, 27, 35, 54, 64, 74, 128, 142, 151, 157, 160, 165, 172, 179, 180, 181], [1, 2, 9, 16, 17, 20, 21, 27, 28, 44, 51, 54, 59, 74, 85, 91, 92, 95, 104, 109, 110, 111, 112, 160, 165, 166, 173, 178, 179, 180, 186, 188], [1, 3, 9, 13, 16, 22, 25, 27, 29, 35, 43, 45, 55, 66, 74, 77, 79, 87, 104, 109, 113, 138, 142, 144, 146, 151, 154, 158, 165, 172, 184, 189], [1, 5, 9, 11, 16, 27, 31, 33, 37, 47, 56, 61, 62, 64, 71, 74, 75, 104, 106, 109, 123, 127, 128, 148, 152, 156, 157, 165, 169, 174, 176, 181], [1, 8, 13, 16, 22, 29, 43, 45, 54, 56, 64, 73, 77, 79, 82, 92, 104, 112, 118, 128, 136, 142, 146, 158, 161, 165, 170, 172, 176, 179, 184, 191], [1, 9, 17, 20, 24, 28, 33, 35, 43, 51, 57, 59, 64, 83, 85, 92, 109, 112, 128, 141, 146, 151, 160, 165, 169, 171, 175, 177, 178, 180, 186, 190], [1, 9, 17, 20, 25, 28, 32, 36, 42, 51, 56, 59, 63, 69, 85, 92, 109, 112, 129, 142, 147, 157, 160, 165, 172, 176, 178, 180, 181, 182, 186, 189], [1, 11, 14, 21, 25, 27, 31, 37, 43, 47, 64, 71, 74, 92, 106, 107, 112, 120, 128, 143, 146, 150, 152, 157, 159, 162, 165, 174, 181, 185, 188, 189], [1, 11, 19, 26, 27, 31, 35, 37, 40, 47, 54, 64, 71, 74, 90, 106, 115, 128, 133, 139, 142, 151, 152, 157, 160, 163, 165, 172, 174, 179, 180, 181], [1, 13, 16, 21, 22, 29, 33, 43, 45, 50, 52, 67, 77, 79, 93, 100, 103, 104, 126, 131, 142, 146, 157, 158, 160, 165, 169, 172, 180, 181, 184, 188], [1, 2, 9, 16, 17, 20, 21, 24, 25, 27, 28, 32, 33, 35, 36, 42, 43, 44, 51, 54, 56, 57, 59, 63, 64, 69, 74, 83, 85, 91, 92, 95, 104, 109, 110, 111, 112, 128, 129, 141, 142, 146, 147, 151, 157, 160, 165, 166, 169, 171, 172, 173, 175, 176, 177, 178, 179, 180, 181, 182, 186, 188, 189, 190], [1, 3, 8, 9, 13, 16, 21, 22, 25, 27, 29, 33, 35, 43, 45, 50, 52, 54, 55, 56, 64, 66, 67, 73, 74, 77, 79, 82, 87, 92, 93, 100, 103, 104, 109, 112, 113, 118, 126, 128, 131, 136, 138, 142, 144, 146, 151, 154, 157, 158, 160, 161, 165, 169, 170, 172, 176, 179, 180, 181, 184, 188, 189, 191], [1, 5, 9, 11, 14, 16, 19, 21, 25, 26, 27, 31, 33, 35, 37, 40, 43, 47, 54, 56, 61, 62, 64, 71, 74, 75, 90, 92, 104, 106, 107, 109, 112, 115, 120, 123, 127, 128, 133, 139, 142, 143, 146, 148, 150, 151, 152, 156, 157, 159, 160, 162, 163, 165, 169, 172, 174, 176, 179, 180, 181, 185, 188, 189], [1, 4, 6, 7, 9, 10, 12, 15, 16, 18, 21, 23, 25, 27, 30, 33, 34, 35, 38, 39, 41, 43, 46, 48, 49, 53, 54, 56, 58, 60, 64, 65, 68, 70, 72, 74, 76, 78, 80, 81, 84, 86, 88, 89, 92, 94, 96, 97, 98, 99, 101, 102, 104, 105, 108, 109, 112, 114, 116, 117, 119, 121, 122, 124, 125, 128, 130, 132, 134, 135, 137, 140, 142, 145, 146, 149, 151, 153, 155, 157, 160, 164, 165, 167, 168, 169, 172, 176, 179, 180, 181, 183, 187, 188, 189, 192]],
(generators = 4,) => [[1, 9, 16, 21, 25, 27, 33, 35, 43, 54, 56, 64, 74, 92, 104, 109, 112, 128, 142, 146, 151, 157, 160, 165, 169, 172, 176, 179, 180, 181, 188, 189]])


text:
# I don't like all these ids I have to make.
#

struct CondRandVar{X, Y} <: RandVar
  x::X
  y::Y
  id::ID
end

"Propagate conditions"
Base.:+(x::CondRandVar, y::CondRandVar, id = uid()) = CondRandVar(x.x + y.x, conjoin(x.y, y.y), id)
conjoin(a::Tuple, b::Tuple) = (a..., b...)

"Requirements are that cah"
struct Observation{X, V}
  x::X
  v::V
end

"""
θ = normal(0, 1)
x = normal(θ, 1)
θ_ = cond(θ, observe(x, 3.0))

θ_ |ₚ (x == 3.0)
θ_ |ᵢ (x => 3.0)
"""
observe(x, v) = Observation(x, v)

cond(x, y::Observation, id = uid()) = CondRandVar(x, (y,), id)

function apl(x::CondRandVar, ω)
  # Need to do a replacement
end

function logpdf_(x::CondRandVar, ω::Ω)

end

ext: jl


text:
using Termbox
@static if VERSION >= v"0.7-"
    using Test
    using Printf
else
    using Base.Test
end
using Compat

# write your own tests here
include("outputexample.jl")
outputexample.main(1e-1)

@test Termbox.tb_utf8_char_length(Char('r')) == 1

# Misc Tests
with_term() do
    tb_clear()
    tb_select_input_mode(TB_INPUT_ESC)
    tb_select_output_mode(TB_OUTPUT_NORMAL)

    tb_set_cursor(TB_HIDE_CURSOR, TB_HIDE_CURSOR)
    tb_set_clear_attributes(0x0001, 0x0000)

    e = tb_event()
    @test tb_peek_event(e, 1) == 0
    @show e
    c = tb_cell()
    @show c

    return nothing
end

# cellBuffer tests
with_term() do
    c = tb_cell('r', 0x0002, 0x0001)
    tb_select_input_mode(TB_INPUT_ESC)
    tb_select_output_mode(TB_OUTPUT_NORMAL)

    tb_set_clear_attributes(0x0001, 0x0000)
    tb_clear()

    tb_put_cell(3, 3, Ref(c))
    tb_put_cell(3, 4, Ref(c))
    tb_present()
    sleep(0.1)

    w = tb_width()
    h = tb_height()
    # "Native" index set for terminal
    CR = CartesianIndices((1:h, 1:w))

    # Linear version of above
    rs = reshape(LinearIndices(CR), size(CR))

    CB = tb_cell_buffer()
    if w>0 && h>0
        prevcInd = rs[3, 3]
        c1 = unsafe_load(CB, prevcInd)
        print(Char(c1.ch))

        tb_present()
    end
    CB = C_NULL # tb_present invalidates the pointer.
    sleep(0.1)
    # @show c
    return nothing
end

include("2048Example.jl")
CLI2048.main_wargames(5e-3)

include("graphExample.jl")
UpdatingGraph.main(5e-4)

include("sugar.jl")

function test_interactive()
    thisJulia = Base.JLOptions().julia_bin
    CLIFile=joinpath(dirname(@__FILE__), "2048Example.jl")
    @static if VERSION >= v"1.0-"
		p = open(`$(unsafe_string(thisJulia)) --code-coverage=user -L $(CLIFile) -e 'CLI2048.main_interactive()'`)
    else
		s, p = open(`$(unsafe_string(thisJulia)) --code-coverage=user -L $(CLIFile) -e 'CLI2048.main_interactive()'`)
	end
    sleep(2)
    try
        process_exited(p) || kill(p)
        process_exited(p) || kill(p, 9) # SIGKILL
    finally
        kill(p, 9)
    end
end
test_interactive()


text:
"""
    LensAssembly{T<:Real}

Structure which contains the elements of the optical system, these can be [`CSGTree`](@ref) or [`Surface`](@ref) objects.

In order to prevent type ambiguities bespoke structs are created for each possible number of elements e.g. `LensAssembly3`.
These are parameterized by the types of the elements to prevent ambiguities.
Basic surface types such as [`Rectangle`](@ref) (which can occur in large numbers) are stored independently in `Vector`s, so type paramters are only needed for CSG objects.

Each struct looks like this:
```julia
struct LensAssemblyN{T,T1,T2,...,TN} <: LensAssembly{T}
    axis::SVector{3,T}
    rectangles::Vector{Rectangle{T}}
    ellipses::Vector{Ellipse{T}}
    hexagons::Vector{Hexagon{T}}
    paraxials::Vector{ParaxialLens{T}}
    E1::T1
    E2::T2
    ...
    EN::TN
end
```

Where `Ti <: Union{Surface{T},CSGTree{T}}`.

To create a LensAssembly object the following functions can be used:
```julia
LensAssembly(elements::Vararg{Union{Surface{T},CSGTree{T},LensAssembly{T}}}; axis = SVector(0.0, 0.0, 1.0)) where {T<:Real}
```
"""
abstract type LensAssembly{T<:Real} end
export LensAssembly

Base.show(io::IO, ::Type{<:LensAssembly}) = print(io, "LensAssembly")
Base.show(io::IO, a::LensAssembly{T}) where {T<:Real} = print(io, "LensAssembly{$T}($(length(a.rectangles) + length(a.ellipses) + length(a.hexagons) + length(typed_elements(a))) elements)")

# in order to prevent type ambiguities lens assembly must be paramaterised by its elements, but we want lens assembly to have an arbitrary number of elements
# as such we have to use macros to create lens assembly structs of a given size, and methods to handle them in a type unambiguous way
# we may often end up with lots of simple surfaces (Rectangle, Ellipse) in the assembly, and we can store these more efficiently in Vectors
macro lensassembly_constructor(N)
    # generate the type definition for a lens assembly of size N
    typesig = [:($(Symbol("T$(i)")) <: Union{Surface{T},CSGTree{T}}) for i in 1:N]
    fields = [:($(Symbol("E$(i)"))::$(Symbol("T$(i)"))) for i in 1:N]
    name = Symbol("LensAssembly$N")
    return esc(quote
        struct $(name){T,$(typesig...)} <: LensAssembly{T}
            axis::SVector{3,T}
            rectangles::Vector{Rectangle{T}}
            ellipses::Vector{Ellipse{T}}
            hexagons::Vector{Hexagon{T}}
            paraxials::Vector{ParaxialLens{T}}
            $(fields...)
        end
    end)
end

# This is the basic idea of what each closestintersection implementation does:
# function closestintersection(ass::LensAssembly{T}, r::AbstractRay{T,N})::Union{Nothing,Intersection{T,N}} where {T<:Real,N}
#     αmin = typemax(T)
#     closest = nothing
#     for element in elements(ass)
#         allintscts = surfaceintersection(element, r)
#         if !(allintscts isa EmptyInterval)
#             intsct = closestintersection(allintscts)
#             if intsct !== nothing
#                 αcurr = α(intsct)
#                 if αcurr < αmin
#                     αmin = αcurr
#                     closest = intsct
#                 end
#             end
#         end
#     end
#     return closest
# end

macro lensassembly_intersection(N)
    # generates the _closestintersection() function for a lens assembly of size N, this should never be called directly, instead call closestintersection() in all cases
    type = :($(Symbol("LensAssembly$(N)")))
    fieldnames = [Symbol("E$(i)") for i in 1:N]
    checks = [quote
        intvl = surfaceintersection(obj.$(fieldnames[i]), r)
        if !(intvl isa EmptyInterval)
            intsct = closestintersection(intvl::Union{Interval{T},DisjointUnion{T}})
            if intsct !== nothing
                αcurr = α(intsct)
                if αcurr < αmin
                    αmin = αcurr
                    closest = intsct
                end
            end
        end
    end for i in 1:N]
    return esc(quote
        function _closestintersection(obj::$(type){T}, r::AbstractRay{T,N})::Union{Nothing,Intersection{T,N}} where {T<:Real,N}
            αmin = typemax(T)
            closest = nothing
            for rect in obj.rectangles
                intvl = surfaceintersection(rect, r)
                if !(intvl isa EmptyInterval)
                    intsct = closestintersection(intvl::Union{Interval{T},DisjointUnion{T}})
                    if intsct !== nothing
                        αcurr = α(intsct)
                        if αcurr < αmin
                            αmin = αcurr
                            closest = intsct
                        end
                    end
                end
            end
            for ell in obj.ellipses
                intvl = surfaceintersection(ell, r)
                if !(intvl isa EmptyInterval)
                    intsct = closestintersection(intvl::Union{Interval{T},DisjointUnion{T}})
                    if intsct !== nothing
                        αcurr = α(intsct)
                        if αcurr < αmin
                            αmin = αcurr
                            closest = intsct
                        end
                    end
                end
            end
            for hex in obj.hexagons
                intvl = surfaceintersection(hex, r)
                if !(intvl isa EmptyInterval)
                    intsct = closestintersection(intvl::Union{Interval{T},DisjointUnion{T}})
                    if intsct !== nothing
                        αcurr = α(intsct)
                        if αcurr < αmin
                            αmin = αcurr
                            closest = intsct
                        end
                    end
                end
            end
            for par in obj.paraxials
                intvl = surfaceintersection(par, r)
                if !(intvl isa EmptyInterval)
                    intsct = closestintersection(intvl::Union{Interval{T},DisjointUnion{T}})
                    if intsct !== nothing
                        αcurr = α(intsct)
                        if αcurr < αmin
                            αmin = αcurr
                            closest = intsct
                        end
                    end
                end
            end
            $(checks...)
            return closest
        end
    end)
end

macro lensassembly_elements(N)
    # generates the _elements() function for a lens assembly of size N, this should never be called directly, instead call elements() in all cases
    type = :($(Symbol("LensAssembly$(N)")))
    fieldnames = [Symbol("E$(i)") for i in 1:N]
    tuple = [:(obj.$(fieldnames[i])) for i in 1:N]
    return esc(quote
        function _typed_elements(obj::$(type){T}) where {T<:Real}
            return tuple($(tuple...))
        end
    end)
end

# pregenerate a small number at compile time
for N in 1:PREGENERATED_LENS_ASSEMBLY_SIZE
    type = :($(Symbol("LensAssembly$(N)")))
    if !isdefined(OpticSim, type)
        @eval @lensassembly_constructor($N)
        @eval @lensassembly_intersection($N)
        @eval @lensassembly_elements($N)
    end
end

function LensAssembly(elements::Vararg{Union{Surface{T},CSGTree{T},LensAssembly{T}}}; axis::SVector{3,T} = SVector{3,T}(0.0, 0.0, 1.0)) where {T<:Real}
    # make the actual object
    actual_elements = []
    rectangles = Vector{Rectangle{T}}(undef, 0)
    ellipses = Vector{Ellipse{T}}(undef, 0)
    hexagons = Vector{Hexagon{T}}(undef, 0)
    paraxials = Vector{ParaxialLens{T}}(undef, 0)
    for e in elements
        if e isa Rectangle{T}
            push!(rectangles, e)
        elseif e isa Ellipse{T}
            push!(ellipses, e)
        elseif e isa Hexagon{T}
            push!(hexagons, e)
        elseif e isa ParaxialLens{T}
            push!(paraxials, e)
        elseif e isa Surface{T} || e isa CSGTree{T}
            push!(actual_elements, e)
        else
            # unpack the lens assembly
            rectangles = vcat(rectangles, e.rectangles)
            ellipses = vcat(ellipses, e.ellipses)
            hexagons = vcat(hexagons, e.hexagons)
            for lae in OpticSim.typed_elements(e)
                push!(actual_elements, lae)
            end
        end
    end
    N = length(actual_elements)
    # make the methods for this N
    type = :($(Symbol("LensAssembly$(N)")))
    if !isdefined(OpticSim, type)
        @eval @lensassembly_constructor($N)
        @eval @lensassembly_intersection($N)
        @eval @lensassembly_elements($N)
    end
    eltypes = typeof.(actual_elements)
    naxis = normalize(axis)
    return eval(:($(type){$T,$(eltypes...)}($naxis, $rectangles, $ellipses, $hexagons, $paraxials, $(actual_elements...))))
end

function typed_elements(ass::LensAssembly{T}) where {T<:Real}
    # under certain circumstances we run into the world age problem (see https://discourse.julialang.org/t/how-to-bypass-the-world-age-problem/7012)
    # to avoid this we need to use invokelatest(), but this is type ambiguous and ruins performance, and in most cases isn't necessary
    # so we can use try/catch to only call it when we need (and warn appropriately), retaining performance in almost all cases
    # NEVER call _typed_elements directly, always use this method
    try
        _typed_elements(ass)
    catch MethodError
        @warn "First time call to LensAssembly of previously unused size, performance will be worse than normal." maxlog = 1
        Base.invokelatest(_typed_elements, ass)
    end
end

elements(ass::LensAssembly{T}) where {T<:Real} = (ass.rectangles..., ass.ellipses..., ass.hexagons..., ass.paraxials..., typed_elements(ass)...)
export elements

function closestintersection(ass::LensAssembly{T}, r::AbstractRay{T,N})::Union{Nothing,Intersection{T,N}} where {T<:Real,N}
    # under certain circumstances we run into the world age problem (see https://discourse.julialang.org/t/how-to-bypass-the-world-age-problem/7012)
    # to avoid this we need to use invokelatest(), but this is type ambiguous and ruins performance, and in most cases isn't necessary
    # so we can use try/catch to only call it when we need (and warn appropriately), retaining performance in almost all cases
    # NEVER call _closestintersection directly, always use this method
    try
        _closestintersection(ass, r)
    catch MethodError
        @warn "First time call to LensAssembly of previously unused size, performance will be worse than normal." maxlog = 1
        Base.invokelatest(_closestintersection, ass, r)
    end
end

function BoundingBox(la::LensAssembly{T}) where {T<:Real}
    es = elements(la)
    bbox = BoundingBox(es[1])
    for b in es[2:end]
        if b isa ParametricSurface{T} || b isa CSGTree{T}
            bbox = union(bbox, b)
        end
    end
    return bbox
end

axis(a::LensAssembly{T}) where {T<:Real} = a.axis
export axis

#############################################################################

"""
    LensTrace{T<:Real,N}

Contains an intersection point and the ray segment leading to it from within an optical trace.
The ray carries the path length, power, wavelength, number of intersections and source number, all of which are accessible directly on this class too.

Has the following accessor methods:
```julia
ray(a::LensTrace{T,N}) -> OpticalRay{T,N}
intersection(a::LensTrace{T,N}) -> Intersection{T,N}
power(a::LensTrace{T,N}) -> T
wavelength(a::LensTrace{T,N}) -> T
pathlength(a::LensTrace{T,N}) -> T
point(a::LensTrace{T,N}) -> SVector{N,T}
uv(a::LensTrace{T,N}) -> SVector{2,T}
sourcenum(a::LensTrace{T,N}) -> Int
nhits(a::LensTrace{T,N}) -> Int
```
"""
struct LensTrace{T<:Real,N}
    ray::OpticalRay{T,N}
    intersection::Intersection{T,N}
end
export LensTrace

ray(a::LensTrace{T,N}) where {T<:Real,N} = a.ray
intersection(a::LensTrace{T,N}) where {T<:Real,N} = a.intersection
power(a::LensTrace{T,N}) where {T<:Real,N} = power(ray(a))
wavelength(a::LensTrace{T,N}) where {T<:Real,N} = wavelength(ray(a))
pathlength(a::LensTrace{T,N}) where {T<:Real,N} = pathlength(ray(a))
point(a::LensTrace{T,N}) where {T<:Real,N} = point(intersection(a))
uv(a::LensTrace{T,N}) where {T<:Real,N} = uv(intersection(a))
sourcenum(a::LensTrace{T,N}) where {T<:Real,N} = sourcenum(ray(a))
nhits(a::LensTrace{T,N}) where {T<:Real,N} = nhits(ray(a))
export intersection

function Base.print(io::IO, a::LensTrace{T,N}) where {T,N}
    println(io, "Ray\n$(ray(a))")
    println(io, "Lensintersection\n$(intersection(a))")
end

struct NoPower end
const nopower = NoPower()

# isnopower(::NoPower) = true
# isnopower(::Any) = false

#############################################################################

"""
    trace(assembly::LensAssembly{T}, r::OpticalRay{T}, temperature::T = 20.0, pressure::T = 1.0; trackrays = nothing, test = false)

Returns the ray as it exits the assembly in the form of a [`LensTrace`](@ref) object if it hits any element in the assembly, otherwise `nothing`.
Recursive rays are offset by a small amount (`RAY_OFFSET`) to prevent it from immediately reintersecting the same lens element.

`trackrays` can be passed an empty vector to accumulate the `LensTrace` objects at each intersection of `ray` with a surface in the assembly.
"""
function trace(assembly::LensAssembly{T}, r::OpticalRay{T,N}, temperature::T = T(OpticSim.GlassCat.TEMP_REF), pressure::T = T(OpticSim.GlassCat.PRESSURE_REF); trackrays::Union{Nothing,Vector{LensTrace{T,N}}} = nothing, test::Bool = false, recursion::Int = 0)::Union{Nothing,NoPower,LensTrace{T,N}} where {T<:Real,N}
    if power(r) < POWER_THRESHOLD || recursion > TRACE_RECURSION_LIMIT
        return nopower
    end
    intsct = closestintersection(assembly, r)
    if intsct === nothing
        return nothing
    else
        surfintsct = point(intsct)
        nml = normal(intsct)
        opticalinterface = interface(intsct)
        λ = wavelength(r)

        if VERSION < v"1.6.0-DEV"
            # TODO REMOVE
            temp = @unionsplit OpticalInterface T opticalinterface processintersection(opticalinterface, surfintsct, nml, r, temperature, pressure, test, recursion == 0)
        else
            temp = processintersection(opticalinterface, surfintsct, nml, r, temperature, pressure, test, recursion == 0)
        end

        if temp === nothing
            return nopower
        else
            raydirection, raypower, raypathlength = temp
            if trackrays !== nothing
                # we want the power that is hitting the surface, i.e. power on incident ray, but the path length at this intersection
                # i.e. with the path length for this intersection added and power modulated by absorption only
                push!(trackrays, LensTrace(OpticalRay(ray(r), raypower, λ, opl = raypathlength, nhits = nhits(r), sourcenum = sourcenum(r), sourcepower = sourcepower(r)), intsct))
            end
            offsetray = OpticalRay(surfintsct + RAY_OFFSET * raydirection, raydirection, raypower, λ, opl = raypathlength, nhits = nhits(r) + 1, sourcenum = sourcenum(r), sourcepower = sourcepower(r))
            res = trace(assembly, offsetray, temperature, pressure, trackrays = trackrays, test = test, recursion = recursion + 1)
            if res === nothing
                return LensTrace(OpticalRay(surfintsct, raydirection, raypower, λ, opl = raypathlength, nhits = nhits(r), sourcenum = sourcenum(r), sourcepower = sourcepower(r)), intsct)
            else
                return res
            end
        end
    end
end

# """approximate focal length of simple double convex lens. For plano convex set one of the radii to Inf"""
focallength(index::Float64, rf::Float64, rb::Float64) = 1 / ((index - 1) * (1 / rf - 1 / rb))


text:
tok  = s -> F.find_tokens(s, F.MD_TOKENS, F.MD_1C_TOKENS)
vfn  = s -> (t = tok(s); F.validate_footnotes!(t); t)
vh   = s -> (t = vfn(s); F.validate_headers!(t); t)
fib  = s -> (t = vh(s); F.find_indented_blocks!(t, s); t)
fib2 = s -> (t = fib(s); F.filter_lr_indent!(t, s); t)

blk  = s -> (F.def_LOCAL_VARS!(); F.find_all_ocblocks(fib2(s), F.MD_OCB_ALL))
blk2 = s -> ((b, t) = blk(s); F.merge_indented_blocks!(b, s); b)
blk3 = s -> (b = blk2(s); F.filter_indented_blocks!(b); b)

blk4 = s -> (b = blk3(s); F.validate_and_store_link_defs!(b); b)

isblk(b, n, s) = b.name == n && b.ss == s
cont(b) = F.content(b)

# Basics
@testset "P:2:blk" begin
    b, t = raw"""
    A <!--

        Hello
    -->
    Then ```julia 1+5``` and ~~~    ~~~.
    """ |> blk
    @test length(b) == 3
    @test isblk(b[1], :COMMENT, "<!--\n\n    Hello\n-->")
    @test isblk(b[2], :CODE_BLOCK_LANG, "```julia 1+5```")
    @test cont(b[2]) == " 1+5"
    @test isblk(b[3], :ESCAPE, "~~~    ~~~")
end

# with indentation
@testset "P:2:blk-ind" begin
    b, t = raw"""
    A

        B1
        B2
        B3
    @@d1
        B
    @@
    """ |> blk
    @test length(b) == 2
    @test isblk(b[1], :CODE_BLOCK_IND, "\n    B1\n    B2\n    B3\n")
    @test isblk(b[2], :DIV, "@@d1\n    B\n@@")

    b, t = raw"""
    @@d1
        @@d2
            B
        @@
    @@
    """ |> blk
    @test length(b) == 1
    @test isblk(b[1], :DIV, "@@d1\n    @@d2\n        B\n    @@\n@@")

    b, t = raw"""
    @@d1
        @@d2
            B
        @@
    @@
    """ |> blk
    @test length(b) == 1
    @test isblk(b[1], :DIV, "@@d1\n    @@d2\n        B\n    @@\n@@")
end

# with indentation and grouping and filtering
@testset "P:2:blk-indF" begin
    b = raw"""
    A

        B1
        B2

        B3
    C
    """ |> blk3
    @test isblk(b[1], :CODE_BLOCK_IND, "\n    B1\n    B2\n\n    B3\n")

    b = raw"""
    A

        B
        C
            D
        E

    F

        G

        H
    """ |> blk3
    @test length(b) == 2
    @test isblk(b[1], :CODE_BLOCK_IND, "\n    B\n    C\n        D\n    E\n")
    @test isblk(b[2], :CODE_BLOCK_IND, "\n    G\n\n\n\n    H\n")

    b = raw"""
    @@d1

        A
        B
        C
    @@
    """ |> blk3
    @test length(b) == 1
    @test isblk(b[1], :DIV, "@@d1\n\n    A\n    B\n    C\n@@")

    b = raw"""
    @@d1
        @@d2
            @@d3
                B
            @@
        @@
    @@
    """ |> blk3
    @test b[1].name == :DIV
end

@testset "More ind" begin
    b = "A\n\n\tB1\n\tB2\n\n\tB3\nC" |> blk3
    @test isblk(b[1], :CODE_BLOCK_IND, "\n\tB1\n\tB2\n\n\tB3\n")
end

@testset "P:2:blk-{}" begin
    b = "{ABC}" |> blk3
    @test F.content(b[1]) == "ABC"
    b = "\\begin{eqnarray} \\sin^2(x)+\\cos^2(x) &=& 1\\end{eqnarray}" |> blk3
    @test cont(b[1]) == " \\sin^2(x)+\\cos^2(x) &=& 1"
    b = raw"""
    a\newcommand{\eqa}[1]{\begin{eqnarray}#1\end{eqnarray}}b@@d .@@
    \eqa{\sin^2(x)+\cos^2(x) &=& 1}
    """ |> blk3
    @test isblk(b[1], :LXB, raw"{\eqa}")
    @test isblk(b[2], :LXB, raw"{\begin{eqnarray}#1\end{eqnarray}}")
    @test isblk(b[3], :LXB, raw"{\sin^2(x)+\cos^2(x) &=& 1}")
end

# links
@testset "P:2:blk-[]" begin
    b = """
    A [A] B.
    [A]: http://example.com""" |> blk4
    @test length(b) == 1
    @test isblk(b[1], :LINK_DEF, "[A]: http://example.com")
    b = """
    A [A][B] C.
    [B]: http://example.com""" |> blk4
    @test isblk(b[1], :LINK_DEF, "[B]: http://example.com")
    b = """
    A [`B`] C
    [`B`]: http://example.com""" |> blk4
    @test length(b) == 2
    @test isblk(b[1], :CODE_INLINE, "`B`")
    @test isblk(b[2], :LINK_DEF,    "[`B`]: http://example.com")
end


text:
# Struct to hold an x, y position. A tuple would also be fine.
struct Pos
    x::Float64
    y::Float64
end

# This returns a measure of how far "left" the vector is with a cross product
function cross(point1::Pos, point2::Pos, point3::Pos)
    vec1 = Pos(point2.x - point1.x, point2.y - point1.y)
    vec2 = Pos(point3.x - point2.x, point3.y - point2.y)
    ret_angle = vec1.x*vec2.y - vec1.y*vec2.x
    return ret_angle*ret_angle
end

function jarvis_march(points::Vector{Pos})
    hull = Vector{Pos}()

    # sorting array based on leftmost point
    sort!(points, by = item -> item.x)
    push!(hull, points[1])

    i = 1
    curr_point = points[2]

    # Find angle between points
    curr_product = cross(Pos(0,0), hull[1], curr_point)

    # We will hold a temp variable with the highest cross product as we iterate
    # through all the points and move our hull vector forward.
    while (curr_point != hull[1])
        for point in points
            product = 0.0

            # Special case for the first element when there is no hull[i-1]
            if (i == 1)
                if (hull[i] != point)
                    product = cross(Pos(0,0), hull[i], point)
                end
            else
                if (hull[i] != point && hull[i-1] != point)
                    product = cross(hull[i-1], hull[i], point)
                end
            end
            if (product > curr_product)
                curr_point = point
                curr_product = product
            end
        end

        # Pushing to hull, moving simulation forward and resetting the product
        push!(hull, curr_point)
        curr_product = 0
        i += 1
    end

    return hull
end

function main()

    # These points are chosen such that there is a clearly defined hull with
    # several interior points. As a note, these will be generated either
    # randomly or via some mesh in practice.
    points = [Pos(2,1.5), Pos(1, 1), Pos(2, 4), Pos(3, 1), Pos(2,2), Pos(2,0.5)]
    hull = jarvis_march(points)
    println(hull)
end

main()

max_issues_repo_name: shiffman/simuleios

max_forks_repo_name: shiffman/simuleios


text:
export Figure, erase!, clf

"""
    Figure(id; opts...)

Return a new `Figure` object with name `id`, if a figure with that name
exists already, return that object.

# Named options:

* `size`: a tuple (width, height) (for rendering prefer sizes ≥ (8, 8) see also fontsize recommendations)
* `font`: a valid font name (note that if you use latex, this is irrelevant)
* `fontsize`: the master font size of the figure in pt (for rendering, prefer fontsizes ≥ 10pt)
* `col`, `color`: the master font color of the figure (any Colors.Colorant can be used)
* `tex`, `hastex`, `latex`, `haslatex`: a boolean indicating whether there is LaTeX to be compiled in the figure
* `texscale`: either `fixed`, `none` or `scale` to match the size of LaTeX expressions to the ambient fontsize (`fixed` and `scale` match, `none` doesn't)
* `preamble`, `texpreamble`: the LaTeX preamble, where you can change the font that is used and also make sure that the symbols you want to use are available.
* `alpha`, `transparent`, `transparency`: a bool indicating whether there may be transparent fillings in which case cairo is used

# Other options (internal use mostly):

* `reset`: a bool, if true will erase the figure if it exists (instead of just returning it).
* `_noreset`: internal to indicate that if the figure has no name and is called whether a new one
should be sent or not (for the user: yes, internally: sometimes not as we want to retrieve
properties)
"""
function Figure(
            id::String = "_fig_";
            #
            reset::Bool    = false,
            _noreset::Bool = false,
            opts...
        )::Figure
    # return a fresh figure when calling Figure() unless _noreset
    if id == "_fig_" && !_noreset
        f = Figure(GLEScript(), id)
        set_properties!(f;  opts...)
        GP_ENV["ALL_FIGS"][id] = f
        GP_ENV["CUR_FIG"]  = f
        GP_ENV["CUR_AXES"] = nothing
        return f
    end
    # otherwise try to find an existing figure, if nothing found, return a new one as well
    f = get(GP_ENV["ALL_FIGS"], id) do
        GP_ENV["ALL_FIGS"][id] = Figure(GLEScript(), id)
    end
    reset && erase!(f)
    GP_ENV["CUR_FIG"]  = f
    GP_ENV["CUR_AXES"] = isempty(f.axes) ? nothing : f.axes[1]
    set_properties!(f;  opts...) # f exists but properties have been given
    return f
end

"""
    add_axes!(fig, ax)

Internal function to add axes `ax` to figure `fig`.
"""
function add_axes!(f::Figure, ax::Axes)
    push!(f.axes, ax)
    GP_ENV["CUR_AXES"] = ax
    return ax
end

"""
    add_axes2d!()

Internal function to add empty `Axes2D` to the current figure.
"""
add_axes2d!() = (f=gcf(); add_axes!(f, Axes2D(f.id)))

# """
#     add_axes3d!()
#
# Internal function to add empty `Axes3D` to the current figure.
# """
# add_axes3d!() = (f=gcf(); add_axes!(f, Axes3D(parent=f.id)))

"""
    erase!(fig)

Replaces `fig`'s current axes by a fresh, empty axes container. Note that
other properties of the figure are preserved (such as its size, latex
properties etc). See also [`clf`](@ref) and [`reset!`](@ref).
"""
function erase!(f::Figure)
    # empty associated buffer
    take!(f.g)
    # give `f` a fresh set of axes
    f.axes = Vector{Axes}()
    GP_ENV["CUR_FIG"]  = f
    GP_ENV["CUR_AXES"] = nothing
    return f
end

"""
    clf()

Reset the current figure keeping only its current name and size, everything else is
set to the default parameters.
See also [`erase!`](@ref) and [`reset!`](@ref)
"""
clf() = (f=gcf(); reset!(f); PreviewFigure(f))

"""
    destroy(fig)

Internal function to remove all references to `fig` and set the current figure to nothing.
"""
function destroy(f::Figure)
    delete!(GP_ENV["ALL_FIGS"], f.id)
    GP_ENV["CUR_FIG"]  = nothing
    GP_ENV["CUR_AXES"] = nothing
    return nothing
end


text:
using XPORTA: POI, IEQ, traf, make_porta_tmp, rm_porta_tmp
using JSON, Dates

export rotate_facet, adjacent_facets, adjacency_decomposition

"""
    rotate_facet(
        F :: Vector{Int64},
        G :: Vector{Int64},
        xbar :: Vector{Int64}
    ) :: Vector{Int64}

Performs a rotation of facet `F` relative to non-included vertex `xbar` and returns
the rotated facet vector. `F` is a polytope facet, `G` is a subfacet of `F` and `xbar`
is a vertex not contained by `F`. By construction, the rotated facet contains
`xbar`.
"""
function rotate_facet(F::Vector{Int64}, G ::Vector{Int64}, xbar::Vector{Int64}) :: Vector{Int64}
    a_rot = (F[end] - F[1:(end-1)]' * xbar)*G[1:(end-1)] - (G[end] - G[1:(end-1)]' * xbar)*F[1:(end-1)]
    β_rot = (F[end] - F[1:(end-1)]' * xbar)*G[end] - (G[end] - G[1:(end-1)]' * xbar)*F[end]

    cat(a_rot, β_rot, dims=1)
end

"""
    adjacent_facets(
        vertices :: Vector{Vector{Int64}},
        F :: Vector{int64};
        dir = "./" :: String,
        cleanup = true ::Bool
    ) :: Vector{Vector{Int64}}

For the polytope represented by `vertices`, returns the set of facets
adjacent to `F`.

Facet vector `F` and the return facet vectors are assumed to ba in the normalized
subspace.

The  `dir` argument specifies where to where to write files and directories from
XPORTA.jl. If `cleanup` is `true`, then a  porta_tmp directory is created as a
subdirectory of `dir`.

If `cleanup` is `false`, the created  `porta_tmp` directory is not removed.
"""
function adjacent_facets(
    vertices::Vector{Vector{Int64}},
    F::Vector{Int64};
    dir = "./" :: String,
    cleanup=true :: Bool
) :: Vector{Vector{Int64}}
    # vertices of facet F
    F_vertices = filter(v -> F[1:(end-1)]' * v == F[end], vertices)

    # find the subfacets of facet F, these subfacets are labeled G
    poi_vertices = hcat(F_vertices...)'[:,:]
    G_ieq = traf(POI(vertices = poi_vertices), dir=dir, cleanup=cleanup)
    G_ineqs = convert.(Int64, G_ieq.inequalities)

    # polytope vertices not in F index 1 is farthest from F
    xbar_vertices = sort(
        filter(v -> F[1:(end-1)]' * v != F[end], vertices),
        by = v -> F[1:(end-1)]' * v, rev=true
    )

    adjacent_facets = Array{Vector{Int64},1}(undef,0)
    for G_row_id in 1:size(G_ineqs,1)
        G = G_ineqs[G_row_id,:]
        for xbar_id in 1:length(xbar_vertices)
            xbar = xbar_vertices[xbar_id]

            # rotate G about x_bar to get G_rot
            G_rot = rotate_facet(F, G, xbar)

            # If no polytope vertices violate G_rot facet then it is a facet of polytope P
            if (findfirst(v -> G_rot[1:(end-1)]' * v > G_rot[end], xbar_vertices) === nothing)
                push!(adjacent_facets, G_rot)

                # breaking misses adjacent facets, but I have not found any cases
                # where a canonical facet class has been missed
                break # TODO: does breaking catch all adjacent facet classes?
            end
        end
    end

    return adjacent_facets
end

"""
    adjacenecy_decomposition(
        vertices :: Vector{Vector{Int64}},
        BG_seed :: BellGame,
        scenario :: LocalSignaling;
        kwargs...
    ) :: Dict

Given a polytpe represented by `vertices`, returns the complete set of canonical
facets for prepare and measure scenario `scenario`. The adjacency_decomposition algorithm
requires a seeded vertex which is supplied with the `BG_seed` argument. Facets
are returned in the lexicographic normal form.

### Returned Dictionary Format

Returns a dictionary where the keys are canonical `BellGames` and the value is a
dictionary with keys

* "considered" => true, if the facet was considered in the algorithm.
* "skipped" => true, if the facet was skipped (not considered).
* "num_vertices" => number of vertices.
* "norm_facet" => facet vector representative (normalized rep) of canonical game

### Keyword  Arguments: `kwargs...`
* `skip_games = [] ::  Vector{BellGame}` - List of games to skip.
* `max_vertices = 100 :: Int64` - The maximum number of vertices to allow in target facets.
* `dir` = "./" :: String` - Directory in which to create `porta_tmp/` and `.json` files.
* `log = false :: Bool` - If true, the facet dictionary is  written to `.json` each iteration.
* `log_filename = "adjacency_decomposition_$(Dates.now).json" :: String`
"""
function adjacency_decomposition(
    vertices :: Vector{Vector{Int64}},
    BG_seed :: BellGame,
    scenario :: LocalSignaling;
    skip_games = Array{BellGame}(undef,0) :: Vector{BellGame},
    max_vertices = 100 :: Int64,
    dir = "./"  :: String,
    log = false :: Bool,
    log_filename = "adjacency_decomposition_$(Dates.now()).json" :: String,
) :: Dict
    # canonicalize facet
    canonical_BG_seed = LocalPolytope.generator_facet(BG_seed, scenario)
    canonical_skip_BGs = map( BG -> LocalPolytope.generator_facet(BG, scenario), skip_games)

    norm_facet_seed = convert(Vector{Int64}, canonical_BG_seed, rep = "normalized")
    num_BG_seed_vertices = length(
        filter(v -> norm_facet_seed[1:(end-1)]' * v == norm_facet_seed[end], vertices)
    )

    # holds considered and unconsidered facets
    facet_dict = Dict{BellGame, Dict}(
        canonical_BG_seed => Dict(
            "considered" => false,
            "skipped" => false,
            "norm_facet"  => norm_facet_seed,
            "generator_facet" => convert(Vector{Int64}, canonical_BG_seed),
            "num_vertices" => num_BG_seed_vertices
        )
    )

    # add skipped facets as considered
    for skip_BG in canonical_skip_BGs
        facet_dict[skip_BG] = Dict(
            "considered" => true,
            "norm_facet" => convert(Vector{Int64}, skip_BG, rep = "normalized"),
            "generator_facet" => convert(Vector{Int64}, skip_BG),
            "skipped" => true
        )
    end

    # create porta_tmp directory
    porta_tmp_dir = make_porta_tmp(dir)

    # loop until all facet are considered
    while !all(d -> d["considered"], collect(values(facet_dict)))
        # select the first uncosidered facet with fewest vertices
        target_BG = sort(
                filter(d -> facet_dict[d]["considered"] == false, collect(keys(facet_dict))),
                by=key -> facet_dict[key]["num_vertices"]
            )[1]

        # get target facet vector in normalized representation
        norm_facet = facet_dict[target_BG]["norm_facet"]

        # compute adjacent facets
        adj_facets = try
            adjacent_facets(vertices, norm_facet, dir=porta_tmp_dir, cleanup=false)
        # if an unexpected error occurs with XPORTA, mark facet as such and move on.
        catch error
            facet_dict[target_BG]["considered"] = true

            push!(facet_dict[target_BG],"error" => true)
            push!(facet_dict[target_BG],"error_msg" => sprint(showerror, error, backtrace()))

            continue
        end

        for adj_facet in adj_facets
            adj_game = convert(BellGame, adj_facet, scenario, rep="normalized")
            canonical_game = LocalPolytope.generator_facet(adj_game, scenario)

            # add new facet to  dictionary
            if !haskey(facet_dict, canonical_game)
                num_vertices = length(filter(v -> adj_facet[1:(end-1)]' * v == adj_facet[end], vertices))

                if num_vertices <= max_vertices
                    facet_dict[canonical_game] = Dict(
                        "considered" => false,
                        "skipped" => false,
                        "norm_facet" => adj_facet,
                        "generator_facet" => convert(Vector{Int64}, canonical_game),
                        "num_vertices" => num_vertices
                    )
                else
                    facet_dict[canonical_game] = Dict(
                        "considered" => true,
                        "skipped" => true,
                        "norm_facet" => adj_facet,
                        "generator_facet" => convert(Vector{Int64}, canonical_game),
                        "num_vertices" => num_vertices
                    )
                end
            end
        end

        # set current bell game "considered" to true
        facet_dict[target_BG]["considered"] = true

        if log
            open(dir*log_filename, "w") do io
                JSON.print(io, facet_dict)
            end
        end
    end

    # cleanup porta_tmp directory after completion
    rm_porta_tmp(dir)

    facet_dict
end


text:
struct LineSourceParams{BT<:Body}
    body :: BT
end

function LineSourceParams(x::Vector{T},y::Vector{T};closure=:closed) where {T}
      closuretype = (closure == :closed ? RigidBodyTools.ClosedBody : RigidBodyTools.OpenBody)
      LineSourceParams(BasicBody(x,y,closuretype=closuretype))
end

struct PrescribedLineSource{BT<:Body,AT,GT<:VectorGridData,ST<:VectorData,RT<:RegularizationMatrix,ET<:InterpolationMatrix}
    body :: BT
    arccoord :: AT
    cache1 :: GT
    τ :: ST
    R :: RT
    E :: ET
end

function PrescribedLineSource(params::LineSourceParams,u::VectorGridData,g::PhysicalGrid;ddftype=CartesianGrids.Yang3)
    @unpack body = params

    pts = VectorData(collect(body))
    regop = _regularization(pts,g,body,ddftype)

    s = VectorData(pts)

    arccoord = ScalarData(pts)
    arccoord .= accumulate(+,dlengthmid(body))

    R = RegularizationMatrix(regop,s,u)
    E = InterpolationMatrix(regop,u,s)
    PrescribedLineSource(body,arccoord,similar(u),similar(s),R,E)
end

set_linesource_strength!(τ_bc::PrescribedLineSource{BT,AT,GT,ST},q::ST) where {BT,AT,GT,ST} = τ_bc.τ .= q

# function set_linesource_strength!(τ_bc::PrescribedLineSource,q::AbstractVector{T}) where {T<:Real}
#     @assert length(q) == length(τ_bc.τ)
#     set_linesource_strength!(τ_bc,VectorData(q))
# end


text:
# using Cbc # slow, not recommended
# solver = CbcSolver(logLevel=0, integerTolerance=1e-9, primalTolerance=1e-9, ratioGap=1e-8)

using Gurobi
solver = GurobiSolver(OutputFlag=0)

# using CPLEX
# solver = CplexSolver(CPX_PARAM_SCRIND=0)

using JuMP
using PiecewiseLinearOpt
using Base.Test
using HDF5

methods_1D = (:CC,:MC,:Logarithmic,:LogarithmicIB,:ZigZag,:ZigZagInteger,:SOS2,:GeneralizedCelaya,:SymmetricCelaya,:Incremental,:DisaggLogarithmic)
methods_2D = (:CC,:MC,:Logarithmic,:LogarithmicIB,:ZigZag,:ZigZagInteger,:SOS2,:GeneralizedCelaya,:SymmetricCelaya,:DisaggLogarithmic)
# methods_2D = (:OptimalIB,) # very slow on all solvers
patterns_2D = (:Upper,:Lower,:UnionJack,:K1,:Random) # not :BestFit because requires function values at midpoints, :OptimalTriangleSelection and :Stencil not supported currently

# tests on network flow model with piecewise-linear objective
# instance data loaded from .h5 files
instance_data_1D = joinpath(dirname(@__FILE__),"1D-pwl-instances.h5")
instance_data_2D = joinpath(dirname(@__FILE__),"2D-pwl-instances.h5")

println("\nunivariate tests")
@testset "instance $instance" for instance in ["10104_1_concave_1"]
    demand = h5read(instance_data_1D, string(instance,"/demand"))
    supply = h5read(instance_data_1D, string(instance,"/supply"))
    ndem = size(demand, 1)
    nsup = size(supply, 1)

    rawd = h5read(instance_data_1D, string(instance,"/d"))
    d = rawd[1,:]
    rawfd = h5read(instance_data_1D, string(instance,"/fd"))
    fd = rawfd[1,:]

    objval1 = NaN
    @testset "1D: $method" for method in methods_1D
        model = Model(solver=solver)
        @variable(model, x[1:nsup,1:ndem] ≥ 0)
        @constraint(model, [j in 1:ndem], sum(x[i,j] for i in 1:nsup) == demand[j])
        @constraint(model, [i in 1:nsup], sum(x[i,j] for j in 1:ndem) == supply[i])
        @objective(model, Min, sum(piecewiselinear(model, x[i,j], d, fd, method=method) for i in 1:nsup, j in 1:ndem))

        @test solve(model) == :Optimal
        if isnan(objval1)
            objval1 = getobjectivevalue(model)
        else
            @test getobjectivevalue(model) ≈ objval1 rtol=1e-4
        end
    end
end

println("\nbivariate tests")
# @testset "numpieces $numpieces, variety $variety, objective $objective" for numpieces in [4,8], variety in 1:5, objective in 1:20
@testset "numpieces $numpieces, variety $variety, objective $objective" for numpieces in [4], variety in 1:1, objective in 1:1
    instance = string(numpieces,"_",variety,"_",objective)

    demand = h5read(instance_data_2D, string(instance,"/demand"))
    supply = h5read(instance_data_2D, string(instance,"/supply"))
    ndem = size(demand, 1)
    nsup = size(supply, 1)

    rawd = h5read(instance_data_2D, string(instance,"/d"))
    d = rawd[1,:]
    rawfd = h5read(instance_data_2D, string(instance,"/fd"))
    fˣ = reshape(rawfd[1,:], length(d), length(d))
    ˣtoⁱ = Dict(d[p] => p for p in 1:length(d))
    f = (pˣ,pʸ) -> fˣ[ˣtoⁱ[pˣ],ˣtoⁱ[pʸ]]

    objval1 = NaN
    @testset "2D: $method, $pattern" for method in methods_2D, pattern in patterns_2D
        model = Model(solver=solver)
        @variable(model, x[1:nsup,1:ndem] ≥ 0)
        @constraint(model, [j in 1:ndem], sum(x[i,j] for i in 1:nsup) == demand[j])
        @constraint(model, [i in 1:nsup], sum(x[i,j] for j in 1:ndem) == supply[i])
        @variable(model, y[1:nsup,1:ndem] ≥ 0)
        @constraint(model, [j in 1:ndem], sum(y[i,j] for i in 1:nsup) == demand[j])
        @constraint(model, [i in 1:nsup], sum(y[i,j] for j in 1:ndem) == supply[i])

        @objective(model, Min, sum(piecewiselinear(model, x[i,j], y[i,j], BivariatePWLFunction(d, d, f, pattern=pattern), method=method, subsolver=solver) for i in 1:nsup, j in 1:ndem))

        @test solve(model) == :Optimal
        if isnan(objval1)
            objval1 = getobjectivevalue(model)
        else
            @test getobjectivevalue(model) ≈ objval1 rtol=1e-4
        end
    end
end


text:
using Documenter, GeneralizedSVD

makedocs(
    authors = "Ji Wang, Zhaojun Bai",
    sitename = "GeneralizedSVD.jl",
    modules = [GeneralizedSVD],
    pages = [
        "Home" => "index.md",
        ],
)

deploydocs(
    repo = "github.com/jiiwang/GeneralizedSVD.jl.git",
)


text:
export FlexMatrix, row_keys, col_keys, delete_row!, delete_col!

struct FlexMatrix{R<:Any,C<:Any,T<:Number}
    data::Dict{Tuple{R,C},T}
    function FlexMatrix{T}(rows,cols) where T<:Number
        R = eltype(rows)
        C = eltype(cols)
        RC = Tuple{R,C}
        d = Dict{RC,T}()
        for r in rows
            for c in cols
                d[r,c] = zero(T)
            end
        end
        new{R,C,T}(d)
    end
end

"""
`FlexMatrix{T}(rows,cols)` creates a new `FlexMatrix` with
rows indexed by `rows`, columns indexed by `cols` and
all zero entries of type `T` (which is `Number` if omitted).

`FlexMatrix(v::FlexVector)` converts `v` into a one-column
`FlexMatrix` whose sole column index is `Int(1)`
"""
FlexMatrix(rows,cols) = FlexMatrix{Number}(rows,cols)
FlexMatrix() = FlexMatrix(Int[],Int[])
function FlexMatrix(v::FlexVector)
    VT = valtype(v)
    IT = valtype(v)
    A = FlexMatrix{VT}(keys(v),1)
    for k in keys(v)
        A[k,1] = v[k]
    end
    return A
end

function FlexOnes(T::Type,rows,cols)
    M = FlexMatrix{T}(rows,cols)
    for r in rows
        for c in cols
            M.data[r,c] = one(T)
        end
    end
    return M
end

FlexOnes(rows,cols) = FlexOnes(Float64,rows,cols)

size(A::FlexMatrix) = (length(row_keys(A)), length(col_keys(A)))

function FlexConvert(A::Matrix{T}) where T
    r,c = size(A)
    M = FlexMatrix{T}(1:r, 1:c)
    for i=1:r
        for j=1:c
            M.data[i,j]=A[i,j]
        end
    end
    return M
end

keys(M::FlexMatrix) = keys(M.data)
values(M::FlexMatrix) = values(M.data)
valtype(M::FlexMatrix) = valtype(M.data)

"""
`row_keys(M::FlexMatrix)` returns a list of the keys to the
rows of `M`.
"""
function row_keys(M::FlexMatrix)
    firsts = unique( [ k[1] for k in keys(M) ] )
    try
        sort!(firsts)
    catch
    end
    return firsts
end

"""
`col_keys(M::FlexMatrix)` returns a list of the keys to the
columns of `M`.
"""
function col_keys(M::FlexMatrix)
    seconds = unique( [ k[2] for k in keys(M) ] )
    try
        sort!(seconds)
    catch
    end
    return seconds
end

function getindex(A::FlexMatrix{R,C,T}, i, j) where {R,C,T}
    if haskey(A.data,(i,j))
        return A.data[i,j]
    end
    return zero(T)
end

setindex!(A::FlexMatrix,x,i,j) = setindex!(A.data,x,i,j)

function (==)(A::FlexMatrix,B::FlexMatrix)
    row_A = row_keys(A)
    row_B = row_keys(B)

    if Set(row_A) != Set(row_B)
        return false
    end

    col_A = col_keys(A)
    col_B = col_keys(B)

    if Set(col_A) != Set(col_B)
        return false
    end

    for i in row_A
        for j in col_A
            if A[i,j] != B[i,j]
                return false
            end
        end
    end
    return true
end

function Matrix(A::FlexMatrix)
    rows = collect(row_keys(A))
    cols = collect(col_keys(A))
    try
        sort!(rows)
    catch
    end
    try
        sort!(cols)
    catch
    end
    r = length(rows)
    c = length(cols)

    R = Array{valtype(A),2}(undef,r,c)
    for i=1:r
        for j=1:c
            R[i,j] = A[rows[i],cols[j]]
        end
    end
    return R
end

function show(io::IO, A::FlexMatrix{R,C,T}) where {R,C,T}
    rows = row_keys(A)
    cols = col_keys(A)

    println(io,"FlexMatrix{($R,$C),$T}:")
    for r in rows
        for c in cols
            println("  $((r,c)) ==> $(A[r,c])")
        end
    end
    nothing
end

## Arithmetic

function _mush(A::FlexMatrix,B::FlexMatrix)
    rows = union(Set(row_keys(A)), Set(row_keys(B)))
    cols = union(Set(col_keys(A)), Set(col_keys(B)))
    TR = eltype(rows)
    TC = eltype(cols)

    TA = valtype(A)
    TB = valtype(B)
    TX = typeof(one(TA)+one(TB))

    M = FlexMatrix{TX}(rows,cols)
    return M
end

function (+)(A::FlexMatrix,B::FlexMatrix)
    M = _mush(A,B)
    for k in keys(M)
        M[k...] = A[k...]+B[k...]
    end
    return M
end

function (-)(A::FlexMatrix,B::FlexMatrix)
    M = _mush(A,B)
    for k in keys(M)
        M[k...] = A[k...]-B[k...]
    end
    return M
end

function (*)(s::Number, A::FlexMatrix)
    if length(A.data)==0
        return A
    end
    x = s*first(A.data)[2]
    rows = row_keys(A)
    cols = col_keys(A)
    sA = FlexMatrix{typeof(x)}(rows,cols)

    for ij = keys(A)
        sA[ij...] = s * A[ij...]
    end
    return sA
end

function (*)(A::FlexMatrix, B::FlexMatrix)
    rowsA = row_keys(A)
    colsB = col_keys(B)

    TA = valtype(A)
    TB = valtype(B)
    TX = typeof(one(TA)+one(TB))

    M = FlexMatrix{TX}(rowsA,colsB)

    common = union( Set(col_keys(A)), Set(row_keys(B)) )

    for i in rowsA
        for j in colsB
            M[i,j] = sum( A[i,k]*B[k,j] for k in common )
        end
    end

    return M
end

(-)(A::FlexMatrix) = -1 * A

function (*)(A::FlexMatrix, v::FlexVector)
    klist = row_keys(A)
    TA = valtype(A)
    Tv = valtype(v)
    Tw = typeof(one(TA)+one(Tv))
    w = FlexVector{Tw}(klist)

    sum_keys = union( Set(col_keys(A)), Set(keys(v)) )

    for k in klist
        w[k] = sum( A[k,j]*v[j] for j in sum_keys )
    end
    return w
end

"""
`delete_row!(A,r)` deletes row `r` from the `FlexMatrix` `A`.
"""
function delete_row!(A::FlexMatrix, r)
    for k in keys(A.data)
        if k[1] == r
            delete!(A.data,k)
        end
    end
end

"""
`delete_col!(A,c)` deletes row `c` from the `FlexMatrix` `A`.
"""
function delete_col!(A::FlexMatrix, c)
    for k in keys(A.data)
        if k[2] == c
            delete!(A.data,k)
        end
    end
end

function LinearAlgebra.adjoint(A::FlexMatrix)
    R = row_keys(A)
    C = col_keys(A)
    T = valtype(A)
    B = FlexMatrix{T}(C,R)
    for k in keys(A)
        i,j = k
        B[j,i] = A[i,j]'
    end
    return B
end


text:
#####
##### Interface to Bo Haglund's Double Dummy Solver
##### https://github.com/dds-bridge/dds
#####

module Solver

import ..Game, ..history, ..WHITE
import AlphaZero: GI, GameInterface, Benchmark, AbstractPlayer, think
include("dds.jl")

const leader_dict = Dict([(0, "N"), (1, "E"), (2, "S"), (3, "W")])
const rank_dict = Dict([(1, "2"), (2, "3"), (3, "4"), (4, "5"), (5, "6"), (6, "7"), (7, "8"), (8, "9"), (9, "T"), (10, "J"), (11, "Q"), (12, "K"), (13, "A")])

struct Player <: AbstractPlayer{Game} end

# Solver protocol
# - Standard input: current hand in desired dds Deal format
# - Standard output: FutureTricks data structure containing optimal actions

function hand_to_deal(p::Player, g)
  # Extract the board from the game
  board = GI.current_state(g).board

  # Calculate the trump suit
  if isnothing(findfirst(iszero, board[:,:,5]))
    trump = 5
  else
    trump = findfirst(x -> x == 1, board[:,:,5])[2]
  end
  trump -= 1

  # Calculate which player is up next
  first = maximum(g.board[:,:,6])

  # Calculate the cards that have been played in the current trick
  ct_ranks = zeros(Cint, 3)
  ct_suits = zeros(Cint, 3)
  if first > 0
    for i in 1:first
      card = findfirst(x -> x == i, board[:,:,6])
      ct_ranks[i] = card[1] + 1
      ct_suits[i] = card[2] - 1
    end
  end

  # Create the PBN representation of the remaining hands
  pbn = string("$(get(leader_dict, first, "")):$(join([join([join(reverse([get(rank_dict, index[1], "") for index in findall(x -> x == 0x01, board[:,i,j])])) for i in 1:4], ".") for j in 1:4], " "))")
  remainCards = zeros(Cchar, 80)
  for (i, c) in enumerate(pbn)
    remainCards[i] = c
  end

  # Return the dds Deal data structure
  return dealPBN(trump, 0, ct_suits, ct_ranks, remainCards)
end

function query_solver(p::Player, g)
  deal = hand_to_deal(p, g)
  result, fut = SolveBoardPBN(deal)
  if result != 1
    throw("Solver Query Failed: $result")
  end
  optimal_actions = [(fut.suit[i] * 13) + (fut.rank[i] - 2) + 1 for i in 1:fut.cards]
  return optimal_actions
end

function think(p::Player, g)
  as = GI.available_actions(g)
  opt_moves = query_solver(p, g)
  opt_mask = findall(x -> x in opt_moves, as)
  π = zeros(length(as))
  π[opt_mask] .= 1 / length(opt_mask)
  return as, π
end

Benchmark.PerfectPlayer(::Type{Game}) = Player

end

max_stars_repo_name: btheodorou/Bridge-NN-Research

max_issues_repo_name: btheodorou/Bridge-NN-Research

max_forks_repo_name: btheodorou/Bridge-NN-Research


text:
const CustomShowArrays = Union{MaskedArray, PropertyArray, AbstractNode, TreeView, AbstractTreeArray, ContinuousView}

struct CDot end
Base.show(io::IO, x::CDot) = print(io, "⋅")

struct ShowWrapper{T, N, A <: AbstractArray{T, N}} <: AbstractArray{T, N}
    parent::A
end
Base.size(x::ShowWrapper) = size(x.parent)
Base.axes(x::ShowWrapper) = axes(x.parent)
@inline function Base.getindex(x::ShowWrapper, i::Int...)
    @_propagate_inbounds_meta
    p = x.parent
    isactive(p, i...) ? maybewrap(p[i...]) : CDot()
end
maybewrap(x) = x
maybewrap(x::CustomShowArrays) = ShowWrapper(x)

Base.summary(io::IO, x::ShowWrapper) = summary(io, x.parent)
Base.show(io::IO, mime::MIME"text/plain", x::CustomShowArrays) = show(io, mime, ShowWrapper(x))
Base.show(io::IO, x::CustomShowArrays) = show(io, ShowWrapper(x))