/*
 *  R : A Computer Language for Statistical Data Analysis
 *  Copyright (C) 1999-2023  The R Core Team.
 *  Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
 *
 *  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/
 */

/* Internal header, not installed */

/* this header is always to be included from others.
   It is only called if COMPILING_R is defined (in util.c) or
   from GNU C systems.

   There are different conventions for inlining across compilation units.
   See http://www.greenend.org.uk/rjk/2003/03/inline.html
 */
#ifndef R_INLINES_H_
#define R_INLINES_H_

/* Probably not able to use C99 semantics in gcc < 4.3.0 */
#if __GNUC__ == 4 && __GNUC_MINOR__ >= 3 && defined(__GNUC_STDC_INLINE__) && !defined(C99_INLINE_SEMANTICS)
#define C99_INLINE_SEMANTICS 1
#endif

/* Apple's gcc build >5400 (since Xcode 3.0) doesn't support GNU inline in C99 mode 
   FIXME: can this possibly still be needed?
*/
#if __APPLE_CC__ > 5400 && !defined(C99_INLINE_SEMANTICS) && __STDC_VERSION__ >= 199901L
#define C99_INLINE_SEMANTICS 1
#endif

#ifdef COMPILING_R
/* defined only in inlined.c: this emits standalone code there */
# define INLINE_FUN
#else
/* This section is normally only used for versions of gcc which do not
   support C99 semantics.  __GNUC_STDC_INLINE__ is defined if
   GCC is following C99 inline semantics by default: we
   switch R's usage to the older GNU semantics via attributes.
   Do this even for __GNUC_GNUC_INLINE__ to shut up warnings in 4.2.x.
   __GNUC_STDC_INLINE__ and __GNUC_GNU_INLINE__ were added in gcc 4.2.0.
*/
# if defined(__GNUC_STDC_INLINE__) || defined(__GNUC_GNU_INLINE__)
#  define INLINE_FUN extern __attribute__((gnu_inline)) inline
# else
#  define INLINE_FUN extern R_INLINE
# endif
#endif /* ifdef COMPILING_R */

#if C99_INLINE_SEMANTICS
# undef INLINE_FUN
# ifdef COMPILING_R
/* force exported copy */
#  define INLINE_FUN extern inline
# else
/* either inline or link to extern version at compiler's choice */
#  define INLINE_FUN inline
# endif /* ifdef COMPILING_R */
#endif /* C99_INLINE_SEMANTICS */


#include <string.h> /* for strlen, strcmp */

/* define inline-able functions */
#ifdef TESTING_WRITE_BARRIER
# define STRICT_TYPECHECK
# define CATCH_ZERO_LENGTH_ACCESS
#endif


#if defined(USE_RINTERNALS) || defined(COMPILING_R)
/* inline version of CAR to support immediate bindings */
INLINE_FUN SEXP CAR(SEXP e)
{
    if (BNDCELL_TAG(e))
	error("bad binding access");
    return CAR0(e);
}
#else
SEXP CAR(SEXP e);
#endif

#ifdef STRICT_TYPECHECK
INLINE_FUN void CHKVEC(SEXP x) {
    switch (TYPEOF(x)) {
    case CHARSXP:
    case LGLSXP:
    case INTSXP:
    case REALSXP:
    case CPLXSXP:
    case STRSXP:
    case VECSXP:
    case EXPRSXP:
    case RAWSXP:
    case WEAKREFSXP:
	break;
    default:
	error("cannot get data pointer of '%s' objects", R_typeToChar(x));
    }
}
#else
# define CHKVEC(x) do {} while(0)
#endif

INLINE_FUN void *DATAPTR(SEXP x) {
    CHKVEC(x);
    if (ALTREP(x))
	return ALTVEC_DATAPTR(x);
#ifdef CATCH_ZERO_LENGTH_ACCESS
    /* Attempts to read or write elements of a zero length vector will
       result in a segfault, rather than read and write random memory.
       Returning NULL would be more natural, but Matrix seems to assume
       that even zero-length vectors have non-NULL data pointers, so
       return (void *) 1 instead. Zero-length CHARSXP objects still
       have a trailing zero byte so they are not handled. */
    else if (STDVEC_LENGTH(x) == 0 && TYPEOF(x) != CHARSXP)
	return (void *) 1;
#endif
    else
	return STDVEC_DATAPTR(x);
}

INLINE_FUN const void *DATAPTR_RO(SEXP x) {
    CHKVEC(x);
    if (ALTREP(x))
	return ALTVEC_DATAPTR_RO(x);
    else
	return STDVEC_DATAPTR(x);
}

INLINE_FUN const void *DATAPTR_OR_NULL(SEXP x) {
    CHKVEC(x);
    if (ALTREP(x))
	return ALTVEC_DATAPTR_OR_NULL(x);
    else
	return STDVEC_DATAPTR(x);
}

#ifdef STRICT_TYPECHECK
# define CHECK_VECTOR_LGL(x) do {				\
	if (TYPEOF(x) != LGLSXP) error("bad LGLSXP vector");	\
    } while (0)
# define CHECK_VECTOR_INT(x) do {				\
	if (! (TYPEOF(x) == INTSXP || TYPEOF(x) == LGLSXP))	\
	    error("bad INTSXP vector");				\
    } while (0)
# define CHECK_VECTOR_REAL(x) do {				\
	if (TYPEOF(x) != REALSXP) error("bad REALSXP vector");	\
    } while (0)
# define CHECK_VECTOR_CPLX(x) do {				\
	if (TYPEOF(x) != CPLXSXP) error("bad CPLXSXP vector");	\
    } while (0)
# define CHECK_VECTOR_RAW(x) do {				\
	if (TYPEOF(x) != RAWSXP) error("bad RAWSXP vector");	\
    } while (0)
#else
# define CHECK_VECTOR_LGL(x) do { } while(0)
# define CHECK_VECTOR_INT(x) do { } while(0)
# define CHECK_VECTOR_REAL(x) do { } while(0)
# define CHECK_VECTOR_CPLX(x) do { } while(0)
# define CHECK_VECTOR_RAW(x) do { } while(0)
#endif

INLINE_FUN const int *LOGICAL_OR_NULL(SEXP x) {
    CHECK_VECTOR_LGL(x);
    return ALTREP(x) ? ALTVEC_DATAPTR_OR_NULL(x) : STDVEC_DATAPTR(x);
}

INLINE_FUN const int *INTEGER_OR_NULL(SEXP x) {
    CHECK_VECTOR_INT(x);
    return ALTREP(x) ? ALTVEC_DATAPTR_OR_NULL(x) : STDVEC_DATAPTR(x);
}

INLINE_FUN const double *REAL_OR_NULL(SEXP x) {
    CHECK_VECTOR_REAL(x);
    return ALTREP(x) ? ALTVEC_DATAPTR_OR_NULL(x) : STDVEC_DATAPTR(x);
}

INLINE_FUN const Rcomplex *COMPLEX_OR_NULL(SEXP x) {
    CHECK_VECTOR_CPLX(x);
    return ALTREP(x) ? ALTVEC_DATAPTR_OR_NULL(x) : STDVEC_DATAPTR(x);
}

INLINE_FUN const Rbyte *RAW_OR_NULL(SEXP x) {
    CHECK_VECTOR_RAW(x);
    return ALTREP(x) ? ALTVEC_DATAPTR_OR_NULL(x) : STDVEC_DATAPTR(x);
}

INLINE_FUN R_xlen_t XLENGTH_EX(SEXP x)
{
    return ALTREP(x) ? ALTREP_LENGTH(x) : STDVEC_LENGTH(x);
}

INLINE_FUN R_xlen_t XTRUELENGTH(SEXP x)
{
    return ALTREP(x) ? ALTREP_TRUELENGTH(x) : STDVEC_TRUELENGTH(x);
}

INLINE_FUN int LENGTH_EX(SEXP x, const char *file, int line)
{
    if (x == R_NilValue) return 0;
    R_xlen_t len = XLENGTH(x);
#ifdef LONG_VECTOR_SUPPORT
    if (len > R_SHORT_LEN_MAX)
	R_BadLongVector(x, file, line);
#endif
    return (int) len;
}

#ifdef STRICT_TYPECHECK
# define CHECK_STDVEC_LGL(x) do {				\
	CHECK_VECTOR_LGL(x);					\
	if (ALTREP(x)) error("bad standard LGLSXP vector");	\
    } while (0)
# define CHECK_STDVEC_INT(x) do {				\
	CHECK_VECTOR_INT(x);					\
	if (ALTREP(x)) error("bad standard INTSXP vector");	\
    } while (0)
# define CHECK_STDVEC_REAL(x) do {				\
	CHECK_VECTOR_REAL(x);					\
	if (ALTREP(x)) error("bad standard REALSXP vector");	\
    } while (0)
# define CHECK_STDVEC_CPLX(x) do {				\
	CHECK_VECTOR_CPLX(x);					\
	if (ALTREP(x)) error("bad standard CPLXSXP vector");	\
    } while (0)
# define CHECK_STDVEC_RAW(x) do {				\
	CHECK_VECTOR_RAW(x);					\
	if (ALTREP(x)) error("bad standard RAWSXP vector");	\
    } while (0)

# define CHECK_SCALAR_LGL(x) do {				\
	CHECK_STDVEC_LGL(x);					\
	if (XLENGTH(x) != 1) error("bad LGLSXP scalar");	\
    } while (0)
# define CHECK_SCALAR_INT(x) do {				\
	CHECK_STDVEC_INT(x);					\
	if (XLENGTH(x) != 1) error("bad INTSXP scalar");	\
    } while (0)
# define CHECK_SCALAR_REAL(x) do {				\
	CHECK_STDVEC_REAL(x);					\
	if (XLENGTH(x) != 1) error("bad REALSXP scalar");	\
    } while (0)
# define CHECK_SCALAR_CPLX(x) do {				\
	CHECK_STDVEC_CPLX(x);					\
	if (XLENGTH(x) != 1) error("bad CPLXSXP scalar");	\
    } while (0)
# define CHECK_SCALAR_RAW(x) do {				\
	CHECK_STDVEC_RAW(x);					\
	if (XLENGTH(x) != 1) error("bad RAWSXP scalar");	\
    } while (0)

# define CHECK_BOUNDS_ELT(x, i) do {			\
	if (i < 0 || i > XLENGTH(x))			\
	    error("subscript out of bounds");		\
    } while (0)

# define CHECK_VECTOR_LGL_ELT(x, i) do {	\
	SEXP ce__x__ = (x);			\
	R_xlen_t ce__i__ = (i);			\
	CHECK_VECTOR_LGL(ce__x__);		\
	CHECK_BOUNDS_ELT(ce__x__, ce__i__);	\
} while (0)
# define CHECK_VECTOR_INT_ELT(x, i) do {	\
	SEXP ce__x__ = (x);			\
	R_xlen_t ce__i__ = (i);			\
	CHECK_VECTOR_INT(ce__x__);		\
	CHECK_BOUNDS_ELT(ce__x__, ce__i__);	\
} while (0)
# define CHECK_VECTOR_REAL_ELT(x, i) do {	\
	SEXP ce__x__ = (x);			\
	R_xlen_t ce__i__ = (i);			\
	CHECK_VECTOR_REAL(ce__x__);		\
	CHECK_BOUNDS_ELT(ce__x__, ce__i__);	\
} while (0)
# define CHECK_VECTOR_CPLX_ELT(x, i) do {	\
	SEXP ce__x__ = (x);			\
	R_xlen_t ce__i__ = (i);			\
	CHECK_VECTOR_CPLX(ce__x__);		\
	CHECK_BOUNDS_ELT(ce__x__, ce__i__);	\
} while (0)
# define CHECK_VECTOR_RAW_ELT(x, i) do {	\
	SEXP ce__x__ = (x);			\
	R_xlen_t ce__i__ = (i);			\
	CHECK_VECTOR_RAW(ce__x__);		\
	CHECK_BOUNDS_ELT(ce__x__, ce__i__);	\
} while (0)
#else
# define CHECK_STDVEC_LGL(x) do { } while(0)
# define CHECK_STDVEC_INT(x) do { } while(0)
# define CHECK_STDVEC_REAL(x) do { } while(0)
# define CHECK_STDVEC_CPLX(x) do { } while(0)
# define CHECK_STDVEC_RAW(x) do { } while(0)

# define CHECK_SCALAR_LGL(x) do { } while(0)
# define CHECK_SCALAR_INT(x) do { } while(0)
# define CHECK_SCALAR_REAL(x) do { } while(0)
# define CHECK_SCALAR_CPLX(x) do { } while(0)
# define CHECK_SCALAR_RAW(x) do { } while(0)

# define CHECK_VECTOR_LGL_ELT(x, i) do { } while(0)
# define CHECK_VECTOR_INT_ELT(x, i) do { } while(0)
# define CHECK_VECTOR_REAL_ELT(x, i) do { } while(0)
# define CHECK_VECTOR_CPLX_ELT(x, i) do { } while(0)
# define CHECK_VECTOR_RAW_ELT(x, i) do { } while(0)
#endif

INLINE_FUN int *LOGICAL0(SEXP x) {
    CHECK_STDVEC_LGL(x);
    return (int *) STDVEC_DATAPTR(x);
}
INLINE_FUN Rboolean SCALAR_LVAL(SEXP x) {
    CHECK_SCALAR_LGL(x);
    return LOGICAL0(x)[0];
}
INLINE_FUN void SET_SCALAR_LVAL(SEXP x, Rboolean v) {
    CHECK_SCALAR_LGL(x);
    LOGICAL0(x)[0] = v;
}

INLINE_FUN int *INTEGER0(SEXP x) {
    CHECK_STDVEC_INT(x);
    return (int *) STDVEC_DATAPTR(x);
}
INLINE_FUN int SCALAR_IVAL(SEXP x) {
    CHECK_SCALAR_INT(x);
    return INTEGER0(x)[0];
}
INLINE_FUN void SET_SCALAR_IVAL(SEXP x, int v) {
    CHECK_SCALAR_INT(x);
    INTEGER0(x)[0] = v;
}

INLINE_FUN double *REAL0(SEXP x) {
    CHECK_STDVEC_REAL(x);
    return (double *) STDVEC_DATAPTR(x);
}
INLINE_FUN double SCALAR_DVAL(SEXP x) {
    CHECK_SCALAR_REAL(x);
    return REAL0(x)[0];
}
INLINE_FUN void SET_SCALAR_DVAL(SEXP x, double v) {
    CHECK_SCALAR_REAL(x);
    REAL0(x)[0] = v;
}

INLINE_FUN Rcomplex *COMPLEX0(SEXP x) {
    CHECK_STDVEC_CPLX(x);
    return (Rcomplex *) STDVEC_DATAPTR(x);
}
INLINE_FUN Rcomplex SCALAR_CVAL(SEXP x) {
    CHECK_SCALAR_CPLX(x);
    return COMPLEX0(x)[0];
}
INLINE_FUN void SET_SCALAR_CVAL(SEXP x, Rcomplex v) {
    CHECK_SCALAR_CPLX(x);
    COMPLEX0(x)[0] = v;
}

INLINE_FUN Rbyte *RAW0(SEXP x) {
    CHECK_STDVEC_RAW(x);
    return (Rbyte *) STDVEC_DATAPTR(x);
}
INLINE_FUN Rbyte SCALAR_BVAL(SEXP x) {
    CHECK_SCALAR_RAW(x);
    return RAW0(x)[0];
}
INLINE_FUN void SET_SCALAR_BVAL(SEXP x, Rbyte v) {
    CHECK_SCALAR_RAW(x);
    RAW0(x)[0] = v;
}

INLINE_FUN SEXP ALTREP_CLASS(SEXP x) { return TAG(x); }

INLINE_FUN SEXP R_altrep_data1(SEXP x) { return CAR(x); }
INLINE_FUN SEXP R_altrep_data2(SEXP x) { return CDR(x); }
INLINE_FUN void R_set_altrep_data1(SEXP x, SEXP v) { SETCAR(x, v); }
INLINE_FUN void R_set_altrep_data2(SEXP x, SEXP v) { SETCDR(x, v); }

INLINE_FUN int INTEGER_ELT(SEXP x, R_xlen_t i)
{
    CHECK_VECTOR_INT_ELT(x, i);
    return ALTREP(x) ? ALTINTEGER_ELT(x, i) : INTEGER0(x)[i];
}

INLINE_FUN void SET_INTEGER_ELT(SEXP x, R_xlen_t i, int v)
{
    CHECK_VECTOR_INT_ELT(x, i);
    if (ALTREP(x)) ALTINTEGER_SET_ELT(x, i, v);
    else INTEGER0(x)[i] = v;
}

INLINE_FUN int LOGICAL_ELT(SEXP x, R_xlen_t i)
{
    CHECK_VECTOR_LGL_ELT(x, i);
    return ALTREP(x) ? ALTLOGICAL_ELT(x, i) : LOGICAL0(x)[i];
}

INLINE_FUN void SET_LOGICAL_ELT(SEXP x, R_xlen_t i, int v)
{
    CHECK_VECTOR_LGL_ELT(x, i);
    if (ALTREP(x)) ALTLOGICAL_SET_ELT(x, i, v);
    else LOGICAL0(x)[i] = v;
}

INLINE_FUN double REAL_ELT(SEXP x, R_xlen_t i)
{
    CHECK_VECTOR_REAL_ELT(x, i);
    return ALTREP(x) ? ALTREAL_ELT(x, i) : REAL0(x)[i];
}

INLINE_FUN void SET_REAL_ELT(SEXP x, R_xlen_t i, double v)
{
    CHECK_VECTOR_REAL_ELT(x, i);
    if (ALTREP(x)) ALTREAL_SET_ELT(x, i, v);
    else REAL0(x)[i] = v;
}

INLINE_FUN Rcomplex COMPLEX_ELT(SEXP x, R_xlen_t i)
{
    CHECK_VECTOR_CPLX_ELT(x, i);
    return ALTREP(x) ? ALTCOMPLEX_ELT(x, i) : COMPLEX0(x)[i];
}

INLINE_FUN void SET_COMPLEX_ELT(SEXP x, R_xlen_t i, Rcomplex v)
{
    CHECK_VECTOR_CPLX_ELT(x, i);
    if (ALTREP(x)) ALTCOMPLEX_SET_ELT(x, i, v);
    else COMPLEX0(x)[i] = v;
}

INLINE_FUN Rbyte RAW_ELT(SEXP x, R_xlen_t i)
{
    CHECK_VECTOR_RAW_ELT(x, i);
    return ALTREP(x) ? ALTRAW_ELT(x, i) : RAW0(x)[i];
}

INLINE_FUN void SET_RAW_ELT(SEXP x, R_xlen_t i, Rbyte v)
{
    CHECK_VECTOR_RAW_ELT(x, i);
    if (ALTREP(x)) ALTRAW_SET_ELT(x, i, v);
    else RAW0(x)[i] = v;
}

#if !defined(COMPILING_R) && !defined(COMPILING_MEMORY_C) &&	\
    !defined(TESTING_WRITE_BARRIER)
/* if not inlining use version in memory.c with more error checking */
INLINE_FUN SEXP STRING_ELT(SEXP x, R_xlen_t i) {
    if (ALTREP(x))
	return ALTSTRING_ELT(x, i);
    else {
	SEXP *ps = STDVEC_DATAPTR(x);
	return ps[i];
    }
}
#else
SEXP STRING_ELT(SEXP x, R_xlen_t i);
#endif

#ifdef INLINE_PROTECT
LibExtern int R_PPStackSize;
LibExtern int R_PPStackTop;
LibExtern SEXP* R_PPStack;

INLINE_FUN SEXP protect(SEXP s)
{
    R_CHECK_THREAD;
    if (R_PPStackTop < R_PPStackSize)
	R_PPStack[R_PPStackTop++] = s;
    else R_signal_protect_error();
    return s;
}

INLINE_FUN void unprotect(int l)
{
    R_CHECK_THREAD;
#ifdef PROTECT_PARANOID
    if (R_PPStackTop >=  l)
	R_PPStackTop -= l;
    else R_signal_unprotect_error();
#else
    R_PPStackTop -= l;
#endif
}

INLINE_FUN void R_ProtectWithIndex(SEXP s, PROTECT_INDEX *pi)
{
    protect(s);
    *pi = R_PPStackTop - 1;
}

INLINE_FUN void R_Reprotect(SEXP s, PROTECT_INDEX i)
{
    R_CHECK_THREAD;
    if (i >= R_PPStackTop || i < 0)
	R_signal_reprotect_error(i);
    R_PPStack[i] = s;
}
#endif /* INLINE_PROTECT */

/* from dstruct.c */

/*  length - length of objects  */

int Rf_envlength(SEXP rho);

/* TODO: a  Length(.) {say} which is  length() + dispatch (S3 + S4) if needed
         for one approach, see do_seq_along() in ../main/seq.c
*/
INLINE_FUN R_len_t length(SEXP s)
{
    switch (TYPEOF(s)) {
    case NILSXP:
	return 0;
    case LGLSXP:
    case INTSXP:
    case REALSXP:
    case CPLXSXP:
    case STRSXP:
    case CHARSXP:
    case VECSXP:
    case EXPRSXP:
    case RAWSXP:
	return LENGTH(s);
    case LISTSXP:
    case LANGSXP:
    case DOTSXP:
    {
	int i = 0;
	while (s != NULL && s != R_NilValue) {
	    i++;
	    s = CDR(s);
	}
	return i;
    }
    case ENVSXP:
	return Rf_envlength(s);
    default:
	return 1;
    }
}

R_xlen_t Rf_envxlength(SEXP rho);

INLINE_FUN R_xlen_t xlength(SEXP s)
{
    switch (TYPEOF(s)) {
    case NILSXP:
	return 0;
    case LGLSXP:
    case INTSXP:
    case REALSXP:
    case CPLXSXP:
    case STRSXP:
    case CHARSXP:
    case VECSXP:
    case EXPRSXP:
    case RAWSXP:
	return XLENGTH(s);
    case LISTSXP:
    case LANGSXP:
    case DOTSXP:
    {
	// it is implausible this would be >= 2^31 elements, but allow it
	R_xlen_t i = 0;
	while (s != NULL && s != R_NilValue) {
	    i++;
	    s = CDR(s);
	}
	return i;
    }
    case ENVSXP:
	return Rf_envxlength(s);
    default:
	return 1;
    }
}

/* regular allocVector() as a special case of allocVector3() with no custom allocator */
INLINE_FUN SEXP allocVector(SEXPTYPE type, R_xlen_t length)
{
    return allocVector3(type, length, NULL);
}

/* from list.c */
/* Return a dotted pair with the given CAR and CDR. */
/* The (R) TAG slot on the cell is set to NULL. */


/* Get the i-th element of a list */
INLINE_FUN SEXP elt(SEXP list, int i)
{
    int j;
    SEXP result = list;

    if ((i < 0) || (i > length(list)))
	return R_NilValue;
    else
	for (j = 0; j < i; j++)
	    result = CDR(result);

    return CAR(result);
}


/* Return the last element of a list */
INLINE_FUN SEXP lastElt(SEXP list)
{
    SEXP result = R_NilValue;
    while (list != R_NilValue) {
	result = list;
	list = CDR(list);
    }
    return result;
}


/* Shorthands for creating small lists */

INLINE_FUN SEXP list1(SEXP s)
{
    return CONS(s, R_NilValue);
}


INLINE_FUN SEXP list2(SEXP s, SEXP t)
{
    PROTECT(s);
    s = CONS(s, list1(t));
    UNPROTECT(1);
    return s;
}


INLINE_FUN SEXP list3(SEXP s, SEXP t, SEXP u)
{
    PROTECT(s);
    s = CONS(s, list2(t, u));
    UNPROTECT(1);
    return s;
}


INLINE_FUN SEXP list4(SEXP s, SEXP t, SEXP u, SEXP v)
{
    PROTECT(s);
    s = CONS(s, list3(t, u, v));
    UNPROTECT(1);
    return s;
}

INLINE_FUN SEXP list5(SEXP s, SEXP t, SEXP u, SEXP v, SEXP w)
{
    PROTECT(s);
    s = CONS(s, list4(t, u, v, w));
    UNPROTECT(1);
    return s;
}

INLINE_FUN SEXP list6(SEXP s, SEXP t, SEXP u, SEXP v, SEXP w, SEXP x)
{
    PROTECT(s);
    s = CONS(s, list5(t, u, v, w, x));
    UNPROTECT(1);
    return s;
}

/* Destructive list append : See also ``append'' */

INLINE_FUN SEXP listAppend(SEXP s, SEXP t)
{
    SEXP r;
    if (s == R_NilValue)
	return t;
    r = s;
    while (CDR(r) != R_NilValue)
	r = CDR(r);
    SETCDR(r, t);
    return s;
}


/* Language based list constructs.  These are identical to the list */
/* constructs, but the results can be evaluated. */

/* Return a (language) dotted pair with the given car and cdr */

INLINE_FUN SEXP lcons(SEXP car, SEXP cdr)
{
    SEXP e = cons(car, cdr);
    SET_TYPEOF(e, LANGSXP);
    return e;
}

INLINE_FUN SEXP lang1(SEXP s)
{
    return LCONS(s, R_NilValue);
}

INLINE_FUN SEXP lang2(SEXP s, SEXP t)
{
    PROTECT(s);
    s = LCONS(s, list1(t));
    UNPROTECT(1);
    return s;
}

INLINE_FUN SEXP lang3(SEXP s, SEXP t, SEXP u)
{
    PROTECT(s);
    s = LCONS(s, list2(t, u));
    UNPROTECT(1);
    return s;
}

INLINE_FUN SEXP lang4(SEXP s, SEXP t, SEXP u, SEXP v)
{
    PROTECT(s);
    s = LCONS(s, list3(t, u, v));
    UNPROTECT(1);
    return s;
}

INLINE_FUN SEXP lang5(SEXP s, SEXP t, SEXP u, SEXP v, SEXP w)
{
    PROTECT(s);
    s = LCONS(s, list4(t, u, v, w));
    UNPROTECT(1);
    return s;
}

INLINE_FUN SEXP lang6(SEXP s, SEXP t, SEXP u, SEXP v, SEXP w, SEXP x)
{
    PROTECT(s);
    s = LCONS(s, list5(t, u, v, w, x));
    UNPROTECT(1);
    return s;
}

/* from util.c */

/* Check to see if the arrays "x" and "y" have the identical extents */

INLINE_FUN Rboolean conformable(SEXP x, SEXP y)
{
    int i, n;
    PROTECT(x = getAttrib(x, R_DimSymbol));
    y = getAttrib(y, R_DimSymbol);
    UNPROTECT(1);
    if ((n = length(x)) != length(y))
	return FALSE;
    for (i = 0; i < n; i++)
	if (INTEGER(x)[i] != INTEGER(y)[i])
	    return FALSE;
    return TRUE;
}

/* NOTE: R's inherits() is based on inherits3() in ../main/objects.c
 * Here, use char / CHAR() instead of the slower more general translateChar()
 */
INLINE_FUN Rboolean inherits(SEXP s, const char *name)
{
    SEXP klass;
    int i, nclass;
    if (OBJECT(s)) {
	klass = getAttrib(s, R_ClassSymbol);
	nclass = length(klass);
	for (i = 0; i < nclass; i++) {
	    if (!strcmp(CHAR(STRING_ELT(klass, i)), name))
		return TRUE;
	}
    }
    return FALSE;
}

INLINE_FUN Rboolean isValidString(SEXP x)
{
    return TYPEOF(x) == STRSXP && LENGTH(x) > 0 && TYPEOF(STRING_ELT(x, 0)) != NILSXP;
}

/* non-empty ("") valid string :*/
INLINE_FUN Rboolean isValidStringF(SEXP x)
{
    return isValidString(x) && CHAR(STRING_ELT(x, 0))[0];
}

INLINE_FUN Rboolean isUserBinop(SEXP s)
{
    if (TYPEOF(s) == SYMSXP) {
	const char *str = CHAR(PRINTNAME(s));
	if (strlen(str) >= 2 && str[0] == '%' && str[strlen(str)-1] == '%')
	    return TRUE;
    }
    return FALSE;
}

INLINE_FUN Rboolean isFunction(SEXP s)
{
    return (TYPEOF(s) == CLOSXP ||
	    TYPEOF(s) == BUILTINSXP ||
	    TYPEOF(s) == SPECIALSXP);
}

INLINE_FUN Rboolean isPrimitive(SEXP s)
{
    return (TYPEOF(s) == BUILTINSXP ||
	    TYPEOF(s) == SPECIALSXP);
}

INLINE_FUN Rboolean isList(SEXP s)
{
    return (s == R_NilValue || TYPEOF(s) == LISTSXP);
}


INLINE_FUN Rboolean isNewList(SEXP s)
{
    return (s == R_NilValue || TYPEOF(s) == VECSXP);
}

INLINE_FUN Rboolean isPairList(SEXP s)
{
    switch (TYPEOF(s)) {
    case NILSXP:
    case LISTSXP:
    case LANGSXP:
    case DOTSXP:
	return TRUE;
    default:
	return FALSE;
    }
}

INLINE_FUN Rboolean isVectorList(SEXP s)
{
    switch (TYPEOF(s)) {
    case VECSXP:
    case EXPRSXP:
	return TRUE;
    default:
	return FALSE;
    }
}

INLINE_FUN Rboolean isVectorAtomic(SEXP s)
{
    switch (TYPEOF(s)) {
    case LGLSXP:
    case INTSXP:
    case REALSXP:
    case CPLXSXP:
    case STRSXP:
    case RAWSXP:
	return TRUE;
    default: /* including NULL */
	return FALSE;
    }
}

INLINE_FUN Rboolean isVector(SEXP s)/* === isVectorList() or isVectorAtomic() */
{
    switch(TYPEOF(s)) {
    case LGLSXP:
    case INTSXP:
    case REALSXP:
    case CPLXSXP:
    case STRSXP:
    case RAWSXP:

    case VECSXP:
    case EXPRSXP:
	return TRUE;
    default:
	return FALSE;
    }
}

INLINE_FUN Rboolean isFrame(SEXP s)
{
    SEXP klass;
    int i;
    if (OBJECT(s)) {
	klass = getAttrib(s, R_ClassSymbol);
	for (i = 0; i < length(klass); i++)
	    if (!strcmp(CHAR(STRING_ELT(klass, i)), "data.frame")) return TRUE;
    }
    return FALSE;
}

/* DIFFERENT than R's  is.language(.) in ../main/coerce.c [do_is(), case 301:]
 *                                    which is   <=>  SYMSXP || LANGSXP || EXPRSXP */
INLINE_FUN Rboolean isLanguage(SEXP s)
{
    return (s == R_NilValue || TYPEOF(s) == LANGSXP);
}

INLINE_FUN Rboolean isMatrix(SEXP s)
{
    SEXP t;
    if (isVector(s)) {
	t = getAttrib(s, R_DimSymbol);
	/* You are not supposed to be able to assign a non-integer dim,
	   although this might be possible by misuse of ATTRIB. */
	if (TYPEOF(t) == INTSXP && LENGTH(t) == 2)
	    return TRUE;
    }
    return FALSE;
}

INLINE_FUN Rboolean isArray(SEXP s)
{
    SEXP t;
    if (isVector(s)) {
	t = getAttrib(s, R_DimSymbol);
	/* You are not supposed to be able to assign a 0-length dim,
	 nor a non-integer dim */
	if (TYPEOF(t) == INTSXP && LENGTH(t) > 0)
	    return TRUE;
    }
    return FALSE;
}

INLINE_FUN Rboolean isTs(SEXP s)
{
    return (isVector(s) && getAttrib(s, R_TspSymbol) != R_NilValue);
}


INLINE_FUN Rboolean isInteger(SEXP s)
{
    return (TYPEOF(s) == INTSXP && !inherits(s, "factor"));
}

INLINE_FUN Rboolean isFactor(SEXP s)
{
    return (TYPEOF(s) == INTSXP  && inherits(s, "factor"));
}

INLINE_FUN int nlevels(SEXP f)
{
    if (!isFactor(f))
	return 0;
    return LENGTH(getAttrib(f, R_LevelsSymbol));
}

/* Is an object of numeric type. */
/* FIXME:  the LGLSXP case should be excluded here
 * (really? in many places we affirm they are treated like INTs)*/

INLINE_FUN Rboolean isNumeric(SEXP s)
{
    switch(TYPEOF(s)) {
    case INTSXP:
	if (inherits(s,"factor")) return FALSE;
    case LGLSXP:
    case REALSXP:
	return TRUE;
    default:
	return FALSE;
    }
}

/** Is an object "Numeric" or  complex */
INLINE_FUN Rboolean isNumber(SEXP s)
{
    switch(TYPEOF(s)) {
    case INTSXP:
	if (inherits(s,"factor")) return FALSE;
    case LGLSXP:
    case REALSXP:
    case CPLXSXP:
	return TRUE;
    default:
	return FALSE;
    }
}


/* As from R 2.4.0 we check that the value is allowed. */
INLINE_FUN SEXP ScalarLogical(int x)
{
    LibExtern SEXP R_LogicalNAValue, R_TrueValue, R_FalseValue;
    if (x == NA_LOGICAL) return R_LogicalNAValue;
    else if (x != 0) return R_TrueValue;
    else return R_FalseValue;
}

INLINE_FUN SEXP ScalarInteger(int x)
{
    SEXP ans = allocVector(INTSXP, 1);
    SET_SCALAR_IVAL(ans, x);
    return ans;
}

INLINE_FUN SEXP ScalarReal(double x)
{
    SEXP ans = allocVector(REALSXP, 1);
    SET_SCALAR_DVAL(ans, x);
    return ans;
}

INLINE_FUN SEXP ScalarComplex(Rcomplex x)
{
    SEXP ans = allocVector(CPLXSXP, 1);
    SET_SCALAR_CVAL(ans, x);
    return ans;
}

INLINE_FUN SEXP ScalarString(SEXP x)
{
    SEXP ans;
    PROTECT(x);
    ans = allocVector(STRSXP, (R_xlen_t)1);
    SET_STRING_ELT(ans, (R_xlen_t)0, x);
    UNPROTECT(1);
    return ans;
}

INLINE_FUN SEXP ScalarRaw(Rbyte x)
{
    SEXP ans = allocVector(RAWSXP, 1);
    SET_SCALAR_BVAL(ans, x);
    return ans;
}

/* Check to see if a list can be made into a vector. */
/* it must have every element being a vector of length 1. */
/* BUT it does not exclude 0! */

INLINE_FUN Rboolean isVectorizable(SEXP s)
{
    if (s == R_NilValue) return TRUE;
    else if (isNewList(s)) {
	R_xlen_t i, n;

	n = XLENGTH(s);
	for (i = 0 ; i < n; i++)
	    if (!isVector(VECTOR_ELT(s, i)) || XLENGTH(VECTOR_ELT(s, i)) > 1)
		return FALSE;
	return TRUE;
    }
    else if (isList(s)) {
	for ( ; s != R_NilValue; s = CDR(s))
	    if (!isVector(CAR(s)) || LENGTH(CAR(s)) > 1) return FALSE;
	return TRUE;
    }
    else return FALSE;
}


/**
 * Create a named vector of type TYP
 *
 * @example const char *nms[] = {"xi", "yi", "zi", ""};
 *          mkNamed(VECSXP, nms);  =~= R  list(xi=, yi=, zi=)
 *
 * @param TYP a vector SEXP type (e.g. REALSXP)
 * @param names names of list elements with null string appended
 *
 * @return (pointer to a) named vector of type TYP
 */
INLINE_FUN SEXP mkNamed(SEXPTYPE TYP, const char **names)
{
    SEXP ans, nms;
    R_xlen_t i, n;

    for (n = 0; strlen(names[n]) > 0; n++) {}
    ans = PROTECT(allocVector(TYP, n));
    nms = PROTECT(allocVector(STRSXP, n));
    for (i = 0; i < n; i++)
	SET_STRING_ELT(nms, i, mkChar(names[i]));
    setAttrib(ans, R_NamesSymbol, nms);
    UNPROTECT(2);
    return ans;
}

/* from gram.y */

/* short cut for  ScalarString(mkChar(s)) : */
INLINE_FUN SEXP mkString(const char *s)
{
    SEXP t;

    PROTECT(t = allocVector(STRSXP, (R_xlen_t)1));
    SET_STRING_ELT(t, (R_xlen_t)0, mkChar(s));
    UNPROTECT(1);
    return t;
}

/* index of a given C string in (translated) R string vector  */
INLINE_FUN int
stringPositionTr(SEXP string, const char *translatedElement) {

    int slen = LENGTH(string);
    int i;

    const void *vmax = vmaxget();
    for (i = 0 ; i < slen; i++) {
	Rboolean found = ! strcmp(translateChar(STRING_ELT(string, i)),
				  translatedElement);
	vmaxset(vmax);
        if (found)
            return i;
    }
    return -1; /* not found */
}

/* duplicate RHS value of complex assignment if necessary to prevent cycles */
INLINE_FUN SEXP R_FixupRHS(SEXP x, SEXP y)
{
    if( y != R_NilValue && MAYBE_REFERENCED(y) ) {
	if (R_cycle_detected(x, y)) {
#ifdef WARNING_ON_CYCLE_DETECT
	    warning("cycle detected");
	    R_cycle_detected(x, y);
#endif
	    y = duplicate(y);
	}
	else ENSURE_NAMEDMAX(y);
    }
    return y;
}
#endif /* R_INLINES_H_ */
