Publish harness and TUI open-source
initial sync from the monorepo
This commit is contained in:
commit
c68e39f604
2734 changed files with 1437016 additions and 0 deletions
47
third_party/graphlib_rust/Cargo.toml
vendored
Normal file
47
third_party/graphlib_rust/Cargo.toml
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# Vendored third-party crate.
|
||||
#
|
||||
# Upstream: graphlib_rust 0.0.2 (Apache-2.0)
|
||||
# Source of truth: https://crates.io/crates/graphlib_rust/0.0.2
|
||||
#
|
||||
# Why vendored: the Rust port of dagre.js's graphlib, used by the vendored
|
||||
# `dagre_rust` layout engine and directly by the Warp `mermaid-to-svg` engine.
|
||||
# It is a young single-author crate on the untrusted-input render path, so
|
||||
# vendoring gives a full audit surface and immunity to upstream yanks. See
|
||||
# third_party/mermaid-to-svg and crates/codegen/xai-grok-mermaid.
|
||||
#
|
||||
# ============================================================================
|
||||
# VENDORING NOTES — re-apply / re-bless ALL of the following on every upgrade:
|
||||
# ============================================================================
|
||||
# Local modifications to the upstream source:
|
||||
# 1. `cargo fmt` (rustfmt) applied so the crate satisfies the workspace fmt
|
||||
# gate; no semantic change. The `ordered_hashmap` dep is repointed to the
|
||||
# sibling vendored crate (path dep below).
|
||||
#
|
||||
# Re-audit checklist:
|
||||
# - No `unsafe`, no filesystem / env / network I/O in the vendored source.
|
||||
[package]
|
||||
name = "graphlib_rust"
|
||||
version = "0.0.2"
|
||||
edition = "2021"
|
||||
description = "Dagre's graphlib in Rust (vendored, library-only)"
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/r3alst/graphlib-rust"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
name = "graphlib_rust"
|
||||
path = "src/lib.rs"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
ordered_hashmap = { path = "../ordered_hashmap" }
|
||||
|
||||
# Vendored third-party code is not restyled to our lint standards, so silence
|
||||
# the lints it trips under the workspace's `-D warnings` (the source is kept
|
||||
# verbatim — only this manifest carries the allows). Our own crate
|
||||
# (xai-grok-mermaid) is still fully linted.
|
||||
[lints.rust]
|
||||
noop_method_call = "allow"
|
||||
|
||||
[lints.clippy]
|
||||
all = { level = "allow", priority = -1 }
|
||||
201
third_party/graphlib_rust/LICENCE
vendored
Normal file
201
third_party/graphlib_rust/LICENCE
vendored
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2023 Ameer Hamza
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
107
third_party/graphlib_rust/src/algo/dfs.rs
vendored
Normal file
107
third_party/graphlib_rust/src/algo/dfs.rs
vendored
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
use crate::Graph;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::fmt::Debug;
|
||||
|
||||
/*
|
||||
* A helper that preforms a pre- or post-order traversal on the input graph
|
||||
* and returns the nodes in the order they were visited. If the graph is
|
||||
* undirected then this algorithm will navigate using neighbors. If the graph
|
||||
* is directed then this algorithm will navigate using successors.
|
||||
*
|
||||
* If the order is not "post", it will be treated as "pre".
|
||||
*/
|
||||
pub fn dfs<GL: Default, N: Default + Clone + Debug, E: Default + Clone + Debug>(
|
||||
g: &mut Graph<GL, N, E>,
|
||||
vs: &Vec<String>,
|
||||
order: &str,
|
||||
) -> Result<Vec<String>, Box<dyn Error>> {
|
||||
let navigation: Box<dyn Fn(&String, &Graph<GL, N, E>) -> Vec<String>> =
|
||||
Box::new(move |v: &String, g: &Graph<GL, N, E>| {
|
||||
if g.is_directed() {
|
||||
g.successors(&v).unwrap_or(vec![])
|
||||
} else {
|
||||
g.neighbors(&v).unwrap_or(vec![])
|
||||
}
|
||||
});
|
||||
|
||||
let order_func = match order {
|
||||
"post" => post_order_dfs,
|
||||
_ => pre_order_dfs,
|
||||
};
|
||||
|
||||
let mut acc: Vec<String> = vec![];
|
||||
let mut visited: HashMap<String, bool> = HashMap::new();
|
||||
for v in vs.iter() {
|
||||
if !g.has_node(v) {
|
||||
return Err(format!("Graph does not have node: {}", v).into());
|
||||
}
|
||||
|
||||
order_func(v, &navigation, &mut visited, &mut acc, g);
|
||||
}
|
||||
|
||||
Ok(acc)
|
||||
}
|
||||
|
||||
fn post_order_dfs<GL: Default, N: Default + Clone + Debug, E: Default + Clone + Debug>(
|
||||
v: &String,
|
||||
navigation: &Box<dyn Fn(&String, &Graph<GL, N, E>) -> Vec<String>>,
|
||||
visited: &mut HashMap<String, bool>,
|
||||
acc: &mut Vec<String>,
|
||||
g: &Graph<GL, N, E>,
|
||||
) {
|
||||
let mut stack: Vec<(String, bool)> = vec![(v.clone(), false)];
|
||||
while stack.len() > 0 {
|
||||
let curr_ = stack.pop();
|
||||
if curr_.is_none() {
|
||||
continue;
|
||||
}
|
||||
let curr = curr_.unwrap();
|
||||
if curr.1 {
|
||||
acc.push(curr.0.clone());
|
||||
} else {
|
||||
if !visited.contains_key(&curr.0) {
|
||||
visited.insert(curr.0.clone(), true);
|
||||
stack.push((curr.0.clone(), true));
|
||||
|
||||
// TODO: for_each_right implement in future
|
||||
let _navigation_nodes: Vec<String> = navigation(&curr.0, g);
|
||||
let mut idx = _navigation_nodes.len();
|
||||
while idx > 0 {
|
||||
let nav_node = _navigation_nodes.get(idx - 1).cloned().unwrap();
|
||||
stack.push((nav_node, false));
|
||||
idx -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pre_order_dfs<GL: Default, N: Default + Clone, E: Default + Clone>(
|
||||
v: &String,
|
||||
navigation: &Box<dyn Fn(&String, &Graph<GL, N, E>) -> Vec<String>>,
|
||||
visited: &mut HashMap<String, bool>,
|
||||
acc: &mut Vec<String>,
|
||||
g: &Graph<GL, N, E>,
|
||||
) {
|
||||
let mut stack: Vec<String> = vec![v.clone()];
|
||||
while stack.len() > 0 {
|
||||
let curr_ = stack.pop();
|
||||
if curr_.is_none() {
|
||||
continue;
|
||||
}
|
||||
let curr = curr_.unwrap();
|
||||
if !visited.contains_key(&curr) {
|
||||
visited.insert(curr.clone(), true);
|
||||
acc.push(curr.clone());
|
||||
|
||||
// TODO: for_each_right implement in future
|
||||
let _navigation_nodes: Vec<String> = navigation(&curr, g);
|
||||
let mut idx = _navigation_nodes.len() as i32;
|
||||
while idx >= 0 {
|
||||
stack.push(String::from(_navigation_nodes.get(idx as usize).unwrap()));
|
||||
idx -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
third_party/graphlib_rust/src/algo/mod.rs
vendored
Normal file
3
third_party/graphlib_rust/src/algo/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
pub mod dfs;
|
||||
pub mod postorder;
|
||||
pub mod preorder;
|
||||
14
third_party/graphlib_rust/src/algo/postorder.rs
vendored
Normal file
14
third_party/graphlib_rust/src/algo/postorder.rs
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
use crate::algo::dfs::dfs;
|
||||
use crate::Graph;
|
||||
use std::fmt::Debug;
|
||||
|
||||
// TODO: need to check if exceptions are required
|
||||
pub fn postorder<GL: Default, N: Default + Clone + Debug, E: Default + Clone + Debug>(
|
||||
g: &mut Graph<GL, N, E>,
|
||||
vs: &Vec<String>,
|
||||
) -> Vec<String> {
|
||||
return match dfs(g, vs, "post") {
|
||||
Ok(t) => t,
|
||||
_ => vec![],
|
||||
};
|
||||
}
|
||||
14
third_party/graphlib_rust/src/algo/preorder.rs
vendored
Normal file
14
third_party/graphlib_rust/src/algo/preorder.rs
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
use crate::algo::dfs::dfs;
|
||||
use crate::Graph;
|
||||
use std::fmt::Debug;
|
||||
|
||||
// TODO: need to check if exceptions are required
|
||||
pub fn preorder<GL: Default, N: Default + Clone + Debug, E: Default + Clone + Debug>(
|
||||
g: &mut Graph<GL, N, E>,
|
||||
vs: &Vec<String>,
|
||||
) -> Vec<String> {
|
||||
return match dfs(g, vs, "pre") {
|
||||
Ok(t) => t,
|
||||
_ => vec![],
|
||||
};
|
||||
}
|
||||
965
third_party/graphlib_rust/src/graph.rs
vendored
Normal file
965
third_party/graphlib_rust/src/graph.rs
vendored
Normal file
|
|
@ -0,0 +1,965 @@
|
|||
use ordered_hashmap::OrderedHashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::error::Error;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
|
||||
pub const DEFAULT_EDGE_NAME: &str = "\x00";
|
||||
pub const GRAPH_NODE: &str = "\x00";
|
||||
pub const EDGE_KEY_DELIM: &str = "\x01";
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Edge {
|
||||
pub v: String,
|
||||
pub w: String,
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct GraphOption {
|
||||
pub directed: Option<bool>,
|
||||
pub multigraph: Option<bool>,
|
||||
pub compound: Option<bool>,
|
||||
}
|
||||
|
||||
pub enum DefaultNodeLabel<N> {
|
||||
Val(Option<N>),
|
||||
Func(Box<dyn Fn(String) -> Option<N>>),
|
||||
}
|
||||
|
||||
pub enum DefaultEdgeLabel<E> {
|
||||
Val(Option<E>),
|
||||
Func(Box<dyn Fn(String) -> Option<E>>),
|
||||
}
|
||||
|
||||
pub enum EdgeOrString<E> {
|
||||
Edge(E),
|
||||
String(String),
|
||||
}
|
||||
|
||||
pub enum EdgeLabelOrString<E> {
|
||||
EdgeLabel(E),
|
||||
String(String),
|
||||
}
|
||||
|
||||
pub struct Graph<GL, N, E>
|
||||
where
|
||||
GL: Default,
|
||||
{
|
||||
// GraphLabel Type, Node Type, Node Index Type, Edge Type, Edge Index Type
|
||||
_is_directed: bool,
|
||||
_is_multigraph: bool,
|
||||
_is_compound: bool,
|
||||
|
||||
// Label for the graph itself
|
||||
_label: GL,
|
||||
|
||||
// Defaults to be set when creating a new node
|
||||
_default_node_label_fn: DefaultNodeLabel<N>,
|
||||
|
||||
// Defaults to be set when creating a new edge
|
||||
_default_edge_label_fn: DefaultEdgeLabel<E>,
|
||||
|
||||
// v -> label
|
||||
_nodes: OrderedHashMap<String, N>,
|
||||
|
||||
// v -> e -> edgeObj
|
||||
_in: OrderedHashMap<String, OrderedHashMap<String, Edge>>,
|
||||
|
||||
// u -> v -> Number
|
||||
_preds: OrderedHashMap<String, OrderedHashMap<String, usize>>,
|
||||
|
||||
// v -> e -> edgeObj
|
||||
_out: OrderedHashMap<String, OrderedHashMap<String, Edge>>,
|
||||
|
||||
// v -> w -> Number
|
||||
_sucs: OrderedHashMap<String, OrderedHashMap<String, usize>>,
|
||||
|
||||
// e -> edgeObj
|
||||
_edge_objs: OrderedHashMap<String, Edge>,
|
||||
|
||||
// e -> label
|
||||
_edge_labels: OrderedHashMap<String, E>,
|
||||
|
||||
/* Number of nodes in the graph. Should only be changed by the implementation. */
|
||||
_node_count: usize,
|
||||
|
||||
/* Number of edges in the graph. Should only be changed by the implementation. */
|
||||
_edge_count: usize,
|
||||
|
||||
// v -> w
|
||||
_parent: OrderedHashMap<String, String>,
|
||||
|
||||
// v -> w -> boolean
|
||||
_children: OrderedHashMap<String, OrderedHashMap<String, bool>>,
|
||||
}
|
||||
|
||||
impl<GL: Default, N, E> Default for Graph<GL, N, E> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
_is_directed: true,
|
||||
_is_multigraph: false,
|
||||
_is_compound: false,
|
||||
_label: GL::default(),
|
||||
_default_node_label_fn: DefaultNodeLabel::Val(None),
|
||||
_default_edge_label_fn: DefaultEdgeLabel::Val(None),
|
||||
_nodes: OrderedHashMap::new(),
|
||||
_in: OrderedHashMap::new(),
|
||||
_preds: OrderedHashMap::new(),
|
||||
_out: OrderedHashMap::new(),
|
||||
_sucs: OrderedHashMap::new(),
|
||||
_edge_objs: OrderedHashMap::new(),
|
||||
_edge_labels: OrderedHashMap::new(),
|
||||
_node_count: 0,
|
||||
_edge_count: 0,
|
||||
_parent: OrderedHashMap::new(),
|
||||
_children: OrderedHashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<GL: Default, N: Default + Clone + Debug, E: Default + Clone + Debug> Graph<GL, N, E> {
|
||||
pub fn new(opts: Option<GraphOption>) -> Self {
|
||||
let mut graph = Self::default();
|
||||
|
||||
if let Some(_opts) = opts {
|
||||
if _opts.directed.is_some() {
|
||||
graph._is_directed = _opts.directed.unwrap();
|
||||
} else {
|
||||
graph._is_directed = true;
|
||||
}
|
||||
|
||||
if _opts.multigraph.is_some() {
|
||||
graph._is_multigraph = _opts.multigraph.unwrap();
|
||||
} else {
|
||||
graph._is_multigraph = false;
|
||||
}
|
||||
|
||||
if _opts.multigraph.is_some() {
|
||||
graph._is_multigraph = _opts.multigraph.unwrap();
|
||||
} else {
|
||||
graph._is_multigraph = false;
|
||||
}
|
||||
|
||||
if _opts.compound.is_some() {
|
||||
graph._is_compound = _opts.compound.unwrap();
|
||||
} else {
|
||||
graph._is_compound = false;
|
||||
}
|
||||
}
|
||||
|
||||
if graph._is_compound {
|
||||
// v -> parent
|
||||
graph._parent = OrderedHashMap::new();
|
||||
|
||||
graph._children = OrderedHashMap::new();
|
||||
graph
|
||||
._children
|
||||
.insert(GRAPH_NODE.clone().to_string(), OrderedHashMap::new());
|
||||
}
|
||||
|
||||
graph
|
||||
}
|
||||
|
||||
/* === Graph functions ========= */
|
||||
|
||||
/**
|
||||
* Whether graph was created with 'directed' flag set to true or not.
|
||||
*/
|
||||
pub fn is_directed(&self) -> bool {
|
||||
return self._is_directed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether graph was created with 'multigraph' flag set to true or not.
|
||||
*/
|
||||
pub fn is_multigraph(&self) -> bool {
|
||||
return self._is_multigraph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether graph was created with 'compound' flag set to true or not.
|
||||
*/
|
||||
pub fn is_compound(&self) -> bool {
|
||||
return self._is_compound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the label of the graph.
|
||||
*/
|
||||
pub fn set_graph(&mut self, label: GL) -> &mut Self {
|
||||
self._label = label;
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the graph label.
|
||||
*/
|
||||
pub fn graph(&self) -> &GL {
|
||||
return &self._label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the graph label.
|
||||
*/
|
||||
pub fn graph_mut(&mut self) -> &mut GL {
|
||||
return &mut self._label;
|
||||
}
|
||||
|
||||
/* === Node functions ========== */
|
||||
|
||||
/**
|
||||
* Sets the default node label. If newDefault is a function, it will be
|
||||
* invoked ach time when setting a label for a node. Otherwise, this label
|
||||
* will be assigned as default label in case if no label was specified while
|
||||
* setting a node.
|
||||
* Complexity: O(1).
|
||||
*/
|
||||
pub fn set_default_node_label(&mut self, new_default: DefaultNodeLabel<N>) -> &mut Self {
|
||||
self._default_node_label_fn = new_default;
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn default_node_label(&self, node_id: String) -> N {
|
||||
let mut _node_label: Option<N> = None;
|
||||
match &self._default_node_label_fn {
|
||||
DefaultNodeLabel::Func(node_label_fn) => {
|
||||
_node_label = node_label_fn(node_id.clone());
|
||||
}
|
||||
DefaultNodeLabel::Val(node_label_) => {
|
||||
if node_label_.is_some() {
|
||||
_node_label = Some(node_label_.clone().unwrap());
|
||||
} else {
|
||||
_node_label = Some(N::default());
|
||||
}
|
||||
}
|
||||
}
|
||||
return _node_label.unwrap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of nodes in the graph.
|
||||
* Complexity: O(1).
|
||||
*/
|
||||
pub fn node_count(&self) -> usize {
|
||||
return self._node_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all nodes of the graph. Note, the in case of compound graph subnodes are
|
||||
* not included in list.
|
||||
* Complexity: O(1).
|
||||
*/
|
||||
pub fn nodes(&self) -> Vec<String> {
|
||||
return self._nodes.keys().cloned().collect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets list of nodes without in-edges.
|
||||
* Complexity: O(|V|).
|
||||
*/
|
||||
pub fn sources(&self) -> Vec<String> {
|
||||
return self
|
||||
.nodes()
|
||||
.iter()
|
||||
.filter(|n| {
|
||||
if let Some(in_edges) = self._in.get(&n.to_owned().clone()) {
|
||||
return in_edges.len() == 0;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.map(|node_id| node_id.clone())
|
||||
.collect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets list of nodes without out-edges.
|
||||
* Complexity: O(|V|).
|
||||
*/
|
||||
pub fn sinks(&self) -> Vec<String> {
|
||||
return self
|
||||
.nodes()
|
||||
.iter()
|
||||
.filter(|n| {
|
||||
if let Some(out_edges) = self._out.get(&n.to_owned().clone()) {
|
||||
return out_edges.len() == 0;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.map(|node_id| node_id.clone())
|
||||
.collect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes setNode method for each node in names list.
|
||||
* Complexity: O(|names|).
|
||||
*/
|
||||
pub fn set_nodes(&mut self, node_ids: Vec<String>, value: Option<N>) -> &mut Self {
|
||||
node_ids.iter().for_each(|node_id| {
|
||||
self.set_node(node_id.to_owned(), value.clone());
|
||||
});
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or updates the value for the node v in the graph. If label is supplied
|
||||
* it is set as the value for the node. If label is not supplied and the node was
|
||||
* created by this call then the default node label will be assigned.
|
||||
* Complexity: O(1).
|
||||
*/
|
||||
pub fn set_node(&mut self, v: String, value: Option<N>) -> &mut Self {
|
||||
if self._nodes.get(&v).is_some() {
|
||||
if value.is_some() {
|
||||
self._nodes.insert(v, value.unwrap());
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
if value.is_some() {
|
||||
self._nodes.insert(v.clone(), value.unwrap());
|
||||
} else {
|
||||
self._nodes
|
||||
.insert(v.clone(), self.default_node_label(v.clone()));
|
||||
}
|
||||
|
||||
if self._is_compound {
|
||||
let _graph_node = GRAPH_NODE.clone().to_string();
|
||||
self._parent.insert(v.clone(), _graph_node.clone());
|
||||
self._children.insert(v.clone(), OrderedHashMap::new());
|
||||
self._children
|
||||
.entry(_graph_node.clone())
|
||||
.or_insert(OrderedHashMap::new())
|
||||
.entry(v.clone())
|
||||
.or_insert(true);
|
||||
}
|
||||
|
||||
self._in.insert(v.clone(), OrderedHashMap::new());
|
||||
self._preds.insert(v.clone(), OrderedHashMap::new());
|
||||
self._out.insert(v.clone(), OrderedHashMap::new());
|
||||
self._sucs.insert(v.clone(), OrderedHashMap::new());
|
||||
self._node_count += 1;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the label of node with specified name.
|
||||
* Complexity: O(|V|).
|
||||
*/
|
||||
pub fn node(&self, v: &String) -> Option<&N> {
|
||||
return self._nodes.get(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the label of node with specified name.
|
||||
* Complexity: O(|V|).
|
||||
*/
|
||||
pub fn node_mut(&mut self, v: &String) -> Option<&mut N> {
|
||||
return self._nodes.get_mut(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects whether graph has a node with specified name or not.
|
||||
*/
|
||||
pub fn has_node(&self, v: &String) -> bool {
|
||||
return self._nodes.contains_key(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the node with the name from the graph or do nothing if the node is not in
|
||||
* the graph. If the node was removed this function also removes any incident
|
||||
* edges.
|
||||
* Complexity: O(1).
|
||||
*/
|
||||
pub fn remove_node(&mut self, v: &String) -> &mut Self {
|
||||
if self._nodes.contains_key(v) {
|
||||
self._nodes.remove(v);
|
||||
|
||||
if self._is_compound {
|
||||
self._remove_from_parents_child_list(v);
|
||||
if self._parent.contains_key(v) {
|
||||
self._parent.remove(v);
|
||||
}
|
||||
self.children(v).iter().for_each(|child_id| {
|
||||
// TODO: exception handling
|
||||
let _ = self.set_parent(child_id, None);
|
||||
});
|
||||
self._children.remove(v);
|
||||
}
|
||||
// removing in edges
|
||||
if let Some(in_edges) = self._in.get(v) {
|
||||
let edge_ids: Vec<String> = in_edges.keys().cloned().collect();
|
||||
edge_ids.iter().for_each(|edge_id| {
|
||||
if let Some(edge) = self._edge_objs.get(edge_id) {
|
||||
self.remove_edge_with_obj(&edge.to_owned());
|
||||
}
|
||||
});
|
||||
self._in.remove(v);
|
||||
}
|
||||
|
||||
self._preds.remove(v);
|
||||
|
||||
// removing out edges
|
||||
if let Some(out_edges) = self._out.get(v) {
|
||||
let edge_ids: Vec<String> = out_edges.keys().cloned().collect();
|
||||
edge_ids.iter().for_each(|edge_id| {
|
||||
if let Some(edge) = self._edge_objs.get(edge_id) {
|
||||
self.remove_edge_with_obj(&edge.to_owned());
|
||||
}
|
||||
});
|
||||
self._out.remove(v);
|
||||
}
|
||||
self._sucs.remove(v);
|
||||
self._node_count -= 1;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets node p as a parent for node v if it is defined, or removes the
|
||||
* parent for v if p is undefined. Method throws an exception in case of
|
||||
* invoking it in context of noncompound graph.
|
||||
* Average-case complexity: O(1).
|
||||
*/
|
||||
pub fn set_parent(
|
||||
&mut self,
|
||||
v: &String,
|
||||
parent: Option<String>,
|
||||
) -> Result<&mut Self, Box<dyn Error>> {
|
||||
if !self._is_compound {
|
||||
return Err("Cannot set parent in a non-compound graph".into());
|
||||
}
|
||||
|
||||
let mut _parent: String = "".to_string();
|
||||
|
||||
if parent.is_none() {
|
||||
_parent = GRAPH_NODE.to_string();
|
||||
} else {
|
||||
_parent = parent.unwrap().clone();
|
||||
let mut ancestor = _parent.clone();
|
||||
while let Some(new_ancestor) = self.parent(&ancestor) {
|
||||
if &new_ancestor == &v {
|
||||
return Err(format!(
|
||||
"Setting {} as parent of {} would create a cycle",
|
||||
_parent.clone(),
|
||||
v
|
||||
)
|
||||
.into());
|
||||
}
|
||||
ancestor = new_ancestor.clone();
|
||||
}
|
||||
|
||||
self.set_node(_parent.clone(), None);
|
||||
}
|
||||
|
||||
self.set_node(v.clone(), None);
|
||||
self._remove_from_parents_child_list(v);
|
||||
self._parent.insert(v.clone(), _parent.clone());
|
||||
self._children
|
||||
.entry(_parent.clone())
|
||||
.or_insert_with(OrderedHashMap::new)
|
||||
.insert(v.clone(), true);
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn _remove_from_parents_child_list(&mut self, v: &String) {
|
||||
if let Some(parent) = self._parent.get(v) {
|
||||
if let Some(children) = self._children.get_mut(parent) {
|
||||
children.remove(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets parent node for node v.
|
||||
* Complexity: O(1).
|
||||
*/
|
||||
pub fn parent(&self, v: &String) -> Option<&String> {
|
||||
if self._is_compound {
|
||||
if let Some(parent) = self._parent.get(v) {
|
||||
if parent != GRAPH_NODE {
|
||||
return Some(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets list of direct children of node v.
|
||||
* Complexity: O(1).
|
||||
*/
|
||||
pub fn children(&self, v: &String) -> Vec<String> {
|
||||
if self._is_compound {
|
||||
if let Some(children) = self._children.get(v) {
|
||||
return children.keys().cloned().collect();
|
||||
}
|
||||
} else if v == GRAPH_NODE {
|
||||
return self._nodes.keys().cloned().collect();
|
||||
} else if self.has_node(&v) {
|
||||
return vec![];
|
||||
}
|
||||
vec![]
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all nodes that are predecessors of the specified node or undefined if node v is not in
|
||||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead.
|
||||
* Complexity: O(|V|).
|
||||
*/
|
||||
pub fn predecessors(&self, v: &String) -> Option<Vec<String>> {
|
||||
if let Some(preds) = self._preds.get(v) {
|
||||
return Some(preds.keys().cloned().collect());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all nodes that are successors of the specified node or undefined if node v is not in
|
||||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead.
|
||||
* Complexity: O(|V|).
|
||||
*/
|
||||
pub fn successors(&self, v: &String) -> Option<Vec<String>> {
|
||||
if let Some(sucs) = self._sucs.get(v) {
|
||||
return Some(sucs.keys().cloned().collect());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all nodes that are predecessors or successors of the specified node or undefined if
|
||||
* node v is not in the graph.
|
||||
* Complexity: O(|V|).
|
||||
*/
|
||||
pub fn neighbors(&self, v: &String) -> Option<Vec<String>> {
|
||||
if let Some(preds) = self.predecessors(v) {
|
||||
let mut union: HashSet<String> = HashSet::new();
|
||||
preds.into_iter().for_each(|pred| {
|
||||
union.insert(pred);
|
||||
});
|
||||
if let Some(sucs) = self.successors(v) {
|
||||
sucs.into_iter().for_each(|successor| {
|
||||
union.insert(successor);
|
||||
});
|
||||
}
|
||||
|
||||
return Some(union.into_iter().collect());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn is_leaf(&self, v: &String) -> bool {
|
||||
let mut _neighbors: Option<Vec<String>> = None;
|
||||
if self.is_directed() {
|
||||
_neighbors = self.successors(v);
|
||||
} else {
|
||||
_neighbors = self.neighbors(v);
|
||||
}
|
||||
|
||||
if _neighbors.is_none() || _neighbors.unwrap().len() == 0 {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new graph with nodes filtered via filter. Edges incident to rejected node
|
||||
* are also removed. In case of compound graph, if parent is rejected by filter,
|
||||
* than all its children are rejected too.
|
||||
* Average-case complexity: O(|E|+|V|).
|
||||
*/
|
||||
pub fn filter_nodes<F>(&self, filter: F) -> Self
|
||||
where
|
||||
F: Fn(&String) -> bool,
|
||||
{
|
||||
let mut copy: Graph<GL, N, E> = Graph::new(Some(GraphOption {
|
||||
directed: Some(self._is_directed.clone()),
|
||||
multigraph: Some(self._is_multigraph.clone()),
|
||||
compound: Some(self._is_compound.clone()),
|
||||
}));
|
||||
|
||||
for (v, value) in self._nodes.iter() {
|
||||
if filter(v) {
|
||||
copy.set_node(v.clone(), Some(value.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
for e_v in self._edge_objs.values() {
|
||||
if copy._nodes.contains_key(&e_v.v) && copy._nodes.contains_key(&e_v.w) {
|
||||
if let Some(edge_label) = self.edge_with_obj(e_v) {
|
||||
let _ = copy.set_edge_with_obj(e_v, Some(edge_label.to_owned()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut parents: OrderedHashMap<String, String> = OrderedHashMap::new();
|
||||
|
||||
if self._is_compound {
|
||||
let node_ids: Vec<String> = copy._nodes.keys().cloned().into_iter().collect();
|
||||
for v in node_ids {
|
||||
let parent = find_parent(&v, &mut parents, &mut copy, self);
|
||||
let _ = copy.set_parent(&v, parent);
|
||||
}
|
||||
}
|
||||
|
||||
copy
|
||||
}
|
||||
|
||||
/* === Edge functions ========== */
|
||||
|
||||
/**
|
||||
* Sets the default edge label or factory function. This label will be
|
||||
* assigned as default label in case if no label was specified while setting
|
||||
* an edge or this function will be invoked each time when setting an edge
|
||||
* with no label specified and returned value * will be used as a label for edge.
|
||||
* Complexity: O(1).
|
||||
*/
|
||||
pub fn set_default_edge_label(&mut self, new_default: DefaultEdgeLabel<E>) -> &mut Self {
|
||||
self._default_edge_label_fn = new_default;
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn default_edge_label(&self, edge_id: String) -> E {
|
||||
let mut _edge_label: Option<E> = None;
|
||||
match &self._default_edge_label_fn {
|
||||
DefaultEdgeLabel::Func(edge_label_fn) => {
|
||||
_edge_label = edge_label_fn(edge_id.clone());
|
||||
}
|
||||
DefaultEdgeLabel::Val(edge_label_) => {
|
||||
if edge_label_.is_some() {
|
||||
_edge_label = Some(edge_label_.clone().unwrap());
|
||||
} else {
|
||||
_edge_label = Some(E::default());
|
||||
}
|
||||
}
|
||||
}
|
||||
return _edge_label.unwrap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of edges in the graph.
|
||||
* Complexity: O(1).
|
||||
*/
|
||||
pub fn edge_count(&self) -> usize {
|
||||
return self._edge_count.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets edges of the graph. In case of compound graph subgraphs are not considered.
|
||||
* Complexity: O(|E|).
|
||||
*/
|
||||
pub fn edges(&self) -> Vec<Edge> {
|
||||
return self._edge_objs.values().cloned().collect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish an edges path over the nodes in nodes list. If some edge is already
|
||||
* exists, it will update its label, otherwise it will create an edge between pair
|
||||
* of nodes with label provided or default label if no label provided.
|
||||
* Complexity: O(|nodes|).
|
||||
*/
|
||||
pub fn set_path(&mut self, vs: &Vec<String>, value: Option<E>) {
|
||||
vs.iter().reduce(|v1, v2| {
|
||||
let _ = self.set_edge(v1, v2, value.clone(), None);
|
||||
v2
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or updates the label for the edge (v, w) with the optionally supplied
|
||||
* name. If label is supplied it is set as the value for the edge. If label is not
|
||||
* supplied and the edge was created by this call then the default edge label will
|
||||
* be assigned. The name parameter is only useful with multigraphs.
|
||||
*/
|
||||
pub fn set_edge(
|
||||
&mut self,
|
||||
v: &String,
|
||||
w: &String,
|
||||
edge_label: Option<E>,
|
||||
name: Option<String>,
|
||||
) -> Result<&mut Self, Box<dyn Error>> {
|
||||
let e = edge_args_to_id(&self._is_directed, v, w, &name);
|
||||
if self._edge_labels.contains_key(&e) {
|
||||
if edge_label.is_some() {
|
||||
self._edge_labels.insert(e.clone(), edge_label.unwrap());
|
||||
}
|
||||
return Ok(self);
|
||||
}
|
||||
|
||||
if name.is_some() && !self._is_multigraph {
|
||||
return Err("Cannot set a named edge when isMultigraph = false".into());
|
||||
}
|
||||
|
||||
// It didn't exist, so we need to create it.
|
||||
// First ensure the nodes exist.
|
||||
self.set_node(v.clone(), None);
|
||||
self.set_node(w.clone(), None);
|
||||
|
||||
if edge_label.is_some() {
|
||||
self._edge_labels
|
||||
.insert(e.clone(), edge_label.clone().unwrap());
|
||||
} else {
|
||||
self._edge_labels
|
||||
.insert(e.clone(), self.default_edge_label(e.clone()));
|
||||
}
|
||||
|
||||
let edge_obj = edge_args_to_obj(&self.is_directed(), v, w, &name);
|
||||
// Ensure we add undirected edges in a consistent way.
|
||||
self._edge_objs.insert(e.clone(), edge_obj.clone());
|
||||
if let Some(preds) = self._preds.get_mut(w) {
|
||||
increment_or_init_entry(preds, v);
|
||||
}
|
||||
if let Some(sucs) = self._sucs.get_mut(v) {
|
||||
increment_or_init_entry(sucs, w);
|
||||
}
|
||||
|
||||
let in_edges = self
|
||||
._in
|
||||
.entry(w.clone())
|
||||
.or_insert_with(OrderedHashMap::new);
|
||||
in_edges.insert(e.clone(), edge_obj.clone());
|
||||
|
||||
let out_edges = self
|
||||
._out
|
||||
.entry(v.clone())
|
||||
.or_insert_with(OrderedHashMap::new);
|
||||
out_edges.insert(e.clone(), edge_obj.clone());
|
||||
|
||||
self._edge_count += 1;
|
||||
return Ok(self);
|
||||
}
|
||||
|
||||
pub fn set_edge_with_obj(
|
||||
&mut self,
|
||||
e: &Edge,
|
||||
edge_label: Option<E>,
|
||||
) -> Result<&mut Self, Box<dyn Error>> {
|
||||
self.set_edge(&e.v, &e.w, edge_label, None)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the label for the specified edge.
|
||||
* Complexity: O(1).
|
||||
*/
|
||||
pub fn edge(&self, v: &String, w: &String, name: Option<String>) -> Option<&E> {
|
||||
let e = edge_args_to_id(&self._is_directed, v, w, &name);
|
||||
return self._edge_labels.get(&e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the label for the specified edge.
|
||||
* Complexity: O(1).
|
||||
*/
|
||||
pub fn edge_with_obj(&self, edge: &Edge) -> Option<&E> {
|
||||
let e = edge_obj_to_id(&self._is_directed, edge);
|
||||
return self._edge_labels.get(&e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the label for the specified edge.
|
||||
* Complexity: O(1).
|
||||
*/
|
||||
pub fn edge_mut(&mut self, v: &String, w: &String, name: Option<String>) -> Option<&mut E> {
|
||||
let e = edge_args_to_id(&self._is_directed, v, w, &name);
|
||||
return self._edge_labels.get_mut(&e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the label for the specified edge.
|
||||
* Complexity: O(1).
|
||||
*/
|
||||
pub fn edge_mut_with_obj(&mut self, edge: &Edge) -> Option<&mut E> {
|
||||
let e = edge_obj_to_id(&self._is_directed, edge);
|
||||
return self._edge_labels.get_mut(&e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects whether the graph contains specified edge or not. No subgraphs are considered.
|
||||
* Complexity: O(1).
|
||||
*/
|
||||
pub fn has_edge(&self, v: &String, w: &String, name: Option<String>) -> bool {
|
||||
let e = edge_args_to_id(&self._is_directed, v, w, &name);
|
||||
self._edge_labels.contains_key(&e)
|
||||
}
|
||||
|
||||
pub fn has_edge_with_obj(&self, edge: &Edge) -> bool {
|
||||
let e = edge_obj_to_id(&self._is_directed, edge);
|
||||
self._edge_labels.contains_key(&e)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified edge from the graph. No subgraphs are considered.
|
||||
* Complexity: O(1).
|
||||
*/
|
||||
pub fn remove_edge(&mut self, v: &String, w: &String, name: Option<String>) -> &mut Self {
|
||||
let e: String = edge_args_to_id(&self._is_directed, v, w, &name);
|
||||
if let Some(edge) = self._edge_objs.get_mut(&e) {
|
||||
let v = edge.v.clone();
|
||||
let w = edge.w.clone();
|
||||
self._edge_labels.remove(&e);
|
||||
self._edge_objs.remove(&e);
|
||||
if self._preds.contains_key(&w) {
|
||||
decrement_or_remove_entry(self._preds.get_mut(&w).unwrap(), &v);
|
||||
}
|
||||
if self._sucs.contains_key(&v) {
|
||||
decrement_or_remove_entry(self._sucs.get_mut(&v).unwrap(), &w);
|
||||
}
|
||||
|
||||
if self._in.contains_key(&w) {
|
||||
self._in.get_mut(&w).unwrap().remove(&e);
|
||||
}
|
||||
|
||||
if self._out.contains_key(&v) {
|
||||
self._out.get_mut(&v).unwrap().remove(&e);
|
||||
}
|
||||
self._edge_count -= 1;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified edge from the graph. No subgraphs are considered.
|
||||
* Complexity: O(1).
|
||||
*/
|
||||
pub fn remove_edge_with_obj(&mut self, e: &Edge) -> &mut Self {
|
||||
self.remove_edge(&e.v, &e.w, None);
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all edges that point to the node v. Optionally filters those edges down to just those
|
||||
* coming from node u. Behavior is undefined for undirected graphs - use nodeEdges instead.
|
||||
* Complexity: O(|E|).
|
||||
*/
|
||||
pub fn in_edges(&self, v: &String, u: Option<String>) -> Option<Vec<Edge>> {
|
||||
if let Some(in_edges) = self._in.get(v) {
|
||||
let mut _in_edges: Vec<Edge> = in_edges.values().cloned().collect();
|
||||
if u.is_none() {
|
||||
return Some(_in_edges.clone());
|
||||
}
|
||||
let _u = u.unwrap();
|
||||
return Some(_in_edges.into_iter().filter(|edge| edge.v == _u).collect());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all edges that are pointed at by node v. Optionally filters those edges down to just
|
||||
* those point to w. Behavior is undefined for undirected graphs - use nodeEdges instead.
|
||||
* Complexity: O(|E|).
|
||||
*/
|
||||
pub fn out_edges(&self, v: &String, w: Option<String>) -> Option<Vec<Edge>> {
|
||||
if let Some(out_edges) = self._out.get(v) {
|
||||
let mut _out_edges: Vec<Edge> = out_edges.values().cloned().collect();
|
||||
if w.is_none() {
|
||||
return Some(_out_edges.clone());
|
||||
}
|
||||
|
||||
let _w = w.unwrap();
|
||||
return Some(_out_edges.into_iter().filter(|edge| edge.w == _w).collect());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all edges to or from node v regardless of direction. Optionally filters those edges
|
||||
* down to just those between nodes v and w regardless of direction.
|
||||
* Complexity: O(|E|).
|
||||
*/
|
||||
pub fn node_edges(&self, v: &String, w: Option<String>) -> Option<Vec<Edge>> {
|
||||
let _in_edges = self.in_edges(v, w.clone());
|
||||
if let Some(mut in_edges) = _in_edges {
|
||||
let _out_edges = self.out_edges(v, w.clone());
|
||||
if let Some(out_edges) = _out_edges {
|
||||
in_edges.append(out_edges.clone().as_mut());
|
||||
}
|
||||
return Some(in_edges);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn increment_or_init_entry<K: Hash + Eq + Clone>(map: &mut OrderedHashMap<K, usize>, k: &K) {
|
||||
if let Some(e) = map.get_mut(&k) {
|
||||
*e += 1;
|
||||
} else {
|
||||
map.insert(k.clone(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
fn decrement_or_remove_entry<K: Hash + Eq + Clone>(map: &mut OrderedHashMap<K, usize>, k: &K) {
|
||||
if let Some(value) = map.get_mut(k) {
|
||||
*value -= 1;
|
||||
if *value <= 0 {
|
||||
map.remove(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn edge_args_to_id(is_directed: &bool, v_: &String, w_: &String, name: &Option<String>) -> String {
|
||||
let mut v: &str = &*v_;
|
||||
let mut w: &str = &*w_;
|
||||
if !is_directed.to_owned() && v > w {
|
||||
let tmp = v;
|
||||
v = w;
|
||||
w = tmp;
|
||||
}
|
||||
|
||||
if name.is_some() {
|
||||
return v.to_owned() + EDGE_KEY_DELIM + w + EDGE_KEY_DELIM + &*name.clone().unwrap();
|
||||
}
|
||||
return v.to_owned() + EDGE_KEY_DELIM + w + EDGE_KEY_DELIM + DEFAULT_EDGE_NAME;
|
||||
}
|
||||
|
||||
fn edge_args_to_obj(is_directed: &bool, v_: &String, w_: &String, name: &Option<String>) -> Edge {
|
||||
let mut v: &str = &*v_;
|
||||
let mut w: &str = &*w_;
|
||||
if !is_directed.to_owned() && v > w {
|
||||
let tmp = v;
|
||||
v = w;
|
||||
w = tmp;
|
||||
}
|
||||
|
||||
return Edge {
|
||||
v: v.to_string(),
|
||||
w: w.to_string(),
|
||||
name: name.clone(),
|
||||
};
|
||||
}
|
||||
|
||||
fn edge_obj_to_id(is_directed: &bool, edge: &Edge) -> String {
|
||||
return edge_args_to_id(is_directed, &edge.v, &edge.w, &edge.name);
|
||||
}
|
||||
|
||||
fn find_parent<GL: Default, N: Default + Clone + Debug, E: Default + Clone + Debug>(
|
||||
v: &String,
|
||||
parents: &mut OrderedHashMap<String, String>,
|
||||
copy: &mut Graph<GL, N, E>,
|
||||
graph: &Graph<GL, N, E>,
|
||||
) -> Option<String> {
|
||||
let parent = graph.parent(v);
|
||||
if parent.is_none() || copy._nodes.contains_key(&parent.unwrap().clone()) {
|
||||
if !parent.is_none() {
|
||||
parents.insert(v.clone(), parent.unwrap().clone());
|
||||
return parent.cloned();
|
||||
}
|
||||
None
|
||||
} else if let Some(parent_value) = parents.get(&parent.unwrap().clone()) {
|
||||
Some(parent_value.clone())
|
||||
} else {
|
||||
if parent.is_some() {
|
||||
find_parent(parent.as_ref().unwrap(), parents, copy, graph)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
10
third_party/graphlib_rust/src/lib.rs
vendored
Normal file
10
third_party/graphlib_rust/src/lib.rs
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
pub mod algo;
|
||||
pub mod graph;
|
||||
|
||||
pub use graph::DefaultEdgeLabel;
|
||||
pub use graph::DefaultNodeLabel;
|
||||
pub use graph::Edge;
|
||||
pub use graph::EdgeLabelOrString;
|
||||
pub use graph::EdgeOrString;
|
||||
pub use graph::Graph;
|
||||
pub use graph::GraphOption;
|
||||
Loading…
Reference in a new issue