File size: 5,246 Bytes
2166d7e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#include "l1_pmat.h"

#include <stdint.h>
#include <stdlib.h>
#include <string.h>

cs_status pmat_new(pmat *m, size_t rows, size_t cols)
{
    if (!m || rows == 0 || cols == 0) return CS_ERR_ARG;
    if (rows > SIZE_MAX / cols || rows * cols > SIZE_MAX / sizeof(gl_t)) return CS_ERR_NOMEM;
    m->d = calloc(rows * cols, sizeof(gl_t));
    if (!m->d) return CS_ERR_NOMEM;
    m->rows = rows;
    m->cols = cols;
    return CS_OK;
}

void pmat_free(pmat *m)
{
    if (!m) return;
    free(m->d);
    m->d = NULL;
    m->rows = m->cols = 0;
}

cs_status pmat_identity(pmat *m, size_t n)
{
    cs_status s = pmat_new(m, n, n);
    if (s != CS_OK) return s;
    for (size_t i = 0; i < n; i++) m->d[i * n + i] = 1;
    return CS_OK;
}

gl_t pmat_get(const pmat *m, size_t r, size_t c) { return m->d[r * m->cols + c]; }
void pmat_set(pmat *m, size_t r, size_t c, gl_t v) { m->d[r * m->cols + c] = gl_reduce(v); }

int pmat_equal(const pmat *a, const pmat *b)
{
    return a->rows == b->rows && a->cols == b->cols &&
           memcmp(a->d, b->d, a->rows * a->cols * sizeof(gl_t)) == 0;
}

static cs_status elementwise(const pmat *a, const pmat *b, pmat *out, int sub)
{
    if (!a || !b || !out || !a->d || !b->d) return CS_ERR_ARG;
    if (a->rows != b->rows || a->cols != b->cols) return CS_ERR_RANGE;
    pmat r;
    cs_status s = pmat_new(&r, a->rows, a->cols);
    if (s != CS_OK) return s;
    for (size_t i = 0; i < a->rows * a->cols; i++)
        r.d[i] = sub ? gl_sub(a->d[i], b->d[i]) : gl_add(a->d[i], b->d[i]);
    *out = r;
    return CS_OK;
}

cs_status pmat_add(const pmat *a, const pmat *b, pmat *out) { return elementwise(a, b, out, 0); }
cs_status pmat_sub(const pmat *a, const pmat *b, pmat *out) { return elementwise(a, b, out, 1); }

cs_status pmat_scale(const pmat *a, gl_t k, pmat *out)
{
    if (!a || !out || !a->d) return CS_ERR_ARG;
    pmat r;
    cs_status s = pmat_new(&r, a->rows, a->cols);
    if (s != CS_OK) return s;
    k = gl_reduce(k);
    for (size_t i = 0; i < a->rows * a->cols; i++) r.d[i] = gl_mul(a->d[i], k);
    *out = r;
    return CS_OK;
}

cs_status pmat_mul(const pmat *a, const pmat *b, pmat *out)
{
    if (!a || !b || !out || !a->d || !b->d) return CS_ERR_ARG;
    if (a->cols != b->rows) return CS_ERR_RANGE;
    pmat r;
    cs_status s = pmat_new(&r, a->rows, b->cols);
    if (s != CS_OK) return s;
    for (size_t i = 0; i < a->rows; i++)
        for (size_t k = 0; k < a->cols; k++) {
            gl_t aik = a->d[i * a->cols + k];
            for (size_t j = 0; j < b->cols; j++)
                r.d[i * r.cols + j] = gl_add(r.d[i * r.cols + j], gl_mul(aik, b->d[k * b->cols + j]));
        }
    *out = r;
    return CS_OK;
}

cs_status pmat_transpose(const pmat *a, pmat *out)
{
    if (!a || !out || !a->d) return CS_ERR_ARG;
    pmat r;
    cs_status s = pmat_new(&r, a->cols, a->rows);
    if (s != CS_OK) return s;
    for (size_t i = 0; i < a->rows; i++)
        for (size_t j = 0; j < a->cols; j++) r.d[j * r.cols + i] = a->d[i * a->cols + j];
    *out = r;
    return CS_OK;
}

/* Gauss-Jordan on [A | I]; accumulates the determinant. */
static cs_status eliminate(const pmat *a, pmat *inv_out, gl_t *det_out)
{
    if (!a || !a->d) return CS_ERR_ARG;
    if (a->rows != a->cols) return CS_ERR_RANGE;
    size_t n = a->rows;
    pmat w, id;
    cs_status s = pmat_new(&w, n, n);
    if (s != CS_OK) return s;
    memcpy(w.d, a->d, n * n * sizeof(gl_t));
    s = pmat_identity(&id, n);
    if (s != CS_OK) { pmat_free(&w); return s; }

    gl_t det = 1;
    for (size_t col = 0; col < n; col++) {
        size_t piv = col;
        while (piv < n && w.d[piv * n + col] == 0) piv++;
        if (piv == n) { det = 0; pmat_free(&w); pmat_free(&id); if (det_out) *det_out = 0; return CS_ERR_SINGULAR; }
        if (piv != col) {
            for (size_t j = 0; j < n; j++) {
                gl_t t = w.d[piv * n + j]; w.d[piv * n + j] = w.d[col * n + j]; w.d[col * n + j] = t;
                t = id.d[piv * n + j]; id.d[piv * n + j] = id.d[col * n + j]; id.d[col * n + j] = t;
            }
            det = gl_neg(det);
        }
        gl_t pv = w.d[col * n + col], pinv;
        det = gl_mul(det, pv);
        (void)gl_inv(pv, &pinv);
        for (size_t j = 0; j < n; j++) {
            w.d[col * n + j] = gl_mul(w.d[col * n + j], pinv);
            id.d[col * n + j] = gl_mul(id.d[col * n + j], pinv);
        }
        for (size_t r = 0; r < n; r++) {
            if (r == col) continue;
            gl_t f = w.d[r * n + col];
            if (f == 0) continue;
            for (size_t j = 0; j < n; j++) {
                w.d[r * n + j] = gl_sub(w.d[r * n + j], gl_mul(f, w.d[col * n + j]));
                id.d[r * n + j] = gl_sub(id.d[r * n + j], gl_mul(f, id.d[col * n + j]));
            }
        }
    }
    if (det_out) *det_out = det;
    pmat_free(&w);
    if (inv_out) *inv_out = id; else pmat_free(&id);
    return CS_OK;
}

cs_status pmat_det(const pmat *a, gl_t *det)
{
    if (!det) return CS_ERR_ARG;
    cs_status s = eliminate(a, NULL, det);
    if (s == CS_ERR_SINGULAR) return CS_OK;     /* det of a singular matrix is 0 */
    return s;
}

cs_status pmat_inverse(const pmat *a, pmat *out)
{
    if (!out) return CS_ERR_ARG;
    return eliminate(a, out, NULL);
}