1function [S,p] = GB_spec_build (I, J, X, nrows, ncols, op, order, sclass)
2%GB_SPEC_BUILD a MATLAB version of GrB_Matrix_build and GrB_vector_build
3%
4% Usage:
5% [S p] = GB_spec_build (I, J, X, nrows, ncols, op, order)
6%
7% GB_spec_build builds a full matrix S, mimicing GB_mex_Matrix_build but in
8% almost pure MATLAB.  This function is very slow since it creates a dense
9% matrix instead of a sparse one.  It is meant only as an executable version of
10% the GraphBLAS spec.  It cannot operate purely in MATLAB, however, because the
11% casting and operator rules in MATLAB differ from the C-style casting and
12% operator rules GraphBLAS.  In MATLAB, adding two int8 values 120 + 30 results
13% in 127; since 150 is larger than the max int8 value of 127, the result is
14% that max value.  In C, the result wraps around, modulo 256, to be -106.
15%
16% S is returned as a struct, with S.matrix being the matrix, S.class the class,
17% and S.pattern the nonzero pattern of S.
18%
19% I: row indices. Indices are zero-based, just like GB_mex_Matrix_build.
20%
21% optional arguments:
22% J: column indices. Default J is a vector of all zeros, for building a column
23%       vector.
24% X: numerical values, with MATLAB class logical, any integer, single, or
25%       double.  I, J, and X must have the same number of entries.
26%       Default X is a logical vector of all true.
27% nrows: number of rows of S.  Default is nrows = max (I) + 1 ;
28% ncols: number of cols of S.  Default is ncols = max (J) + 1 ;
29% op: binary operator z=f(x,y) for assembling duplicates.  See
30%       GB_spec_operator.  The GraphBLAS spec requires op to be associative
31%       (min, max, plus, or times) but any binary operator with x,y,z
32%       types the same will work; see the 'order' parameter.
33% order: 'natural', or 'random'.  Default is 'natural'.
34%       The GraphBLAS spec does not state what order the duplicates are
35%       assembled.  It only guarantees the result if op is associative.  The
36%       'natural' order sums them up in the order they appear in [I,J,X], and
37%       this is what GB_mex_Matrix_build does.  To check the results against
38%       any possible order, use 'random', which sums them up in a random
39%       order.  The results of 'natural' and 'random' will differ if op is not
40%       associative.  With floating-point values, roundoff may vary slightly,
41%       which should be acceptable.  If the results differ greatly then the
42%       problem is very ill-conditioned.  The output p returns the ordering
43%       used to assemble the entries, a permutation of 1:length(X).
44%
45% Use an empty value ([ ] or '') to obtain the default value for optional
46% parameters, or pass fewer inputs.  For exampe S = GB_spec_build (I, J, X,
47% nrows, ncols) uses defaults for op, and order, but not X, nrows and ncols.
48
49% SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2021, All Rights Reserved.
50% SPDX-License-Identifier: Apache-2.0
51
52%-------------------------------------------------------------------------------
53% get inputs
54%-------------------------------------------------------------------------------
55
56if (nargout > 2 || nargin < 1 || nargin > 8)
57    error ('usage: [S p] = GB_spec_build (I, J, X, nrows, ncols, op, order, sclass)') ;
58end
59
60% get I
61nnz = length (I) ;
62
63% get J
64if (nargin < 2)
65    J = [ ] ;
66end
67if (isempty (J))
68    J = zeros (nnz, 1) ;
69end
70if (nnz ~= length (J))
71    error ('I and J must have the same size') ;
72end
73
74% get X
75if (nargin < 3)
76    X = [ ] ;
77end
78if (isempty (X))
79    X = ones (nnz, 1, 'logical') ;
80end
81if (nnz ~= length (X))
82    error ('I and X must have the same size') ;
83end
84
85% get the number of rows
86if (nargin < 4)
87    nrows = [ ] ;
88end
89if (isempty (nrows))
90    nrows = max (I) + 1 ;
91end
92
93% get the number of cols
94if (nargin < 5)
95    ncols = [ ]
96end
97if (isempty (ncols))
98    ncols = max (J) + 1 ;
99end
100
101% get the op
102if (nargin < 6)
103    op = [ ] ;
104end
105if (isempty (op))
106    op = 'plus' ;
107end
108[opname optype ztype xtype ytype] = GB_spec_operator (op, GB_spec_type (X)) ;
109
110if (GB_spec_is_positional (opname))
111    error ('dup operator cannot be positional') ;
112end
113
114assert (isequal (ztype, xtype)) ;
115assert (isequal (ztype, ytype)) ;
116
117% get the ordering
118if (nargin < 7)
119    order = '' ;
120end
121
122%-------------------------------------------------------------------------------
123% do the work via a clean MATLAB interpretation of the entire GraphBLAS spec
124%-------------------------------------------------------------------------------
125
126% sort or randomize the tuples
127if (isequal (order, 'random'))
128    % the GraphBLAS spec allows for any ordering
129    p = randperm (nnz) ;
130else
131    % the 'natural' ordering is used in this implementation of GrB_Matrix_build
132    [~, p] = sortrows ([J I (1:nnz)']) ;
133end
134I = I (p) ;
135J = J (p) ;
136X = X (p) ;
137
138% initialize the matrix S and its pattern
139S.matrix = GB_spec_zeros ([nrows ncols], optype) ;
140S.pattern = false (nrows, ncols) ;
141S.class = optype ;
142
143% assemble the tuples into S
144for t = 1:nnz
145    i = 1 + I (t) ;     % convert from 0-based GraphBLAS to 1-based MATLAB
146    j = 1 + J (t) ;
147    if (~S.pattern (i,j))
148        % first time S(i,j) is modified: cast x into S
149        S.matrix (i,j) = GB_mex_cast (X (t), optype) ;
150        S.pattern (i,j) = true ;
151    else
152        % a duplicate entry to be assembled with the operator op
153        % cast x into the class of S and the operator
154        x = GB_mex_cast (X (t), optype) ;
155        % apply the operator, result is of class optype
156        S.matrix (i,j) = GB_spec_op (op, S.matrix (i,j), x) ;
157    end
158end
159
160% get the sclass
161if (nargin < 8)
162    sclass = optype ;  % default is optype
163end
164
165% typecast S into the desired class
166if (~isequal (optype, sclass))
167    S.matrix = GB_mex_cast (S.matrix, sclass) ;
168    S.class = sclass ;
169end
170
171