cpm <- function(y, ...)
UseMethod("cpm")

cpm.DGEList <- function(y, normalized.lib.sizes=TRUE, log=FALSE, prior.count=2, ...)
#	Counts per million for a DGEList
#	Davis McCarthy and Gordon Smyth.
#	Created 20 June 2011. Last modified 22 October 2020.
{
	lib.size <- y$samples$lib.size
	if(!is.null(y$offset)){
		if( min(y$offset) > max(log(lib.size)) || min(log(lib.size)) > max(y$offset) ) warning("Offset may not reflect library sizes. Scaling offset may be required.")
		lib.size <- NULL
	} else {
		if(normalized.lib.sizes) lib.size <- lib.size*y$samples$norm.factors
	}

	cpm.default(y$counts, lib.size=lib.size, offset=y$offset, log=log, prior.count=prior.count)
}

cpm.SummarizedExperiment <- function(y, normalized.lib.sizes=TRUE, log=FALSE, prior.count=2, ...)
#	Counts per million for a SummarizedExperiment
#	Created 03 April 2020.  Last modified 1 June 2020.
{
	y <- SE2DGEList(y)
	cpm.DGEList(y, normalized.lib.sizes=normalized.lib.sizes, log=log, prior.count=prior.count, ...)
}

cpm.DGELRT <- cpm.DGEGLM <- function(y, log=FALSE, shrunk=TRUE, ...)
#	Fitted counts per million from a fitted model object.
#	Created 19 April 2020.  Last modified 30 Sep 2025.
{
	if(shrunk) {
		eta <- y$coefficients %*% t(y$design)
	} else {
		eta <- y$unshrunk.coefficients %*% t(y$design)
	}

	if(log) {
		(eta + log(1e6)) / log(2)
	} else {
		exp(eta + log(1e6))
	}
}

cpm.MArrayLM <- function(y, log=FALSE, ...)
#	Fitted counts per million from a limma fitted model object.
#	Created 9 Oct 2025.  Last modified 9 Oct 2025.
{
	eta <- fitted(y)
	if(log) {
		eta
	} else {
		2^eta
	}
}

cpm.default <- function(y, lib.size=NULL, offset=NULL, log=FALSE, prior.count=2, ...)
#	Counts per million for a matrix
#	Davis McCarthy and Gordon Smyth. C++ version by Aaron Lun. C version by Lizhong Chen.
#	Created 20 June 2011. Last modified 13 Jul 2024.
{
#	Check y
	ymin <- min(y)
	if(is.na(ymin)) stop("NA counts not allowed")
	if(ymin < 0) stop("Negative counts not allowed")
	y <- as.matrix(y)
	if (any(dim(y)==0L)) {
		return(y)
	}

	if(!is.null(offset)) {
		if(is.matrix(offset)) {
			if(any(dim(offset)!=dim(y))) stop("dimensions are not consistent between counts and offset")
		} else {
			if(length(offset)!=ncol(y)) stop("Length of offset differs from number of libraries")
		}
		if(!is.null(lib.size)) warning("lib.size is ignored in the presence of offset")
		lib.size <- exp(offset)
	} else {
		if(is.null(lib.size)) lib.size <- colSums(y)
	}

	if(!is.double(lib.size)) {
		if(!is.numeric(lib.size)) stop("lib.size must be numeric")
		storage.mode(lib.size) <- "double"
	}

#	Check lib.size
	minlibsize <- min(lib.size)
	if(is.na(minlibsize)) stop("NA library sizes not allowed")
	if(minlibsize <= 0) stop("library sizes should be greater than zero")

	lib.size <- makeCompressedMatrix(lib.size, dim(y), byrow=TRUE)

#	Calculating in C++ for max efficiency
	if(log) {
		prior.count <- .compressPrior(y, prior.count)
		out <- .Call(.cxx_calculate_cpm_log, y, lib.size, prior.count)
	} else {
		out <- .Call(.cxx_calculate_cpm_raw, y, lib.size)
	}

	out
}
