r-devel / r-dev-day

Repo to organize tasks for R Dev Days
7 stars 1 forks source link

Bug 15027 - max.print is ignored for matrix headers #4

Open hturner opened 4 months ago

hturner commented 4 months ago

This bug was analysed by @huizezhang-sherry and @gmbecker and the Bug BBQ in 2024 and also by @elinw on the original bug report.

I think a potential solution would be to avoid printing the column headers from matrices/data.frames when all rows are omitted due to the max argument or the max.print option. This is consistent with the behaviour for arrays, e.g.

``` r
print(array(dim= c(6, 6, 6)), max =5)
#>  [ reached getOption("max.print") -- omitted 6 matrix slice(s) ]
```

(Note the message is misleading here, see #3).

According to ?print.data.frame, this function

formats the data frame column-by-column, then converts to a character matrix and dispatches to the print method for matrices.

So, without digging too deep, I think a fix for both cases might be implemented by modifying the C function printMatrix, e.g. changing the logic to skip printing the matrix and just print the message when there are zero rows.

Potential a good issue for someone new to working on C code in base R.

hanneoberman commented 1 month ago

Options:

hanneoberman commented 1 month ago

TODO:

hanneoberman commented 1 month ago

This seems to work

Index: src/main/printarray.c
===================================================================
--- src/main/printarray.c   (revision 86890)
+++ src/main/printarray.c   (working copy)
@@ -116,8 +116,9 @@
  * We define macros that will be re-used in the other functions,
  * and comment the common code here (only):
 */
-static void printLogicalMatrix(SEXP sx, int offset, int r_pr, int r, int c,
-                  SEXP rl, SEXP cl, const char *rn, const char *cn,
+static void printLogicalMatrix(SEXP sx, int offset, int r_pr, int c_pr, 
+                  int r, int c, SEXP rl, SEXP cl, 
+                  const char *rn, const char *cn,
                   Rboolean print_ij)
 {
 /* initialization; particularly of row labels, rl= dimnames(.)[[1]] and
@@ -181,7 +182,7 @@
        MatrixRowLabel(rl, i, rlabw, lbloff);           \
    Rprintf("\n");                          \
     }                                  \
-    else while (jmin < c) {                        \
+    else while (jmin < c_pr) {                     \
    /* print columns  jmin:(jmax-1)  where jmax has to be determined first */ \
                                    \
    width = rlabw;                          \
@@ -190,7 +191,7 @@
        width += w[jmax] _W_EXTRA_;                 \
        jmax++;                         \
    }                               \
-   while (jmax < c && width + w[jmax] _W_EXTRA_ < R_print.width);  \
+   while (jmax < c_pr && width + w[jmax] _W_EXTRA_ < R_print.width);   \
                                    \
    _PRINT_ROW_LAB;                         \
                                    \
@@ -220,7 +221,7 @@

 }

-static void printIntegerMatrix(SEXP sx, int offset, int r_pr, int r, int c,
+static void printIntegerMatrix(SEXP sx, int offset, int r_pr, int c_pr, int r, int c,
                   SEXP rl, SEXP cl, const char *rn, const char *cn,
                   Rboolean print_ij)
 {
@@ -233,7 +234,7 @@
           Rprintf("%s", EncodeInteger(x[i + j * (R_xlen_t) r], w[j])));
 }

-static void printRealMatrix(SEXP sx, int offset, int r_pr, int r, int c,
+static void printRealMatrix(SEXP sx, int offset, int r_pr, int c_pr, int r, int c,
                SEXP rl, SEXP cl, const char *rn, const char *cn,
                Rboolean print_ij)
 {
@@ -250,7 +251,7 @@
                                              w[j], d[j], e[j], OutDec)) );
 }

-static void printComplexMatrix(SEXP sx, int offset, int r_pr, int r, int c,
+static void printComplexMatrix(SEXP sx, int offset, int r_pr, int c_pr, int r, int c,
                   SEXP rl, SEXP cl, const char *rn, const char *cn,
                   Rboolean print_ij)
 {
@@ -283,7 +284,7 @@
                         wi[j], di[j], ei[j], OutDec)) )
 }

-static void printStringMatrix(SEXP sx, int offset, int r_pr, int r, int c,
+static void printStringMatrix(SEXP sx, int offset, int r_pr, int c_pr, int r, int c,
                  int quote, int right, SEXP rl, SEXP cl,
                  const char *rn, const char *cn, Rboolean print_ij)
 {
@@ -309,7 +310,7 @@
                                w[j], quote, right)) );
 }

-static void printRawMatrix(SEXP sx, int offset, int r_pr, int r, int c,
+static void printRawMatrix(SEXP sx, int offset, int r_pr, int c_pr, int r, int c,
               SEXP rl, SEXP cl, const char *rn, const char *cn,
               Rboolean print_ij)
 {
@@ -345,28 +346,32 @@
    return;
     }
     r_pr = r;
+   int c_pr = c;
+   if (c > 0 && R_print.max < c) 
+       c_pr = R_print.max;
     if(c > 0 && R_print.max / c < r) /* avoid integer overflow */
    /* using floor(), not ceil(), since 'c' could be huge: */
    r_pr = R_print.max / c;
+   if(c > c_pr && r_pr < 1 && r > 0) r_pr = 1;
     switch (TYPEOF(x)) {
     case LGLSXP:
-   printLogicalMatrix(x, offset, r_pr, r, c, rl, cl, rn, cn, TRUE);
+   printLogicalMatrix(x, offset, r_pr, c_pr, r, c, rl, cl, rn, cn, TRUE);
    break;
     case INTSXP:
-   printIntegerMatrix(x, offset, r_pr, r, c, rl, cl, rn, cn, TRUE);
+   printIntegerMatrix(x, offset, r_pr, c_pr, r, c, rl, cl, rn, cn, TRUE);
    break;
     case REALSXP:
-   printRealMatrix   (x, offset, r_pr, r, c, rl, cl, rn, cn, TRUE);
+   printRealMatrix   (x, offset, r_pr, c_pr, r, c, rl, cl, rn, cn, TRUE);
    break;
     case CPLXSXP:
-   printComplexMatrix(x, offset, r_pr, r, c, rl, cl, rn, cn, TRUE);
+   printComplexMatrix(x, offset, r_pr, c_pr, r, c, rl, cl, rn, cn, TRUE);
    break;
     case STRSXP:
    if (quote) quote = '"';
-   printStringMatrix (x, offset, r_pr, r, c, quote, right, rl, cl, rn, cn, TRUE);
+   printStringMatrix (x, offset, r_pr, c_pr, r, c, quote, right, rl, cl, rn, cn, TRUE);
    break;
     case RAWSXP:
-   printRawMatrix    (x, offset, r_pr, r, c, rl, cl, rn, cn, TRUE);
+   printRawMatrix    (x, offset, r_pr, c_pr, r, c, rl, cl, rn, cn, TRUE);
    break;
     default:
    UNIMPLEMENTED_TYPE("printMatrix", x);
@@ -382,6 +387,17 @@
    Rprintf(" [ reached getOption(\"max.print\") -- omitted %d rows ]\n",
        r - r_pr);
 #endif
+#ifdef ENABLE_NLS
+    if(c_pr < c) // number of formats must be consistent here
+   Rprintf(ngettext(" [ reached getOption(\"max.print\") -- omitted %d column ]\n",
+            " [ reached getOption(\"max.print\") -- omitted %d columns ]\n",
+            c - c_pr),
+       c - c_pr);
+#else
+    if(c_pr < c)
+   Rprintf(" [ reached getOption(\"max.print\") -- omitted %d columns ]\n",
+       c - c_pr);
+#endif
     vmaxset(vmax);
 }

@@ -447,6 +463,7 @@
        Rboolean do_ij = nb > 0,
        i_last = (i == nb_pr - 1); /* for the last slice */
        int use_nr = i_last ? nr_last : nr;
+       int use_nc = nc;
        if(do_ij) {
        int k = 1;
        Rprintf(", ");
@@ -472,24 +489,24 @@
        }
        switch (TYPEOF(x)) {
        case LGLSXP:
-       printLogicalMatrix(x, i * b, use_nr, nr, nc, dn0, dn1, rn, cn, do_ij);
+       printLogicalMatrix(x, i * b, use_nr, use_nc, nr, nc, dn0, dn1, rn, cn, do_ij);
        break;
        case INTSXP:
-       printIntegerMatrix(x, i * b, use_nr, nr, nc, dn0, dn1, rn, cn, do_ij);
+       printIntegerMatrix(x, i * b, use_nr, use_nc, nr, nc, dn0, dn1, rn, cn, do_ij);
        break;
        case REALSXP:
-       printRealMatrix   (x, i * b, use_nr, nr, nc, dn0, dn1, rn, cn, do_ij);
+       printRealMatrix   (x, i * b, use_nr, use_nc, nr, nc, dn0, dn1, rn, cn, do_ij);
        break;
        case CPLXSXP:
-       printComplexMatrix(x, i * b, use_nr, nr, nc, dn0, dn1, rn, cn, do_ij);
+       printComplexMatrix(x, i * b, use_nr, use_nc, nr, nc, dn0, dn1, rn, cn, do_ij);
        break;
        case STRSXP:
        if (quote) quote = '"';
-       printStringMatrix (x, i * b, use_nr, nr, nc,
+       printStringMatrix (x, i * b, use_nr, use_nc, nr, nc,
                   quote, right, dn0, dn1, rn, cn, do_ij);
        break;
        case RAWSXP:
-       printRawMatrix    (x, i * b, use_nr, nr, nc, dn0, dn1, rn, cn, do_ij);
+       printRawMatrix    (x, i * b, use_nr, use_nc, nr, nc, dn0, dn1, rn, cn, do_ij);
        break;
        }
        Rprintf("\n");
hanneoberman commented 1 month ago

Full file

/*
 *  R : A Computer Language for Statistical Data Analysis
 *  Copyright (C) 1995, 1996    Robert Gentleman and Ross Ihaka
 *  Copyright (C) 2000--2023    The R Core Team
 *  Copyright (C) 2001--2012    The R Foundation
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, a copy is available at
 *  https://www.R-project.org/Licenses/
 *
 *
 *  EXPORTS printMatrix()
 *      printArray()
 *
 *  See ./printutils.c   for general remarks on Printing
 *           and the Encode.. utils.
 *
 *  See ./format.c   for the  format_FOO_  functions used below.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <Defn.h>
#include <Print.h>

#include <stdlib.h> /* for div() */

/* We need display width of a string.
   Used only for row/column names found by GetMatrixDimnames,
   so in native encoding.  (NULL ones from do_prmatrix are skipped.)
*/
int Rstrwid(const char *str, int slen, int enc, int quote);  /* from printutils.c */
#define strwidth(x) Rstrwid(x, (int) strlen(x), CE_NATIVE, 0)

/* ceil_DIV(a,b) :=  ceil(a / b)  in _int_ arithmetic : */
static R_INLINE
int ceil_DIV(int a, int b)
{
    div_t div_res = div(a, b);
    return div_res.quot + ((div_res.rem != 0) ? 1 : 0);
}

/* moved from printutils.c */

static void MatrixColumnLabel(SEXP cl, int j, int w)
{
    if (!isNull(cl)) {
    SEXP tmp = STRING_ELT(cl, j);
    int l = (tmp == NA_STRING) ? R_print.na_width_noquote : Rstrlen(tmp, 0);
    Rprintf("%*s%s", w-l, "",
        EncodeString(tmp, l, 0, Rprt_adj_left));
    }
    else {
    Rprintf("%*s[,%ld]", w-IndexWidth(j+1)-3, "", (long)j+1);
    }
}

static void RightMatrixColumnLabel(SEXP cl, int j, int w)
{
    if (!isNull(cl)) {
    SEXP tmp = STRING_ELT(cl, j);
    int l = (tmp == NA_STRING) ? R_print.na_width_noquote : Rstrlen(tmp, 0);
    /* This does not work correctly at least on FC3
    Rprintf("%*s", R_print.gap+w,
        EncodeString(tmp, l, 0, Rprt_adj_right)); */
    Rprintf("%*s%s", R_print.gap+w-l, "",
        EncodeString(tmp, l, 0, Rprt_adj_right));
    }
    else {
    Rprintf("%*s[,%ld]%*s", R_print.gap, "", (long)j+1, w-IndexWidth(j+1)-3, "");
    }
}

static void LeftMatrixColumnLabel(SEXP cl, int j, int w)
{
    if (!isNull(cl)) {
    SEXP tmp = STRING_ELT(cl, j);
    int l = (tmp == NA_STRING) ? R_print.na_width_noquote : Rstrlen(tmp, 0);
    Rprintf("%*s%s%*s", R_print.gap, "",
        EncodeString(tmp, l, 0, Rprt_adj_left), w-l, "");
    }
    else {
    Rprintf("%*s[,%ld]%*s", R_print.gap, "", (long)j+1, w-IndexWidth(j+1)-3, "");
    }
}

static void MatrixRowLabel(SEXP rl, int i, int rlabw, int lbloff)
{
    if (!isNull(rl)) {
    SEXP tmp = STRING_ELT(rl, i);
    int l = (tmp == NA_STRING) ? R_print.na_width_noquote : Rstrlen(tmp, 0);
    Rprintf("\n%*s%s%*s", lbloff, "",
        EncodeString(tmp, l, 0, Rprt_adj_left),
        rlabw-l-lbloff, "");
    }
    else {
    Rprintf("\n%*s[%ld,]", rlabw-3-IndexWidth(i + 1), "", (long)i+1);
    }
}

/* This is the first (of 6)  print<TYPE>Matrix()  functions.
 * We define macros that will be re-used in the other functions,
 * and comment the common code here (only):
*/
static void printLogicalMatrix(SEXP sx, int offset, int r_pr, int c_pr, 
                   int r, int c, SEXP rl, SEXP cl, 
                   const char *rn, const char *cn,
                   Rboolean print_ij)
{
/* initialization; particularly of row labels, rl= dimnames(.)[[1]] and
 * rn = names(dimnames(.))[1] : */
#define _PRINT_INIT_rl_rn               \
    int *w = (int *) R_alloc(c, sizeof(int));       \
    int width, rlabw = -1, clabw = -1; /* -Wall */  \
    int i, j, jmin = 0, jmax = 0, lbloff = 0;       \
                            \
    if (!isNull(rl))                    \
    formatString(STRING_PTR_RO(rl), r, &rlabw, 0);  \
    else                        \
    rlabw = IndexWidth(r + 1) + 3;          \
                            \
    if (rn) {                       \
    int rnw = strwidth(rn);             \
    if ( rnw < rlabw + R_MIN_LBLOFF )       \
        lbloff = R_MIN_LBLOFF;          \
    else                        \
        lbloff = rnw - rlabw;           \
                            \
    rlabw += lbloff;                \
    }

#   define _COMPUTE_W2_(_FORMAT_j_, _LAST_j_)               \
    /* compute w[j] = column-width of j(+1)-th column : */      \
    for (j = 0; j < c; j++) {                       \
    if(print_ij) { _FORMAT_j_; } else w[j] = 0;         \
                                    \
    if (!isNull(cl)) {                      \
        const void *vmax = vmaxget();               \
        if(STRING_ELT(cl, j) == NA_STRING)              \
        clabw = R_print.na_width_noquote;           \
        else clabw = strwidth(translateChar(STRING_ELT(cl, j)));    \
        vmaxset(vmax);                      \
    } else                              \
        clabw = IndexWidth(j + 1) + 3;              \
                                    \
    if (w[j] < clabw)                       \
        w[j] = clabw;                       \
    _LAST_j_;                           \
    }

#   define _COMPUTE_W_(F_j) _COMPUTE_W2_(F_j, w[j] += R_print.gap)
    //                               _LAST_j  ------------------- for all but String

#   define _PRINT_ROW_LAB           \
                        \
    if (cn != NULL)             \
    Rprintf("%*s%s\n", rlabw, "", cn);  \
    if (rn != NULL)             \
    Rprintf("%*s", -rlabw, rn);     \
    else                    \
    Rprintf("%*s", rlabw, "")

#   define _PRINT_MATRIX_(_W_EXTRA_, DO_COLUMN_LABELS, ENCODE_I_J)  \
                                    \
    if (c == 0) {                           \
    _PRINT_ROW_LAB;                         \
    for (i = 0; i < r; i++)                     \
        MatrixRowLabel(rl, i, rlabw, lbloff);           \
    Rprintf("\n");                          \
    }                                   \
    else while (jmin < c_pr) {                      \
    /* print columns  jmin:(jmax-1)  where jmax has to be determined first */ \
                                    \
    width = rlabw;                          \
    /* initially, jmax = jmin */                    \
    do {                                \
        width += w[jmax] _W_EXTRA_;                 \
        jmax++;                         \
    }                               \
    while (jmax < c_pr && width + w[jmax] _W_EXTRA_ < R_print.width);   \
                                    \
    _PRINT_ROW_LAB;                         \
                                    \
    DO_COLUMN_LABELS;                       \
                                    \
    for (i = 0; i < r_pr; i++) {                    \
        MatrixRowLabel(rl, i, rlabw, lbloff); /* starting with an "\n" */ \
        if(print_ij) for (j = jmin; j < jmax; j++) {        \
        ENCODE_I_J;                     \
        }                               \
    }                               \
    Rprintf("\n");                          \
    jmin = jmax;                            \
    }

#   define STD_ColumnLabels         \
    for (j = jmin; j < jmax ; j++)      \
        MatrixColumnLabel(cl, j, w[j])

    _PRINT_INIT_rl_rn;
    const int *x = LOGICAL_RO(sx) + offset;

    _COMPUTE_W_( formatLogical(&x[j * (R_xlen_t) r], (R_xlen_t) r, &w[j]) );

    _PRINT_MATRIX_( , STD_ColumnLabels,
           Rprintf("%s", EncodeLogical(x[i + j * (R_xlen_t) r], w[j])));

}

static void printIntegerMatrix(SEXP sx, int offset, int r_pr, int c_pr, int r, int c,
                   SEXP rl, SEXP cl, const char *rn, const char *cn,
                   Rboolean print_ij)
{
    _PRINT_INIT_rl_rn;
    const int *x = INTEGER_RO(sx) + offset;

    _COMPUTE_W_( formatInteger(&x[j * (R_xlen_t) r], (R_xlen_t) r, &w[j]) );

    _PRINT_MATRIX_( , STD_ColumnLabels,
           Rprintf("%s", EncodeInteger(x[i + j * (R_xlen_t) r], w[j])));
}

static void printRealMatrix(SEXP sx, int offset, int r_pr, int c_pr, int r, int c,
                SEXP rl, SEXP cl, const char *rn, const char *cn,
                Rboolean print_ij)
{
    _PRINT_INIT_rl_rn;
    const double *x = REAL_RO(sx) + offset;
    int *d = (int *) R_alloc(c, sizeof(int)),
    *e = (int *) R_alloc(c, sizeof(int));

    _COMPUTE_W_( formatReal(&x[j * (R_xlen_t) r], (R_xlen_t) r, &w[j],
                            &d[j], &e[j], 0) );

    _PRINT_MATRIX_( , STD_ColumnLabels,
           Rprintf("%s", EncodeReal0(x[i + j * (R_xlen_t) r],
                                             w[j], d[j], e[j], OutDec)) );
}

static void printComplexMatrix(SEXP sx, int offset, int r_pr, int c_pr, int r, int c,
                   SEXP rl, SEXP cl, const char *rn, const char *cn,
                   Rboolean print_ij)
{
    _PRINT_INIT_rl_rn;
    const Rcomplex *x = COMPLEX_RO(sx) + offset;
    int *dr = (int *) R_alloc(c, sizeof(int)),
    *er = (int *) R_alloc(c, sizeof(int)),
    *wr = (int *) R_alloc(c, sizeof(int)),
    *di = (int *) R_alloc(c, sizeof(int)),
    *ei = (int *) R_alloc(c, sizeof(int)),
    *wi = (int *) R_alloc(c, sizeof(int));

    /* Determine the column widths */
    _COMPUTE_W_( formatComplex(&x[j * (R_xlen_t) r], (R_xlen_t) r,
                   &wr[j], &dr[j], &er[j],
                   &wi[j], &di[j], &ei[j], 0);
         w[j] = wr[j] + wi[j] + 2 );

    _PRINT_MATRIX_( , STD_ColumnLabels,
           if (ISNA(x[i + j * (R_xlen_t) r].r) ||
               ISNA(x[i + j * (R_xlen_t) r].i))

               Rprintf("%s", EncodeReal0(NA_REAL, w[j], 0, 0, OutDec));
           else
               /* Note that the label printing may modify w[j], so wr[j] is not 
                  necessarily still valid, and we use w[j] - wi[j] - 2  */
               Rprintf("%s",
                   EncodeComplex(x[i + j * (R_xlen_t) r],
                         w[j] - wi[j] - 2, dr[j], er[j],
                         wi[j], di[j], ei[j], OutDec)) )
}

static void printStringMatrix(SEXP sx, int offset, int r_pr, int c_pr, int r, int c,
                  int quote, int right, SEXP rl, SEXP cl,
                  const char *rn, const char *cn, Rboolean print_ij)
{
    _PRINT_INIT_rl_rn;
    const SEXP *x = STRING_PTR_RO(sx)+offset;

    _COMPUTE_W2_( formatString(&x[j * (R_xlen_t) r], (R_xlen_t) r,
                               &w[j], quote), );

    _PRINT_MATRIX_( + R_print.gap,
               /* DO_COLUMN_LABELS = */
           if (right) {
               for (j = jmin; j < jmax ; j++)
               RightMatrixColumnLabel(cl, j, w[j]);
           }
           else {
               for (j = jmin; j < jmax ; j++)
               LeftMatrixColumnLabel(cl, j, w[j]);
           },
           /* ENCODE_I = */
           Rprintf("%*s%s", R_print.gap, "",
               EncodeString(x[i + j * (R_xlen_t) r],
                                w[j], quote, right)) );
}

static void printRawMatrix(SEXP sx, int offset, int r_pr, int c_pr, int r, int c,
               SEXP rl, SEXP cl, const char *rn, const char *cn,
               Rboolean print_ij)
{
    _PRINT_INIT_rl_rn;
    const Rbyte *x = RAW_RO(sx) + offset;

    _COMPUTE_W_( formatRaw(&x[j * (R_xlen_t) r], (R_xlen_t) r, &w[j]) )

    _PRINT_MATRIX_( , STD_ColumnLabels,
           Rprintf("%*s%s", w[j]-2, "",
           EncodeRaw(x[i + j * (R_xlen_t) r], "")) );
}

/* rm and cn are found by GetMatrixDimnames so in native encoding */
attribute_hidden
void printMatrix(SEXP x, int offset, SEXP dim, int quote, int right,
         SEXP rl, SEXP cl, const char *rn, const char *cn)
{
/* 'rl' and 'cl' are dimnames(.)[[1]] and dimnames(.)[[2]]  whereas
 * 'rn' and 'cn' are the  names(dimnames(.))
 */
    const void *vmax = vmaxget();
    const int *pdim = INTEGER_RO(dim);
    int r = pdim[0];
    int c = pdim[1], r_pr;
    /* PR#850 */
    if ((rl != R_NilValue) && (r > length(rl)))
    error(_("too few row labels"));
    if ((cl != R_NilValue) && (c > length(cl)))
    error(_("too few column labels"));
    if (r == 0 && c == 0) { // FIXME?  names(dimnames(.)) :
    Rprintf("<0 x 0 matrix>\n");
    return;
    }
    r_pr = r;

    //
    int c_pr = c > R_print.max ? R_print.max : c;

    if(c > 0 && R_print.max / c < r) /* avoid integer overflow */
    /* using floor(), not ceil(), since 'c' could be huge: */
    r_pr = R_print.max / c;
    if(c > c_pr && r_pr < 1 && r > 0) r_pr = 1;
    switch (TYPEOF(x)) {
    case LGLSXP:
    printLogicalMatrix(x, offset, r_pr, c_pr, r, c, rl, cl, rn, cn, TRUE);
    break;
    case INTSXP:
    printIntegerMatrix(x, offset, r_pr, c_pr, r, c, rl, cl, rn, cn, TRUE);
    break;
    case REALSXP:
    printRealMatrix   (x, offset, r_pr, c_pr, r, c, rl, cl, rn, cn, TRUE);
    break;
    case CPLXSXP:
    printComplexMatrix(x, offset, r_pr, c_pr, r, c, rl, cl, rn, cn, TRUE);
    break;
    case STRSXP:
    if (quote) quote = '"';
    printStringMatrix (x, offset, r_pr, c_pr, r, c, quote, right, rl, cl, rn, cn, TRUE);
    break;
    case RAWSXP:
    printRawMatrix    (x, offset, r_pr, c_pr, r, c, rl, cl, rn, cn, TRUE);
    break;
    default:
    UNIMPLEMENTED_TYPE("printMatrix", x);
    }
    if (r_pr < r || c_pr < c) {
        Rprintf(" [ reached getOption(\"max.print\") -- omitted");
        if (r_pr < r) {
            Rprintf(ngettext(" %d row", " %d rows", r - r_pr), r - r_pr);
        }
        if (c_pr < c) {
            if (r_pr < r) Rprintf(" and");
            Rprintf(ngettext(" %d column", " %d columns", c - c_pr), c - c_pr);
        }
        Rprintf(" ]\n");
    }
    vmaxset(vmax);
}

attribute_hidden
void printArray(SEXP x, SEXP dim, int quote, int right, SEXP dimnames)
{
/* == printArray(.) */
    const void *vmax = vmaxget();
    int ndim = LENGTH(dim), nprotect = 0;
    const char *rn = NULL, *cn = NULL;

    if (ndim == 1)
    printVector(x, 1, quote);
    else if (ndim == 2) {
    SEXP rl, cl;
    GetMatrixDimnames(x, &rl, &cl, &rn, &cn);
    printMatrix(x, 0, dim, quote, 0, rl, cl, rn, cn);
    }
    else { /* ndim >= 3 */
    SEXP dn, dnn, dn0, dn1;
    const int *dims = INTEGER_RO(dim);
    int i, j, nb, nb_pr, nr_last,
        nr = dims[0], nc = dims[1],
        b = nr * nc;
    Rboolean max_reached, has_dimnames = (dimnames != R_NilValue),
        has_dnn = has_dimnames;

    if (!has_dimnames) {
        dn0 = R_NilValue;
        dn1 = R_NilValue;
        dnn = R_NilValue; /* -Wall */
    }
    else {
        dn0 = VECTOR_ELT(dimnames, 0);
        dn1 = VECTOR_ELT(dimnames, 1);
        dnn = getAttrib(dimnames, R_NamesSymbol);
        has_dnn = !isNull(dnn);
        if ( has_dnn ) {
        PROTECT(dnn);
        nprotect++;
        rn = (char *) translateChar(STRING_ELT(dnn, 0));
        cn = (char *) translateChar(STRING_ELT(dnn, 1));
        }
    }
    /* nb := #{entries} in a slice such as x[1,1,..] or equivalently,
     *       the number of matrix slices   x[ , , *, ..]  which
     *       are printed as matrices -- if options("max.print") allows */
    for (i = 2, nb = 1; i < ndim; i++)
        nb *= dims[i];
    max_reached = (b > 0 && R_print.max / b < nb);
    if (max_reached) { /* i.e., also  b > 0, nr > 0, nc > 0, nb > 0 */
        /* nb_pr := the number of matrix slices to be printed */
        nb_pr = ceil_DIV(R_print.max, b);
        /* for the last, (nb_pr)th matrix slice, use only nr_last rows;
         *  using floor(), not ceil(), since 'nc' could be huge: */
        nr_last = (R_print.max - b * (nb_pr - 1)) / nc;
        if(nr_last == 0) { nb_pr--; nr_last = nr; }
    } else {
        nb_pr = (nb > 0) ? nb : 1; // do print *something* when dim = c(a,b,0)
        nr_last = nr;
    }
    for (i = 0; i < nb_pr; i++) {
        Rboolean do_ij = nb > 0,
        i_last = (i == nb_pr - 1); /* for the last slice */
        int use_nr = i_last ? nr_last : nr;
        int use_nc = nc;
        if(do_ij) {
        int k = 1;
        Rprintf(", ");
        for (j = 2 ; j < ndim; j++) {
            int l = (i / k) % dims[j] + 1;
            if (has_dimnames &&
            ((dn = VECTOR_ELT(dimnames, j)) != R_NilValue)) {
            if ( has_dnn )
                Rprintf(", %s = %s",
                    translateChar(STRING_ELT(dnn, j)),
                    translateChar(STRING_ELT(dn, l - 1)));
            else
                Rprintf(", %s", translateChar(STRING_ELT(dn, l - 1)));
            } else
            Rprintf(", %d", l);
            k *= dims[j];
        }
        Rprintf("\n\n");
        } else { // nb == 0 -- e.g. <2 x 3 x 0 array of logical>
        for (i = 0; i < ndim; i++)
            Rprintf("%s%d", (i == 0) ? "<" : " x ", dims[i]);
        Rprintf(" array of %s>\n", CHAR(type2str_nowarn(TYPEOF(x))));
        }
        switch (TYPEOF(x)) {
        case LGLSXP:
        printLogicalMatrix(x, i * b, use_nr, use_nc, nr, nc, dn0, dn1, rn, cn, do_ij);
        break;
        case INTSXP:
        printIntegerMatrix(x, i * b, use_nr, use_nc, nr, nc, dn0, dn1, rn, cn, do_ij);
        break;
        case REALSXP:
        printRealMatrix   (x, i * b, use_nr, use_nc, nr, nc, dn0, dn1, rn, cn, do_ij);
        break;
        case CPLXSXP:
        printComplexMatrix(x, i * b, use_nr, use_nc, nr, nc, dn0, dn1, rn, cn, do_ij);
        break;
        case STRSXP:
        if (quote) quote = '"';
        printStringMatrix (x, i * b, use_nr, use_nc, nr, nc,
                   quote, right, dn0, dn1, rn, cn, do_ij);
        break;
        case RAWSXP:
        printRawMatrix    (x, i * b, use_nr, use_nc, nr, nc, dn0, dn1, rn, cn, do_ij);
        break;
        }
        Rprintf("\n");
    }

    if(max_reached && nb_pr < nb) {
        Rprintf(" [ reached getOption(\"max.print\") -- omitted");
        if(nr_last < nr) Rprintf(" %d row(s) and", nr - nr_last);
        Rprintf(" %d matrix slice(s) ]\n", nb - nb_pr);
    }
    }
    UNPROTECT(nprotect);
    vmaxset(vmax);
}
loreabad6 commented 1 month ago

Potential tests for print-tests.R

## max.print and max 
## bug 15027 https://bugs.r-project.org/show_bug.cgi?id=15027
## whenever the columns are larger than max.print, no values inside the matrix are displayed
print(matrix(nrow = 100, ncol = 4), max = 5) ## works
## fix: ommiting rows **and** columns and warning printed
## case: omiting rows and columns
options(max.print = 100)
matrix(nrow=20000,ncol=20000)
print(matrix(nrow = 10, ncol = 4), max = 3)
## case: omitting rows
print(matrix(nrow = 10, ncol = 2), max = 5)
## case: omitting cols, at least one row prints
print(matrix(nrow = 1, ncol = 6), max = 5)
## in R 4.4.0 there should be a warning for ommited rows (NEW BUG)
print(array(dim= c(2, 2, 1)), max = 2)
## this does not print anything but it should show 
## at least one element according to the logic of max.print
print(array(dim= c(2, 2, 1)), max = 1)
lionel- commented 1 month ago

Patch submitted at https://bugs.r-project.org/show_bug.cgi?id=15027#c6

It was great working with you on this patch @hanneoberman @loreabad6 @Abhi-1U @ekaterina-akimova!

Still needs a related fix for arrays, in case anyone wants to work on one:

print(array(dim = c(2, 2, 1)), max = 1)
#> [ reached getOption("max.print") -- omitted 1 matrix slice(s) ]