R/fill-anomer-pos.R

Defines functions .fill_anomer_pos_value .fill_anomer_pos_single fill_anomer_pos

Documented in fill_anomer_pos

#' Fill Anomer Positions
#'
#' Add anomer positions to glycan structures with missing anomer position
#' information. For example, `"Gal(??-?)GalNAc(??-"` is converted to
#' `"Gal(?1-?)GalNAc(?1-"`.
#'
#' For anomer positions that are already specified in the input structures,
#' this function does not modify them.
#'
#' For a structure with floating parts, the reducing-end position is inferred
#' from the root of the main tree. Positions in virtual floating-part
#' attachment linkages are inferred from each floating part's root residue.
#'
#' @param strucs A [glycan_structure()] vector or glycan `igraph` with concrete
#'   or generic monosaccharides.
#'
#' @returns An object of the same representation as `strucs` with anomer
#'   positions added where missing. Graph input retains its vertex IDs and
#'   order.
#'
#' @examples
#' glycans <- as_glycan_structure(c(
#'   "Gal(??-?)GalNAc(??-",
#'   "Neu5Ac(??-?)Gal(??-?)GalNAc(??-"
#' ))
#' fill_anomer_pos(glycans)
#'
#' @export
fill_anomer_pos <- function(strucs) {
  if (inherits(strucs, "igraph")) {
    return(.fill_anomer_pos_single(strucs))
  }
  checkmate::assert_class(strucs, "glyrepr_structure")
  .smap_structure_impl(
    strucs,
    .fill_anomer_pos_single,
    dots = list(),
    validation = "floating"
  )
}


#' Fill One Glycan Graph's Anomer Positions
#'
#' @param struc An igraph glycan structure.
#'
#' @returns An igraph glycan structure with missing anomer positions filled.
#' @noRd
.fill_anomer_pos_single <- function(struc) {
  raw_parts <- igraph::graph_attr(struc, "floating_parts")
  if (is.null(raw_parts)) {
    root <- which(igraph::degree(struc, mode = "in") == 0)
  } else {
    parts <- normalize_floating_parts(struc)
    main_vertices <- if (length(parts) == 0) {
      seq_len(igraph::vcount(struc))
    } else {
      floating_main_vertices(struc, parts)
    }
    root <- intersect(
      which(igraph::degree(struc, mode = "in") == 0),
      main_vertices
    )
  }

  root_mono <- igraph::vertex_attr(struc, "mono", index = root)
  root_anomer <- igraph::graph_attr(struc, "anomer")
  struc <- igraph::set_graph_attr(
    struc,
    "anomer",
    value = .fill_anomer_pos_value(root_anomer, root_mono)
  )

  linkages <- igraph::edge_attr(struc, "linkage")
  if (length(linkages) > 0) {
    edges <- igraph::ends(struc, igraph::E(struc), names = FALSE)
    donor_monos <- igraph::vertex_attr(struc, "mono", index = edges[, 2])
    linkages <- purrr::map2_chr(linkages, donor_monos, .fill_anomer_pos_value)
    struc <- igraph::set_edge_attr(struc, "linkage", value = linkages)
  }

  if (is.null(raw_parts)) {
    return(struc)
  }

  parts <- purrr::map(parts, function(part) {
    root_mono <- igraph::vertex_attr(struc, "mono", index = part$root)
    part$linkage <- .fill_anomer_pos_value(part$linkage, root_mono)
    part
  })
  set_floating_parts_attr(struc, parts)
}


#' Fill the Anomer Position in One Linkage or Reducing-End Anomer
#'
#' @param anomer A linkage string like `"??-?"` or reducing-end anomer like
#'   `"??"`.
#' @param mono A concrete or generic monosaccharide name.
#'
#' @returns `anomer` with the second character filled when missing.
#' @noRd
.fill_anomer_pos_value <- function(anomer, mono) {
  if (stringr::str_sub(anomer, 2, 2) != "?") {
    return(anomer)
  }

  stringr::str_c(
    stringr::str_sub(anomer, 1, 1),
    infer_anomer_pos(mono),
    stringr::str_sub(anomer, 3)
  )
}

Try the glyrepr package in your browser

Any scripts or data that you put into this service are public.

glyrepr documentation built on Sept. 22, 2026, 5:09 p.m.