add deduce_structural_eq_flags transform
All checks were successful
/ test (pull_request) Successful in 4m17s
All checks were successful
/ test (pull_request) Successful in 4m17s
This commit is contained in:
parent
98e7e91fc9
commit
4bd6db3de8
14 changed files with 4418 additions and 37 deletions
|
|
@ -4,7 +4,7 @@
|
|||
use crate::{
|
||||
expr::{
|
||||
CastToBits, Expr, HdlPartialEq, HdlPartialEqImpl, ReduceBits, ToExpr, ValueType, Valueless,
|
||||
ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator},
|
||||
ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, StructuralEq},
|
||||
},
|
||||
int::{Bool, DYN_SIZE, DynSize, KnownSize, Size, SizeType},
|
||||
intern::{Intern, Interned, LazyInterned},
|
||||
|
|
@ -389,6 +389,11 @@ where
|
|||
}
|
||||
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
|
||||
assert_eq!(lhs.ty().len(), rhs.ty().len());
|
||||
if Self::TRY_STRUCTURAL_EQ {
|
||||
if let Ok(retval) = StructuralEq::try_new(Expr::canonical(lhs), Expr::canonical(rhs)) {
|
||||
return retval.to_expr();
|
||||
}
|
||||
}
|
||||
lhs.into_iter()
|
||||
.zip(rhs)
|
||||
.map(|(l, r)| l.cmp_eq(r))
|
||||
|
|
@ -398,6 +403,11 @@ where
|
|||
}
|
||||
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
|
||||
assert_eq!(lhs.ty().len(), rhs.ty().len());
|
||||
if Self::TRY_STRUCTURAL_EQ {
|
||||
if let Ok(retval) = StructuralEq::try_new(Expr::canonical(lhs), Expr::canonical(rhs)) {
|
||||
return !retval.to_expr();
|
||||
}
|
||||
}
|
||||
lhs.into_iter()
|
||||
.zip(rhs)
|
||||
.map(|(l, r)| l.cmp_ne(r))
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use crate::{
|
|||
use eyre::{ContextCompat, eyre};
|
||||
use petgraph::{
|
||||
algo::{DfsSpace, kosaraju_scc, toposort},
|
||||
graph::DiGraph,
|
||||
graph::{DiGraph, NodeIndex},
|
||||
visit::{GraphBase, Visitable},
|
||||
};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error, ser::SerializeSeq};
|
||||
|
|
@ -465,7 +465,7 @@ impl JobGraph {
|
|||
}
|
||||
})
|
||||
.expect("we know there's a cycle");
|
||||
let cycle_set = HashSet::from_iter(cycle.iter().copied());
|
||||
let cycle_set = HashSet::<NodeIndex>::from_iter(cycle.iter().copied());
|
||||
let job = cycle
|
||||
.into_iter()
|
||||
.find_map(|node_id| {
|
||||
|
|
@ -701,7 +701,7 @@ impl JobGraph {
|
|||
job: DynJob,
|
||||
thread: ScopedJoinHandle<'scope, eyre::Result<Vec<JobItem>>>,
|
||||
}
|
||||
let mut running_jobs = HashMap::default();
|
||||
let mut running_jobs = HashMap::<NodeIndex, RunningJob>::default();
|
||||
let (finished_jobs_sender, finished_jobs_receiver) = mpsc::channel();
|
||||
let mut next_finished_job = None;
|
||||
loop {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::{
|
|||
expr::{
|
||||
CastToBits, Expr, HdlPartialEqImpl, ReduceBits, ToExpr, ToSimValueInner, ValueType,
|
||||
Valueless,
|
||||
ops::{ArrayLiteral, BundleLiteral},
|
||||
ops::{ArrayLiteral, BundleLiteral, StructuralEq},
|
||||
value_category::{ValueCategoryCommon, ValueCategoryExpr, ValueCategoryValue},
|
||||
},
|
||||
int::{Bool, DynSize},
|
||||
|
|
@ -727,6 +727,11 @@ macro_rules! impl_tuples {
|
|||
|
||||
#[track_caller]
|
||||
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
|
||||
if Self::TRY_STRUCTURAL_EQ {
|
||||
if let Ok(retval) = StructuralEq::try_new(Expr::canonical(lhs), Expr::canonical(rhs)) {
|
||||
return retval.to_expr();
|
||||
}
|
||||
}
|
||||
let ($($lhs_var,)*) = *lhs;
|
||||
let ($($rhs_var,)*) = *rhs;
|
||||
ArrayLiteral::<Bool, DynSize>::new(
|
||||
|
|
@ -739,6 +744,11 @@ macro_rules! impl_tuples {
|
|||
|
||||
#[track_caller]
|
||||
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
|
||||
if Self::TRY_STRUCTURAL_EQ {
|
||||
if let Ok(retval) = StructuralEq::try_new(Expr::canonical(lhs), Expr::canonical(rhs)) {
|
||||
return !retval.to_expr();
|
||||
}
|
||||
}
|
||||
let ($($lhs_var,)*) = *lhs;
|
||||
let ($($rhs_var,)*) = *rhs;
|
||||
ArrayLiteral::<Bool, DynSize>::new(
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ pub struct TargetPathToTraceAsString {
|
|||
|
||||
impl fmt::Display for TargetPathToTraceAsString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, ".to_trace_as_string(...)")
|
||||
write!(f, ".to_trace_as_string()")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2248,7 +2248,7 @@ impl<T: BundleType> Module<T> {
|
|||
clocks_for_past,
|
||||
simulation: Some(simulation),
|
||||
}) => {
|
||||
let mut clocks_for_past_set = HashSet::default();
|
||||
let mut clocks_for_past_set = HashSet::<Target>::default();
|
||||
*clocks_for_past = clocks_for_past
|
||||
.iter()
|
||||
.copied()
|
||||
|
|
@ -2269,7 +2269,9 @@ impl<T: BundleType> Module<T> {
|
|||
}
|
||||
if simulation.sim_io_to_generator_map.len() > module_io.len() {
|
||||
// if sim_io_to_generator_map is bigger, then there must be a key that's not in module_io
|
||||
let module_io_set = HashSet::from_iter(module_io.iter().map(|v| v.module_io));
|
||||
let module_io_set = HashSet::<ModuleIO<CanonicalType>>::from_iter(
|
||||
module_io.iter().map(|v| v.module_io),
|
||||
);
|
||||
for (sim_io, generator_io) in simulation.sim_io_to_generator_map.iter() {
|
||||
if !module_io_set.contains(&**sim_io) {
|
||||
panic!(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
pub mod deduce_resets;
|
||||
pub mod deduce_structural_eq_flags;
|
||||
pub mod simplify_enums;
|
||||
pub mod simplify_memories;
|
||||
pub mod visit;
|
||||
|
|
|
|||
1319
crates/fayalite/src/module/transform/deduce_structural_eq_flags.rs
Normal file
1319
crates/fayalite/src/module/transform/deduce_structural_eq_flags.rs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -11,7 +11,10 @@ use crate::{
|
|||
memory::{DynPortType, MemPort},
|
||||
module::{
|
||||
Block, Id, NameId, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire,
|
||||
transform::visit::{Fold, Folder},
|
||||
transform::{
|
||||
deduce_structural_eq_flags::deduce_structural_eq_flags,
|
||||
visit::{Fold, Folder},
|
||||
},
|
||||
},
|
||||
prelude::*,
|
||||
util::HashMap,
|
||||
|
|
@ -1262,7 +1265,7 @@ pub fn simplify_enums(
|
|||
module: Interned<Module<Bundle>>,
|
||||
kind: SimplifyEnumsKind,
|
||||
) -> Result<Interned<Module<Bundle>>, SimplifyEnumsError> {
|
||||
// TODO: deduce StructuralEq's assume_padding_is_zeroed
|
||||
let module = deduce_structural_eq_flags(module);
|
||||
module.fold(&mut State {
|
||||
enum_types: HashMap::default(),
|
||||
replacement_mem_ports: HashMap::default(),
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ pub type DefaultBuildHasher = test_hasher::DefaultBuildHasher;
|
|||
#[cfg(not(feature = "unstable-test-hasher"))]
|
||||
pub(crate) type DefaultBuildHasher = hashbrown::DefaultHashBuilder;
|
||||
|
||||
pub(crate) type HashMap<K, V> = hashbrown::HashMap<K, V, DefaultBuildHasher>;
|
||||
pub(crate) type HashSet<T> = hashbrown::HashSet<T, DefaultBuildHasher>;
|
||||
pub(crate) type HashMap<K, V, H = DefaultBuildHasher> = hashbrown::HashMap<K, V, H>;
|
||||
pub(crate) type HashSet<T, H = DefaultBuildHasher> = hashbrown::HashSet<T, H>;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool};
|
||||
|
|
@ -43,7 +43,10 @@ pub use misc::{
|
|||
};
|
||||
pub(crate) use misc::{InternedStrCompareAsStr, chain, copy_le_bytes_to_bitslice};
|
||||
|
||||
pub(crate) mod indented_print;
|
||||
pub mod job_server;
|
||||
pub mod map_trait;
|
||||
pub mod prefix_sum;
|
||||
pub mod ready_valid;
|
||||
pub(crate) mod serde_by_id;
|
||||
pub mod union_find_map;
|
||||
|
|
|
|||
117
crates/fayalite/src/util/indented_print.rs
Normal file
117
crates/fayalite/src/util/indented_print.rs
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use std::{
|
||||
fmt::{self, Write as _},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
struct IndentState {
|
||||
indent: usize,
|
||||
need_indent: bool,
|
||||
buf: String,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static INDENT_STATE: std::cell::RefCell<IndentState> = const {
|
||||
std::cell::RefCell::new(IndentState {
|
||||
indent: 0,
|
||||
need_indent: true,
|
||||
buf: String::new(),
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
struct IndentedOut;
|
||||
|
||||
impl fmt::Write for IndentedOut {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
INDENT_STATE.with_borrow_mut(|state| {
|
||||
let IndentState {
|
||||
indent,
|
||||
need_indent,
|
||||
buf,
|
||||
} = state;
|
||||
buf.clear();
|
||||
for ch in s.chars() {
|
||||
if ch == '\n' {
|
||||
*need_indent = true;
|
||||
} else {
|
||||
if *need_indent {
|
||||
*need_indent = false;
|
||||
for _ in 0..*indent {
|
||||
buf.push_str(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
buf.push(ch)
|
||||
}
|
||||
std::print!("{buf}");
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct PushIndent(PhantomData<*const ()>);
|
||||
|
||||
impl Drop for PushIndent {
|
||||
fn drop(&mut self) {
|
||||
let _ = INDENT_STATE.try_with(|state| state.borrow_mut().indent -= 1);
|
||||
}
|
||||
}
|
||||
|
||||
impl PushIndent {
|
||||
#[allow(unused)]
|
||||
pub(crate) fn new() -> Self {
|
||||
INDENT_STATE.with_borrow_mut(|state| state.indent += 1);
|
||||
Self(PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn indented_print_fmt<const LN: bool>(args: fmt::Arguments<'_>) {
|
||||
if LN {
|
||||
writeln!(IndentedOut, "{args}").expect("writing can't fail")
|
||||
} else {
|
||||
IndentedOut.write_fmt(args).expect("writing can't fail")
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
macro_rules! indented_print {
|
||||
($($args:tt)*) => {
|
||||
$crate::util::indented_print::indented_print_fmt::<false>($crate::__std::format_args!($($args)*))
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) use indented_print;
|
||||
|
||||
#[allow(unused)]
|
||||
macro_rules! indented_println {
|
||||
($($args:tt)*) => {
|
||||
$crate::util::indented_print::indented_print_fmt::<true>($crate::__std::format_args!($($args)*))
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) use indented_println;
|
||||
|
||||
#[allow(unused)]
|
||||
macro_rules! indented_dbg {
|
||||
($expr:expr) => {{
|
||||
let v = $expr;
|
||||
$crate::util::indented_print::indented_println!(
|
||||
"[{}:{}:{}] {} = {v:#?}",
|
||||
file!(),
|
||||
line!(),
|
||||
column!(),
|
||||
stringify!($expr),
|
||||
);
|
||||
v
|
||||
}};
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) use indented_dbg;
|
||||
463
crates/fayalite/src/util/map_trait.rs
Normal file
463
crates/fayalite/src/util/map_trait.rs
Normal file
|
|
@ -0,0 +1,463 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use std::fmt;
|
||||
|
||||
pub enum Entry<'a, M: Map + 'a> {
|
||||
Vacant(M::VacantEntry<'a>),
|
||||
Occupied(M::OccupiedEntry<'a>),
|
||||
}
|
||||
|
||||
impl<'a, M: Map + 'a> Entry<'a, M> {
|
||||
pub fn and_modify<F: FnOnce(&mut M::Value)>(mut self, f: F) -> Self {
|
||||
if let Self::Occupied(entry) = &mut self {
|
||||
f(entry.get_mut());
|
||||
}
|
||||
self
|
||||
}
|
||||
pub fn insert_entry(self, v: M::Value) -> M::OccupiedEntry<'a> {
|
||||
match self {
|
||||
Self::Vacant(entry) => entry.insert_entry(v),
|
||||
Self::Occupied(mut entry) => {
|
||||
entry.insert(v);
|
||||
entry
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn key(&self) -> &M::Key {
|
||||
match self {
|
||||
Self::Vacant(entry) => entry.key(),
|
||||
Self::Occupied(entry) => entry.key(),
|
||||
}
|
||||
}
|
||||
pub fn or_default(self) -> &'a mut M::Value
|
||||
where
|
||||
M::Value: Default,
|
||||
{
|
||||
self.or_insert_with(Default::default)
|
||||
}
|
||||
pub fn or_insert(self, v: M::Value) -> &'a mut M::Value {
|
||||
match self {
|
||||
Self::Vacant(entry) => entry.insert(v),
|
||||
Self::Occupied(entry) => entry.into_mut(),
|
||||
}
|
||||
}
|
||||
pub fn or_insert_with<F: FnOnce() -> M::Value>(self, f: F) -> &'a mut M::Value {
|
||||
match self {
|
||||
Self::Vacant(entry) => entry.insert(f()),
|
||||
Self::Occupied(entry) => entry.into_mut(),
|
||||
}
|
||||
}
|
||||
pub fn or_insert_with_key<F: FnOnce(&M::Key) -> M::Value>(self, f: F) -> &'a mut M::Value {
|
||||
match self {
|
||||
Self::Vacant(entry) => {
|
||||
let v = f(entry.key());
|
||||
entry.insert(v)
|
||||
}
|
||||
Self::Occupied(entry) => entry.into_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, M: Map<OccupiedEntry<'a>: fmt::Debug, VacantEntry<'a>: fmt::Debug> + 'a> fmt::Debug
|
||||
for Entry<'a, M>
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Vacant(v) => f.debug_tuple("Vacant").field(v).finish(),
|
||||
Self::Occupied(v) => f.debug_tuple("Occupied").field(v).finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait VacantEntry<'a>: Sized {
|
||||
type Map: Map<VacantEntry<'a> = Self> + 'a;
|
||||
fn insert(self, v: <Self::Map as Map>::Value) -> &'a mut <Self::Map as Map>::Value;
|
||||
fn insert_entry(self, v: <Self::Map as Map>::Value) -> <Self::Map as Map>::OccupiedEntry<'a>;
|
||||
fn into_key(self) -> <Self::Map as Map>::Key;
|
||||
fn key(&self) -> &<Self::Map as Map>::Key;
|
||||
}
|
||||
|
||||
pub trait OccupiedEntry<'a>: Sized {
|
||||
type Map: Map<OccupiedEntry<'a> = Self> + 'a;
|
||||
fn get(&self) -> &<Self::Map as Map>::Value;
|
||||
fn get_mut(&mut self) -> &mut <Self::Map as Map>::Value;
|
||||
fn insert(&mut self, v: <Self::Map as Map>::Value) -> <Self::Map as Map>::Value;
|
||||
fn into_mut(self) -> &'a mut <Self::Map as Map>::Value;
|
||||
fn key(&self) -> &<Self::Map as Map>::Key;
|
||||
fn remove(self) -> <Self::Map as Map>::Value;
|
||||
fn remove_entry(self) -> (<Self::Map as Map>::Key, <Self::Map as Map>::Value);
|
||||
}
|
||||
|
||||
pub trait Map:
|
||||
Sized
|
||||
+ IntoIterator<Item = (<Self as Map>::Key, <Self as Map>::Value)>
|
||||
+ Extend<(<Self as Map>::Key, <Self as Map>::Value)>
|
||||
+ FromIterator<(<Self as Map>::Key, <Self as Map>::Value)>
|
||||
{
|
||||
type Key;
|
||||
type Value;
|
||||
type IntoKeys: Iterator<Item = Self::Key>;
|
||||
type IntoValues: Iterator<Item = Self::Value>;
|
||||
type Iter<'a>: Iterator<Item = (&'a Self::Key, &'a Self::Value)>
|
||||
where
|
||||
Self: 'a,
|
||||
Self::Key: 'a,
|
||||
Self::Value: 'a;
|
||||
type IterMut<'a>: Iterator<Item = (&'a Self::Key, &'a mut Self::Value)>
|
||||
where
|
||||
Self: 'a,
|
||||
Self::Key: 'a,
|
||||
Self::Value: 'a;
|
||||
type Keys<'a>: Iterator<Item = &'a Self::Key>
|
||||
where
|
||||
Self: 'a,
|
||||
Self::Key: 'a;
|
||||
type Values<'a>: Iterator<Item = &'a Self::Value>
|
||||
where
|
||||
Self: 'a,
|
||||
Self::Value: 'a;
|
||||
type ValuesMut<'a>: Iterator<Item = &'a mut Self::Value>
|
||||
where
|
||||
Self: 'a,
|
||||
Self::Value: 'a;
|
||||
type OccupiedEntry<'a>: OccupiedEntry<'a, Map = Self>
|
||||
where
|
||||
Self: 'a;
|
||||
type VacantEntry<'a>: VacantEntry<'a, Map = Self>
|
||||
where
|
||||
Self: 'a;
|
||||
fn clear(&mut self);
|
||||
fn entry(&mut self, k: Self::Key) -> Entry<'_, Self>;
|
||||
fn insert(&mut self, k: Self::Key, v: Self::Value) -> Option<Self::Value>;
|
||||
fn into_keys(self) -> Self::IntoKeys;
|
||||
fn into_values(self) -> Self::IntoValues;
|
||||
fn is_empty(&self) -> bool;
|
||||
fn iter(&self) -> Self::Iter<'_>;
|
||||
fn iter_mut(&mut self) -> Self::IterMut<'_>;
|
||||
fn keys(&self) -> Self::Keys<'_>;
|
||||
fn len(&self) -> usize;
|
||||
fn retain<F: FnMut(&Self::Key, &mut Self::Value) -> bool>(&mut self, f: F);
|
||||
fn values(&self) -> Self::Values<'_>;
|
||||
fn values_mut(&mut self) -> Self::ValuesMut<'_>;
|
||||
}
|
||||
|
||||
pub trait MapGet<Q: ?Sized>: Map {
|
||||
fn contains_key(&self, k: &Q) -> bool;
|
||||
fn get(&self, k: &Q) -> Option<&Self::Value>;
|
||||
fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Value>;
|
||||
fn remove(&mut self, k: &Q) -> Option<Self::Value>;
|
||||
fn remove_entry(&mut self, k: &Q) -> Option<(Self::Key, Self::Value)>;
|
||||
}
|
||||
|
||||
mod hash_map {
|
||||
use super::*;
|
||||
use crate::util::HashMap;
|
||||
use hashbrown::{Equivalent, hash_map};
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
|
||||
impl<K: Eq + Hash, V, H: BuildHasher + Default> Map for HashMap<K, V, H> {
|
||||
type Key = K;
|
||||
type Value = V;
|
||||
type IntoKeys = hash_map::IntoKeys<K, V>;
|
||||
type IntoValues = hash_map::IntoValues<K, V>;
|
||||
type Iter<'a>
|
||||
= hash_map::Iter<'a, K, V>
|
||||
where
|
||||
Self: 'a,
|
||||
Self::Key: 'a,
|
||||
Self::Value: 'a;
|
||||
type IterMut<'a>
|
||||
= hash_map::IterMut<'a, K, V>
|
||||
where
|
||||
Self: 'a,
|
||||
Self::Key: 'a,
|
||||
Self::Value: 'a;
|
||||
type Keys<'a>
|
||||
= hash_map::Keys<'a, K, V>
|
||||
where
|
||||
Self: 'a,
|
||||
Self::Key: 'a;
|
||||
type Values<'a>
|
||||
= hash_map::Values<'a, K, V>
|
||||
where
|
||||
Self: 'a,
|
||||
Self::Value: 'a;
|
||||
type ValuesMut<'a>
|
||||
= hash_map::ValuesMut<'a, K, V>
|
||||
where
|
||||
Self: 'a,
|
||||
Self::Value: 'a;
|
||||
type OccupiedEntry<'a>
|
||||
= hash_map::OccupiedEntry<'a, K, V, H>
|
||||
where
|
||||
Self: 'a;
|
||||
type VacantEntry<'a>
|
||||
= hash_map::VacantEntry<'a, K, V, H>
|
||||
where
|
||||
Self: 'a;
|
||||
fn clear(&mut self) {
|
||||
self.clear();
|
||||
}
|
||||
fn entry(&mut self, k: Self::Key) -> Entry<'_, Self> {
|
||||
use hash_map::Entry::*;
|
||||
match self.entry(k) {
|
||||
Occupied(entry) => Entry::Occupied(entry),
|
||||
Vacant(entry) => Entry::Vacant(entry),
|
||||
}
|
||||
}
|
||||
fn insert(&mut self, k: Self::Key, v: Self::Value) -> Option<Self::Value> {
|
||||
self.insert(k, v)
|
||||
}
|
||||
fn into_keys(self) -> Self::IntoKeys {
|
||||
self.into_keys()
|
||||
}
|
||||
fn into_values(self) -> Self::IntoValues {
|
||||
self.into_values()
|
||||
}
|
||||
fn is_empty(&self) -> bool {
|
||||
self.is_empty()
|
||||
}
|
||||
fn iter(&self) -> Self::Iter<'_> {
|
||||
self.iter()
|
||||
}
|
||||
fn iter_mut(&mut self) -> Self::IterMut<'_> {
|
||||
self.iter_mut()
|
||||
}
|
||||
fn keys(&self) -> Self::Keys<'_> {
|
||||
self.keys()
|
||||
}
|
||||
fn len(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
fn retain<F: FnMut(&Self::Key, &mut Self::Value) -> bool>(&mut self, f: F) {
|
||||
self.retain(f);
|
||||
}
|
||||
fn values(&self) -> Self::Values<'_> {
|
||||
self.values()
|
||||
}
|
||||
fn values_mut(&mut self) -> Self::ValuesMut<'_> {
|
||||
self.values_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Eq + Hash, V, H: BuildHasher + Default, Q: ?Sized + Hash + Equivalent<K>> MapGet<Q>
|
||||
for HashMap<K, V, H>
|
||||
{
|
||||
fn contains_key(&self, k: &Q) -> bool {
|
||||
self.contains_key(k)
|
||||
}
|
||||
fn get(&self, k: &Q) -> Option<&Self::Value> {
|
||||
self.get(k)
|
||||
}
|
||||
fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Value> {
|
||||
self.get_mut(k)
|
||||
}
|
||||
fn remove(&mut self, k: &Q) -> Option<Self::Value> {
|
||||
self.remove(k)
|
||||
}
|
||||
fn remove_entry(&mut self, k: &Q) -> Option<(Self::Key, Self::Value)> {
|
||||
self.remove_entry(k)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K: Eq + Hash, V, H: BuildHasher + Default> VacantEntry<'a>
|
||||
for hash_map::VacantEntry<'a, K, V, H>
|
||||
{
|
||||
type Map = HashMap<K, V, H>;
|
||||
fn insert(self, v: <Self::Map as Map>::Value) -> &'a mut <Self::Map as Map>::Value {
|
||||
self.insert(v)
|
||||
}
|
||||
fn insert_entry(
|
||||
self,
|
||||
v: <Self::Map as Map>::Value,
|
||||
) -> <Self::Map as Map>::OccupiedEntry<'a> {
|
||||
self.insert_entry(v)
|
||||
}
|
||||
fn into_key(self) -> <Self::Map as Map>::Key {
|
||||
self.into_key()
|
||||
}
|
||||
fn key(&self) -> &<Self::Map as Map>::Key {
|
||||
self.key()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K: Eq + Hash, V, H: BuildHasher + Default> OccupiedEntry<'a>
|
||||
for hash_map::OccupiedEntry<'a, K, V, H>
|
||||
{
|
||||
type Map = HashMap<K, V, H>;
|
||||
fn get(&self) -> &<Self::Map as Map>::Value {
|
||||
self.get()
|
||||
}
|
||||
fn get_mut(&mut self) -> &mut <Self::Map as Map>::Value {
|
||||
self.get_mut()
|
||||
}
|
||||
fn insert(&mut self, v: <Self::Map as Map>::Value) -> <Self::Map as Map>::Value {
|
||||
self.insert(v)
|
||||
}
|
||||
fn into_mut(self) -> &'a mut <Self::Map as Map>::Value {
|
||||
self.into_mut()
|
||||
}
|
||||
fn key(&self) -> &<Self::Map as Map>::Key {
|
||||
self.key()
|
||||
}
|
||||
fn remove(self) -> <Self::Map as Map>::Value {
|
||||
self.remove()
|
||||
}
|
||||
fn remove_entry(self) -> (<Self::Map as Map>::Key, <Self::Map as Map>::Value) {
|
||||
self.remove_entry()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod btree_map {
|
||||
use super::*;
|
||||
use std::collections::{BTreeMap, btree_map};
|
||||
|
||||
impl<K: Ord, V> Map for BTreeMap<K, V> {
|
||||
type Key = K;
|
||||
type Value = V;
|
||||
type IntoKeys = btree_map::IntoKeys<K, V>;
|
||||
type IntoValues = btree_map::IntoValues<K, V>;
|
||||
type Iter<'a>
|
||||
= btree_map::Iter<'a, K, V>
|
||||
where
|
||||
Self: 'a,
|
||||
Self::Key: 'a,
|
||||
Self::Value: 'a;
|
||||
type IterMut<'a>
|
||||
= btree_map::IterMut<'a, K, V>
|
||||
where
|
||||
Self: 'a,
|
||||
Self::Key: 'a,
|
||||
Self::Value: 'a;
|
||||
type Keys<'a>
|
||||
= btree_map::Keys<'a, K, V>
|
||||
where
|
||||
Self: 'a,
|
||||
Self::Key: 'a;
|
||||
type Values<'a>
|
||||
= btree_map::Values<'a, K, V>
|
||||
where
|
||||
Self: 'a,
|
||||
Self::Value: 'a;
|
||||
type ValuesMut<'a>
|
||||
= btree_map::ValuesMut<'a, K, V>
|
||||
where
|
||||
Self: 'a,
|
||||
Self::Value: 'a;
|
||||
type OccupiedEntry<'a>
|
||||
= btree_map::OccupiedEntry<'a, K, V>
|
||||
where
|
||||
Self: 'a;
|
||||
type VacantEntry<'a>
|
||||
= btree_map::VacantEntry<'a, K, V>
|
||||
where
|
||||
Self: 'a;
|
||||
fn clear(&mut self) {
|
||||
self.clear();
|
||||
}
|
||||
fn entry(&mut self, k: Self::Key) -> Entry<'_, Self> {
|
||||
use btree_map::Entry::*;
|
||||
match self.entry(k) {
|
||||
Occupied(entry) => Entry::Occupied(entry),
|
||||
Vacant(entry) => Entry::Vacant(entry),
|
||||
}
|
||||
}
|
||||
fn insert(&mut self, k: Self::Key, v: Self::Value) -> Option<Self::Value> {
|
||||
self.insert(k, v)
|
||||
}
|
||||
fn into_keys(self) -> Self::IntoKeys {
|
||||
self.into_keys()
|
||||
}
|
||||
fn into_values(self) -> Self::IntoValues {
|
||||
self.into_values()
|
||||
}
|
||||
fn is_empty(&self) -> bool {
|
||||
self.is_empty()
|
||||
}
|
||||
fn iter(&self) -> Self::Iter<'_> {
|
||||
self.iter()
|
||||
}
|
||||
fn iter_mut(&mut self) -> Self::IterMut<'_> {
|
||||
self.iter_mut()
|
||||
}
|
||||
fn keys(&self) -> Self::Keys<'_> {
|
||||
self.keys()
|
||||
}
|
||||
fn len(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
fn retain<F: FnMut(&Self::Key, &mut Self::Value) -> bool>(&mut self, f: F) {
|
||||
self.retain(f);
|
||||
}
|
||||
fn values(&self) -> Self::Values<'_> {
|
||||
self.values()
|
||||
}
|
||||
fn values_mut(&mut self) -> Self::ValuesMut<'_> {
|
||||
self.values_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Ord + std::borrow::Borrow<Q>, V, Q: ?Sized + Ord> MapGet<Q> for BTreeMap<K, V> {
|
||||
fn contains_key(&self, k: &Q) -> bool {
|
||||
self.contains_key(k)
|
||||
}
|
||||
fn get(&self, k: &Q) -> Option<&Self::Value> {
|
||||
self.get(k)
|
||||
}
|
||||
fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Value> {
|
||||
self.get_mut(k)
|
||||
}
|
||||
fn remove(&mut self, k: &Q) -> Option<Self::Value> {
|
||||
self.remove(k)
|
||||
}
|
||||
fn remove_entry(&mut self, k: &Q) -> Option<(Self::Key, Self::Value)> {
|
||||
self.remove_entry(k)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K: Ord, V> VacantEntry<'a> for btree_map::VacantEntry<'a, K, V> {
|
||||
type Map = BTreeMap<K, V>;
|
||||
fn insert(self, v: <Self::Map as Map>::Value) -> &'a mut <Self::Map as Map>::Value {
|
||||
self.insert(v)
|
||||
}
|
||||
fn insert_entry(
|
||||
self,
|
||||
v: <Self::Map as Map>::Value,
|
||||
) -> <Self::Map as Map>::OccupiedEntry<'a> {
|
||||
self.insert_entry(v)
|
||||
}
|
||||
fn into_key(self) -> <Self::Map as Map>::Key {
|
||||
self.into_key()
|
||||
}
|
||||
fn key(&self) -> &<Self::Map as Map>::Key {
|
||||
self.key()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K: Ord, V> OccupiedEntry<'a> for btree_map::OccupiedEntry<'a, K, V> {
|
||||
type Map = BTreeMap<K, V>;
|
||||
fn get(&self) -> &<Self::Map as Map>::Value {
|
||||
self.get()
|
||||
}
|
||||
fn get_mut(&mut self) -> &mut <Self::Map as Map>::Value {
|
||||
self.get_mut()
|
||||
}
|
||||
fn insert(&mut self, v: <Self::Map as Map>::Value) -> <Self::Map as Map>::Value {
|
||||
self.insert(v)
|
||||
}
|
||||
fn into_mut(self) -> &'a mut <Self::Map as Map>::Value {
|
||||
self.into_mut()
|
||||
}
|
||||
fn key(&self) -> &<Self::Map as Map>::Key {
|
||||
self.key()
|
||||
}
|
||||
fn remove(self) -> <Self::Map as Map>::Value {
|
||||
self.remove()
|
||||
}
|
||||
fn remove_entry(self) -> (<Self::Map as Map>::Key, <Self::Map as Map>::Value) {
|
||||
self.remove_entry()
|
||||
}
|
||||
}
|
||||
}
|
||||
352
crates/fayalite/src/util/union_find_map.rs
Normal file
352
crates/fayalite/src/util/union_find_map.rs
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::util::{
|
||||
HashMap,
|
||||
map_trait::{self, Map, MapGet, OccupiedEntry as _, VacantEntry as _},
|
||||
};
|
||||
use petgraph::unionfind::UnionFind;
|
||||
use std::{collections::BTreeMap, fmt, marker::PhantomData};
|
||||
|
||||
pub struct UnionFindMap<K, V, M = HashMap<K, usize>> {
|
||||
uf: UnionFind<usize>,
|
||||
keys_to_indexes: M,
|
||||
values: Vec<Option<V>>,
|
||||
_phantom: PhantomData<K>,
|
||||
}
|
||||
|
||||
impl<K: fmt::Debug, V: fmt::Debug, M: Map<Key = K, Value = usize>> fmt::Debug
|
||||
for UnionFindMap<K, V, M>
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut indexes_to_keys = vec![None; self.len()];
|
||||
for (k, &index) in self.keys_to_indexes.iter() {
|
||||
indexes_to_keys[index] = Some(k);
|
||||
}
|
||||
let mut debug_map = f.debug_map();
|
||||
for (index, key) in indexes_to_keys.into_iter().enumerate() {
|
||||
if let Some(key) = key {
|
||||
debug_map.key(key);
|
||||
} else {
|
||||
debug_map.key(&fmt::from_fn(|f| {
|
||||
f.write_str("<<there's a misbehaving key>>")
|
||||
}));
|
||||
}
|
||||
let set_index = self.uf.find(index);
|
||||
debug_map.value(&fmt::from_fn(|f| {
|
||||
write!(f, "@{set_index} ")?;
|
||||
if set_index == index {
|
||||
let Some(value) = &self.values[index] else {
|
||||
unreachable!();
|
||||
};
|
||||
value.fmt(f)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}));
|
||||
}
|
||||
debug_map.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, M: Map<Key = K, Value = usize>> UnionFindMap<K, V, M> {
|
||||
/// returns the number of keys, not the number of sets/values
|
||||
pub fn len(&self) -> usize {
|
||||
self.values.len()
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.values.capacity()
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn equiv<K1: ?Sized, K2: ?Sized>(&self, k1: &K1, k2: &K2) -> bool
|
||||
where
|
||||
M: MapGet<K1> + MapGet<K2>,
|
||||
{
|
||||
self.try_equiv(k1, k2).expect("key not found")
|
||||
}
|
||||
pub fn try_equiv<K1: ?Sized, K2: ?Sized>(&self, k1: &K1, k2: &K2) -> Option<bool>
|
||||
where
|
||||
M: MapGet<K1> + MapGet<K2>,
|
||||
{
|
||||
let &index1 = self.keys_to_indexes.get(k1)?;
|
||||
let &index2 = self.keys_to_indexes.get(k2)?;
|
||||
Some(self.uf.equiv(index1, index2))
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn find<Q: ?Sized>(&self, k: &Q) -> &V
|
||||
where
|
||||
M: MapGet<Q>,
|
||||
{
|
||||
self.try_find(k).expect("key not found")
|
||||
}
|
||||
pub fn try_find<Q: ?Sized>(&self, k: &Q) -> Option<&V>
|
||||
where
|
||||
M: MapGet<Q>,
|
||||
{
|
||||
let &index = self.keys_to_indexes.get(k)?;
|
||||
self.values[self.uf.find(index)].as_ref()
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn find_mut<Q: ?Sized>(&mut self, k: &Q) -> &mut V
|
||||
where
|
||||
M: MapGet<Q>,
|
||||
{
|
||||
self.try_find_mut(k).expect("key not found")
|
||||
}
|
||||
pub fn try_find_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V>
|
||||
where
|
||||
M: MapGet<Q>,
|
||||
{
|
||||
let &index = self.keys_to_indexes.get(k)?;
|
||||
self.values[self.uf.find_mut(index)].as_mut()
|
||||
}
|
||||
/// inserts a new key as a new set, otherwise replaces the value for the set containing the passed-in key
|
||||
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
|
||||
match self.entry(k) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(v);
|
||||
None
|
||||
}
|
||||
Entry::Occupied(mut entry) => Some(entry.insert(v)),
|
||||
}
|
||||
}
|
||||
pub fn entry(&mut self, k: K) -> Entry<'_, K, V, M> {
|
||||
match self.keys_to_indexes.entry(k) {
|
||||
map_trait::Entry::Vacant(keys_to_indexes_entry) => Entry::Vacant(VacantEntry {
|
||||
keys_to_indexes_entry,
|
||||
uf: &mut self.uf,
|
||||
values: &mut self.values,
|
||||
}),
|
||||
map_trait::Entry::Occupied(keys_to_indexes_entry) => {
|
||||
let set_index = self.uf.find_mut(*keys_to_indexes_entry.get());
|
||||
Entry::Occupied(OccupiedEntry {
|
||||
keys_to_indexes_entry,
|
||||
set_index,
|
||||
values: &mut self.values,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Unify the two sets containing `k1` and `k2`.
|
||||
/// If the sets were the same, returns `Some((false, value))`,
|
||||
/// otherwise calling `merge` to merge their values and returning `Some((true, value))`.
|
||||
/// Returns `None` if either of the keys weren't found.
|
||||
pub fn try_union<K1: ?Sized, K2: ?Sized, F>(
|
||||
&mut self,
|
||||
k1: &K1,
|
||||
k2: &K2,
|
||||
merge: F,
|
||||
) -> Option<(bool, &mut V)>
|
||||
where
|
||||
M: MapGet<K1> + MapGet<K2>,
|
||||
F: FnOnce(&K1, V, &K2, V) -> V,
|
||||
{
|
||||
let &index1 = self.keys_to_indexes.get(k1)?;
|
||||
let &index2 = self.keys_to_indexes.get(k2)?;
|
||||
let index1 = self.uf.find_mut(index1);
|
||||
let index2 = self.uf.find_mut(index2);
|
||||
if index1 == index2 {
|
||||
return Some((false, self.values[index1].as_mut()?));
|
||||
}
|
||||
assert!(self.uf.union(index1, index2));
|
||||
let v1 = self.values[index1].take().expect("known to be Some");
|
||||
let v2 = self.values[index2].take().expect("known to be Some");
|
||||
let dest = &mut self.values[self.uf.find_mut(index1)];
|
||||
let dest = dest.insert(merge(k1, v1, k2, v2));
|
||||
Some((true, dest))
|
||||
}
|
||||
/// Unify the two sets containing `k1` and `k2`.
|
||||
/// If the sets were the same, returns `(false, value)`,
|
||||
/// otherwise calling `merge` to merge their values and returning `(true, value)`.
|
||||
/// panics if either of the keys weren't found.
|
||||
#[track_caller]
|
||||
pub fn union<K1: ?Sized, K2: ?Sized, F>(&mut self, k1: &K1, k2: &K2, merge: F) -> (bool, &mut V)
|
||||
where
|
||||
M: MapGet<K1> + MapGet<K2>,
|
||||
F: FnOnce(&K1, V, &K2, V) -> V,
|
||||
{
|
||||
self.try_union(k1, k2, merge).expect("key not found")
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> UnionFindMap<K, V> {
|
||||
pub fn new() -> Self {
|
||||
Self::with_hasher(Default::default())
|
||||
}
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Self::with_capacity_and_hasher(capacity, Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> UnionFindMap<K, V, BTreeMap<K, usize>> {
|
||||
pub const fn new_btree() -> Self {
|
||||
Self {
|
||||
uf: UnionFind::new_empty(),
|
||||
keys_to_indexes: BTreeMap::new(),
|
||||
values: Vec::new(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, H> UnionFindMap<K, V, HashMap<K, usize, H>> {
|
||||
pub const fn with_hasher(hash_builder: H) -> Self {
|
||||
Self {
|
||||
uf: UnionFind::new_empty(),
|
||||
keys_to_indexes: HashMap::with_hasher(hash_builder),
|
||||
values: Vec::new(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
pub fn with_capacity_and_hasher(capacity: usize, hash_builder: H) -> Self {
|
||||
Self {
|
||||
uf: UnionFind::with_capacity(capacity),
|
||||
keys_to_indexes: HashMap::with_capacity_and_hasher(capacity, hash_builder),
|
||||
values: Vec::with_capacity(capacity),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, M: Default> Default for UnionFindMap<K, V, M> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
uf: UnionFind::new_empty(),
|
||||
keys_to_indexes: M::default(),
|
||||
values: Vec::new(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OccupiedEntry<'a, K, V, M: Map<Key = K, Value = usize> + 'a> {
|
||||
keys_to_indexes_entry: M::OccupiedEntry<'a>,
|
||||
set_index: usize,
|
||||
values: &'a mut [Option<V>],
|
||||
}
|
||||
|
||||
impl<'a, K, V, M: Map<Key = K, Value = usize> + 'a> OccupiedEntry<'a, K, V, M> {
|
||||
pub fn get(&self) -> &V {
|
||||
let Some(v) = &self.values[self.set_index] else {
|
||||
unreachable!()
|
||||
};
|
||||
v
|
||||
}
|
||||
pub fn get_mut(&mut self) -> &mut V {
|
||||
let Some(v) = &mut self.values[self.set_index] else {
|
||||
unreachable!()
|
||||
};
|
||||
v
|
||||
}
|
||||
/// replaces the value for this set
|
||||
pub fn insert(&mut self, v: V) -> V {
|
||||
std::mem::replace(self.get_mut(), v)
|
||||
}
|
||||
pub fn into_mut(self) -> &'a mut V {
|
||||
let Some(v) = &mut self.values[self.set_index] else {
|
||||
unreachable!()
|
||||
};
|
||||
v
|
||||
}
|
||||
pub fn key(&self) -> &K {
|
||||
self.keys_to_indexes_entry.key()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VacantEntry<'a, K, V, M: Map<Key = K, Value = usize> + 'a> {
|
||||
keys_to_indexes_entry: M::VacantEntry<'a>,
|
||||
uf: &'a mut UnionFind<usize>,
|
||||
values: &'a mut Vec<Option<V>>,
|
||||
}
|
||||
|
||||
impl<'a, K, V, M: Map<Key = K, Value = usize> + 'a> VacantEntry<'a, K, V, M> {
|
||||
/// inserts a new key as a new set
|
||||
pub fn insert(self, v: V) -> &'a mut V {
|
||||
self.insert_entry(v).into_mut()
|
||||
}
|
||||
/// inserts a new key as a new set
|
||||
pub fn insert_entry(self, v: V) -> OccupiedEntry<'a, K, V, M> {
|
||||
let Self {
|
||||
keys_to_indexes_entry,
|
||||
uf,
|
||||
values,
|
||||
} = self;
|
||||
let set_index = uf.new_set();
|
||||
values.push(Some(v));
|
||||
OccupiedEntry {
|
||||
keys_to_indexes_entry: keys_to_indexes_entry.insert_entry(set_index),
|
||||
set_index,
|
||||
values,
|
||||
}
|
||||
}
|
||||
pub fn into_key(self) -> K {
|
||||
self.keys_to_indexes_entry.into_key()
|
||||
}
|
||||
pub fn key(&self) -> &K {
|
||||
self.keys_to_indexes_entry.key()
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Entry<'a, K, V, M: Map<Key = K, Value = usize> + 'a> {
|
||||
Vacant(VacantEntry<'a, K, V, M>),
|
||||
Occupied(OccupiedEntry<'a, K, V, M>),
|
||||
}
|
||||
|
||||
impl<'a, K, V, M: Map<Key = K, Value = usize> + 'a> Entry<'a, K, V, M> {
|
||||
pub fn and_modify<F: FnOnce(&mut V)>(mut self, f: F) -> Self {
|
||||
if let Self::Occupied(entry) = &mut self {
|
||||
f(entry.get_mut());
|
||||
}
|
||||
self
|
||||
}
|
||||
/// inserts a new key as a new set, otherwise replaces the value for the set containing the passed-in key
|
||||
pub fn insert_entry(self, v: V) -> OccupiedEntry<'a, K, V, M> {
|
||||
match self {
|
||||
Self::Vacant(entry) => entry.insert_entry(v),
|
||||
Self::Occupied(mut entry) => {
|
||||
entry.insert(v);
|
||||
entry
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn key(&self) -> &K {
|
||||
match self {
|
||||
Self::Vacant(entry) => entry.key(),
|
||||
Self::Occupied(entry) => entry.key(),
|
||||
}
|
||||
}
|
||||
/// inserts a new key as a new set
|
||||
pub fn or_default(self) -> &'a mut V
|
||||
where
|
||||
V: Default,
|
||||
{
|
||||
self.or_insert_with(V::default)
|
||||
}
|
||||
/// inserts a new key as a new set
|
||||
pub fn or_insert(self, v: V) -> &'a mut V {
|
||||
match self {
|
||||
Self::Vacant(entry) => entry.insert(v),
|
||||
Self::Occupied(entry) => entry.into_mut(),
|
||||
}
|
||||
}
|
||||
/// inserts a new key as a new set
|
||||
pub fn or_insert_with<F: FnOnce() -> V>(self, f: F) -> &'a mut V {
|
||||
match self {
|
||||
Self::Vacant(entry) => entry.insert(f()),
|
||||
Self::Occupied(entry) => entry.into_mut(),
|
||||
}
|
||||
}
|
||||
/// inserts a new key as a new set
|
||||
pub fn or_insert_with_key<F: FnOnce(&K) -> V>(self, f: F) -> &'a mut V {
|
||||
match self {
|
||||
Self::Vacant(entry) => {
|
||||
let v = f(entry.key());
|
||||
entry.insert(v)
|
||||
}
|
||||
Self::Occupied(entry) => entry.into_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
2117
crates/fayalite/tests/deduce_structural_eq_flags.rs
Normal file
2117
crates/fayalite/tests/deduce_structural_eq_flags.rs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -4863,32 +4863,16 @@ circuit check_struct_cmp_eq:
|
|||
input test_struct_3_rhs: Ty3 @[module-XXXXXXXXXX.rs 21:1]
|
||||
output test_struct_3_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 22:1]
|
||||
output test_struct_3_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 24:1]
|
||||
wire _array_literal_expr: UInt<1>[3]
|
||||
connect _array_literal_expr[0], eq(tuple_lhs.`0`, tuple_rhs.`0`)
|
||||
connect _array_literal_expr[1], eq(tuple_lhs.`1`, tuple_rhs.`1`)
|
||||
connect _array_literal_expr[2], eq(tuple_lhs.`2`, tuple_rhs.`2`)
|
||||
wire _cast_array_to_bits_expr: UInt<1>[3]
|
||||
connect _cast_array_to_bits_expr[0], _array_literal_expr[0]
|
||||
connect _cast_array_to_bits_expr[1], _array_literal_expr[1]
|
||||
connect _cast_array_to_bits_expr[2], _array_literal_expr[2]
|
||||
wire _cast_to_bits_expr: UInt<3>
|
||||
connect _cast_to_bits_expr, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0]))
|
||||
connect tuple_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1]
|
||||
wire _array_literal_expr_1: UInt<1>[3]
|
||||
connect _array_literal_expr_1[0], neq(tuple_lhs.`0`, tuple_rhs.`0`)
|
||||
connect _array_literal_expr_1[1], neq(tuple_lhs.`1`, tuple_rhs.`1`)
|
||||
connect _array_literal_expr_1[2], neq(tuple_lhs.`2`, tuple_rhs.`2`)
|
||||
wire _cast_array_to_bits_expr_1: UInt<1>[3]
|
||||
connect _cast_array_to_bits_expr_1[0], _array_literal_expr_1[0]
|
||||
connect _cast_array_to_bits_expr_1[1], _array_literal_expr_1[1]
|
||||
connect _cast_array_to_bits_expr_1[2], _array_literal_expr_1[2]
|
||||
wire _cast_to_bits_expr_1: UInt<3>
|
||||
connect _cast_to_bits_expr_1, cat(_cast_array_to_bits_expr_1[2], cat(_cast_array_to_bits_expr_1[1], _cast_array_to_bits_expr_1[0]))
|
||||
connect tuple_cmp_ne, orr(_cast_to_bits_expr_1) @[module-XXXXXXXXXX.rs 7:1]
|
||||
wire _bundle_structural_eq: UInt<1>
|
||||
connect _bundle_structural_eq, and(eq(test_struct_lhs.a, test_struct_rhs.a), eq(test_struct_lhs.b, test_struct_rhs.b))
|
||||
connect test_struct_cmp_eq, _bundle_structural_eq @[module-XXXXXXXXXX.rs 11:1]
|
||||
connect test_struct_cmp_ne, not(_bundle_structural_eq) @[module-XXXXXXXXXX.rs 13:1]
|
||||
connect _bundle_structural_eq, and(eq(tuple_lhs.`0`, tuple_rhs.`0`), eq(tuple_lhs.`1`, tuple_rhs.`1`))
|
||||
wire _bundle_structural_eq_1: UInt<1>
|
||||
connect _bundle_structural_eq_1, and(_bundle_structural_eq, eq(tuple_lhs.`2`, tuple_rhs.`2`))
|
||||
connect tuple_cmp_eq, _bundle_structural_eq_1 @[module-XXXXXXXXXX.rs 5:1]
|
||||
connect tuple_cmp_ne, not(_bundle_structural_eq_1) @[module-XXXXXXXXXX.rs 7:1]
|
||||
wire _bundle_structural_eq_2: UInt<1>
|
||||
connect _bundle_structural_eq_2, and(eq(test_struct_lhs.a, test_struct_rhs.a), eq(test_struct_lhs.b, test_struct_rhs.b))
|
||||
connect test_struct_cmp_eq, _bundle_structural_eq_2 @[module-XXXXXXXXXX.rs 11:1]
|
||||
connect test_struct_cmp_ne, not(_bundle_structural_eq_2) @[module-XXXXXXXXXX.rs 13:1]
|
||||
connect test_struct_2_cmp_eq, eq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 17:1]
|
||||
connect test_struct_2_cmp_ne, not(eq(test_struct_2_lhs.v, test_struct_2_rhs.v)) @[module-XXXXXXXXXX.rs 19:1]
|
||||
connect test_struct_3_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 23:1]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue