• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dance858 / PSLP / 22279069886

22 Feb 2026 02:32PM UTC coverage: 89.311% (+0.3%) from 89.013%
22279069886

Pull #38

github

web-flow
Merge bae50547e into e13878911
Pull Request #38: [WIP] Speedup

1281 of 1432 branches covered (89.46%)

Branch coverage included in aggregate %.

181 of 193 new or added lines in 10 files covered. (93.78%)

67 existing lines in 7 files now uncovered.

3983 of 4462 relevant lines covered (89.26%)

6008.65 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.39
/src/core/Matrix.c
1
/*
2
 * Copyright 2025 Daniel Cederberg
3
 *
4
 * This file is part of the PSLP project (LP Presolver).
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18

19
#include "Matrix.h"
20
#include "Binary_search.h"
21
#include "Debugger.h"
22
#include "Memory_wrapper.h"
23
#include "Numerics.h"
24
#include "PSLP_warnings.h"
25
#include "RowColViews.h"
26
#include "glbopts.h"
27
#include "stdlib.h"
28
#include "string.h"
29

30
Matrix *matrix_new(const double *Ax, const int *Ai, const int *Ap, size_t n_rows,
34✔
31
                   size_t n_cols, size_t nnz)
32
{
33
    DEBUG(ASSERT_NO_ZEROS_D(Ax, nnz));
34✔
34
    Matrix *A = matrix_alloc(n_rows, n_cols, nnz);
34✔
35
    RETURN_PTR_IF_NULL(A, NULL);
34✔
36
    size_t i, len;
37
    int offset, row_size, row_alloc;
38

39
    offset = 0;
34✔
40
    for (i = 0; i < n_rows; ++i)
2,189✔
41
    {
42
        A->p[i].start = Ap[i] + offset;
2,155✔
43
        len = (size_t) (Ap[i + 1] - Ap[i]);
2,155✔
44
        memcpy(A->x + A->p[i].start, Ax + Ap[i], len * sizeof(double));
2,155✔
45
        memcpy(A->i + A->p[i].start, Ai + Ap[i], len * sizeof(int));
2,155✔
46
        A->p[i].end = Ap[i + 1] + offset;
2,155✔
47
        row_size = A->p[i].end - A->p[i].start;
2,155✔
48
        row_alloc = calc_memory_row(row_size, EXTRA_ROW_SPACE, EXTRA_MEMORY_RATIO);
2,155✔
49
        offset += row_alloc - row_size;
2,155✔
50
    }
51

52
    A->p[n_rows].start = Ap[n_rows] + offset;
34✔
53
    A->p[n_rows].end = A->p[n_rows].start;
34✔
54

55
    return A;
34✔
56
}
57

58
// needed for the transpose function
59
Matrix *matrix_alloc(size_t n_rows, size_t n_cols, size_t nnz)
486✔
60
{
61
    Matrix *A = (Matrix *) ps_malloc(1, sizeof(Matrix));
486✔
62
    RETURN_PTR_IF_NULL(A, NULL);
486✔
63

64
    A->m = n_rows;
486✔
65
    A->n = n_cols;
486✔
66
    A->nnz = nnz;
486✔
67
    A->n_alloc = calc_memory(nnz, n_rows, EXTRA_ROW_SPACE, EXTRA_MEMORY_RATIO);
486✔
68

69
#ifdef TESTING
70
    A->i = (int *) ps_calloc(A->n_alloc, sizeof(int));
486✔
71
    A->p = (RowRange *) ps_calloc(n_rows + 1, sizeof(RowRange));
486✔
72
    A->x = (double *) ps_calloc(A->n_alloc, sizeof(double));
486✔
73
#else
74
    A->i = (int *) ps_malloc(A->n_alloc, sizeof(int));
75
    A->p = (RowRange *) ps_malloc(n_rows + 1, sizeof(RowRange));
76
    A->x = (double *) ps_malloc(A->n_alloc, sizeof(double));
77
#endif
78

79
    if (!A->i || !A->p || !A->x)
486✔
80
    {
81
        free_matrix(A);
×
82
        return NULL;
×
83
    }
84

85
    return A;
486✔
86
}
87

88
Matrix *matrix_new_no_extra_space(const double *Ax, const int *Ai, const int *Ap,
99✔
89
                                  size_t n_rows, size_t n_cols, size_t nnz)
90
{
91
    Matrix *A = (Matrix *) ps_malloc(1, sizeof(Matrix));
99✔
92
    RETURN_PTR_IF_NULL(A, NULL);
99✔
93

94
    A->m = n_rows;
99✔
95
    A->n = n_cols;
99✔
96
    A->nnz = nnz;
99✔
97
    A->n_alloc = nnz;
99✔
98
    A->i = (int *) ps_malloc(A->n_alloc, sizeof(int));
99✔
99
    A->p = (RowRange *) ps_malloc(n_rows + 1, sizeof(RowRange));
99✔
100
    A->x = (double *) ps_malloc(A->n_alloc, sizeof(double));
99✔
101

102
    if (!A->i || !A->p || !A->x)
99✔
103
    {
104
        free_matrix(A);
×
105
        return NULL;
×
106
    }
107

108
    memcpy(A->x, Ax, nnz * sizeof(double));
99✔
109
    memcpy(A->i, Ai, nnz * sizeof(int));
99✔
110

111
    for (int i = 0; i <= n_rows; ++i)
613✔
112
    {
113
        A->p[i].start = Ap[i];
514✔
114
        A->p[i].end = Ap[i + 1];
514✔
115
    }
116

117
    return A;
99✔
118
}
119

120
Matrix *transpose(const Matrix *A, int *work_n_cols)
452✔
121
{
122
    Matrix *AT = matrix_alloc(A->n, A->m, A->nnz);
452✔
123
    RETURN_PTR_IF_NULL(AT, NULL);
452✔
124
    int i, j, start;
125
    int *count = work_n_cols;
452✔
126
    memset(count, 0, A->n * sizeof(int));
452✔
127

128
    // -------------------------------------------------------------------
129
    //  compute nnz in each column of A
130
    // -------------------------------------------------------------------
131
    for (i = 0; i < A->m; ++i)
2,374✔
132
    {
133
        for (j = A->p[i].start; j < A->p[i].end; ++j)
8,085✔
134
        {
135
            count[A->i[j]]++;
6,163✔
136
        }
137
    }
138
    // ------------------------------------------------------------------
139
    //  compute row pointers, taking the extra space into account
140
    // ------------------------------------------------------------------
141
    AT->p[0].start = 0;
452✔
142
    for (i = 0; i < A->n; ++i)
2,721✔
143
    {
144
        start = AT->p[i].start;
2,269✔
145
        AT->p[i].end = start + count[i];
2,269✔
146
        AT->p[i + 1].start =
2,269✔
147
            start + calc_memory_row(count[i], EXTRA_ROW_SPACE, EXTRA_MEMORY_RATIO);
2,269✔
148
        count[i] = start;
2,269✔
149
    }
150

151
    AT->p[A->n].start = (int) AT->n_alloc;
452✔
152
    AT->p[A->n].end = (int) AT->n_alloc;
452✔
153

154
    // ------------------------------------------------------------------
155
    //  fill transposed matrix (this is a bottleneck)
156
    // ------------------------------------------------------------------
157
    for (i = 0; i < A->m; ++i)
2,374✔
158
    {
159
        for (j = A->p[i].start; j < A->p[i].end; j++)
8,085✔
160
        {
161
            AT->x[count[A->i[j]]] = A->x[j];
6,163✔
162
            AT->i[count[A->i[j]]] = i;
6,163✔
163
            count[A->i[j]]++;
6,163✔
164
        }
165
    }
166

167
    return AT;
452✔
168
}
169

170
size_t calc_memory(size_t nnz, size_t n_rows, size_t extra_row_space,
486✔
171
                   double memory_ratio)
172
{
173
    /* disable conversion compiler warning*/
174
    PSLP_DIAG_PUSH();
175
    PSLP_DIAG_IGNORE_CONVERSION();
176

177
    /* intentional truncation */
178
    size_t result = (size_t) (nnz * memory_ratio) + n_rows * extra_row_space;
486✔
179

180
    /* enable conversion compiler warnings */
181
    PSLP_DIAG_POP();
182
    return result;
486✔
183
}
184

185
int calc_memory_row(int size, int extra_row_space, double memory_ratio)
5,147✔
186
{
187
    return (int) (size * memory_ratio) + extra_row_space;
5,147✔
188
}
189

190
void free_matrix(Matrix *A)
585✔
191
{
192
    if (A)
585✔
193
    {
194
        PS_FREE(A->i);
585✔
195
        PS_FREE(A->p);
585✔
196
        PS_FREE(A->x);
585✔
197
    }
198

199
    PS_FREE(A);
585✔
200
}
585✔
201

202
void remove_extra_space(Matrix *A, const int *row_sizes, bool remove_all,
200✔
203
                        const int *col_idxs_map, size_t new_n_cols)
204
{
205
    int j, start, end, len, row_alloc, curr;
206
    int extra_row_space = (remove_all) ? 0 : EXTRA_ROW_SPACE;
200✔
207
    double extra_mem_ratio = (remove_all) ? 1.0 : EXTRA_MEMORY_RATIO;
200✔
208
    curr = 0;
200✔
209
    size_t i, n_deleted_rows;
210

211
    // --------------------------------------------------------------------------
212
    // loop through the rows and remove redundant space, including inactive
213
    // rows.
214
    // --------------------------------------------------------------------------
215
    n_deleted_rows = 0;
200✔
216
    for (i = 0; i < A->m; ++i)
1,119✔
217
    {
218
        if (row_sizes[i] == SIZE_INACTIVE_ROW)
919✔
219
        {
220
            n_deleted_rows++;
196✔
221
            continue;
196✔
222
        }
223

224
        start = A->p[i].start;
723✔
225
        end = A->p[i].end;
723✔
226
        len = end - start;
723✔
227
        memmove(A->x + curr, A->x + start, (size_t) (len) * sizeof(double));
723✔
228
        memmove(A->i + curr, A->i + start, (size_t) (len) * sizeof(int));
723✔
229
        A->p[i - n_deleted_rows].start = curr;
723✔
230
        A->p[i - n_deleted_rows].end = curr + len;
723✔
231
        row_alloc = calc_memory_row(len, extra_row_space, extra_mem_ratio);
723✔
232
        curr += row_alloc;
723✔
233
    }
234

235
    A->m -= n_deleted_rows;
200✔
236
    A->p[A->m].start = curr;
200✔
237
    A->p[A->m].end = curr;
200✔
238

239
    // shrink size
240
    A->x = (double *) ps_realloc(A->x, (size_t) MAX(curr, 1), sizeof(double));
200✔
241
    A->i = (int *) ps_realloc(A->i, (size_t) MAX(curr, 1), sizeof(int));
200✔
242
    A->p = (RowRange *) ps_realloc(A->p, (size_t) (A->m + 1), sizeof(RowRange));
200✔
243

244
    // -------------------------------------------------------------------------
245
    //                        update column indices
246
    // -------------------------------------------------------------------------
247
    A->n = new_n_cols;
200✔
248
    for (i = 0; i < A->m; ++i)
923✔
249
    {
250
        for (j = A->p[i].start; j < A->p[i].end; ++j)
2,821✔
251
        {
252
            A->i[j] = col_idxs_map[A->i[j]];
2,098✔
253
        }
254
    }
255
}
200✔
256

257
bool shift_row(Matrix *A, int row, int extra_space, int max_shift)
28✔
258
{
259
    int left, right, missing_space, remaining_shifts, left_shifts;
260
    int right_shifts, space_left, space_right, n_move_right, n_move_left;
261
    int next_start, next_end;
262
    bool shift_left;
263
    RowRange *row_r = A->p;
28✔
264
    left = row;
28✔
265
    right = row + 1;
28✔
266
    remaining_shifts = max_shift;
28✔
267
    left_shifts = 0;
28✔
268
    right_shifts = 0;
28✔
269
    missing_space = extra_space - (row_r[right].start - row_r[row].end);
28✔
270

271
    if (missing_space <= 0)
28✔
272
    {
273
        return true;
2✔
274
    }
275

276
    // ------------------------------------------------------------------------
277
    // compute the new start index for row 'row' and a lower bound on the start
278
    // index for the the first active row following row 'row'.
279
    // ------------------------------------------------------------------------
280
    while (missing_space > 0)
84✔
281
    {
282
        if (left == 0 && right == A->m)
65✔
283
        {
284
            return false;
×
285
        }
286

287
        // space_left is the number of steps we can shift 'left' row to the
288
        // left without overwriting row 'left - 1'.
289
        // space_right is the number of steps we can shift 'right' row to
290
        // the right without overwriting row 'right + 1'.
291
        assert(left >= 0 && right <= A->m);
65✔
292
        space_left = (left == 0) ? 0 : row_r[left].start - row_r[left - 1].end;
65✔
293
        space_right =
65✔
294
            (right == A->m) ? 0 : row_r[right + 1].start - row_r[right].end;
65✔
295
        assert(space_left >= 0 && space_right >= 0);
65✔
296

297
        // number of elements that must be moved left resp. right if we shift
298
        // in a certain direction
299
        n_move_right = row_r[right].end - row_r[right].start;
65✔
300
        n_move_left = row_r[left].end - row_r[left].start;
65✔
301

302
        // decide which direction to shift
303
        if (left == 0)
65✔
304
        {
305
            if (right != A->m && n_move_right <= remaining_shifts)
7✔
306
            {
307
                shift_left = false;
5✔
308
            }
309
            else
310
            {
311
                return false;
2✔
312
            }
313
        }
314
        else if (right == A->m)
58✔
315
        {
316
            if (left != 0 && n_move_left <= remaining_shifts)
5✔
317
            {
318
                shift_left = true;
4✔
319
            }
320
            else
321
            {
322
                return false;
1✔
323
            }
324
        }
325
        else if (n_move_left == 0)
53✔
326
        {
327
            shift_left = true;
×
328
        }
329
        else if (n_move_right == 0)
53✔
330
        {
331
            shift_left = false;
11✔
332
        }
333
        else if (n_move_left <= remaining_shifts &&
42✔
334
                 (space_left / (double) (n_move_left) >=
37✔
335
                  space_right / (double) (n_move_right)))
37✔
336
        {
337
            shift_left = true;
16✔
338
        }
339
        else if (n_move_right <= remaining_shifts)
26✔
340
        {
341
            shift_left = false;
22✔
342
        }
343
        else
344
        {
345
            return false;
4✔
346
        }
347

348
        assert(!(shift_left && left == 0) && !(!shift_left && right == A->m));
58✔
349

350
        if (shift_left)
58✔
351
        {
352
            left_shifts = MIN(missing_space, space_left);
20✔
353
            missing_space -= left_shifts;
20✔
354
            remaining_shifts -= n_move_left;
20✔
355
            left -= 1;
20✔
356
        }
357
        else
358
        {
359
            right_shifts = MIN(missing_space, space_right);
38✔
360
            missing_space -= right_shifts;
38✔
361
            remaining_shifts -= n_move_right;
38✔
362
            right += 1;
38✔
363
        }
364
    }
365
    assert(remaining_shifts >= 0);
19✔
366

367
    // ------------------------------------------------------------------------
368
    //                 execute total left shift
369
    // ------------------------------------------------------------------------
370
    next_start = row_r[left + 1].start - left_shifts;
19✔
371
    for (; left < row; left++)
39✔
372
    {
373
        int len = row_r[left + 1].end - row_r[left + 1].start;
20✔
374
        if (len > 0)
20✔
375
        {
376
            memmove(A->x + next_start, A->x + row_r[left + 1].start,
20✔
377
                    ((size_t) len) * sizeof(double));
20✔
378
            memmove(A->i + next_start, A->i + row_r[left + 1].start,
20✔
379
                    ((size_t) len) * sizeof(int));
20✔
380
        }
381
        row_r[left + 1].start = next_start;
20✔
382
        row_r[left + 1].end = next_start + len;
20✔
383
        next_start += len;
20✔
384
    }
385

386
    // ------------------------------------------------------------------------
387
    //                 execute total right shift
388
    // ------------------------------------------------------------------------
389
    next_end = row_r[right - 1].end + right_shifts;
19✔
390
    for (; right > row + 1; right--)
56✔
391
    {
392
        int len = row_r[right - 1].end - row_r[right - 1].start;
37✔
393
        if (len > 0)
37✔
394
        {
395
            memmove(A->x + next_end - len, A->x + row_r[right - 1].start,
25✔
396
                    ((size_t) len) * sizeof(double));
25✔
397
            memmove(A->i + next_end - len, A->i + row_r[right - 1].start,
25✔
398
                    ((size_t) len) * sizeof(int));
25✔
399
        }
400
        row_r[right - 1].start = next_end - len;
37✔
401
        row_r[right - 1].end = next_end;
37✔
402
        next_end = row_r[right - 1].start;
37✔
403
    }
404

405
    assert(row_r[row + 1].start - row_r[row].end == extra_space);
19✔
406
    return true;
19✔
407
}
408

409
void print_row_starts(const RowRange *row_ranges, size_t len)
×
410
{
411
    for (size_t i = 0; i < len; ++i)
×
412
    {
413
        printf("%d ", row_ranges[i].start);
×
414
    }
415
    printf("\n");
×
416
}
×
417

418
double insert_or_update_coeff(Matrix *A, int row, int col, double val, int *row_size)
61✔
419
{
420
    // int i, start, end, insertion;
421
    double old_val = 0.0;
61✔
422
    int start = A->p[row].start;
61✔
423
    int end = A->p[row].end;
61✔
424
    // int insertion = end;
425

426
    // -----------------------------------------------------------------
427
    //             find where it should be inserted
428
    // -----------------------------------------------------------------
429
    // for (i = start; i < end; ++i)
430
    // {
431
    //     if (A->i[i] >= col)
432
    //     {
433
    //         insertion = i;
434
    //         break;
435
    //     }
436
    // }
437

438
    int rel_ins = sorted_lower_bound(A->i + start, end - start, col);
61✔
439
    int insertion = start + rel_ins;
61✔
440

441
    // -----------------------------------------------------------------
442
    // Insert the new value if it is nonzero. If it exists or should be
443
    // inserted in the end, we don't need to shift values.
444
    // -----------------------------------------------------------------
445
    if (ABS(val) > ZERO_TOL)
61✔
446
    {
447
        // assert(!IS_ZERO_FEAS_TOL(val));
448
        if (insertion == end)
51✔
449
        {
450
            A->x[insertion] = val;
4✔
451
            A->i[insertion] = col;
4✔
452
            A->p[row].end += 1;
4✔
453
            A->nnz += 1;
4✔
454
            *row_size += 1;
4✔
455
        }
456
        else if (A->i[insertion] == col)
47✔
457
        {
458
            old_val = A->x[insertion];
19✔
459
            A->x[insertion] = val;
19✔
460
        }
461
        else
462
        {
463
            size_t len = (size_t) (end - insertion);
28✔
464
            memmove(A->x + insertion + 1, A->x + insertion, len * sizeof(double));
28✔
465
            memmove(A->i + insertion + 1, A->i + insertion, len * sizeof(int));
28✔
466

467
            // insert new value
468
            A->x[insertion] = val;
28✔
469
            A->i[insertion] = col;
28✔
470
            A->p[row].end += 1;
28✔
471
            A->nnz += 1;
28✔
472
            *row_size += 1;
28✔
473
        }
474
    }
475
    // if the new value is zero, we just have to shift
476
    else
477
    {
478
        // we only expect that the new value is zero if the coefficient
479
        // already exists
480
        assert(A->i[insertion] == col);
10✔
481

482
        // we only have to shift values if the zero is not in the end
483
        if (insertion != end - 1)
10✔
484
        {
485
            size_t len = (size_t) (end - insertion - 1);
6✔
486
            memmove(A->x + insertion, A->x + insertion + 1, len * sizeof(double));
6✔
487
            memmove(A->i + insertion, A->i + insertion + 1, len * sizeof(int));
6✔
488
        }
489

490
        A->p[row].end -= 1;
10✔
491
        A->nnz -= 1;
10✔
492
        *row_size -= 1;
10✔
493
    }
494

495
    assert(A->p[row].end <= A->p[row + 1].start);
61✔
496
    return old_val;
61✔
497
}
498

499
void remove_coeff(RowView *row, int col)
35✔
500
{
501
    int shift = 0;
35✔
502
    int len = *row->len;
35✔
503
    for (int i = 0; i < len; ++i)
194✔
504
    {
505
        if (row->cols[i] == col)
159✔
506
        {
507
            shift = 1;
35✔
508
        }
509

510
        row->vals[i] = row->vals[i + shift];
159✔
511
        row->cols[i] = row->cols[i + shift];
159✔
512
    }
513

514
    assert(shift != 0);
35✔
515
    (*row->range).end -= 1;
35✔
516
    *row->len -= 1;
35✔
517
}
35✔
518

519
void count_rows(const Matrix *A, int *row_sizes)
198✔
520
{
521
    for (int i = 0; i < A->m; ++i)
1,087✔
522
    {
523
        row_sizes[i] = A->p[i].end - A->p[i].start;
889✔
524
    }
525
}
198✔
526

527
#ifdef TESTING
528
// Function to create a random CSR matrix
529
Matrix *random_matrix_new(size_t n_rows, size_t n_cols, double density)
2✔
530
{
531
    // allocate memory
532

533
    /* disable conversion compiler warning*/
534
    PSLP_DIAG_PUSH();
535
    PSLP_DIAG_IGNORE_CONVERSION();
536
    /* intentional truncation */
537
    size_t n_alloc_nnz = (size_t) (density * n_rows * n_cols);
2✔
538

539
    /* enable conversion compiler warnings */
540
    PSLP_DIAG_POP();
541
    double *Ax = (double *) ps_malloc(n_alloc_nnz, sizeof(double));
2✔
542
    int *Ai = (int *) ps_malloc(n_alloc_nnz, sizeof(int));
2✔
543
    int *Ap = (int *) ps_malloc(n_rows + 1, sizeof(int));
2✔
544
    if (!Ax || !Ai || !Ap)
2✔
545
    {
UNCOV
546
        PS_FREE(Ax);
×
UNCOV
547
        PS_FREE(Ai);
×
UNCOV
548
        PS_FREE(Ap);
×
UNCOV
549
        return NULL;
×
550
    }
551

552
    // Initialize random number generator
553
    srand(1);
2✔
554

555
    size_t nnz_count = 0; // Counter for nonzero elements
2✔
556
    Ap[0] = 0;
2✔
557

558
    for (size_t i = 0; i < n_rows; ++i)
2,002✔
559
    {
560
        size_t row_nnz = 0;
2,000✔
561

562
        // Randomly determine the number of nonzeros in this row
563
        for (size_t j = 0; j < n_cols; ++j)
3,999,508✔
564
        {
565
            if ((double) rand() / RAND_MAX < density)
3,997,510✔
566
            {
567
                if (nnz_count >= n_alloc_nnz)
400,002✔
568
                {
569
                    break;
2✔
570
                }
571

572
                Ax[nnz_count] = ((double) (rand() - rand()) / RAND_MAX) * 20.0;
400,000✔
573
                Ai[nnz_count] = (int) j;
400,000✔
574
                ++nnz_count;
400,000✔
575
                ++row_nnz;
400,000✔
576
            }
577
        }
578
        Ap[i + 1] = Ap[i] + (int) row_nnz;
2,000✔
579
    }
580

581
    // create matrix in modified CSR format
582
    Matrix *A = matrix_new(Ax, Ai, Ap, n_rows, n_cols, nnz_count);
2✔
583
    PS_FREE(Ax);
2✔
584
    PS_FREE(Ai);
2✔
585
    PS_FREE(Ap);
2✔
586

587
    return A;
2✔
588
}
589

590
void replace_row_A(Matrix *A, int row, double ratio, double *new_vals, int *cols_new,
42✔
591
                   int new_len)
592
{
593
    int i, len, start, n_new_elements;
594
    len = A->p[row].end - A->p[row].start;
42✔
595
    n_new_elements = new_len - len;
42✔
596

597
    // potentially shift row to get extra space
598
    if (n_new_elements > 0)
42✔
599
    {
600
        assert(shift_row(A, row, n_new_elements, 2000));
8✔
601
    }
602

603
    // replace the row
604
    start = A->p[row].start;
42✔
605
    for (i = 0; i < new_len; ++i)
8,078✔
606
    {
607
        A->x[start + i] = ratio * new_vals[i];
8,036✔
608
        A->i[start + i] = cols_new[i];
8,036✔
609
    }
610
    A->p[row].end = A->p[row].start + new_len;
42✔
611
    assert(A->p[row].end <= A->p[row + 1].start);
42✔
612
}
42✔
613

614
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc