/* Copyright (c) 2026 Nenad Mićić <nenad@micic.be>
 * SPDX-License-Identifier: Apache-2.0 */
/*
 * ktuplet_pattern.c — pattern table and helper implementations.
 * Generated: 2026-05-10
 *
 * Catalog source of truth: tools/patterns/catalog/k{NN}_d{DDD}.json
 * Catalog generator:       tools/patterns/pattern_enum (C/OpenMP)
 * Header generator:        tools/gen_pattern_header.py
 *
 * Authoritative reference for prime k-tuplet patterns and Hardy-Littlewood
 * constants:  https://pzktupel.de/ktpatt_hl.php   (Norman Luhn)
 *
 * The tools in this repo (pattern_enum, gen_pattern_header.py) are a
 * convenience: they emit the same admissible patterns in machine-readable
 * JSON for engine consumption. They are NOT peer-reviewed; if there is any
 * discrepancy between this generated catalog and pzktupel.de, the canonical
 * pzktupel.de listing is correct and our tools have a bug. Cross-check
 * before publishing or relying on any pattern.
 *
 * Do not edit by hand. Run: python3 tools/gen_pattern_header.py
 */

#include "ktuplet_pattern.h"
#include <string.h>

const KTupletPattern KT_PATTERNS[] = {
    /* KT3_P0     from k03_d006.json */ { 3, 6, {0, 2, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT3_P0" },
    /* KT3_P1     from k03_d006.json */ { 3, 6, {0, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT3_P1" },
    /* KT4_P0     from k04_d008.json */ { 4, 8, {0, 2, 6, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT4_P0" },
    /* KT5_P0     from k05_d012.json */ { 5, 12, {0, 2, 6, 8, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT5_P0" },
    /* KT5_P1     from k05_d012.json */ { 5, 12, {0, 4, 6, 10, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT5_P1" },
    /* KT6_P0     from k06_d016.json */ { 6, 16, {0, 4, 6, 10, 12, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT6_P0" },
    /* KT7_P0     from k07_d020.json */ { 7, 20, {0, 2, 6, 8, 12, 18, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT7_P0" },
    /* KT7_P1     from k07_d020.json */ { 7, 20, {0, 2, 8, 12, 14, 18, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT7_P1" },
    /* KT8_P0     from k08_d026.json */ { 8, 26, {0, 2, 6, 8, 12, 18, 20, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT8_P0" },
    /* KT8_P1     from k08_d026.json */ { 8, 26, {0, 2, 6, 12, 14, 20, 24, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT8_P1" },
    /* KT8_P2     from k08_d026.json */ { 8, 26, {0, 6, 8, 14, 18, 20, 24, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT8_P2" },
    /* KT9_P0     from k09_d030.json */ { 9, 30, {0, 2, 6, 8, 12, 18, 20, 26, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT9_P0" },
    /* KT9_P1     from k09_d030.json */ { 9, 30, {0, 2, 6, 12, 14, 20, 24, 26, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT9_P1" },
    /* KT9_P2     from k09_d030.json */ { 9, 30, {0, 4, 6, 10, 16, 18, 24, 28, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT9_P2" },
    /* KT9_P3     from k09_d030.json */ { 9, 30, {0, 4, 10, 12, 18, 22, 24, 28, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT9_P3" },
    /* KT10_P0    from k10_d032.json */ { 10, 32, {0, 2, 6, 8, 12, 18, 20, 26, 30, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT10_P0" },
    /* KT10_P1    from k10_d032.json */ { 10, 32, {0, 2, 6, 12, 14, 20, 24, 26, 30, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT10_P1" },
    /* KT11_P0    from k11_d036.json */ { 11, 36, {0, 2, 6, 8, 12, 18, 20, 26, 30, 32, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT11_P0" },
    /* KT11_P1    from k11_d036.json */ { 11, 36, {0, 4, 6, 10, 16, 18, 24, 28, 30, 34, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT11_P1" },
    /* KT12_P0    from k12_d042.json */ { 12, 42, {0, 2, 6, 8, 12, 18, 20, 26, 30, 32, 36, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT12_P0" },
    /* KT12_P1    from k12_d042.json */ { 12, 42, {0, 6, 10, 12, 16, 22, 24, 30, 34, 36, 40, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT12_P1" },
    /* KT13_P0    from k13_d048.json */ { 13, 48, {0, 2, 6, 8, 12, 18, 20, 26, 30, 32, 36, 42, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT13_P0" },
    /* KT13_P1    from k13_d048.json */ { 13, 48, {0, 2, 8, 14, 18, 20, 24, 30, 32, 38, 42, 44, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT13_P1" },
    /* KT13_P2    from k13_d048.json */ { 13, 48, {0, 2, 12, 14, 18, 20, 24, 30, 32, 38, 42, 44, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT13_P2" },
    /* KT13_P3    from k13_d048.json */ { 13, 48, {0, 4, 6, 10, 16, 18, 24, 28, 30, 34, 36, 46, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT13_P3" },
    /* KT13_P4    from k13_d048.json */ { 13, 48, {0, 4, 6, 10, 16, 18, 24, 28, 30, 34, 40, 46, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT13_P4" },
    /* KT13_P5    from k13_d048.json */ { 13, 48, {0, 6, 12, 16, 18, 22, 28, 30, 36, 40, 42, 46, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT13_P5" },
    /* KT14_P0    from k14_d050.json */ { 14, 50, {0, 2, 6, 8, 12, 18, 20, 26, 30, 32, 36, 42, 48, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT14_P0" },
    /* KT14_P1    from k14_d050.json */ { 14, 50, {0, 2, 8, 14, 18, 20, 24, 30, 32, 38, 42, 44, 48, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT14_P1" },
    /* KT15_P0    from k15_d056.json */ { 15, 56, {0, 2, 6, 8, 12, 18, 20, 26, 30, 32, 36, 42, 48, 50, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT15_P0" },
    /* KT15_P1    from k15_d056.json */ { 15, 56, {0, 2, 6, 12, 14, 20, 24, 26, 30, 36, 42, 44, 50, 54, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT15_P1" },
    /* KT15_P2    from k15_d056.json */ { 15, 56, {0, 2, 6, 12, 14, 20, 26, 30, 32, 36, 42, 44, 50, 54, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT15_P2" },
    /* KT15_P3    from k15_d056.json */ { 15, 56, {0, 6, 8, 14, 20, 24, 26, 30, 36, 38, 44, 48, 50, 54, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT15_P3" },
    /* KT16_P0    from k16_d060.json */ { 16, 60, {0, 2, 6, 12, 14, 20, 26, 30, 32, 36, 42, 44, 50, 54, 56, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT16_P0" },
    /* KT16_P1    from k16_d060.json */ { 16, 60, {0, 4, 6, 10, 16, 18, 24, 28, 30, 34, 40, 46, 48, 54, 58, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT16_P1" },
    /* KT17_P0    from k17_d066.json */ { 17, 66, {0, 2, 6, 12, 14, 20, 24, 26, 30, 36, 42, 44, 50, 54, 56, 62, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT17_P0" },
    /* KT17_P1    from k17_d066.json */ { 17, 66, {0, 4, 6, 10, 16, 18, 24, 28, 30, 34, 40, 46, 48, 54, 58, 60, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT17_P1" },
    /* KT17_P2    from k17_d066.json */ { 17, 66, {0, 4, 10, 12, 16, 22, 24, 30, 36, 40, 42, 46, 52, 54, 60, 64, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT17_P2" },
    /* KT17_P3    from k17_d066.json */ { 17, 66, {0, 6, 8, 12, 18, 20, 26, 32, 36, 38, 42, 48, 50, 56, 60, 62, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT17_P3" },
    /* KT18_P0    from k18_d070.json */ { 18, 70, {0, 4, 6, 10, 16, 18, 24, 28, 30, 34, 40, 46, 48, 54, 58, 60, 66, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT18_P0" },
    /* KT18_P1    from k18_d070.json */ { 18, 70, {0, 4, 10, 12, 16, 22, 24, 30, 36, 40, 42, 46, 52, 54, 60, 64, 66, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT18_P1" },
    /* KT19_P0    from k19_d076.json */ { 19, 76, {0, 4, 6, 10, 12, 16, 24, 30, 34, 40, 42, 46, 52, 54, 60, 66, 70, 72, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT19_P0" },
    /* KT19_P1    from k19_d076.json */ { 19, 76, {0, 4, 6, 10, 16, 18, 24, 28, 30, 34, 40, 46, 48, 54, 58, 60, 66, 70, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT19_P1" },
    /* KT19_P2    from k19_d076.json */ { 19, 76, {0, 4, 6, 10, 16, 22, 24, 30, 34, 36, 42, 46, 52, 60, 64, 66, 70, 72, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT19_P2" },
    /* KT19_P3    from k19_d076.json */ { 19, 76, {0, 6, 10, 16, 18, 22, 28, 30, 36, 42, 46, 48, 52, 58, 60, 66, 70, 72, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "KT19_P3" },
    /* KT20_P0    from k20_d080.json */ { 20, 80, {0, 2, 6, 8, 12, 20, 26, 30, 36, 38, 42, 48, 50, 56, 62, 66, 68, 72, 78, 80, 0, 0, 0, 0, 0, 0, 0, 0}, "KT20_P0" },
    /* KT20_P1    from k20_d080.json */ { 20, 80, {0, 2, 8, 12, 14, 18, 24, 30, 32, 38, 42, 44, 50, 54, 60, 68, 72, 74, 78, 80, 0, 0, 0, 0, 0, 0, 0, 0}, "KT20_P1" },
    /* KT21_P0    from k21_d084.json */ { 21, 84, {0, 2, 8, 12, 14, 18, 24, 30, 32, 38, 42, 44, 50, 54, 60, 68, 72, 74, 78, 80, 84, 0, 0, 0, 0, 0, 0, 0}, "KT21_P0" },
    /* KT21_P1    from k21_d084.json */ { 21, 84, {0, 4, 6, 10, 12, 16, 24, 30, 34, 40, 42, 46, 52, 54, 60, 66, 70, 72, 76, 82, 84, 0, 0, 0, 0, 0, 0, 0}, "KT21_P1" },
    /* KT22_P0    from k22_d090.json */ { 22, 90, {0, 2, 6, 8, 12, 20, 26, 30, 36, 38, 42, 48, 50, 56, 62, 66, 68, 72, 78, 80, 86, 90, 0, 0, 0, 0, 0, 0}, "KT22_P0" },
    /* KT22_P1    from k22_d090.json */ { 22, 90, {0, 4, 6, 10, 12, 16, 24, 30, 34, 40, 42, 46, 52, 54, 60, 66, 70, 72, 76, 82, 84, 90, 0, 0, 0, 0, 0, 0}, "KT22_P1" },
    /* KT22_P2    from k22_d090.json */ { 22, 90, {0, 4, 10, 12, 18, 22, 24, 28, 34, 40, 42, 48, 52, 54, 60, 64, 70, 78, 82, 84, 88, 90, 0, 0, 0, 0, 0, 0}, "KT22_P2" },
    /* KT22_P3    from k22_d090.json */ { 22, 90, {0, 6, 8, 14, 18, 20, 24, 30, 36, 38, 44, 48, 50, 56, 60, 66, 74, 78, 80, 84, 86, 90, 0, 0, 0, 0, 0, 0}, "KT22_P3" },
    /* KT23_P0    from k23_d094.json */ { 23, 94, {0, 4, 6, 10, 12, 16, 24, 30, 34, 40, 42, 46, 52, 54, 60, 66, 70, 72, 76, 82, 84, 90, 94, 0, 0, 0, 0, 0}, "KT23_P0" },
    /* KT23_P1    from k23_d094.json */ { 23, 94, {0, 4, 10, 12, 18, 22, 24, 28, 34, 40, 42, 48, 52, 54, 60, 64, 70, 78, 82, 84, 88, 90, 94, 0, 0, 0, 0, 0}, "KT23_P1" },
    /* KT24_P0    from k24_d100.json */ { 24, 100, {0, 4, 6, 10, 12, 16, 24, 30, 34, 40, 42, 46, 52, 60, 66, 70, 72, 76, 82, 84, 90, 94, 96, 100, 0, 0, 0, 0}, "KT24_P0" },
    /* KT24_P1    from k24_d100.json */ { 24, 100, {0, 4, 6, 10, 16, 18, 24, 28, 30, 34, 40, 48, 54, 58, 60, 66, 70, 76, 84, 88, 90, 94, 96, 100, 0, 0, 0, 0}, "KT24_P1" },
    /* KT24_P2    from k24_d100.json */ { 24, 100, {0, 4, 6, 12, 16, 24, 30, 34, 40, 42, 46, 52, 54, 60, 66, 70, 72, 76, 82, 84, 90, 94, 96, 100, 0, 0, 0, 0}, "KT24_P2" },
    /* KT24_P3    from k24_d100.json */ { 24, 100, {0, 4, 6, 10, 16, 18, 24, 28, 30, 34, 40, 46, 48, 54, 58, 60, 66, 70, 76, 84, 88, 94, 96, 100, 0, 0, 0, 0}, "KT24_P3" },
    /* KT25_P0    from k25_d110.json */ { 25, 110, {0, 2, 6, 8, 12, 18, 20, 26, 30, 32, 36, 42, 48, 50, 56, 62, 68, 72, 78, 86, 90, 96, 98, 102, 110, 0, 0, 0}, "KT25_P0" },
    /* KT25_P1    from k25_d110.json */ { 25, 110, {0, 2, 6, 8, 12, 18, 20, 26, 30, 32, 36, 42, 50, 56, 62, 68, 72, 78, 86, 90, 92, 96, 98, 102, 110, 0, 0, 0}, "KT25_P1" },
    /* KT25_P2    from k25_d110.json */ { 25, 110, {0, 2, 6, 8, 12, 20, 26, 30, 36, 38, 42, 48, 50, 56, 66, 68, 72, 78, 80, 86, 90, 92, 98, 108, 110, 0, 0, 0}, "KT25_P2" },
    /* KT25_P3    from k25_d110.json */ { 25, 110, {0, 2, 6, 8, 12, 20, 26, 30, 36, 38, 42, 48, 56, 62, 66, 68, 72, 78, 80, 86, 90, 92, 96, 108, 110, 0, 0, 0}, "KT25_P3" },
    /* KT25_P4    from k25_d110.json */ { 25, 110, {0, 2, 6, 8, 12, 20, 26, 30, 36, 38, 42, 48, 56, 66, 68, 72, 78, 80, 86, 90, 92, 96, 98, 108, 110, 0, 0, 0}, "KT25_P4" },
    /* KT25_P5    from k25_d110.json */ { 25, 110, {0, 2, 6, 8, 12, 20, 26, 30, 36, 38, 42, 50, 56, 62, 66, 68, 72, 78, 80, 86, 90, 92, 96, 108, 110, 0, 0, 0}, "KT25_P5" },
    /* KT25_P6    from k25_d110.json */ { 25, 110, {0, 2, 6, 8, 12, 20, 26, 30, 36, 38, 42, 50, 56, 66, 68, 72, 78, 80, 86, 90, 92, 96, 98, 108, 110, 0, 0, 0}, "KT25_P6" },
    /* KT25_P7    from k25_d110.json */ { 25, 110, {0, 2, 6, 12, 14, 20, 24, 26, 30, 32, 42, 44, 54, 56, 60, 66, 72, 74, 80, 86, 90, 96, 102, 104, 110, 0, 0, 0}, "KT25_P7" },
    /* KT25_P8    from k25_d110.json */ { 25, 110, {0, 2, 8, 12, 14, 18, 24, 30, 32, 38, 42, 44, 50, 54, 60, 68, 72, 74, 78, 80, 84, 98, 102, 108, 110, 0, 0, 0}, "KT25_P8" },
    /* KT25_P9    from k25_d110.json */ { 25, 110, {0, 2, 8, 12, 26, 30, 32, 36, 38, 42, 50, 56, 60, 66, 68, 72, 78, 80, 86, 92, 96, 98, 102, 108, 110, 0, 0, 0}, "KT25_P9" },
    /* KT25_P10   from k25_d110.json */ { 25, 110, {0, 2, 12, 14, 18, 20, 24, 30, 32, 38, 42, 44, 54, 60, 68, 72, 74, 80, 84, 90, 98, 102, 104, 108, 110, 0, 0, 0}, "KT25_P10" },
    /* KT25_P11   from k25_d110.json */ { 25, 110, {0, 2, 12, 14, 18, 20, 24, 30, 32, 38, 42, 44, 54, 62, 68, 72, 74, 80, 84, 90, 98, 102, 104, 108, 110, 0, 0, 0}, "KT25_P11" },
    /* KT25_P12   from k25_d110.json */ { 25, 110, {0, 2, 12, 18, 20, 24, 30, 32, 38, 42, 44, 54, 60, 62, 68, 72, 74, 80, 84, 90, 98, 102, 104, 108, 110, 0, 0, 0}, "KT25_P12" },
    /* KT25_P13   from k25_d110.json */ { 25, 110, {0, 2, 14, 18, 20, 24, 30, 32, 38, 42, 44, 48, 54, 60, 68, 72, 74, 80, 84, 90, 98, 102, 104, 108, 110, 0, 0, 0}, "KT25_P13" },
    /* KT25_P14   from k25_d110.json */ { 25, 110, {0, 2, 14, 18, 20, 24, 30, 32, 38, 42, 44, 48, 54, 62, 68, 72, 74, 80, 84, 90, 98, 102, 104, 108, 110, 0, 0, 0}, "KT25_P14" },
    /* KT25_P15   from k25_d110.json */ { 25, 110, {0, 6, 8, 14, 20, 24, 30, 36, 38, 44, 50, 54, 56, 66, 68, 78, 80, 84, 86, 90, 96, 98, 104, 108, 110, 0, 0, 0}, "KT25_P15" },
    /* KT25_P16   from k25_d110.json */ { 25, 110, {0, 8, 12, 14, 18, 20, 24, 32, 38, 42, 48, 54, 60, 68, 74, 78, 80, 84, 90, 92, 98, 102, 104, 108, 110, 0, 0, 0}, "KT25_P16" },
    /* KT25_P17   from k25_d110.json */ { 25, 110, {0, 8, 12, 14, 20, 24, 32, 38, 42, 48, 54, 60, 62, 68, 74, 78, 80, 84, 90, 92, 98, 102, 104, 108, 110, 0, 0, 0}, "KT25_P17" },
    /* KT26_P0    from k26_d114.json */ { 26, 114, {0, 2, 14, 18, 20, 24, 30, 32, 38, 42, 44, 48, 54, 62, 68, 72, 74, 80, 84, 90, 98, 102, 104, 108, 110, 114, 0, 0}, "KT26_P0" },
    /* KT26_P1    from k26_d114.json */ { 26, 114, {0, 4, 6, 10, 12, 16, 24, 30, 34, 40, 42, 46, 52, 60, 66, 70, 72, 76, 82, 84, 90, 94, 96, 100, 112, 114, 0, 0}, "KT26_P1" },
    /* KT27_P0    from k27_d120.json */ { 27, 120, {0, 2, 6, 8, 12, 18, 20, 26, 30, 32, 36, 42, 48, 50, 56, 62, 68, 72, 78, 86, 90, 96, 98, 102, 110, 116, 120, 0}, "KT27_P0" },
    /* KT27_P1    from k27_d120.json */ { 27, 120, {0, 2, 6, 12, 14, 20, 26, 32, 36, 42, 44, 50, 54, 56, 60, 62, 72, 84, 86, 90, 92, 102, 104, 110, 114, 116, 120, 0}, "KT27_P1" },
    /* KT27_P2    from k27_d120.json */ { 27, 120, {0, 2, 6, 14, 20, 26, 32, 36, 42, 44, 50, 54, 60, 62, 72, 74, 84, 86, 90, 92, 96, 102, 104, 110, 114, 116, 120, 0}, "KT27_P2" },
    /* KT27_P3    from k27_d120.json */ { 27, 120, {0, 2, 8, 12, 26, 30, 32, 36, 38, 42, 50, 56, 60, 66, 68, 72, 78, 80, 86, 92, 96, 98, 102, 108, 110, 116, 120, 0}, "KT27_P3" },
    /* KT27_P4    from k27_d120.json */ { 27, 120, {0, 4, 6, 10, 16, 18, 24, 28, 30, 34, 36, 46, 48, 58, 60, 66, 70, 76, 78, 84, 88, 94, 100, 106, 114, 118, 120, 0}, "KT27_P4" },
    /* KT27_P5    from k27_d120.json */ { 27, 120, {0, 4, 6, 10, 16, 18, 28, 30, 34, 36, 48, 58, 60, 64, 66, 70, 76, 78, 84, 88, 94, 100, 106, 108, 114, 118, 120, 0}, "KT27_P5" },
    /* KT27_P6    from k27_d120.json */ { 27, 120, {0, 4, 10, 12, 18, 22, 24, 28, 34, 40, 42, 48, 52, 54, 60, 64, 70, 78, 82, 84, 88, 90, 94, 108, 112, 118, 120, 0}, "KT27_P6" },
    /* KT27_P7    from k27_d120.json */ { 27, 120, {0, 4, 10, 18, 22, 24, 30, 34, 42, 48, 52, 58, 64, 70, 72, 78, 84, 88, 90, 94, 100, 102, 108, 112, 114, 118, 120, 0}, "KT27_P7" },
    /* KT28_P0    from k28_d126.json */ { 28, 126, {0, 2, 6, 8, 12, 20, 26, 30, 36, 38, 42, 48, 56, 66, 68, 72, 78, 80, 86, 90, 92, 96, 98, 108, 110, 120, 122, 126}, "KT28_P0" },
    /* KT28_P1    from k28_d126.json */ { 28, 126, {0, 2, 6, 12, 14, 24, 26, 30, 32, 44, 54, 56, 60, 62, 66, 72, 74, 80, 84, 90, 96, 102, 104, 110, 114, 116, 122, 126}, "KT28_P1" },
    /* KT28_P2    from k28_d126.json */ { 28, 126, {0, 2, 6, 14, 20, 26, 32, 36, 42, 44, 50, 54, 60, 62, 72, 74, 84, 86, 90, 92, 96, 102, 104, 110, 114, 116, 120, 126}, "KT28_P2" },
    /* KT28_P3    from k28_d126.json */ { 28, 126, {0, 4, 6, 10, 16, 18, 24, 28, 30, 34, 36, 46, 48, 58, 60, 66, 70, 76, 78, 84, 88, 94, 100, 106, 114, 118, 120, 126}, "KT28_P3" },
    /* KT28_P4    from k28_d126.json */ { 28, 126, {0, 4, 6, 10, 16, 18, 28, 30, 34, 36, 48, 58, 60, 64, 66, 70, 76, 78, 84, 88, 94, 100, 106, 108, 114, 118, 120, 126}, "KT28_P4" },
    /* KT28_P5    from k28_d126.json */ { 28, 126, {0, 4, 6, 16, 18, 28, 30, 34, 36, 40, 46, 48, 54, 58, 60, 70, 78, 84, 88, 90, 96, 100, 106, 114, 118, 120, 124, 126}, "KT28_P5" },
    /* KT28_P6    from k28_d126.json */ { 28, 126, {0, 4, 10, 12, 16, 22, 24, 30, 36, 42, 46, 52, 54, 60, 64, 66, 70, 72, 82, 94, 96, 100, 102, 112, 114, 120, 124, 126}, "KT28_P6" },
    /* KT28_P7    from k28_d126.json */ { 28, 126, {0, 6, 8, 12, 18, 20, 26, 32, 38, 42, 48, 50, 56, 60, 62, 66, 68, 78, 90, 92, 96, 98, 108, 110, 116, 120, 122, 126}, "KT28_P7" },
    /* KT28_P8    from k28_d126.json */ { 28, 126, {0, 6, 8, 12, 20, 26, 32, 38, 42, 48, 50, 56, 60, 66, 68, 78, 80, 90, 92, 96, 98, 102, 108, 110, 116, 120, 122, 126}, "KT28_P8" },
    /* KT28_P9    from k28_d126.json */ { 28, 126, {0, 6, 10, 12, 16, 22, 24, 30, 34, 36, 40, 42, 52, 54, 64, 66, 72, 76, 82, 84, 90, 94, 100, 106, 112, 120, 124, 126}, "KT28_P9" },
};

const int KT_PATTERNS_COUNT = 97;

/* Forbidden residues for pattern pat at prime q.
 * Formula: ((q - (b_i % q)) % q) for each offset b_i, deduplicated. */
int kt_pattern_forbidden_residues(const KTupletPattern* pat, uint32_t q, uint32_t* out) {
    int count = 0;
    for (int i = 0; i < pat->k; i++) {
        uint32_t r = (q - ((uint32_t)pat->offsets[i] % q)) % q;
        int dup = 0;
        for (int j = 0; j < count; j++) { if (out[j] == r) { dup = 1; break; } }
        if (!dup) out[count++] = r;
    }
    return count;
}

int kt_pattern_is_admissible(const KTupletPattern* pat, int q_max, int* bad_q) {
    uint32_t buf[KT_MAX_K];
    for (int q = 2; q <= q_max; q++) {
        int is_prime = 1;
        for (int d = 2; d * d <= q; d++) { if (q % d == 0) { is_prime = 0; break; } }
        if (!is_prime) continue;
        int cnt = kt_pattern_forbidden_residues(pat, (uint32_t)q, buf);
        if (cnt >= q) { if (bad_q) *bad_q = q; return 0; }
    }
    return 1;
}

const KTupletPattern* kt_pattern_by_name(const char* name) {
    for (int i = 0; i < KT_PATTERNS_COUNT; i++)
        if (strcmp(KT_PATTERNS[i].name, name) == 0) return &KT_PATTERNS[i];
    return NULL;
}

const KTupletPattern* kt_pattern_match(int k, const int* offsets) {
    for (int i = 0; i < KT_PATTERNS_COUNT; i++) {
        if (KT_PATTERNS[i].k != k) continue;
        int match = 1;
        for (int j = 0; j < k; j++)
            if (KT_PATTERNS[i].offsets[j] != offsets[j]) { match = 0; break; }
        if (match) return &KT_PATTERNS[i];
    }
    return NULL;
}
