use std::collections::{HashMap, hash_map::Entry};
use proc_macro2::Ident;
use syn::Expr;
pub struct IncreasingInteger {
value: usize,
}
impl IncreasingInteger {
pub fn new() -> Self {
Self { value: 0 }
}
pub fn get_next(&mut self) -> usize {
let current = self.value;
self.value += 1;
current
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum HeadKind {
Tensor,
Indexer,
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum IndexingPositionContent {
TensorIndex(usize, usize),
IndexerIndex(usize, usize),
IndexerResult(usize),
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct IndexingPosition {
pub name: Ident,
pub content: IndexingPositionContent,
}
#[cfg(test)]
impl PartialEq<(&str, usize, HeadKind)> for IndexingPosition {
fn eq(&self, other: &(&str, usize, HeadKind)) -> bool {
if self.name != other.0 {
return false;
}
match self.content {
IndexingPositionContent::IndexerResult(_) => {
other.2 == HeadKind::Indexer && other.1 == usize::MAX
}
IndexingPositionContent::IndexerIndex(pos, _) => {
other.2 == HeadKind::Indexer && other.1 == pos
}
IndexingPositionContent::TensorIndex(pos, _) => {
other.2 == HeadKind::Tensor && other.1 == pos
}
}
}
}
pub enum AliasSource {
FromIndex { ricci_number: usize },
FromReindexing { reindexing_name: Ident, rank: usize },
}
pub struct IndexingPositionEquivalence {
pub ricci_number: usize,
pub index: Ident,
pub alias_source: Option<AliasSource>,
pub positions: Vec<IndexingPosition>,
}
impl IndexingPositionEquivalence {
pub fn new(ricci_number: usize, index: Ident) -> Self {
Self {
ricci_number,
index,
alias_source: None,
positions: Vec::new(),
}
}
pub fn new_alias_declaration(
ricci_number: usize,
index: Ident,
alias_source: AliasSource,
) -> Self {
Self {
ricci_number,
index,
alias_source: Some(alias_source),
positions: Vec::new(),
}
}
}
pub struct PositionalPlainValue {
pub plain_number: usize,
pub position: IndexingPosition,
pub expr: Expr,
}
pub struct IndexingPositionMapping {
pub equivalences: Vec<IndexingPositionEquivalence>,
pub plain_values: Vec<PositionalPlainValue>,
}
impl IndexingPositionMapping {
pub fn new() -> Self {
Self {
equivalences: Vec::new(),
plain_values: Vec::new(),
}
}
}
pub struct InspectionCollector {
free_ricci_number: IncreasingInteger,
free_plain_number: IncreasingInteger,
equivalences_per_index: HashMap<Ident, IndexingPositionEquivalence>,
mapping: IndexingPositionMapping,
}
impl InspectionCollector {
pub fn new() -> Self {
Self {
free_ricci_number: IncreasingInteger::new(),
free_plain_number: IncreasingInteger::new(),
equivalences_per_index: HashMap::new(),
mapping: IndexingPositionMapping::new(),
}
}
pub fn try_get_ricci_number(&self, index: &Ident) -> Option<usize> {
self.equivalences_per_index
.get(index)
.map(|equivalence| equivalence.ricci_number)
}
pub fn try_declare_index(&mut self, index: Ident) -> bool {
if let Entry::Vacant(entry) = self.equivalences_per_index.entry(index.clone()) {
let ricci_number = self.free_ricci_number.get_next();
let equivalence = IndexingPositionEquivalence::new(ricci_number, index);
entry.insert(equivalence);
true
} else {
false
}
}
pub fn try_declare_alias(&mut self, index: Ident, alias_source: AliasSource) -> bool {
if let Entry::Vacant(entry) = self.equivalences_per_index.entry(index.clone()) {
let ricci_number = self.free_ricci_number.get_next();
let equivalence = IndexingPositionEquivalence::new_alias_declaration(
ricci_number,
index,
alias_source,
);
entry.insert(equivalence);
true
} else {
false
}
}
pub fn try_add_position_to_existing_index(
&mut self,
index: &Ident,
position: IndexingPosition,
) -> bool {
if let Some(equivalence) = self.equivalences_per_index.get_mut(index) {
equivalence.positions.push(position);
true
} else {
false
}
}
pub fn add_plain_value(&mut self, position: IndexingPosition, expr: Expr) {
let plain_number = self.free_plain_number.get_next();
let plain_value = PositionalPlainValue {
position,
expr,
plain_number,
};
self.mapping.plain_values.push(plain_value);
}
pub fn save_existing_index(&mut self, index: &Ident) -> bool {
let equivalence = self
.equivalences_per_index
.remove(index)
.unwrap_or_else(|| panic!("Equivalence not found for index {}", index));
if !equivalence.positions.is_empty() {
self.mapping.equivalences.push(equivalence);
true
} else {
false
}
}
pub fn save_reindexing_result_equivalence(
&mut self,
position: IndexingPosition,
reindexing_name: &Ident,
rank: usize,
) {
let ricci_number = self.free_ricci_number.get_next();
let reindexing_position = IndexingPosition {
name: reindexing_name.clone(),
content: IndexingPositionContent::IndexerResult(rank),
};
let equivalence = IndexingPositionEquivalence {
ricci_number,
index: reindexing_name.clone(),
alias_source: None,
positions: vec![position, reindexing_position],
};
self.mapping.equivalences.push(equivalence);
}
pub fn into_mapping(self) -> IndexingPositionMapping {
self.mapping
}
}