Nothing
#' Determine if a Glycan Structure has Linkages
#'
#' Unknown linkages in a glycan structure are represented by "??-?".
#' Also, a linkage can be partially known (e.g. "a?-?").
#' This function checks if a glycan structure has linkages,
#' in a strict or lenient way.
#'
#' @param glycan A [glycan_structure()] vector or a glycan `igraph`.
#' @param strict A logical value.
#' * If `FALSE` (default), a glycan is considered to have linkages if any
#' linkage is partially known (not "??-?").
#' * If `TRUE`, a glycan is considered to have linkages only if all linkages
#' are fully determined (no "?" or multiple positions in the linkage).
#' Linkages include both graph edges and the virtual attachment linkage of
#' each floating part.
#'
#' @returns A logical vector for structure-vector input, or a logical scalar for
#' graph input.
#'
#' @examples
#' glycan <- o_glycan_core_1(linkage = TRUE)
#' has_linkages(glycan)
#' print(glycan)
#'
#' glycan <- remove_linkages(glycan)
#' has_linkages(glycan)
#' print(glycan)
#'
#' glycan <- as_glycan_structure("Gal(b1-?)GalNAc(a1-")
#' has_linkages(glycan)
#' has_linkages(glycan, strict = TRUE)
#'
#' @seealso [remove_linkages()], [possible_linkages()]
#'
#' @export
has_linkages <- function(glycan, strict = FALSE) {
checkmate::assert_flag(strict)
if (inherits(glycan, "igraph")) {
return(.has_linkages_single(glycan, strict))
}
checkmate::assert_class(glycan, "glyrepr_structure")
smap_lgl(glycan, .has_linkages_single, strict = strict)
}
# Internal function to check linkages in a single igraph
.has_linkages_single <- function(glycan, strict) {
linkages <- igraph::E(glycan)$linkage
parts <- igraph::graph_attr(glycan, "floating_parts")
if (length(parts) > 0) {
floating_linkages <- vapply(
parts,
function(part) part$linkage,
character(1)
)
linkages <- c(linkages, floating_linkages)
}
if (strict) {
anomer <- glycan$anomer
all(!stringr::str_detect(c(linkages, anomer), stringr::fixed("?"))) &&
all(!stringr::str_detect(linkages, stringr::fixed("/")))
} else {
any(linkages != "??-?") | glycan$anomer != "??"
}
}
#' Generate Possible Linkages
#'
#' @description
#' Given an obscure linkage format (having "?", e.g. "a2-?"),
#' this function generates all possible linkages based on the format.
#' See [valid_linkages()] for details.
#'
#' The ranges of possible anomers, first positions, and second positions
#' can be specified using `anomer_range`, `pos1_range`, and `pos2_range`.
#'
#' @param linkage A linkage string.
#' @param anomer_range A character vector of possible anomers.
#' Default is `c("a", "b")`.
#' @param pos1_range A numeric vector of possible first positions.
#' Default is `1:2`.
#' @param pos2_range A numeric vector of possible second positions.
#' Default is `1:9`.
#' @param include_unknown A logical value. If `TRUE`, "?" will be included.
#' Default is `FALSE`.
#'
#' @returns A character vector of possible linkages.
#'
#' @examples
#' possible_linkages("a2-?")
#' possible_linkages("??-2")
#' possible_linkages("a1-3")
#' possible_linkages("a?-?", pos1_range = 2, pos2_range = c(2, 3))
#' possible_linkages("?1-6", include_unknown = TRUE)
#'
#' @seealso [has_linkages()], [remove_linkages()], [valid_linkages()]
#'
#' @export
possible_linkages <- function(
linkage,
anomer_range = c("a", "b"),
pos1_range = 1:2,
pos2_range = 1:9,
include_unknown = FALSE
) {
# Input checks
checkmate::assert_character(linkage, len = 1)
checkmate::assert_character(anomer_range, pattern = "^[ab]$", unique = TRUE)
checkmate::assert_numeric(pos1_range, lower = 1, upper = 2, unique = TRUE)
checkmate::assert_numeric(pos2_range, lower = 1, upper = 9, unique = TRUE)
checkmate::assert_flag(include_unknown)
# Check if the linkage is valid
if (!valid_linkages(linkage)) {
cli::cli_abort("Invalid linkage format.")
}
# Add unknown elements
if (include_unknown) {
anomer_range <- c(anomer_range, "?")
pos1_range <- c(pos1_range, "?")
pos2_range <- c(pos2_range, "?")
}
# Possible linkage elements
current_anomer <- stringr::str_sub(linkage, 1, 1)
current_pos1 <- stringr::str_sub(linkage, 2, 2)
current_pos2 <- stringr::str_sub(linkage, 4, -1)
anomers <- if (current_anomer == "?") anomer_range else current_anomer
first_positions <- if (current_pos1 == "?") pos1_range else current_pos1
if (current_pos2 == "?") {
second_positions <- pos2_range
} else if (stringr::str_detect(current_pos2, "/")) {
second_positions <- stringr::str_split(current_pos2, "/")[[1]]
} else {
second_positions <- current_pos2
}
# Generate possible linkages
purrr::pmap_chr(
expand.grid(anomers, first_positions, second_positions),
~ paste0(..1, ..2, "-", ..3)
)
}
#' Remove All Linkages from a Glycan
#'
#' This function replaces all graph-edge and floating-part attachment
#' linkages in a glycan structure with "??-?", as well as the reducing end
#' anomer with "??-".
#'
#' @param glycan A glyrepr_structure vector or a glycan `igraph`.
#'
#' @returns An object of the same representation as `glycan` with all linkages
#' removed. Graph input retains its vertex IDs and order.
#'
#' @examples
#' glycan <- o_glycan_core_1(linkage = TRUE)
#' glycan
#' remove_linkages(glycan)
#'
#' @export
remove_linkages <- function(glycan) {
if (inherits(glycan, "igraph")) {
return(.remove_linkages_single(glycan))
}
if (!is_glycan_structure(glycan)) {
cli::cli_abort(c(
"Input must be a glyrepr_structure vector.",
"i" = "Use `glycan_structure()` to create a glyrepr_structure from igraph objects."
))
}
.smap_structure_impl(
glycan,
.remove_linkages_single,
dots = list(),
validation = "floating"
)
}
# Internal function to remove linkages from a single igraph
.remove_linkages_single <- function(glycan) {
res <- igraph::set_edge_attr(glycan, "linkage", value = "??-?")
res <- igraph::set_graph_attr(res, "anomer", value = "??")
if (is.null(igraph::graph_attr(res, "floating_parts"))) {
return(res)
}
parts <- normalize_floating_parts(res)
parts <- purrr::map(parts, function(part) {
part$linkage <- "??-?"
part
})
set_floating_parts_attr(res, parts)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.