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

dance858 / PSLP / 22260918361

21 Feb 2026 05:16PM UTC coverage: 89.482% (+0.5%) from 89.013%
22260918361

Pull #38

github

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

1278 of 1426 branches covered (89.62%)

Branch coverage included in aggregate %.

153 of 155 new or added lines in 9 files covered. (98.71%)

89 existing lines in 8 files now uncovered.

3971 of 4440 relevant lines covered (89.44%)

6311.76 hits per line

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

95.45
/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 "Debugger.h"
21
#include "Memory_wrapper.h"
22
#include "Numerics.h"
23
#include "PSLP_warnings.h"
24
#include "RowColViews.h"
25
#include "glbopts.h"
26
#include "stdlib.h"
27
#include "string.h"
28

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

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

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

54
    return A;
34✔
55
}
56

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

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

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

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

84
    return A;
486✔
85
}
86

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

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

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

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

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

116
    return A;
99✔
117
}
118

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

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

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

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

166
    return AT;
452✔
167
}
168

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

486
        A->p[row].end -= 1;
10✔
487
        A->nnz -= 1;
10✔
488
        *row_size -= 1;
10✔
489
    }
490

491
    assert(A->p[row].end <= A->p[row + 1].start);
61✔
492
    return old_val;
61✔
493
}
494

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

506
        row->vals[i] = row->vals[i + shift];
159✔
507
        row->cols[i] = row->cols[i + shift];
159✔
508
    }
509

510
    assert(shift != 0);
35✔
511
    (*row->range).end -= 1;
35✔
512
    *row->len -= 1;
35✔
513
}
35✔
514

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

523
#ifdef TESTING
524
// Function to create a random CSR matrix
525
Matrix *random_matrix_new(size_t n_rows, size_t n_cols, double density)
2✔
526
{
527
    // allocate memory
528

529
    /* disable conversion compiler warning*/
530
    PSLP_DIAG_PUSH();
531
    PSLP_DIAG_IGNORE_CONVERSION();
532
    /* intentional truncation */
533
    size_t n_alloc_nnz = (size_t) (density * n_rows * n_cols);
2✔
534

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

548
    // Initialize random number generator
549
    srand(1);
2✔
550

551
    size_t nnz_count = 0; // Counter for nonzero elements
2✔
552
    Ap[0] = 0;
2✔
553

554
    for (size_t i = 0; i < n_rows; ++i)
2,002✔
555
    {
556
        size_t row_nnz = 0;
2,000✔
557

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

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

577
    // create matrix in modified CSR format
578
    Matrix *A = matrix_new(Ax, Ai, Ap, n_rows, n_cols, nnz_count);
2✔
579
    PS_FREE(Ax);
2✔
580
    PS_FREE(Ai);
2✔
581
    PS_FREE(Ap);
2✔
582

583
    return A;
2✔
584
}
585

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

593
    // potentially shift row to get extra space
594
    if (n_new_elements > 0)
42✔
595
    {
596
        assert(shift_row(A, row, n_new_elements, 2000));
8✔
597
    }
598

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

610
#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