Remove unused fields from omxData
[openmx:openmx.git] / src / omxData.c
1 /*
2  *  Copyright 2007-2013 The OpenMx Project
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *       http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License.
15  */
16
17 /***********************************************************
18  *
19  *  omxData.cc
20  *
21  *  Created: Timothy R. Brick   Date: 2009-07-15
22  *
23  *      Contains code for the omxData class
24  *   omxData objects hold data in whatever form it takes
25  *
26  **********************************************************/
27 #include "omxData.h"
28 #include "npsolWrap.h"
29
30 omxData* omxInitData(omxState* os) {
31
32         omxData *od = Calloc(1, omxData);
33
34         od->currentState = os;
35         
36         if(OMX_DEBUG) {Rprintf("Data's state object is at 0x%x.\n", od->currentState);}
37
38         return od;
39
40 }
41
42 omxData* omxDataLookupFromState(SEXP dataObject, omxState* state) {
43         int dataIdx = INTEGER(dataObject)[0];
44
45         return state->dataList[dataIdx];
46 }
47
48 omxData* omxNewDataFromMxData(SEXP dataObject, omxState* state) {
49         if(OMX_DEBUG) {Rprintf("Initializing data Element.\n");}
50         if(dataObject == NULL) {
51                 error("Null Data Object detected.  This is an internal error, and should be reported on the forums.\n");
52         }
53
54         omxData* od = omxInitData(state);
55
56         SEXP dataLoc, dataVal;
57         int numCols;
58         int numInts=0;
59         int numReals=0;
60
61         // PARSE MxData Structure
62         if(OMX_DEBUG) {Rprintf("Processing Data Type.\n");}
63         PROTECT(dataLoc = GET_SLOT(dataObject, install("type")));
64         if(dataLoc == NULL) { error("Data has no type.  Sorry.\nThis is an internal error, and should be reported on the forums.\n");}
65         PROTECT(dataVal = STRING_ELT(dataLoc,0));
66         od->_type = CHAR(dataVal);
67         if(OMX_DEBUG) {Rprintf("Element is type %s.\n", od->_type);}
68
69         PROTECT(dataLoc = GET_SLOT(dataObject, install("observed")));
70         if(OMX_DEBUG) {Rprintf("Processing Data Elements.\n");}
71         if(isFrame(dataLoc)) {
72                 if(OMX_DEBUG) {Rprintf("Data is a frame.\n");}
73                 // Process Data Frame into Columns
74                 od->cols = length(dataLoc);
75                 if(OMX_DEBUG) {Rprintf("Data has %d columns.\n", od->cols);}
76                 numCols = od->cols;
77                 od->realData = (double**) R_alloc(numCols, sizeof(double*));
78                 od->intData = (int**) R_alloc(numCols, sizeof(int*));
79                 od->location = (int*) R_alloc(numCols, sizeof(int));
80                 for(int j = 0; j < numCols; j++) {
81                         SEXP rcol;
82                         PROTECT(rcol = VECTOR_ELT(dataLoc, j));
83                         if(isFactor(rcol)) {
84                                 if(OMX_DEBUG) {Rprintf("Column %d is a factor.\n", j);}
85                                 od->intData[numInts] = INTEGER(rcol);
86                                 od->location[j] = ~(numInts++);
87                                 od->numFactor++;
88                         } else if (isInteger(rcol)) {
89                                 error("Internal error: Column %d is in integer format.", j);
90                         } else {
91                                 if(OMX_DEBUG) {Rprintf("Column %d is a numeric.\n", j);}
92                                 od->realData[numReals] = REAL(rcol);
93                                 od->location[j] = (numReals++);
94                                 od->numNumeric++;
95                         }
96                 }
97                 od->rows = length(VECTOR_ELT(dataLoc, 0));
98                 if(OMX_DEBUG) {Rprintf("And %d rows.\n", od->rows);}
99         } else {
100                 if(OMX_DEBUG) {Rprintf("Data contains a matrix.\n");}
101                 od->dataMat = omxNewMatrixFromRPrimitive(dataLoc, od->currentState, 0, 0);
102                 
103                 if (od->dataMat->colMajor && strncmp(od->_type, "raw", 3) == 0) { 
104                         omxToggleRowColumnMajor(od->dataMat);
105                 }
106                 od->cols = od->dataMat->cols;
107                 od->rows = od->dataMat->rows;
108                 od->numNumeric = od->cols;
109         }
110
111         if(OMX_DEBUG) {Rprintf("Processing Means Matrix.\n");}
112         PROTECT(dataLoc = GET_SLOT(dataObject, install("means")));
113         od->meansMat = omxNewMatrixFromRPrimitive(dataLoc, od->currentState, 0, 0);
114         if(od->meansMat->rows == 1 && od->meansMat->cols == 1 && 
115            (!R_finite(omxMatrixElement(od->meansMat, 0, 0)) ||
116             !isfinite(omxMatrixElement(od->meansMat, 0, 0)))) {
117                 omxFreeMatrixData(od->meansMat); // Clear just-allocated memory.
118                 od->meansMat = NULL;  // 1-by-1 matrix of NAs is a null means matrix.
119                 // FIXME: The above check may cause problems for dynamic data if the means
120                 //          originally is a 1x1 that has not yet been calculated.  This should be
121                 //          adjusted.
122         }
123         
124         if(OMX_DEBUG) {
125                 if(od->meansMat == NULL) {Rprintf("No means found.\n");}
126                 else {omxPrint(od->meansMat, "Means Matrix is:");}
127         }
128
129         if(strncmp(od->_type, "raw", 3) != 0) {
130                 if(OMX_DEBUG) {Rprintf("Processing Observation Count.\n");}
131                 PROTECT(dataLoc = GET_SLOT(dataObject, install("numObs")));
132                 od->numObs = REAL(dataLoc)[0];
133         } else {
134                 od->numObs = od->rows;
135                 if(OMX_DEBUG) {Rprintf("Processing presort metadata.\n");}
136                 /* For raw data, process sorting metadata. */
137                 // Process unsorted indices:  // TODO: Generate reverse lookup table
138                 PROTECT(dataLoc = GET_SLOT(dataObject, install("indexVector")));
139                 od->indexVector = INTEGER(dataLoc);
140                 if(od->indexVector[0] == R_NaInt) od->indexVector = NULL;
141                 // Process pre-computed identicality checks
142                 if(OMX_DEBUG) {Rprintf("Processing definition variable identicality.\n");}
143                 PROTECT(dataLoc = GET_SLOT(dataObject, install("identicalDefVars")));
144                 od->identicalDefs = INTEGER(dataLoc);
145                 if(od->identicalDefs[0] == R_NaInt) od->identicalDefs = NULL;
146                 if(OMX_DEBUG) {Rprintf("Processing missingness identicality.\n");}
147                 PROTECT(dataLoc = GET_SLOT(dataObject, install("identicalMissingness")));
148                 od->identicalMissingness = INTEGER(dataLoc);
149                 if(od->identicalMissingness[0] == R_NaInt) od->identicalMissingness = NULL;
150                 if(OMX_DEBUG) {Rprintf("Processing row identicality.\n");}
151                 PROTECT(dataLoc = GET_SLOT(dataObject, install("identicalRows")));
152                 od->identicalRows = INTEGER(dataLoc);
153                 if(od->identicalRows[0] == R_NaInt) od->identicalRows = NULL;
154         }
155
156         return od;
157 }
158
159 void resetDefinitionVariables(double *oldDefs, int numDefs) {
160         int nextDef;
161
162         for(nextDef = 0; nextDef < numDefs; nextDef++) {
163                 oldDefs[nextDef] = NA_REAL;                                     // Def Vars default to NA
164         }
165
166 }
167
168 void omxFreeData(omxData* od) {
169         omxFreeAllMatrixData(od->dataMat);
170         omxFreeAllMatrixData(od->meansMat);
171         Free(od);
172 }
173
174 double omxDoubleDataElement(omxData *od, int row, int col) {
175         if(od->dataMat != NULL) {
176                 return omxMatrixElement(od->dataMat, row, col);
177         }
178         int location = od->location[col];
179         if(location < 0) {
180                 return (double)(od->intData[~location][row]);
181         } else {
182                 return od->realData[location][row];
183         }
184 }
185
186 int omxIntDataElement(omxData *od, int row, int col) {
187         if(od->dataMat != NULL) {
188                 error("Use a data frame for factor data");
189         }
190
191         int location = od->location[col];
192         if(location < 0) {
193                 return (od->intData[~location][row]);
194         } else {
195                 return (int)(od->realData[location][row]);
196         }
197 }
198
199 omxMatrix* omxDataMatrix(omxData *od, omxMatrix* om) {
200         double dataElement;
201
202         if(od->dataMat != NULL) {               // Data was entered as a matrix.
203                 if(om != NULL) {                        // It stays as such
204                         omxCopyMatrix(om, od->dataMat);
205                         return om;
206                 }
207                 return od->dataMat;
208         }
209         // Otherwise, we must construct the matrix.
210         int numRows = od->rows, numCols = od->cols;
211
212         if(om == NULL) {
213                 om = omxInitMatrix(om, numRows, numCols, TRUE, od->currentState);
214         }
215
216         if(om->rows != numRows || om->cols != numCols) {
217                 omxResizeMatrix(om, numRows, numCols, FALSE);
218         }
219
220         for(int j = 0; j < numCols; j++) {
221                 for(int k = 0; k < numRows; k++) {
222                         int location = od->location[j];
223                         if(location < 0) {
224                                 dataElement = (double) od->intData[~location][k];
225                         } else {
226                                 dataElement = od->realData[location][k];
227                         }
228                         omxSetMatrixElement(om, k, j, dataElement);
229                 }
230         }
231         return om;
232 }
233
234 unsigned short int omxDataColumnIsFactor(omxData *od, int col) {
235         if(od->dataMat != NULL) return FALSE;
236         if(col <= od->cols) return (od->location[col] < 0);
237
238         error("Attempted to access column %d of a %d-column data object", col, od->cols);
239         return 0; // not reached
240 }
241
242 omxMatrix* omxDataMeans(omxData *od, omxMatrix* colList, omxMatrix* om) {
243
244         if(od->meansMat == NULL) return NULL;
245
246         if(colList == NULL) {
247                 if(om == NULL) return od->meansMat;
248                 omxCopyMatrix(om, od->meansMat);
249                 return om;
250         }
251
252         int cols = colList->cols;
253
254         if(colList == NULL || cols == 0 || cols > od->cols) {
255                 cols = od->cols;
256                 if(om == NULL) return od->meansMat;
257                 omxCopyMatrix(om, od->meansMat);
258                 return om;
259         }
260
261         if(om == NULL) {
262                 om = omxInitMatrix(om, 1, cols, TRUE, od->currentState);
263         }
264
265         for(int i = 0; i < cols; i++) {
266                 omxSetMatrixElement(om, 1, i, omxVectorElement(od->meansMat, omxVectorElement(colList, i)));
267         }
268
269         return om;
270 }
271
272
273 void omxSetContiguousDataColumns(omxContiguousData* contiguous, omxData* data, omxMatrix* colList) {
274
275         contiguous->isContiguous = FALSE;   // Assume not contiguous
276
277         if (data->dataMat == NULL) return; // Data has no matrix elements, so skip.
278
279         omxMatrix* dataMat = data->dataMat;
280         if (dataMat->colMajor) return;      // If data matrix is column-major, there's no continuity
281         
282         int colListLength = colList->cols;              // # of columns in the cov matrix
283         double start = omxVectorElement(colList, 0);    // Data col of first column of the covariance
284         contiguous->start = (int) start;                // That's our starting point.
285         contiguous->length = colListLength;             // And the length is ncol(cov)
286         
287         for(int i = 1; i < colListLength; i++) {        // Make sure that the col list is 
288                 double next = omxVectorElement(colList, i); // contiguously increasing in column number
289                 if (next != (start + i)) return;            // If it isn't, it's not contiguous data
290         }
291         
292         contiguous->isContiguous = TRUE;    // Passed.  This is contiguous.
293 }
294
295 omxMatrix* omxContiguousDataRow(omxData *od, int row, int start, int length, omxMatrix* om) {
296         // TODO: Might be better to combine this with omxDataRow to make a single accessor omxDataRow with a second signature that accepts an omxContiguousData argument.
297         if(row > od->rows) return NULL; // Sanity check
298
299         if(om == NULL) {
300                 om = omxInitMatrix(om, 1, od->cols, TRUE, od->currentState);
301         }
302         
303         int numcols = od->cols;
304         omxMatrix* dataMat = od->dataMat;
305         double *dest = om->data;
306         double *source = dataMat->data + row * numcols + start;
307         memcpy(dest, source, sizeof(double) * length);
308         return(om);
309 }
310
311 omxMatrix* omxDataRow(omxData *od, int row, omxMatrix* colList, omxMatrix* om) {
312
313         if(colList == NULL || row > od->rows) return NULL;      // Sanity check
314
315         if(om == NULL) {
316                 om = omxInitMatrix(om, 1, od->cols, TRUE, od->currentState);
317         }
318
319         int numcols = om->cols;
320         if(od->dataMat != NULL) { // Matrix Object
321                 omxMatrix* dataMat = od->dataMat;
322                 for(int j = 0; j < numcols; j++) {
323                         omxSetMatrixElement(om, 0, j, omxMatrixElement(dataMat, row, 
324                                                                        omxVectorElement(colList, j)));
325                 }
326         } else {                // Data Frame object
327                 double dataElement;
328                 int* locations = od->location;
329                 int** intDataColumns = od->intData;
330                 double **realDataColumns = od->realData;
331                 for(int j = 0; j < numcols; j++) {
332                         int location = locations[(int)omxVectorElement(colList, j)];
333                         if(location < 0) {
334                                 dataElement = (double) intDataColumns[~location][row];
335                         } else {
336                                 dataElement = realDataColumns[location][row];
337                         }
338                         omxSetMatrixElement(om, 0, j, dataElement);
339                 }
340         }
341         return om;
342 }
343
344 int omxDataIndex(omxData *od, int row) {
345         if(od->indexVector != NULL)
346                 return od->indexVector[row];
347         else return row;
348 }
349
350 int omxDataNumIdenticalRows(omxData *od, int row) {
351         if(od->identicalRows != NULL)
352                 return od->identicalRows[row];
353         else return 1;
354 }
355 int omxDataNumIdenticalMissingness(omxData *od, int row) {
356         if(od->identicalMissingness != NULL)
357                 return od->identicalMissingness[row];
358         else return 1;
359 }
360
361 int omxDataNumIdenticalDefs(omxData *od, int row){
362         if(od->identicalDefs != NULL)
363                 return od->identicalDefs[row];
364         else return 1;
365 }
366
367 int omxDataNumIdenticalContinuousRows(omxData *od, int row) {
368         if(od->numNumeric <= 0) {
369                 return od->rows;
370         }
371         return omxDataNumIdenticalRows(od, row);
372 }
373
374 int omxDataNumIdenticalContinuousMissingness(omxData *od, int row) {
375         if(od->numNumeric <= 0) {
376                 return od->rows;
377         }
378         return omxDataNumIdenticalMissingness(od, row);
379 }
380
381 int omxDataNumIdenticalOrdinalRows(omxData *od, int row) {
382         if(od->numFactor <= 0) {
383                 return od->rows;
384         }
385         return omxDataNumIdenticalRows(od, row);
386 }
387
388 int omxDataNumIdenticalOrdinalMissingness(omxData *od, int row) {
389         if(od->numFactor <= 0) {
390                 return od->rows;
391         }
392         return omxDataNumIdenticalMissingness(od, row);
393 }
394
395
396 double omxDataNumObs(omxData *od) {
397         return od->numObs;
398 }
399
400 int omxDataNumFactor(omxData *od) {
401         return od->numFactor;
402 }
403
404 int omxDataNumNumeric(omxData *od) {
405         return od->numNumeric;
406 }
407
408
409 const char *omxDataType(omxData *od) {
410         return od->_type;
411 }
412
413 int elementEqualsDataframe(SEXP column, int offset1, int offset2) {
414         switch (TYPEOF(column)) {
415         case REALSXP:
416                 if(ISNA(REAL(column)[offset1])) return ISNA(REAL(column)[offset2]);
417                 if(ISNA(REAL(column)[offset2])) return ISNA(REAL(column)[offset1]);
418                 return(REAL(column)[offset1] == REAL(column)[offset2]);
419         case LGLSXP:
420         case INTSXP:
421                 return(INTEGER(column)[offset1] == INTEGER(column)[offset2]);           
422         }
423         return(0);
424 }
425
426 int testRowDataframe(SEXP data, int numrow, int numcol, int i, int *row, int base) {
427         SEXP column;
428         int j, equal = TRUE;
429
430         if (i == numrow) {
431                 equal = FALSE;
432         } else {
433                 for(j = 0; j < numcol && equal; j++) {
434                         column = VECTOR_ELT(data, j);
435                         equal = elementEqualsDataframe(column, base, i);
436                 }
437         }
438
439         if (!equal) {
440                 int gap = i - base;
441                 for(j = 0; j < gap; j++) {
442                         row[base + j] = gap - j;
443                 }
444                 base = i;
445         }
446         return(base);
447 }
448
449 int elementEqualsMatrix(SEXP data, int row1, int row2, int numrow, int col) {
450         int coloffset = col * numrow;
451         switch (TYPEOF(data)) {
452         case REALSXP:
453                 if(ISNA(REAL(data)[row1 + coloffset])) return ISNA(REAL(data)[row2 + coloffset]);
454                 if(ISNA(REAL(data)[row2 + coloffset])) return ISNA(REAL(data)[row1 + coloffset]);
455                 return(REAL(data)[row1 + coloffset] == REAL(data)[row2 + coloffset]);
456         case LGLSXP:
457         case INTSXP:
458                 return(INTEGER(data)[row1 + coloffset] == INTEGER(data)[row2 + coloffset]);
459         }
460         return(0);
461 }
462
463 int testRowMatrix(SEXP data, int numrow, int numcol, int i, int *row, int base) {
464         int j, equal = TRUE;
465
466         if (i == numrow) {
467                 equal = FALSE;
468         } else {
469                 for(j = 0; j < numcol && equal; j++) {
470                         equal = elementEqualsMatrix(data, i, base, numrow, j);
471                 }
472         }
473
474         if (!equal) {
475                 int gap = i - base;
476                 for(j = 0; j < gap; j++) {
477                         row[base + j] = gap - j;
478                 }
479                 base = i;
480         }
481         return(base);
482 }
483
484 SEXP findIdenticalMatrix(SEXP data, SEXP missing, SEXP defvars,
485                          SEXP skipMissingExp, SEXP skipDefvarsExp) {
486
487         SEXP retval, identicalRows, identicalMissing, identicalDefvars;
488         int i, numrow, numcol, defvarcol;
489         int *irows, *imissing, *idefvars;
490         int baserows, basemissing, basedefvars;
491         int skipMissing, skipDefvars;
492
493         skipMissing = LOGICAL(skipMissingExp)[0];
494         skipDefvars = LOGICAL(skipDefvarsExp)[0];
495         numrow = nrows(data);
496         numcol = ncols(data);
497         defvarcol = ncols(defvars);
498         PROTECT(retval = allocVector(VECSXP, 3));
499         PROTECT(identicalRows = allocVector(INTSXP, numrow));
500         PROTECT(identicalMissing = allocVector(INTSXP, numrow));
501         PROTECT(identicalDefvars = allocVector(INTSXP, numrow));
502         irows = INTEGER(identicalRows);
503         imissing = INTEGER(identicalMissing);
504         idefvars = INTEGER(identicalDefvars);
505         if (skipMissing) {
506                 for(i = 0; i < numrow; i++) {
507                         imissing[i] = numrow - i;
508                 }
509         }
510         if (skipDefvars) {
511                 for(i = 0; i < numrow; i++) {
512                         idefvars[i] = numrow - i;
513                 }
514         }
515         baserows = 0;
516         basemissing = 0;
517         basedefvars = 0;
518         for(i = 1; i <= numrow; i++) {
519                 baserows = testRowMatrix(data, numrow, numcol, i, irows, baserows); 
520                 if (!skipMissing) {
521                         basemissing = testRowMatrix(missing, numrow, numcol, i, imissing, basemissing); 
522                 }
523                 if (!skipDefvars) {
524                         basedefvars = testRowMatrix(defvars, numrow, defvarcol, i, idefvars, basedefvars);
525                 }
526         }
527         SET_VECTOR_ELT(retval, 0, identicalRows);
528         SET_VECTOR_ELT(retval, 1, identicalMissing);
529         SET_VECTOR_ELT(retval, 2, identicalDefvars);
530         UNPROTECT(4); // retval, identicalRows, identicalMissing, identicalDefvars
531         return retval;
532 }
533
534 SEXP findIdenticalDataFrame(SEXP data, SEXP missing, SEXP defvars,
535                             SEXP skipMissingExp, SEXP skipDefvarsExp) {
536
537         SEXP retval, identicalRows, identicalMissing, identicalDefvars;
538         int i, numrow, numcol, defvarcol;
539         int *irows, *imissing, *idefvars;
540         int baserows, basemissing, basedefvars;
541         int skipMissing, skipDefvars;
542
543         skipMissing = LOGICAL(skipMissingExp)[0];
544         skipDefvars = LOGICAL(skipDefvarsExp)[0];
545         numrow = length(VECTOR_ELT(data, 0));
546         numcol = length(data);
547         defvarcol = length(defvars);
548         PROTECT(retval = allocVector(VECSXP, 3));
549         PROTECT(identicalRows = allocVector(INTSXP, numrow));
550         PROTECT(identicalMissing = allocVector(INTSXP, numrow));
551         PROTECT(identicalDefvars = allocVector(INTSXP, numrow));
552         irows = INTEGER(identicalRows);
553         imissing = INTEGER(identicalMissing);
554         idefvars = INTEGER(identicalDefvars);
555         if (skipMissing) {
556                 for(i = 0; i < numrow; i++) {
557                         imissing[i] = numrow - i;
558                 }
559         }
560         if (skipDefvars) {
561                 for(i = 0; i < numrow; i++) {
562                         idefvars[i] = numrow - i;
563                 }
564         }
565         baserows = 0;
566         basemissing = 0;
567         basedefvars = 0;
568         for(i = 1; i <= numrow; i++) {
569                 baserows = testRowDataframe(data, numrow, numcol, i, irows, baserows); 
570                 if (!skipMissing) {
571                         basemissing = testRowMatrix(missing, numrow, numcol, i, imissing, basemissing);
572                 }
573                 if (!skipDefvars) {
574                         basedefvars = testRowDataframe(defvars, numrow, defvarcol, i, idefvars, basedefvars);
575                 }
576         }
577         SET_VECTOR_ELT(retval, 0, identicalRows);
578         SET_VECTOR_ELT(retval, 1, identicalMissing);
579         SET_VECTOR_ELT(retval, 2, identicalDefvars);
580         UNPROTECT(4); // retval, identicalRows, identicalMissing, identicalDefvars
581         return retval;
582 }
583
584 SEXP findIdenticalRowsData(SEXP data, SEXP missing, SEXP defvars,
585                            SEXP skipMissingness, SEXP skipDefvars) {
586         if (isMatrix(data)) {
587                 return(findIdenticalMatrix(data, missing, defvars, skipMissingness, skipDefvars));
588         } else {
589                 return(findIdenticalDataFrame(data, missing, defvars, skipMissingness, skipDefvars));
590         }
591 }
592
593 void omxPrintData(omxData *od, const char *header) {
594         if (!header) error("omxPrintData: header is NULL");
595
596         if (!od) {
597                 Rprintf("%s: NULL\n", header);
598                 return;
599         }
600
601         Rprintf("%s(%s): %f observations %d x %d\n", header, od->_type, od->numObs,
602                 od->rows, od->cols);
603         Rprintf("numNumeric %d numFactor %d\n", od->numNumeric, od->numFactor);
604
605         if (od->location) {
606                 for(int j = 0; j < od->cols; j++) {
607                         int loc = od->location[j];
608                         if (loc < 0) {
609                                 Rprintf("Integer[%d]:", j);
610                                 int *val = od->intData[~loc];
611                                 for (int vx=0; vx < od->numObs; vx++) {
612                                         Rprintf(" %d", val[vx]);
613                                 }
614                         } else {
615                                 Rprintf("Numeric[%d]:", j);
616                                 double *val = od->realData[loc];
617                                 for (int vx=0; vx < od->numObs; vx++) {
618                                         Rprintf(" %.3g", val[vx]);
619                                 }
620                         }
621                         Rprintf("\n");
622                 }
623         }
624
625         if (od->identicalRows) {
626                 Rprintf("\trow\tmissing\tdefvars\n");
627                 for(int j = 0; j < od->rows; j++) {
628                         Rprintf("[%d]\t%d\t%d\t%d\n", j, od->identicalRows[j],
629                                 od->identicalMissingness[j], od->identicalDefs[j]);
630                 }
631         }
632
633         if (od->dataMat) omxPrintMatrix(od->dataMat, "dataMat");
634         if (od->meansMat) omxPrintMatrix(od->meansMat, "meansMat");
635 }
636