#  File src/library/base/R/interaction.R
#  Part of the R package, https://www.R-project.org
#
#  Copyright (C) 1995-2025 The R Core Team
#
#  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.
#
#  A copy of the GNU General Public License is available at
#  https://www.R-project.org/Licenses/

### This is almost like the Primitive ":" for factors
### but with drop=TRUE, used in reshape and ave
interaction <- function(..., drop = FALSE, sep = ".", lex.order = FALSE)
{
    args <- list(...)
    narg <- length(args)
    if (narg < 1L)
	stop("No factors specified")
    if (narg == 1L && is.list(args[[1L]])) {
	args <- args[[1L]]
	narg <- length(args)
    }
    for(i in narg:1L) {
        x <- as.factor(args[[i]])[, drop = drop]
        ax <- as.integer(x) - 1L
        lx <- levels(x)
        if(i == narg) {
            ay <- ax
            ly <- lx
        } else {
            nx <- length(lx)
            ny <- length(ly)
            if(lex.order) {
                ay <- ay + as.numeric(ny) * ax
                if(drop) {
                    az <- sort(unique(ay))
                    ly <- paste(lx[az %/% ny + 1L], ly[az %% ny + 1L],
                                sep = sep)
                    ay <- match(ay, az) - 1L
                } else {
                    ly <- paste(rep(lx, each = ny), rep(ly, nx),
                                sep = sep)
                }
            } else {
                ay <- ay * as.numeric(nx) + ax
                if(drop) {
                    az <- sort(unique(ay))
                    ly <- paste(lx[az %% nx + 1L], ly[az %/% nx + 1L],
                                sep = sep)
                    ay <- match(ay, az) - 1L
                } else {
                    ly <- paste(rep(lx, ny), rep(ly, each = nx),
                                sep = sep)
                }
            }
            while(j <- anyDuplicated(ly)) {
                ## If levels at positions i and j > i are the same, we
                ## need to drop the one at j, change the code for that
                ## level to the code for level i, and decrease all codes
                ## beyond the code for level j by one.
                i <- match(ly[j], ly)
                ly <- ly[-j]
                j <- j - 1L
                ay[ay == j] <- i - 1L
                ay[ay > j] <- ay[ay > j] - 1L
            }
        }
    }
    structure(as.integer(ay + 1L), levels = ly, class = "factor")
}
