1
2#   IGraph R package
3#   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
4#   334 Harvard street, Cambridge, MA 02139 USA
5#
6#   This program is free software; you can redistribute it and/or modify
7#   it under the terms of the GNU General Public License as published by
8#   the Free Software Foundation; either version 2 of the License, or
9#   (at your option) any later version.
10#
11#   This program is distributed in the hope that it will be useful,
12#   but WITHOUT ANY WARRANTY; without even the implied warranty of
13#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14#   GNU General Public License for more details.
15#
16#   You should have received a copy of the GNU General Public License
17#   along with this program; if not, write to the Free Software
18#   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
19#   02110-1301 USA
20#
21###################################################################
22
23###################################################################
24# Constructors
25###################################################################
26
27update_es_ref <- update_vs_ref <- function(graph) {
28  env <- get_vs_ref(graph)
29  if (!is.null(env)) assign("me", graph, envir = env)
30}
31
32get_es_ref <- get_vs_ref <- function(graph) {
33  if (is_igraph(graph) && !warn_version(graph)) {
34    .Call(C_R_igraph_mybracket, graph, 10L)
35  } else {
36    NULL
37  }
38}
39
40get_es_graph <- get_vs_graph <- function(seq) {
41  at <- attr(seq, "env")
42  if (class(at) == "weakref") {
43    weak_ref_key(at)$me
44  } else if (class(at) == "environment") {
45    get("graph", envir = at)
46  } else {
47    NULL
48  }
49}
50
51has_es_graph <- has_vs_graph <- function(seq) {
52  !is.null(weak_ref_key(attr(seq, "env")))
53}
54
55get_es_graph_id <- get_vs_graph_id <- function(seq) {
56  new_g <- attr(seq, "graph")
57  if (!is.null(new_g)) {
58    new_g
59  } else if (!is.null(attr(seq, "env"))) {
60    get("graph", envir = attr(seq, "env"))
61  } else {
62    NULL
63  }
64}
65
66#' Decide if two graphs are identical
67#'
68#' This is similar to \code{identical} in the \code{base} package,
69#' but ignores the mutable piece of igraph objects, that might be
70#' different, even if the two graphs are identical.
71#'
72#' @param g1,g2 The two graphs
73#' @return Logical scalar
74#' @export
75
76identical_graphs <- function(g1, g2) {
77  stopifnot(is_igraph(g1), is_igraph(g2))
78  .Call(C_R_igraph_identical_graphs, g1, g2)
79}
80
81add_vses_graph_ref <- function(vses, graph) {
82  ref <- get_vs_ref(graph)
83  if (!is.null(ref)) {
84    attr(vses, "env") <- make_weak_ref(ref, NULL)
85    attr(vses, "graph") <- get_graph_id(graph)
86  } else {
87    ne <- new.env()
88    assign("graph", graph, envir = ne)
89    attr(vses, "env") <- ne
90  }
91
92  vses
93}
94
95#' Get the id of a graph
96#'
97#' Graph ids are used to check that a vertex or edge sequence
98#' belongs to a graph. If you create a new graph by changing the
99#' structure of a graph, the new graph will have a new id.
100#' Changing the attributes will not change the id.
101#'
102#' @param x A graph or a vertex sequence or an edge sequence.
103#' @param ... Not used currently.
104#' @return The id of the graph, a character scalar. For
105#' vertex and edge sequences the id of the graph they were created from.
106#'
107#' @export
108#' @examples
109#' g <- make_ring(10)
110#' graph_id(g)
111#' graph_id(V(g))
112#' graph_id(E(g))
113#'
114#' g2 <- g + 1
115#' graph_id(g2)
116
117graph_id <- function(x, ...)
118  UseMethod("graph_id")
119
120#' @method graph_id igraph
121#' @export
122
123graph_id.igraph <- function(x, ...) {
124  get_graph_id(x)
125}
126
127#' @method graph_id igraph.vs
128#' @export
129
130graph_id.igraph.vs <- function(x, ...) {
131  get_vs_graph_id(x) %||% NA_character_
132}
133
134#' @method graph_id igraph.es
135#' @export
136
137graph_id.igraph.es <- function(x, ...) {
138  get_es_graph_id(x) %||% NA_character_
139}
140
141#' Vertices of a graph
142#'
143#' Create a vertex sequence (vs) containing all vertices of a graph.
144#'
145#' @details
146#' A vertex sequence is just what the name says it is: a sequence of
147#' vertices. Vertex sequences are usually used as igraph function arguments
148#' that refer to vertices of a graph.
149#'
150#' A vertex sequence is tied to the graph it refers to: it really denoted
151#' the specific vertices of that graph, and cannot be used together with
152#' another graph.
153#'
154#' At the implementation level, a vertex sequence is simply a vector
155#' containing numeric vertex ids, but it has a special class attribute
156#' which makes it possible to perform graph specific operations on it, like
157#' selecting a subset of the vertices based on graph structure, or vertex
158#' attributes.
159#'
160#' A vertex sequence is most often created by the \code{V()} function. The
161#' result of this includes all vertices in increasing vertex id order. A
162#' vertex sequence can be indexed by a numeric vector, just like a regular
163#' R vector. See \code{\link{[.igraph.vs}} and additional links to other
164#' vertex sequence operations below.
165#'
166#' @section Indexing vertex sequences:
167#' Vertex sequences mostly behave like regular vectors, but there are some
168#' additional indexing operations that are specific for them;
169#' e.g. selecting vertices based on graph structure, or based on vertex
170#' attributes. See \code{\link{[.igraph.vs}} for details.
171#'
172#' @section Querying or setting attributes:
173#' Vertex sequences can be used to query or set attributes for the
174#' vertices in the sequence. See \code{\link{$.igraph.vs}} for details.
175#'
176#' @param graph The graph
177#' @return A vertex sequence containing all vertices, in the order
178#'   of their numeric vertex ids.
179#'
180#' @family vertex and edge sequences
181#' @export
182#' @examples
183#' # Vertex ids of an unnamed graph
184#' g <- make_ring(10)
185#' V(g)
186#'
187#' # Vertex ids of a named graph
188#' g2 <- make_ring(10) %>%
189#'   set_vertex_attr("name", value = letters[1:10])
190#' V(g2)
191
192V <- function(graph) {
193  if (!is_igraph(graph)) {
194    stop("Not a graph object")
195  }
196
197  update_vs_ref(graph)
198
199  res <- seq_len(vcount(graph))
200  if (is_named(graph)) names(res) <- vertex_attr(graph)$name
201  class(res) <- "igraph.vs"
202  add_vses_graph_ref(res, graph)
203}
204
205create_vs <- function(graph, idx, na_ok = FALSE) {
206  if (na_ok) idx <- ifelse(idx < 1 | idx > gorder(graph), NA, idx)
207  res <- simple_vs_index(V(graph), idx, na_ok = na_ok)
208  add_vses_graph_ref(res, graph)
209}
210
211#' Edges of a graph
212#'
213#' An edge sequence is a vector containing numeric edge ids, with a special
214#' class attribute that allows custom operations: selecting subsets of
215#' edges based on attributes, or graph structure, creating the
216#' intersection, union of edges, etc.
217#'
218#' @details
219#' Edge sequences are usually used as igraph function arguments that
220#' refer to edges of a graph.
221#'
222#' An edge sequence is tied to the graph it refers to: it really denoted
223#' the specific edges of that graph, and cannot be used together with
224#' another graph.
225#'
226#' An edge sequence is most often created by the \code{E()} function. The
227#' result includes edges in increasing edge id order by default (if. none
228#' of the \code{P} and \code{path} arguments are used). An edge
229#' sequence can be indexed by a numeric vector, just like a regular R
230#' vector. See links to other edge sequence operations below.
231#'
232#' @section Indexing edge sequences:
233#' Edge sequences mostly behave like regular vectors, but there are some
234#' additional indexing operations that are specific for them;
235#' e.g. selecting edges based on graph structure, or based on edge
236#' attributes. See \code{\link{[.igraph.es}} for details.
237#'
238#' @section Querying or setting attributes:
239#' Edge sequences can be used to query or set attributes for the
240#' edges in the sequence. See \code{\link{$.igraph.es}} for details.
241#'
242#' @param graph The graph.
243#' @param P A list of vertices to select edges via pairs of vertices.
244#'   The first and second vertices select the first edge, the third
245#'   and fourth the second, etc.
246#' @param path A list of vertices, to select edges along a path.
247#'   Note that this only works reliable for simple graphs. If the graph
248#'   has multiple edges, one of them will be chosen arbitrarily to
249#'   be included in the edge sequence.
250#' @param directed Whether to consider edge directions in the \code{P}
251#'   argument, for directed graphs.
252#' @return An edge sequence of the graph.
253#'
254#' @export
255#' @family vertex and edge sequences
256#' @examples
257#' # Edges of an unnamed graph
258#' g <- make_ring(10)
259#' E(g)
260#'
261#' # Edges of a named graph
262#' g2 <- make_ring(10) %>%
263#'   set_vertex_attr("name", value = letters[1:10])
264#' E(g2)
265
266E <- function(graph, P=NULL, path=NULL, directed=TRUE) {
267  if (!is_igraph(graph)) {
268    stop("Not a graph object")
269  }
270
271  update_es_ref(graph)
272
273  if (!is.null(P) && !is.null(path)) {
274    stop("Cannot give both `P' and `path' at the same time")
275  }
276
277  if (is.null(P) && is.null(path)) {
278    ec <- ecount(graph)
279    res <- seq_len(ec)
280  } else if (!is.null(P)) {
281    on.exit( .Call(C_R_igraph_finalizer) )
282    res <- .Call(C_R_igraph_es_pairs, graph, as.igraph.vs(graph, P)-1,
283                 as.logical(directed)) + 1
284  } else {
285    on.exit(.Call(C_R_igraph_finalizer) )
286    res <- .Call(C_R_igraph_es_path, graph, as.igraph.vs(graph, path)-1,
287                 as.logical(directed)) + 1
288  }
289
290  if ("name" %in% edge_attr_names(graph)) {
291    names(res) <- edge_attr(graph)$name[res]
292  }
293  if (is.named(graph)) {
294    el <- ends(graph, es = res)
295    attr(res, "vnames") <- paste(el[,1], el[,2], sep = "|")
296  }
297
298  class(res) <- "igraph.es"
299  add_vses_graph_ref(res, graph)
300}
301
302create_es <- function(graph, idx, na_ok = FALSE) {
303  if (na_ok) idx <- ifelse(idx < 1 | idx > gsize(graph), NA, idx)
304  simple_es_index(E(graph), idx)
305}
306
307simple_vs_index <- function(x, i, na_ok = FALSE) {
308  res <- unclass(x)[i]
309  if (!na_ok && any(is.na(res))) stop('Unknown vertex selected')
310  class(res) <- "igraph.vs"
311  res
312}
313
314#' Indexing vertex sequences
315#'
316#' Vertex sequences can be indexed very much like a plain numeric R vector,
317#' with some extras.
318#'
319#' @details
320#' Vertex sequences can be indexed using both the single bracket and
321#' the double bracket operators, and they both work the same way.
322#' The only difference between them is that the double bracket operator
323#' marks the result for printing vertex attributes.
324#'
325#' @section Multiple indices:
326#' When using multiple indices within the bracket, all of them
327#' are evaluated independently, and then the results are concatenated
328#' using the \code{c()} function (except for the \code{na_ok} argument,
329#' which is special an must be named. E.g. \code{V(g)[1, 2, .nei(1)]}
330#' is equivalent to \code{c(V(g)[1], V(g)[2], V(g)[.nei(1)])}.
331#'
332#' @section Index types:
333#' Vertex sequences can be indexed with positive numeric vectors,
334#' negative numeric vectors, logical vectors, character vectors:
335#' \itemize{
336#'   \item When indexed with positive numeric vectors, the vertices at the
337#'     given positions in the sequence are selected. This is the same as
338#'     indexing a regular R atomic vector with positive numeric vectors.
339#'   \item When indexed with negative numeric vectors, the vertices at the
340#'     given positions in the sequence are omitted. Again, this is the same
341#'     as indexing a regular R atomic vector.
342#'   \item When indexed with a logical vector, the lengths of the vertex
343#'     sequence and the index must match, and the vertices for which the
344#'     index is \code{TRUE} are selected.
345#'   \item Named graphs can be indexed with character vectors,
346#'     to select vertices with the given names.
347#' }
348#'
349#' @section Vertex attributes:
350#' When indexing vertex sequences, vertex attributes can be referred
351#' to simply by using their names. E.g. if a graph has a \code{name} vertex
352#' attribute, then \code{V(g)[name == "foo"]} is equivalent to
353#' \code{V(g)[V(g)$name == "foo"]}. See examples below.
354#'
355#' @section Special functions:
356#' There are some special igraph functions that can be used only
357#' in expressions indexing vertex sequences: \describe{
358#'   \item{\code{.nei}}{takes a vertex sequence as its argument
359#'     and selects neighbors of these vertices. An optional \code{mode}
360#'     argument can be used to select successors (\code{mode="out"}), or
361#'     predecessors (\code{mode="in"}) in directed graphs.}
362#'   \item{\code{.inc}}{Takes an edge sequence as an argument, and
363#'     selects vertices that have at least one incident edge in this
364#'     edge sequence.}
365#'   \item{\code{.from}}{Similar to \code{.inc}, but only considers the
366#'     tails of the edges.}
367#'   \item{\code{.to}}{Similar to \code{.inc}, but only considers the
368#'     heads of the edges.}
369#'   \item{\code{.innei}, \code{.outnei}}{\code{.innei(v)} is a shorthand for
370#'     \code{.nei(v, mode = "in")}, and \code{.outnei(v)} is a shorthand for
371#'     \code{.nei(v, mode = "out")}.
372#'   }
373#' }
374#' Note that multiple special functions can be used together, or with
375#' regular indices, and then their results are concatenated. See more
376#' examples below.
377#'
378#' @param x A vertex sequence.
379#' @param ... Indices, see details below.
380#' @param na_ok Whether it is OK to have \code{NA}s in the vertex
381#'   sequence.
382#' @return Another vertex sequence, referring to the same graph.
383#'
384#' @method [ igraph.vs
385#' @name igraph-vs-indexing
386#' @export
387#' @family vertex and edge sequences
388#' @family vertex and edge sequence operations
389#'
390#' @examples
391#' # -----------------------------------------------------------------
392#' # Setting attributes for subsets of vertices
393#' largest_comp <- function(graph) {
394#'   cl <- components(graph)
395#'   V(graph)[which.max(cl$csize) == cl$membership]
396#' }
397#' g <- sample_(gnp(100, 2/100),
398#'   with_vertex_(size = 3, label = ""),
399#'   with_graph_(layout = layout_with_fr)
400#' )
401#' giant_v <- largest_comp(g)
402#' V(g)$color <- "green"
403#' V(g)[giant_v]$color <- "red"
404#' plot(g)
405#'
406#' # -----------------------------------------------------------------
407#' # nei() special function
408#' g <- graph( c(1,2, 2,3, 2,4, 4,2) )
409#' V(g)[ .nei( c(2,4) ) ]
410#' V(g)[ .nei( c(2,4), "in") ]
411#' V(g)[ .nei( c(2,4), "out") ]
412#'
413#' # -----------------------------------------------------------------
414#' # The same with vertex names
415#' g <- graph(~ A -+ B, B -+ C:D, D -+ B)
416#' V(g)[ .nei( c('B', 'D') ) ]
417#' V(g)[ .nei( c('B', 'D'), "in" ) ]
418#' V(g)[ .nei( c('B', 'D'), "out" ) ]
419
420`[.igraph.vs` <- function(x, ..., na_ok = FALSE) {
421
422  args <- lazy_dots(..., .follow_symbols = FALSE)
423
424  ## If indexing has no argument at all, then we still get one,
425  ## but it is "empty", a name that is  ""
426
427  ## Special case, no argument (but we might get an artificial
428  ## empty one
429  if (length(args) < 1 ||
430      (length(args) == 1 && class(args[[1]]$expr) == "name" &&
431         as.character(args[[1]]$expr) == "")) {
432    return(x)
433  }
434
435  ## Special case: single numeric argument
436  if (length(args) == 1 && class(args[[1]]$expr) == "numeric") {
437    res <- simple_vs_index(x, args[[1]]$expr)
438    return (add_vses_graph_ref(res, get_vs_graph(x)))
439  }
440
441  ## Special case: single symbol argument, no such attribute
442  if (length(args) == 1 && class(args[[1]]$expr) == "name") {
443    graph <- get_vs_graph(x)
444    if (! (as.character(args[[1]]$expr) %in% vertex_attr_names(graph))) {
445      res <- simple_vs_index(x, lazy_eval(args[[1]]))
446      return (add_vses_graph_ref(res, graph))
447    }
448  }
449
450  .nei <- function(v, mode=c("all", "in", "out", "total")) {
451    ## TRUE iff the vertex is a neighbor (any type)
452    ## of at least one vertex in v
453    mode <- igraph.match.arg(mode)
454    mode <- switch(mode, "out"=1, "in"=2, "all"=3, "total"=3)
455
456    if (is.logical(v)) {
457      v <- which(v)
458    }
459    on.exit(.Call(C_R_igraph_finalizer) )
460    tmp <- .Call(C_R_igraph_vs_nei, graph, x, as.igraph.vs(graph, v)-1,
461                 as.numeric(mode))
462    tmp[as.numeric(x)]
463  }
464  nei <- function(...) { .nei(...) }
465  .innei <- function(v, mode=c("in", "all", "out", "total")) {
466    .nei(v, mode = mode[1])
467  }
468  innei <- function(...) { .innei(...) }
469  .outnei <- function(v, mode=c("out", "all", "in", "total")) {
470    .nei(v, mode = mode[1])
471  }
472  outnei <- function(...) { .outnei(...) }
473  .inc <- function(e) {
474    ## TRUE iff the vertex (in the vs) is incident
475    ## to at least one edge in e
476    if (is.logical(e)) {
477      e <- which(e)
478    }
479    on.exit(.Call(C_R_igraph_finalizer) )
480    tmp <- .Call(C_R_igraph_vs_adj, graph, x, as.igraph.es(graph, e)-1,
481                 as.numeric(3))
482    tmp[as.numeric(x)]
483  }
484  inc <- function(...) { .inc(...) }
485  adj <- function(...) { .inc(...) }
486  .from <- function(e) {
487    ## TRUE iff the vertex is the source of at least one edge in e
488    if (is.logical(e)) {
489      e <- which(e)
490    }
491    on.exit(.Call(C_R_igraph_finalizer) )
492    tmp <- .Call(C_R_igraph_vs_adj, graph, x, as.igraph.es(graph, e)-1,
493                 as.numeric(1))
494    tmp[as.numeric(x)]
495  }
496  from <- function(...) { .from(...) }
497  .to <- function(e) {
498    ## TRUE iff the vertex is the target of at least one edge in e
499    if (is.logical(e)) {
500      e <- which(e)
501    }
502    on.exit(.Call(C_R_igraph_finalizer) )
503    tmp <- .Call(C_R_igraph_vs_adj, graph, x, as.igraph.es(graph, e)-1,
504                 as.numeric(2))
505    tmp[as.numeric(x)]
506  }
507  to <- function(...) { .to(...) }
508
509  graph <- get_vs_graph(x)
510
511  if (is.null(graph)) {
512    res <- lapply(lazy_eval(args), simple_vs_index, x = x, na_ok = na_ok)
513
514  } else {
515
516    attrs <- vertex_attr(graph)
517    xvec <- as.vector(x)
518    for (i in seq_along(attrs)) attrs[[i]] <- attrs[[i]][xvec]
519
520    res <- lazy_eval(
521      args,
522      data =  c(attrs, .nei = .nei, nei = nei, .innei = .innei,
523        innei = innei, .outnei = .outnei,  outnei = outnei, adj = adj,
524        .inc = .inc, inc = inc, .from = .from, from = from, .to = .to,
525        to = to)
526    )
527    res <- lapply(res, function(ii) {
528      if (is.null(ii)) return(NULL)
529      ii <- simple_vs_index(x, ii, na_ok)
530      attr(ii, "env") <- attr(x, "env")
531      attr(ii, "graph") <- attr(x, "graph")
532      class(ii) <- class(x)
533      ii
534    })
535  }
536
537  res <- drop_null(res)
538  if (length(res)) {
539    do_call(c, res)
540  } else {
541    x[FALSE]
542  }
543}
544
545#' Select vertices and show their metadata
546#'
547#' The double bracket operator can be used on vertex sequences, to print
548#' the meta-data (vertex attributes) of the vertices in the sequence.
549#'
550#' @details
551#' Technically, when used with vertex sequences, the double bracket
552#' operator does exactly the same as the single bracket operator,
553#' but the resulting vertex sequence is printed differently: all
554#' attributes of the vertices in the sequence are printed as well.
555#'
556#' See \code{\link{[.igraph.vs}} for more about indexing vertex sequences.
557#'
558#' @param x A vertex sequence.
559#' @param ... Additional arguments, passed to \code{[}.
560#' @return The double bracket operator returns another vertex sequence,
561#'   with meta-data (attribute) printing turned on. See details below.
562#'
563#' @method [[ igraph.vs
564#' @name igraph-vs-indexing2
565#' @family vertex and edge sequences
566#' @family vertex and edge sequence operations
567#' @export
568#' @examples
569#' g <- make_ring(10) %>%
570#'   set_vertex_attr("color", value = "red") %>%
571#'   set_vertex_attr("name", value = LETTERS[1:10])
572#' V(g)
573#' V(g)[[]]
574#' V(g)[1:5]
575#' V(g)[[1:5]]
576
577`[[.igraph.vs` <- function(x, ...) {
578  res <- x[...]
579  attr(res, "single") <- TRUE
580  res
581}
582
583#' Select edges and show their metadata
584#'
585#' The double bracket operator can be used on edge sequences, to print
586#' the meta-data (edge attributes) of the edges in the sequence.
587#'
588#' @details
589#' Technically, when used with edge sequences, the double bracket
590#' operator does exactly the same as the single bracket operator,
591#' but the resulting edge sequence is printed differently: all
592#' attributes of the edges in the sequence are printed as well.
593#'
594#' See \code{\link{[.igraph.es}} for more about indexing edge sequences.
595#'
596#' @param x An edge sequence.
597#' @param ... Additional arguments, passed to \code{[}.
598#' @return Another edge sequence, with metadata printing turned on.
599#' See details below.
600#'
601#' @method [[ igraph.es
602#' @name igraph-es-indexing2
603#' @family vertex and edge sequences
604#' @family vertex and edge sequence operations
605#' @export
606#' @examples
607#' g <- make_(ring(10),
608#'   with_vertex_(name = LETTERS[1:10]),
609#'   with_edge_(weight = 1:10, color = "green"))
610#' E(g)
611#' E(g)[[]]
612#' E(g)[[.inc('A')]]
613
614`[[.igraph.es` <- function(x, ...) {
615  res <- x[...]
616  attr(res, "single") <- TRUE
617  res
618}
619
620simple_es_index <- function(x, i) {
621  if (!is.null(attr(x, "vnames"))) {
622    wh1 <- structure(seq_along(x), names = names(x))[i]
623    wh2 <- structure(seq_along(x), names = attr(x, "vnames"))[i]
624    wh <- ifelse(is.na(wh1), wh2, wh1)
625    res <- unclass(x)[wh]
626    names(res) <- names(x)[wh]
627    attr(res, "vnames") <- attr(x, "vnames")[wh]
628  } else {
629    res <- unclass(x)[i]
630  }
631  if (any(is.na(res))) stop('Unknown edge selected')
632
633  attr(res, "env") <- attr(x, "env")
634  attr(res, "graph") <- attr(x, "graph")
635  class(res) <- "igraph.es"
636  res
637}
638
639#' Indexing edge sequences
640#'
641#' Edge sequences can be indexed very much like a plain numeric R vector,
642#' with some extras.
643#'
644#' @section Multiple indices:
645#' When using multiple indices within the bracket, all of them
646#' are evaluated independently, and then the results are concatenated
647#' using the \code{c()} function. E.g. \code{E(g)[1, 2, .inc(1)]}
648#' is equivalent to \code{c(E(g)[1], E(g)[2], E(g)[.inc(1)])}.
649#'
650#' @section Index types:
651#' Edge sequences can be indexed with positive numeric vectors,
652#' negative numeric vectors, logical vectors, character vectors:
653#' \itemize{
654#'   \item When indexed with positive numeric vectors, the edges at the
655#'     given positions in the sequence are selected. This is the same as
656#'     indexing a regular R atomic vector with positive numeric vectors.
657#'   \item When indexed with negative numeric vectors, the edges at the
658#'     given positions in the sequence are omitted. Again, this is the same
659#'     as indexing a regular R atomic vector.
660#'   \item When indexed with a logical vector, the lengths of the edge
661#'     sequence and the index must match, and the edges for which the
662#'     index is \code{TRUE} are selected.
663#'   \item Named graphs can be indexed with character vectors,
664#'     to select edges with the given names. Note that a graph may
665#'     have edge names and vertex names, and both can be used to select
666#'     edges. Edge names are simply used as names of the numeric
667#'     edge id vector. Vertex names effectively only work in graphs without
668#'     multiple edges, and must be separated with a \code{|} bar character
669#'     to select an edges that incident to the two given vertices. See
670#'     examples below.
671#' }
672#'
673#' @section Edge attributes:
674#' When indexing edge sequences, edge attributes can be referred
675#' to simply by using their names. E.g. if a graph has a \code{weight} edge
676#' attribute, then \code{E(G)[weight > 1]} selects all edges with a larger
677#' than one weight. See more examples below.
678#'
679#' @section Special functions:
680#' There are some special igraph functions that can be used
681#' only in expressions indexing edge sequences: \describe{
682#'   \item{\code{.inc}}{takes a vertex sequence, and selects
683#'     all edges that have at least one incident vertex in the vertex
684#'     sequence.}
685#'   \item{\code{.from}}{similar to \code{.inc()}, but only
686#'     the tails of the edges are considered.}
687#'   \item{\code{.to}}{is similar to \code{.inc()}, but only
688#'     the heads of the edges are considered.}
689#'   \item{\code{\%--\%}}{a special operator that can be
690#'     used to select all edges between two sets of vertices. It ignores
691#'     the edge directions in directed graphs.}
692#'   \item{\code{\%->\%}}{similar to \code{\%--\%},
693#'     but edges \emph{from} the left hand side argument, pointing
694#'     \emph{to} the right hand side argument, are selected, in directed
695#'     graphs.}
696#'   \item{\code{\%<-\%}}{similar to \code{\%--\%},
697#'     but edges \emph{to} the left hand side argument, pointing
698#'     \emph{from} the right hand side argument, are selected, in directed
699#'     graphs.}
700#' }
701#' Note that multiple special functions can be used together, or with
702#' regular indices, and then their results are concatenated. See more
703#' examples below.
704#'
705#' @aliases %--% %<-% %->%
706#' @param x An edge sequence
707#' @param ... Indices, see details below.
708#' @return Another edge sequence, referring to the same graph.
709#'
710#' @method [ igraph.es
711#' @name igraph-es-indexing
712#'
713#' @export
714#' @family vertex and edge sequences
715#' @family vertex and edge sequence operations
716#' @examples
717#' # special operators for indexing based on graph structure
718#' g <- sample_pa(100, power = 0.3)
719#' E(g) [ 1:3 %--% 2:6 ]
720#' E(g) [ 1:5 %->% 1:6 ]
721#' E(g) [ 1:3 %<-% 2:6 ]
722#'
723#' # the edges along the diameter
724#' g <- sample_pa(100, directed = FALSE)
725#' d <- get_diameter(g)
726#' E(g, path = d)
727#'
728#' # select edges based on attributes
729#' g <- sample_gnp(20, 3/20) %>%
730#'   set_edge_attr("weight", value = rnorm(gsize(.)))
731#' E(g)[[ weight < 0 ]]
732
733`[.igraph.es` <- function(x, ...) {
734
735  args <- lazy_dots(..., .follow_symbols = TRUE)
736
737  ## If indexing has no argument at all, then we still get one,
738  ## but it is "empty", a name that is ""
739
740  if (length(args) < 1 ||
741      (length(args) == 1 && class(args[[1]]$expr) == "name" &&
742         as.character(args[[1]]$expr) == "")) {
743    return(x)
744  }
745
746  .inc <- function(v) {
747    ## TRUE iff the edge is incident to at least one vertex in v
748    on.exit(.Call(C_R_igraph_finalizer) )
749    tmp <- .Call(C_R_igraph_es_adj, graph, x, as.igraph.vs(graph, v)-1,
750                 as.numeric(3))
751    tmp[ as.numeric(x) ]
752  }
753  adj <- function(...) { .inc(...) }
754  inc <- function(...) { .inc(...) }
755  .from <- function(v) {
756    ## TRUE iff the edge originates from at least one vertex in v
757    on.exit(.Call(C_R_igraph_finalizer) )
758    tmp <- .Call(C_R_igraph_es_adj, graph, x, as.igraph.vs(graph, v)-1,
759                 as.numeric(1))
760    tmp[ as.numeric(x) ]
761  }
762  from <- function(...) { .from(...) }
763  .to <- function(v) {
764    ## TRUE iff the edge points to at least one vertex in v
765    on.exit(.Call(C_R_igraph_finalizer) )
766    tmp <- .Call(C_R_igraph_es_adj, graph, x, as.igraph.vs(graph, v)-1,
767                 as.numeric(2))
768    tmp[ as.numeric(x) ]
769  }
770  to <- function(...) { .to(...) }
771
772  graph <- get_es_graph(x)
773
774  if (is.null(graph)) {
775    res <- lapply(lazy_eval(args), simple_es_index, x = x)
776
777  } else {
778
779    attrs <- edge_attr(graph)
780    xvec <- as.vector(x)
781    for (i in seq_along(attrs)) attrs[[i]] <- attrs[[i]][xvec]
782
783    res <- lazy_eval(
784        args,
785      data = c(attrs, .inc = .inc, inc = inc, adj = adj, .from = .from,
786        from = from, .to = .to, to = to,
787        .igraph.from = list(.Call(C_R_igraph_mybracket,
788          graph, 3L)[ as.numeric(x) ]),
789        .igraph.to = list(.Call(C_R_igraph_mybracket,
790          graph, 4L)[as.numeric(x)]),
791        .igraph.graph = list(graph),
792        `%--%`=`%--%`, `%->%`=`%->%`, `%<-%`=`%<-%`)
793    )
794    res <- lapply(res, function(ii) {
795      if (is.null(ii)) return(NULL)
796      ii <- simple_es_index(x, ii)
797      attr(ii, "env") <- attr(x, "env")
798      attr(ii, "graph") <- attr(x, "graph")
799      class(ii) <- class(x)
800      ii
801    })
802  }
803
804  res <- drop_null(res)
805  if (length(res) == 1) {
806    res[[1]]
807  } else if (length(res)) {
808    do_call(c, res)
809  } else {
810    x[FALSE]
811  }
812}
813
814#' @export
815
816`%--%` <- function(f, t) {
817  from <- get(".igraph.from", parent.frame())
818  to <- get(".igraph.to", parent.frame())
819  graph <- get(".igraph.graph", parent.frame())
820  f <- as.igraph.vs(graph, f)-1
821  t <- as.igraph.vs(graph, t)-1
822  (from %in% f & to %in% t) | (to %in% f & from %in% t)
823}
824
825#' @export
826
827`%->%` <- function(f, t) {
828  from <- get(".igraph.from", parent.frame())
829  to <- get(".igraph.to", parent.frame())
830  graph <- get(".igraph.graph", parent.frame())
831  f <- as.igraph.vs(graph, f)-1
832  t <- as.igraph.vs(graph, t)-1
833  if (is_directed(graph)) {
834    from %in% f & to %in% t
835  } else {
836    (from %in% f & to %in% t) | (to %in% f & from %in% t)
837  }
838}
839
840#' @export
841
842`%<-%` <- function(t, value) {
843  from <- get(".igraph.from", parent.frame())
844  to <- get(".igraph.to", parent.frame())
845  graph <- get(".igraph.graph", parent.frame())
846  value <- as.igraph.vs(graph, value)-1
847  t <- as.igraph.vs(graph, t)-1
848  if (is_directed(graph)) {
849    from %in% value & to %in% t
850  } else {
851    (from %in% value & to %in% t) | (to %in% value & from %in% t)
852  }
853}
854
855#' @param i Index.
856#' @method [[<- igraph.vs
857#' @name igraph-vs-attributes
858#' @export
859
860`[[<-.igraph.vs` <- function(x, i, value) {
861  if (! "name"  %in% names(attributes(value)) ||
862      ! "value" %in% names(attributes(value))) {
863    stop("invalid indexing")
864  }
865  if (is.null(get_vs_graph(x))) stop("Graph is unknown")
866  value
867}
868
869#' @method [<- igraph.vs
870#' @name igraph-vs-attributes
871#' @export
872
873`[<-.igraph.vs` <-  `[[<-.igraph.vs`
874
875#' @param i Index.
876#' @method [[<- igraph.es
877#' @name igraph-es-attributes
878#' @export
879
880`[[<-.igraph.es` <- function(x, i, value) {
881  if (! "name"  %in% names(attributes(value)) ||
882      ! "value" %in% names(attributes(value))) {
883    stop("invalid indexing")
884  }
885  if (is.null(get_es_graph(x))) stop("Graph is unknown")
886  value
887}
888
889#' @method [<- igraph.es
890#' @name igraph-es-attributes
891#' @export
892
893`[<-.igraph.es` <-  `[[<-.igraph.es`
894
895#' Query or set attributes of the vertices in a vertex sequence
896#'
897#' The \code{$} operator is a syntactic sugar to query and set the
898#' attributes of the vertices in a vertex sequence.
899#'
900#' @details
901#' The query form of \code{$} is a shortcut for
902#' \code{\link{vertex_attr}}, e.g. \code{V(g)[idx]$attr} is equivalent
903#' to \code{vertex_attr(g, attr, V(g)[idx])}.
904#'
905#' The assignment form of \code{$} is a shortcut for
906#' \code{\link{set_vertex_attr}}, e.g. \code{V(g)[idx]$attr <- value} is
907#' equivalent to \code{g <- set_vertex_attr(g, attr, V(g)[idx], value)}.
908#'
909#' @param x A vertex sequence. For \code{V<-} it is a graph.
910#' @param name Name of the vertex attribute to query or set.
911#' @return A vector or list, containing the values of
912#'   attribute \code{name} for the vertices in the vertex sequence.
913#'   For numeric, character or logical attributes, it is a vector of the
914#'   appropriate type, otherwise it is a list.
915#'
916#' @method $ igraph.vs
917#' @name igraph-vs-attributes
918#'
919#' @export
920#' @family vertex and edge sequences
921#' @family graph attributes
922#' @examples
923#' g <- make_(ring(10),
924#'   with_vertex_(
925#'     name = LETTERS[1:10],
926#'     color = sample(1:2, 10, replace=TRUE)
927#'   )
928#' )
929#' V(g)$name
930#' V(g)$color
931#' V(g)$frame.color <- V(g)$color
932#'
933#' # color vertices of the largest component
934#' largest_comp <- function(graph) {
935#'   cl <- components(graph)
936#'   V(graph)[which.max(cl$csize) == cl$membership]
937#' }
938#' g <- sample_(gnp(100, 2/100),
939#'   with_vertex_(size = 3, label = ""),
940#'   with_graph_(layout = layout_with_fr)
941#' )
942#' giant_v <- largest_comp(g)
943#' V(g)$color <- "blue"
944#' V(g)[giant_v]$color <- "orange"
945#' plot(g)
946
947`$.igraph.vs` <- function(x, name) {
948  graph <- get_vs_graph(x)
949  if (is.null(graph)) stop("Graph is unknown")
950  res <- vertex_attr(graph, name, x)
951  if ("single" %in% names(attributes(x)) && attr(x, "single")) {
952    res[[1]]
953  } else {
954    res
955  }
956}
957
958#' Query or set attributes of the edges in an edge sequence
959#'
960#' The \code{$} operator is a syntactic sugar to query and set
961#' edge attributes, for edges in an edge sequence.
962#'
963#' @details
964#' The query form of \code{$} is a shortcut for \code{\link{edge_attr}},
965#' e.g. \code{E(g)[idx]$attr} is equivalent to \code{edge_attr(g, attr,
966#' E(g)[idx])}.
967#'
968#' The assignment form of \code{$} is a shortcut for
969#' \code{\link{set_edge_attr}}, e.g. \code{E(g)[idx]$attr <- value} is
970#' equivalent to \code{g <- set_edge_attr(g, attr, E(g)[idx], value)}.
971#'
972#' @param x An edge sequence. For \code{E<-} it is a graph.
973#' @param name Name of the edge attribute to query or set.
974#' @return A vector or list, containing the values of the attribute
975#'   \code{name} for the edges in the sequence. For numeric, character or
976#'   logical attributes, it is a vector of the appropriate type, otherwise
977#'   it is a list.
978#'
979#' @method $ igraph.es
980#' @name igraph-es-attributes
981#'
982#' @export
983#' @family vertex and edge sequences
984#' @examples
985#' # color edges of the largest component
986#' largest_comp <- function(graph) {
987#'   cl <- components(graph)
988#'   V(graph)[which.max(cl$csize) == cl$membership]
989#' }
990#' g <- sample_(gnp(100, 1/100),
991#'   with_vertex_(size = 3, label = ""),
992#'   with_graph_(layout = layout_with_fr)
993#' )
994#' giant_v <- largest_comp(g)
995#' E(g)$color <- "orange"
996#' E(g)[giant_v %--% giant_v]$color <- "blue"
997#' plot(g)
998
999`$.igraph.es` <- function(x, name) {
1000  graph <- get_es_graph(x)
1001  if (is.null(graph)) stop("Graph is unknown")
1002  res <- edge_attr(graph, name, x)
1003  if ("single" %in% names(attributes(x)) && attr(x, "single")) {
1004    res[[1]]
1005  } else {
1006    res
1007  }
1008}
1009
1010#' @param value New value of the attribute, for the vertices in the
1011#'   vertex sequence.
1012#'
1013#' @method $<- igraph.vs
1014#' @name igraph-vs-attributes
1015#' @export
1016
1017`$<-.igraph.vs` <- function(x, name, value) {
1018  if (is.null(get_vs_graph(x))) stop("Graph is unknown")
1019  attr(x, "name") <- name
1020  attr(x, "value") <- value
1021  x
1022}
1023
1024#' @param value New value of the attribute, for the edges in the edge
1025#'   sequence.
1026#' @method $<- igraph.es
1027#' @name igraph-es-attributes
1028#' @export
1029#' @family vertex and edge sequences
1030
1031`$<-.igraph.es` <- function(x, name, value) {
1032  if (is.null(get_es_graph(x))) stop("Graph is unknown")
1033  attr(x, "name") <- name
1034  attr(x, "value") <- value
1035  x
1036}
1037
1038#' @name igraph-vs-attributes
1039#' @export
1040
1041`V<-` <- function(x, value) {
1042  if (!is_igraph(x)) {
1043    stop("Not a graph object")
1044  }
1045  if (! "name"  %in% names(attributes(value)) ||
1046      ! "value" %in% names(attributes(value))) {
1047    stop("invalid indexing")
1048  }
1049  i_set_vertex_attr(x, attr(value, "name"), index=value,
1050                    value=attr(value, "value"), check = FALSE)
1051}
1052
1053#' @param path Select edges along a path, given by a vertex sequence See
1054#'   \code{\link{E}}.
1055#' @param P Select edges via pairs of vertices. See \code{\link{E}}.
1056#' @param directed Whether to use edge directions for the \code{path} or
1057#'   \code{P} arguments.
1058#' @name igraph-es-attributes
1059#' @export
1060
1061`E<-` <- function(x, path=NULL, P=NULL, directed=NULL, value) {
1062  if (!is_igraph(x)) {
1063    stop("Not a graph object")
1064  }
1065  if (! "name"  %in% names(attributes(value)) ||
1066      ! "value" %in% names(attributes(value))) {
1067    stop("invalid indexing")
1068  }
1069  i_set_edge_attr(x, attr(value, "name"), index=value,
1070                  value=attr(value, "value"), check = FALSE)
1071}
1072
1073#' Show a vertex sequence on the screen
1074#'
1075#' For long vertex sequences, the printing is truncated to fit to the
1076#' screen. Use \code{print} explicitly and the \code{full} argument to
1077#' see the full sequence.
1078#'
1079#' Vertex sequence created with the double bracket operator are
1080#' printed differently, together with all attributes of the vertices
1081#' in the sequence, as a table.
1082#'
1083#' @param x A vertex sequence.
1084#' @param full Whether to show the full sequence, or truncate the output
1085#'   to the screen size.
1086#' @param ... These arguments are currently ignored.
1087#' @return The vertex sequence, invisibly.
1088#'
1089#' @method print igraph.vs
1090#' @export
1091#' @family vertex and edge sequences
1092#' @examples
1093#' # Unnamed graphs
1094#' g <- make_ring(10)
1095#' V(g)
1096#'
1097#' # Named graphs
1098#' g2 <- make_ring(10) %>%
1099#'   set_vertex_attr("name", value = LETTERS[1:10])
1100#' V(g2)
1101#'
1102#' # All vertices in the sequence
1103#' g3 <- make_ring(1000)
1104#' V(g3)
1105#' print(V(g3), full = TRUE)
1106#'
1107#' # Metadata
1108#' g4 <- make_ring(10) %>%
1109#'   set_vertex_attr("name", value = LETTERS[1:10]) %>%
1110#'   set_vertex_attr("color", value = "red")
1111#' V(g4)[[]]
1112#' V(g4)[[2:5, 7:8]]
1113
1114print.igraph.vs <- function(x, full = igraph_opt("print.full"), ...) {
1115
1116  graph <- get_vs_graph(x)
1117  len <- length(x)
1118  id <- graph_id(x)
1119
1120  title <- "+ " %+% chr(len) %+% "/" %+%
1121    (if (is.null(graph)) "?" else chr(vcount(graph))) %+%
1122    (if (len == 1) " vertex" else " vertices") %+%
1123    (if (!is.null(names(x))) ", named" else "") %+%
1124    (if (!is.na(id)) paste(", from", substr(id, 1, 7)) else "") %+%
1125    (if (is.null(graph)) " (deleted)" else "") %+%
1126    ":\n"
1127  cat(title)
1128
1129  if (!is.null(attr(x, "single")) && attr(x, "single") &&
1130      !is.null(graph) && length(vertex_attr_names(graph) > 0)) {
1131    ## Double bracket
1132    va <- vertex_attr(graph)
1133    if (all(sapply(va, is.atomic))) {
1134      print(as.data.frame(va, stringsAsFactors =
1135                            FALSE)[as.vector(x),, drop = FALSE])
1136    } else {
1137      print(lapply(va, "[", as.vector(x)))
1138    }
1139
1140  } else {
1141    ## Single bracket
1142
1143    x2 <- if (!is.null(names(x))) names(x) else as.vector(x)
1144    if (length(x2)) {
1145      if (is.logical(full) && full) {
1146        print(x2, quote = FALSE)
1147      }  else {
1148        head_print(x2, omitted_footer = "+ ... omitted several vertices\n",
1149                   quote = FALSE, max_lines = igraph_opt("auto.print.lines"))
1150      }
1151    }
1152  }
1153
1154  invisible(x)
1155}
1156
1157#' Print an edge sequence to the screen
1158#'
1159#' For long edge sequences, the printing is truncated to fit to the
1160#' screen. Use \code{print} explicitly and the code{full} argument to
1161#' see the full sequence.
1162#'
1163#' Edge sequences created with the double bracket operator are printed
1164#' differently, together with all attributes of the edges in the sequence,
1165#' as a table.
1166#'
1167#' @param x An edge sequence.
1168#' @param full Whether to show the full sequence, or truncate the output
1169#'   to the screen size.
1170#' @param ... Currently ignored.
1171#' @return The edge sequence, invisibly.
1172#'
1173#' @method print igraph.es
1174#' @export
1175#' @family vertex and edge sequences
1176#' @examples
1177#' # Unnamed graphs
1178#' g <- make_ring(10)
1179#' E(g)
1180#'
1181#' # Named graphs
1182#' g2 <- make_ring(10) %>%
1183#'   set_vertex_attr("name", value = LETTERS[1:10])
1184#' E(g2)
1185#'
1186#' # All edges in a long sequence
1187#' g3 <- make_ring(200)
1188#' E(g3)
1189#' E(g3) %>% print(full = TRUE)
1190#'
1191#' # Metadata
1192#' g4 <- make_ring(10) %>%
1193#'   set_vertex_attr("name", value = LETTERS[1:10]) %>%
1194#'   set_edge_attr("weight", value = 1:10) %>%
1195#'   set_edge_attr("color", value = "green")
1196#' E(g4)
1197#' E(g4)[[]]
1198#' E(g4)[[1:5]]
1199
1200print.igraph.es <- function(x, full = igraph_opt("print.full"), ...) {
1201  graph <- get_es_graph(x)
1202  ml <- if (identical(full, TRUE)) NULL else igraph_opt("auto.print.lines")
1203  .print.edges.compressed(x = graph, edges = x, max.lines = ml, names = TRUE,
1204                          num = TRUE)
1205  invisible(x)
1206}
1207
1208# these are internal
1209
1210as.igraph.vs <- function(graph, v, na.ok=FALSE) {
1211  if (inherits(v, "igraph.vs") && !is.null(graph) &&
1212      !warn_version(graph)) {
1213    if (get_graph_id(graph) != get_vs_graph_id(v)) {
1214      stop("Cannot use a vertex sequence from another graph.")
1215    }
1216  }
1217  if (is.character(v) && "name" %in% vertex_attr_names(graph)) {
1218    v <- as.numeric(match(v, V(graph)$name))
1219    if (!na.ok && any(is.na(v))) {
1220      stop("Invalid vertex names")
1221    }
1222    v
1223  } else {
1224    if (is.logical(v)) {
1225      res <- as.vector(V(graph))[v]
1226    } else if (is.numeric(v) && any(v<0)){
1227      res <- as.vector(V(graph))[v]
1228    } else {
1229      res <- as.numeric(v)
1230    }
1231    if (!na.ok && any(is.na(res))) {
1232      stop("Invalid vertex name(s)")
1233    }
1234    res
1235  }
1236}
1237
1238as.igraph.es <- function(graph, e) {
1239  if (inherits(e, "igraph.es") && !is.null(graph)
1240      && !warn_version(graph)) {
1241    if (get_graph_id(graph) != get_es_graph_id(e)) {
1242      stop("Cannot use an edge sequence from another graph.")
1243    }
1244  }
1245  if (is.character(e)) {
1246    Pairs <- grep("|", e, fixed=TRUE)
1247    Names <- if (length(Pairs)==0) seq_along(e) else -Pairs
1248    res <- numeric(length(e))
1249
1250    ## Based on vertex ids/names
1251    if (length(Pairs)!=0) {
1252      vv <- strsplit(e[Pairs], "|", fixed=TRUE)
1253      vl <- sapply(vv, length)
1254      if (any(vl != 2)) {
1255        stop("Invalid edge name: ", e[Pairs][vl!=2][1])
1256      }
1257      vp <- unlist(vv)
1258      if (! "name" %in% vertex_attr_names(graph)) {
1259        vp <- as.numeric(vp)
1260      }
1261      res[Pairs] <- get.edge.ids(graph, vp)
1262    }
1263
1264    ## Based on edge ids/names
1265    if (length(Names) != 0) {
1266      if ("name" %in% edge_attr_names(graph)) {
1267        res[Names] <- as.numeric(match(e[Names], E(graph)$name))
1268      } else {
1269        res[Names] <- as.numeric(e[Names])
1270      }
1271    }
1272
1273  } else {
1274    res <- as.numeric(e)
1275  }
1276  if (any(is.na(res))) {
1277    stop("Invalid edge names")
1278  }
1279  res
1280}
1281
1282
1283is_igraph_vs <- function(x) {
1284  inherits(x, "igraph.vs")
1285}
1286
1287
1288is_igraph_es <- function(x) {
1289  inherits(x, "igraph.es")
1290}
1291
1292
1293parse_op_args <- function(..., what, is_fun, as_fun, check_graph = TRUE) {
1294
1295  args <- list(...)
1296
1297  if (any(! sapply(args, is_fun))) stop("Not ", what, " sequence")
1298
1299  ## get the ids of all graphs
1300  graph_id <- sapply(args, get_vs_graph_id) %>%
1301    unique()
1302
1303  if (length(graph_id) != 1) {
1304    warning("Combining vertex/edge sequences from different graphs.\n",
1305            "This will not work in future igraph versions")
1306  }
1307
1308  graphs <- args %>%
1309    lapply(get_vs_graph) %>%
1310    drop_null()
1311
1312  addresses <- graphs %>%
1313    sapply(function(x) x %&&% address(x)) %>%
1314    unique()
1315
1316  if (check_graph && length(addresses) >= 2) {
1317    warning("Combining vertex/edge sequences from different graphs.\n",
1318            "This will not work in future igraph versions")
1319  }
1320
1321  graph <- if (length(graphs)) graphs[[1]] else NULL
1322
1323  args <- lapply(args, unclass)
1324
1325  list(graph = graph, args = args, id = graph_id)
1326}
1327
1328
1329parse_vs_op_args <- function(...) {
1330  parse_op_args(..., what = "a vertex", is_fun = is_igraph_vs,
1331                as_fun = as.igraph.vs)
1332}
1333
1334
1335parse_es_op_args <- function(...) {
1336  parse_op_args(..., what = "an edge", is_fun = is_igraph_es,
1337                as_fun = as.igraph.es)
1338}
1339
1340
1341create_op_result <- function(parsed, result, class, args) {
1342  result <- add_vses_graph_ref(result, parsed$graph)
1343  class(result) <- class
1344  ## c() drops names for zero length vectors. Why???
1345  if (! length(result) &&
1346      any(sapply(args, function(x) !is.null(names(x))))) {
1347    names(result) <- character()
1348  }
1349  result
1350}
1351
1352
1353#' Remove duplicate vertices from a vertex sequence
1354#'
1355#' @param x A vertex sequence.
1356#' @param incomparables a vector of values that cannot be compared.
1357#'   Passed to base function \code{duplicated}. See details there.
1358#' @param ... Passed to base function \code{duplicated()}.
1359#' @return A vertex sequence with the duplicate vertices removed.
1360#'
1361#' @method unique igraph.vs
1362#' @family vertex and edge sequence operations
1363#' @export
1364#' @examples
1365#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10]))
1366#' V(g)[1, 1:5, 1:10, 5:10]
1367#' V(g)[1, 1:5, 1:10, 5:10] %>% unique()
1368
1369unique.igraph.vs <- function(x, incomparables = FALSE, ...) {
1370  x[!duplicated(x, incomparables = incomparables, ...)]
1371}
1372
1373
1374#' Remove duplicate edges from an edge sequence
1375#'
1376#' @param x An edge sequence.
1377#' @param incomparables a vector of values that cannot be compared.
1378#'   Passed to base function \code{duplicated}. See details there.
1379#' @param ... Passed to base function \code{duplicated()}.
1380#' @return An edge sequence with the duplicate vertices removed.
1381#'
1382#' @method unique igraph.es
1383#' @family vertex and edge sequence operations
1384#' @export
1385#' @examples
1386#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10]))
1387#' E(g)[1, 1:5, 1:10, 5:10]
1388#' E(g)[1, 1:5, 1:10, 5:10] %>% unique()
1389
1390unique.igraph.es <- function(x, incomparables = FALSE, ...) {
1391  x[!duplicated(x, incomparables = incomparables, ...)]
1392}
1393
1394
1395#' Concatenate vertex sequences
1396#'
1397#' @param ... The vertex sequences to concatenate. They must
1398#'   refer to the same graph.
1399#' @param recursive Ignored, included for S3 compatibility with
1400#'   the base \code{c} function.
1401#' @return A vertex sequence, the input sequences concatenated.
1402#'
1403#' @method c igraph.vs
1404#' @family vertex and edge sequence operations
1405#' @export
1406#' @examples
1407#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10]))
1408#' c(V(g)[1], V(g)['A'], V(g)[1:4])
1409
1410c.igraph.vs <- function(..., recursive = FALSE) {
1411  parsed <- parse_vs_op_args(...)
1412  res <- do_call(c, .args = parsed$args)
1413  create_op_result(parsed, res, "igraph.vs", list(...))
1414}
1415
1416
1417#' Concatenate edge sequences
1418#'
1419#' @param ... The edge sequences to concatenate. They must
1420#'   all refer to the same graph.
1421#' @param recursive Ignored, included for S3 compatibility with the
1422#'   base \code{c} function.
1423#' @return An edge sequence, the input sequences concatenated.
1424#'
1425#' @method c igraph.es
1426#' @family vertex and edge sequence operations
1427#' @export
1428#' @examples
1429#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10]))
1430#' c(E(g)[1], E(g)['A|B'], E(g)[1:4])
1431
1432c.igraph.es <- function(..., recursive = FALSE) {
1433  parsed <- parse_es_op_args(...)
1434  res <- do_call(c, .args = parsed$args)
1435  res <- create_op_result(parsed, res, "igraph.es", list(...))
1436  attr(res, "vnames") <- do_call(c, .args = lapply(list(...), attr, "vnames"))
1437  res
1438}
1439
1440
1441#' Union of vertex sequences
1442#'
1443#' @details
1444#' They must belong to the same graph. Note that this function has
1445#' \sQuote{set} semantics and the multiplicity of vertices is lost in the
1446#' result. (This is to match the behavior of the based \code{unique}
1447#' function.)
1448#'
1449#' @param ... The vertex sequences to take the union of.
1450#' @return A vertex sequence that contains all vertices in the given
1451#' sequences, exactly once.
1452#'
1453#' @method union igraph.vs
1454#' @family vertex and edge sequence operations
1455#' @export
1456#' @examples
1457#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10]))
1458#' union(V(g)[1:6], V(g)[5:10])
1459
1460union.igraph.vs <- function(...) {
1461  unique(c(...))
1462}
1463
1464
1465#' Union of edge sequences
1466#'
1467#' @details
1468#' They must belong to the same graph. Note that this function has
1469#' \sQuote{set} semantics and the multiplicity of edges is lost in the
1470#' result. (This is to match the behavior of the based \code{unique}
1471#' function.)
1472#'
1473#' @param ... The edge sequences to take the union of.
1474#' @return An edge sequence that contains all edges in the given
1475#' sequences, exactly once.
1476#'
1477#' @method union igraph.es
1478#' @family vertex and edge sequence operations
1479#' @export
1480#' @examples
1481#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10]))
1482#' union(E(g)[1:6], E(g)[5:9], E(g)['A|J'])
1483
1484union.igraph.es <- union.igraph.vs
1485
1486
1487#' Intersection of vertex sequences
1488#'
1489#' @details
1490#' They must belong to the same graph. Note that this function has
1491#' \sQuote{set} semantics and the multiplicity of vertices is lost in the
1492#' result.
1493#'
1494#' @param ... The vertex sequences to take the intersection of.
1495#' @return A vertex sequence that contains vertices that appear in all
1496#' given sequences, each vertex exactly once.
1497#'
1498#' @method intersection igraph.vs
1499#' @family vertex and edge sequence operations
1500#' @export
1501#' @examples
1502#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10]))
1503#' intersection(E(g)[1:6], E(g)[5:9])
1504
1505intersection.igraph.vs <- function(...) {
1506  ifun <- function(x, y) {
1507    unique(y[match(as.vector(x), y, 0L)])
1508  }
1509  Reduce(ifun, list(...))
1510}
1511
1512
1513#' Intersection of edge sequences
1514#'
1515#' @details
1516#' They must belong to the same graph. Note that this function has
1517#' \sQuote{set} semantics and the multiplicity of edges is lost in the
1518#' result.
1519#'
1520#' @param ... The edge sequences to take the intersection of.
1521#' @return An edge sequence that contains edges that appear in all
1522#' given sequences, each edge exactly once.
1523#'
1524#' @method intersection igraph.es
1525#' @family vertex and edge sequence operations
1526#' @export
1527#' @examples
1528#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10]))
1529#' intersection(E(g)[1:6], E(g)[5:9])
1530
1531intersection.igraph.es <- intersection.igraph.vs
1532
1533
1534#' Difference of vertex sequences
1535#'
1536#' @details
1537#' They must belong to the same graph. Note that this function has
1538#' \sQuote{set} semantics and the multiplicity of vertices is lost in the
1539#' result.
1540#'
1541#' @param big The \sQuote{big} vertex sequence.
1542#' @param small The \sQuote{small} vertex sequence.
1543#' @param ... Ignored, included for S3 signature compatibility.
1544#' @return A vertex sequence that contains only vertices that are part of
1545#' \code{big}, but not part of \code{small}.
1546#'
1547#' @method difference igraph.vs
1548#' @family vertex and edge sequence operations
1549#' @export
1550#' @examples
1551#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10]))
1552#' difference(V(g), V(g)[6:10])
1553
1554difference.igraph.vs <- function(big, small, ...) {
1555  if (!length(big)) {
1556    big
1557  } else {
1558    big[ match(big, small, 0L) == 0L ]
1559  }
1560}
1561
1562
1563#' Difference of edge sequences
1564#'
1565#' @details
1566#' They must belong to the same graph. Note that this function has
1567#' \sQuote{set} semantics and the multiplicity of edges is lost in the
1568#' result.
1569#'
1570#' @param big The \sQuote{big} edge sequence.
1571#' @param small The \sQuote{small} edge sequence.
1572#' @param ... Ignored, included for S3 signature compatibility.
1573#' @return An edge sequence that contains only edges that are part of
1574#' \code{big}, but not part of \code{small}.
1575#'
1576#' @method difference igraph.es
1577#' @family vertex and edge sequence operations
1578#' @export
1579#' @examples
1580#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10]))
1581#' difference(V(g), V(g)[6:10])
1582
1583difference.igraph.es <- difference.igraph.vs
1584
1585
1586#' Reverse the order in a vertex sequence
1587#'
1588#' @param x The vertex sequence to reverse.
1589#' @return The reversed vertex sequence.
1590#'
1591#' @method rev igraph.vs
1592#' @family vertex and edge sequence operations
1593#' @export
1594#' @examples
1595#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10]))
1596#' V(g) %>% rev()
1597
1598rev.igraph.vs <- function(x) {
1599  x[rev(seq_along(x))]
1600}
1601
1602
1603#' Reverse the order in an edge sequence
1604#'
1605#' @param x The edge sequence to reverse.
1606#' @return The reversed edge sequence.
1607#'
1608#' @method rev igraph.es
1609#' @family vertex and edge sequence operations
1610#' @export
1611#' @examples
1612#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10]))
1613#' E(g)
1614#' E(g) %>% rev()
1615
1616rev.igraph.es <- rev.igraph.vs
1617
1618#' Convert a vertex or edge sequence to an ordinary vector
1619#'
1620#' @details
1621#' For graphs without names, a numeric vector is returned, containing the
1622#' internal numeric vertex or edge ids.
1623#'
1624#' For graphs with names, and vertex sequences, the vertex names are
1625#' returned in a character vector.
1626#'
1627#' For graphs with names and edge sequences, a character vector is
1628#' returned, with the \sQuote{bar} notation: \code{a|b} means an edge from
1629#' vertex \code{a} to vertex \code{b}.
1630#'
1631#' @param seq The vertex or edge sequence.
1632#' @return A character or numeric vector, see details below.
1633#'
1634#' @export
1635#' @examples
1636#' g <- make_ring(10)
1637#' as_ids(V(g))
1638#' as_ids(E(g))
1639#'
1640#' V(g)$name <- letters[1:10]
1641#' as_ids(V(g))
1642#' as_ids(E(g))
1643
1644as_ids <- function(seq)
1645  UseMethod("as_ids")
1646
1647#' @method as_ids igraph.vs
1648#' @rdname as_ids
1649#' @export
1650
1651as_ids.igraph.vs <- function(seq) {
1652  names(seq) %||% as.vector(seq)
1653}
1654
1655#' @method as_ids igraph.es
1656#' @rdname as_ids
1657#' @export
1658
1659as_ids.igraph.es <- function(seq) {
1660  attr(seq, "vnames") %||% as.vector(seq)
1661}
1662