11#define PY_SSIZE_T_CLEAN // Ensure Py_ssize_t is defined before including Python.h
22#include <Python.h> // Python C API header
3- #include <stdint.h> // for fixed-width integer types like uint32_t, uint16_t
3+ #include <stdint.h> // for fixed-width integer types like int32_t, int16_t
44
55/* Decision type constants - must match constants.py */
66#define DECISION_EMPTY 0
1010#define DECISION_DEFECT 4
1111#define DECISION_PURE 5
1212
13+ /* Defect array access macros (each defect entry has NR_DEFECT_FIELDS fields) */
14+ #define NR_DEFECT_FIELDS 6
15+ #define DEF_X (d ) defects[(d)*NR_DEFECT_FIELDS + 0] /* x start of defect bounding box */
16+ #define DEF_Y (d ) defects[(d)*NR_DEFECT_FIELDS + 1] /* y start of defect bounding box */
17+ #define DEF_W (d ) defects[(d)*NR_DEFECT_FIELDS + 2] /* width of defect bounding box */
18+ #define DEF_H (d ) defects[(d)*NR_DEFECT_FIELDS + 3] /* height of defect bounding box */
19+ #define DEF_X_END (d ) defects[(d)*NR_DEFECT_FIELDS + 4] /* x end of defect bounding box (exclusive) */
20+ #define DEF_Y_END (d ) defects[(d)*NR_DEFECT_FIELDS + 5] /* y end of defect bounding box (exclusive) */
21+
1322/*
14- * Bit layout for Fd_packed (uint16_t ):
23+ * Bit layout for Fd_packed (int16_t ):
1524 * bits 15-13 : decision type (DECISION_* constants, values 0-5, fits in 3 bits)
1625 * bits 12-0 : cut parameter (cut position z, max value 8191, covers any practical sheet)
1726 *
18- * Pack: packed = ((uint16_t )type << 13) | ((uint16_t )param & 0x1FFF)
27+ * Pack: packed = ((int16_t )type << 13) | ((int16_t )param & 0x1FFF)
1928 * Unpack: type = (packed >> 13) & 0x7
2029 * param = packed & 0x1FFF
2130 */
22- #define PACK_FD (type , param ) (((uint16_t )(type) << 13) | ((uint16_t )(param) & 0x1FFF))
31+ #define PACK_FD (type , param ) (((int16_t )(type) << 13) | ((int16_t )(param) & 0x1FFF))
2332#define UNPACK_TYPE (packed ) (((packed) >> 13) & 0x7)
2433#define UNPACK_PARAM (packed ) ((packed) & 0x1FFF)
2534
2635/*
2736 * Array index macros.
2837 * prefix, F_values are 2D: shape (W0+1, H0+1)
29- * Fd_values is 4D uint32_t : shape (W0+1, H0+1, W0+1, H0+1)
30- * Fd_packed is 4D uint16_t : shape (W0+1, H0+1, W0+1, H0+1), same layout
38+ * Fd_values is 4D int32_t : shape (W0+1, H0+1, W0+1, H0+1)
39+ * Fd_packed is 4D int16_t : shape (W0+1, H0+1, W0+1, H0+1), same layout
3140 */
3241#define IDX_2D (arr , stride1 , i , j ) ((arr)[(i) * (stride1) + (j)])
3342#define IDX_4D (arr , stride0 , stride1 , stride2 , w , h , x , y ) ((arr)[(w) * (stride0) + (h) * (stride1) + (x) * (stride2) + (y)])
@@ -96,20 +105,26 @@ static void fill_F_core(
96105 int stride = H0 + 1 ;
97106
98107 for (int w = 1 ; w <= W0 ; w ++ ) {
99- int nx = np_x_len [w ];
108+ int nx = np_x_len [w ];
109+ int w_off = w * stride ;
110+ int x_cut_base = w * max_cuts_x ;
111+ int half_w = w >> 1 ;
112+
100113 for (int h = 1 ; h <= H0 ; h ++ ) {
101- int ny = np_y_len [h ];
114+ int ny = np_y_len [h ];
115+ int y_cut_base = h * max_cuts_y ;
116+ int half_h = h >> 1 ;
102117
103- int32_t best_val = g_values [w * stride + h ];
104- int best_type = (g_indices [w * stride + h ] >= 0 ) ? DECISION_FILL : DECISION_EMPTY ;
105- int32_t best_param = (g_indices [w * stride + h ] >= 0 ) ? g_indices [w * stride + h ] : 0 ;
118+ int32_t g_idx = g_indices [w_off + h ];
119+ int32_t best_val = g_values [w_off + h ];
120+ int best_type = (g_idx >= 0 ) ? DECISION_FILL : DECISION_EMPTY ;
121+ int32_t best_param = (g_idx >= 0 ) ? g_idx : 0 ;
106122
107123 /* Vertical cuts — symmetry: only z <= w/2 */
108- int half_w = w >> 1 ;
109124 for (int i = 0 ; i < nx ; i ++ ) {
110- int z = np_x_arr [w * max_cuts_x + i ];
125+ int z = np_x_arr [x_cut_base + i ];
111126 if (z > half_w ) break ;
112- int32_t total = F_values [z * stride + h ] + F_values [( w - z ) * stride + h ];
127+ int32_t total = F_values [z * stride + h ] + F_values [w_off - z * stride + h ];
113128 if (total > best_val ) {
114129 best_val = total ;
115130 best_type = DECISION_CUT_X ;
@@ -118,21 +133,20 @@ static void fill_F_core(
118133 }
119134
120135 /* Horizontal cuts — symmetry: only z <= h/2 */
121- int half_h = h >> 1 ;
122136 for (int i = 0 ; i < ny ; i ++ ) {
123- int z = np_y_arr [h * max_cuts_y + i ];
137+ int z = np_y_arr [y_cut_base + i ];
124138 if (z > half_h ) break ;
125- int32_t total = F_values [w * stride + z ] + F_values [w * stride + (h - z )];
139+ int32_t total = F_values [w_off + z ] + F_values [w_off + (h - z )];
126140 if (total > best_val ) {
127141 best_val = total ;
128142 best_type = DECISION_CUT_Y ;
129143 best_param = z ;
130144 }
131145 }
132146
133- F_values [w * stride + h ] = best_val ;
134- F_type [w * stride + h ] = (int8_t )best_type ;
135- F_param [w * stride + h ] = best_param ;
147+ F_values [w_off + h ] = best_val ;
148+ F_type [w_off + h ] = (int8_t )best_type ;
149+ F_param [w_off + h ] = best_param ;
136150 }
137151 }
138152}
@@ -142,17 +156,17 @@ static void fill_F_core(
142156 * All arrays are passed as raw pointers — numpy guarantees contiguous memory.
143157 *
144158 * Memory layout vs previous version:
145- * Fd_values: int32 → uint32 (values are always non-negative, doubles max representable value )
146- * Fd_type + Fd_param → Fd_packed uint16 (saves 1 array, 33% less memory for Fd tables)
159+ * Fd_values: int32_t (values are always non-negative)
160+ * Fd_type + Fd_param → Fd_packed int16_t (saves 1 array, 33% less memory for Fd tables)
147161 */
148162static void fill_Fd_core (
149163 int W0 , int H0 ,
150- int32_t * prefix ,
151- int32_t * F_values ,
152- uint32_t * Fd_values ,
153- int32_t * np_x_arr , int32_t * np_x_len , int max_cuts_x ,
154- int32_t * np_y_arr , int32_t * np_y_len , int max_cuts_y ,
155- int32_t * defects , int n_def
164+ int32_t * prefix ,
165+ int32_t * F_values ,
166+ int32_t * Fd_values ,
167+ int32_t * np_x_arr , int32_t * np_x_len , int max_cuts_x ,
168+ int32_t * np_y_arr , int32_t * np_y_len , int max_cuts_y ,
169+ int32_t * defects , int n_def
156170) {
157171 int stride_p = H0 + 1 ;
158172 int stride_F = H0 + 1 ;
@@ -176,54 +190,58 @@ static void fill_Fd_core(
176190 + IDX_2D (prefix , stride_p , x , y );
177191
178192 if (defect_count == 0 ) {
179- IDX_4D (Fd_values , stride0 , stride1 , stride2 , w , h , x , y ) = ( uint32_t ) IDX_2D (F_values , stride_F , w , h );
193+ IDX_4D (Fd_values , stride0 , stride1 , stride2 , w , h , x , y ) = IDX_2D (F_values , stride_F , w , h );
180194 continue ;
181195 }
182196
183- uint32_t best_val = 0 ;
197+ int32_t best_val = 0 ;
184198
185- /* X cuts */
199+ /* X cuts — normal pattern positions */
186200 for (int i = 0 ; i < nx ; i ++ ) {
187201 int z = np_x_arr [w * max_cuts_x + i ];
188- uint32_t total = IDX_4D (Fd_values , stride0 , stride1 , stride2 , z , h , x , y ) +
189- IDX_4D (Fd_values , stride0 , stride1 , stride2 , w - z , h , x + z , y );
202+ int32_t total = IDX_4D (Fd_values , stride0 , stride1 , stride2 , z , h , x , y ) +
203+ IDX_4D (Fd_values , stride0 , stride1 , stride2 , w - z , h , x + z , y );
190204 if (total > best_val ) best_val = total ;
191205 }
192206
207+ /* X cuts — defect-aligned positions */
193208 for (int d = 0 ; d < n_def ; d ++ ) {
194- int dx = defects [d * 6 + 0 ], dx_end = defects [d * 6 + 4 ];
195- if (x >= dx_end || dx >= x + w ) continue ;
196- int z1 = dx - x ;
197- int z2 = dx_end - x ;
198- int cuts [2 ] = { z1 , z2 };
199- for (int ci = 0 ; ci < 2 ; ci ++ ) {
200- int z = cuts [ci ];
201- if (z <= 0 || z >= w ) continue ;
202- uint32_t total = IDX_4D (Fd_values , stride0 , stride1 , stride2 , z , h , x , y ) +
203- IDX_4D (Fd_values , stride0 , stride1 , stride2 , w - z , h , x + z , y );
209+ if (x >= DEF_X_END (d ) || DEF_X (d ) >= x + w ) continue ;
210+ int z1 = DEF_X (d ) - x ;
211+ int z2 = DEF_X_END (d ) - x ;
212+ if (z1 > 0 && z1 < h ) {
213+ int32_t total = IDX_4D (Fd_values , stride0 , stride1 , stride2 , w , z1 , x , y ) +
214+ IDX_4D (Fd_values , stride0 , stride1 , stride2 , w , h - z1 , x , y + z1 );
215+ if (total > best_val ) best_val = total ;
216+ }
217+ if (z2 > 0 && z2 < h ) {
218+ int32_t total = IDX_4D (Fd_values , stride0 , stride1 , stride2 , w , z2 , x , y ) +
219+ IDX_4D (Fd_values , stride0 , stride1 , stride2 , w , h - z2 , x , y + z2 );
204220 if (total > best_val ) best_val = total ;
205221 }
206222 }
207223
208- /* Y cuts */
224+ /* Y cuts — normal pattern positions */
209225 for (int i = 0 ; i < ny ; i ++ ) {
210226 int z = np_y_arr [h * max_cuts_y + i ];
211- uint32_t total = IDX_4D (Fd_values , stride0 , stride1 , stride2 , w , z , x , y ) +
212- IDX_4D (Fd_values , stride0 , stride1 , stride2 , w , h - z , x , y + z );
227+ int32_t total = IDX_4D (Fd_values , stride0 , stride1 , stride2 , w , z , x , y ) +
228+ IDX_4D (Fd_values , stride0 , stride1 , stride2 , w , h - z , x , y + z );
213229 if (total > best_val ) best_val = total ;
214230 }
215231
232+ /* Y cuts — defect-aligned positions */
216233 for (int d = 0 ; d < n_def ; d ++ ) {
217- int dy = defects [d * 6 + 1 ], dy_end = defects [d * 6 + 5 ];
218- if (y >= dy_end || dy >= y + h ) continue ;
219- int z1 = dy - y ;
220- int z2 = dy_end - y ;
221- int cuts [2 ] = { z1 , z2 };
222- for (int ci = 0 ; ci < 2 ; ci ++ ) {
223- int z = cuts [ci ];
224- if (z <= 0 || z >= h ) continue ;
225- uint32_t total = IDX_4D (Fd_values , stride0 , stride1 , stride2 , w , z , x , y ) +
226- IDX_4D (Fd_values , stride0 , stride1 , stride2 , w , h - z , x , y + z );
234+ if (y >= DEF_Y_END (d ) || DEF_Y (d ) >= y + h ) continue ;
235+ int z1 = DEF_Y (d ) - y ;
236+ int z2 = DEF_Y_END (d ) - y ;
237+ if (z1 > 0 && z1 < h ) {
238+ int32_t total = IDX_4D (Fd_values , stride0 , stride1 , stride2 , w , z1 , x , y ) +
239+ IDX_4D (Fd_values , stride0 , stride1 , stride2 , w , h - z1 , x , y + z1 );
240+ if (total > best_val ) best_val = total ;
241+ }
242+ if (z2 > 0 && z2 < h ) {
243+ int32_t total = IDX_4D (Fd_values , stride0 , stride1 , stride2 , w , z2 , x , y ) +
244+ IDX_4D (Fd_values , stride0 , stride1 , stride2 , w , h - z2 , x , y + z2 );
227245 if (total > best_val ) best_val = total ;
228246 }
229247 }
@@ -240,9 +258,8 @@ static PyObject* py_fill_Fd(PyObject *self, PyObject *args) {
240258 int W0 , H0 , max_cuts_x , max_cuts_y , n_def ;
241259 PyObject * o_pre , * o_F , * o_Fd , * o_nx , * o_nlx , * o_ny , * o_nly , * o_def ;
242260
243- /* FIXED: Format string "iiOOOOOOOOiii" now correctly counts 8 objects and 5 ints */
244- if (!PyArg_ParseTuple (args , "iiOOOOOOOOiii" , & W0 , & H0 , & o_pre , & o_F , & o_Fd ,
245- & o_nx , & o_nlx , & o_ny , & o_nly , & o_def ,
261+ if (!PyArg_ParseTuple (args , "iiOOOOOOOOiii" , & W0 , & H0 , & o_pre , & o_F , & o_Fd ,
262+ & o_nx , & o_nlx , & o_ny , & o_nly , & o_def ,
246263 & max_cuts_x , & max_cuts_y , & n_def )) return NULL ;
247264
248265 Py_buffer b_pre , b_F , b_Fd , b_nx , b_nlx , b_ny , b_nly , b_def ;
@@ -255,8 +272,8 @@ static PyObject* py_fill_Fd(PyObject *self, PyObject *args) {
255272 PyObject_GetBuffer (o_nly , & b_nly , PyBUF_SIMPLE );
256273 PyObject_GetBuffer (o_def , & b_def , PyBUF_SIMPLE );
257274
258- fill_Fd_core (W0 , H0 , (int32_t * )b_pre .buf , (int32_t * )b_F .buf , (uint32_t * )b_Fd .buf ,
259- (int32_t * )b_nx .buf , (int32_t * )b_nlx .buf , max_cuts_x ,
275+ fill_Fd_core (W0 , H0 , (int32_t * )b_pre .buf , (int32_t * )b_F .buf , (int32_t * )b_Fd .buf ,
276+ (int32_t * )b_nx .buf , (int32_t * )b_nlx .buf , max_cuts_x ,
260277 (int32_t * )b_ny .buf , (int32_t * )b_nly .buf , max_cuts_y , (int32_t * )b_def .buf , n_def );
261278
262279 PyBuffer_Release (& b_pre ); PyBuffer_Release (& b_F ); PyBuffer_Release (& b_Fd );
@@ -381,7 +398,6 @@ static PyObject* py_fill_F(PyObject *self, PyObject *args)
381398 return NULL ;
382399}
383400
384- /* method table */
385401/* method table */
386402static PyMethodDef SolverMethods [] = {
387403 {"fill_g" , py_fill_g , METH_VARARGS , "Precompute best single-item tiling (g tables)." },
0 commit comments