use super::abelian::{quotient_invariants, AbelianInvariants};
use super::cohomology::GModule;
use super::error::FpGroupError;
use super::reidemeister::SubgroupPresentation;
use super::todd_coxeter::{self, default_max_cosets, CosetTable};
use super::word::{FreeGroup, Word};
use crate::group::PermutationGroup;
use rug::Integer;
use std::fmt;
pub const MAX_MULTIPLICATION_TABLE_ORDER: usize = 1024;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FpGroup {
free: FreeGroup,
relators: Vec<Word>,
}
impl FpGroup {
pub fn new(free: FreeGroup, relators: Vec<Word>) -> Result<FpGroup, FpGroupError> {
let rank = free.rank();
for r in &relators {
if let Some(g) = r.max_generator() {
if g >= rank {
return Err(FpGroupError::InvalidGenerator {
letter: g as i32 + 1,
rank,
});
}
}
}
Ok(FpGroup { free, relators })
}
pub fn free(rank: usize) -> Result<FpGroup, FpGroupError> {
Ok(FpGroup {
free: FreeGroup::new(rank)?,
relators: Vec::new(),
})
}
pub fn from_strings<S: AsRef<str>, T: AsRef<str>>(
generators: &[S],
relators: &[T],
) -> Result<FpGroup, FpGroupError> {
let free = FreeGroup::with_names(generators)?;
let mut rels = Vec::with_capacity(relators.len());
for r in relators {
rels.push(free.parse(r.as_ref())?);
}
FpGroup::new(free, rels)
}
pub fn free_group(&self) -> &FreeGroup {
&self.free
}
pub fn rank(&self) -> usize {
self.free.rank()
}
pub fn generator_names(&self) -> &[String] {
self.free.names()
}
pub fn relators(&self) -> &[Word] {
&self.relators
}
pub fn parse_word(&self, text: &str) -> Result<Word, FpGroupError> {
self.free.parse(text)
}
pub fn word(&self, letters: &[i32]) -> Result<Word, FpGroupError> {
self.free.word(letters)
}
pub fn parse_words<S: AsRef<str>>(&self, texts: &[S]) -> Result<Vec<Word>, FpGroupError> {
texts.iter().map(|t| self.free.parse(t.as_ref())).collect()
}
pub fn coset_table(&self, subgroup_generators: &[Word]) -> Result<CosetTable, FpGroupError> {
self.coset_table_with_cap(subgroup_generators, default_max_cosets(self.rank()))
}
pub fn coset_table_with_cap(
&self,
subgroup_generators: &[Word],
max_cosets: usize,
) -> Result<CosetTable, FpGroupError> {
todd_coxeter::enumerate(self.rank(), &self.relators, subgroup_generators, max_cosets)
}
pub fn index(&self, subgroup_generators: &[Word]) -> Result<usize, FpGroupError> {
Ok(self.coset_table(subgroup_generators)?.index())
}
pub fn index_with_cap(
&self,
subgroup_generators: &[Word],
max_cosets: usize,
) -> Result<usize, FpGroupError> {
Ok(self
.coset_table_with_cap(subgroup_generators, max_cosets)?
.index())
}
pub fn order(&self) -> Result<Integer, FpGroupError> {
self.order_with_cap(default_max_cosets(self.rank()))
}
pub fn order_with_cap(&self, max_cosets: usize) -> Result<Integer, FpGroupError> {
let abelian = self.abelian_invariants()?;
if abelian.free_rank() > 0 {
return Err(FpGroupError::ProvablyInfinite {
abelian_invariants: abelian.to_string(),
});
}
let table = self.coset_table_with_cap(&[], max_cosets)?;
Ok(Integer::from(table.index()))
}
pub fn permutation_group(
&self,
subgroup_generators: &[Word],
) -> Result<PermutationGroup, FpGroupError> {
self.coset_table(subgroup_generators)?.permutation_group()
}
pub fn regular_representation(&self) -> Result<PermutationGroup, FpGroupError> {
self.permutation_group(&[])
}
pub fn multiplication_table(&self) -> Result<Vec<Vec<usize>>, FpGroupError> {
let table = self.coset_table(&[])?;
let n = table.index();
if n > MAX_MULTIPLICATION_TABLE_ORDER {
return Err(FpGroupError::MultiplicationTableTooLarge {
order: n,
max: MAX_MULTIPLICATION_TABLE_ORDER,
});
}
let transversal = table.transversal()?;
let mut mult = vec![vec![0usize; n]; n];
for (a, row) in mult.iter_mut().enumerate() {
for (b, cell) in row.iter_mut().enumerate() {
*cell = table.trace(a, &transversal[b])?;
}
}
Ok(mult)
}
pub fn subgroup_presentation(
&self,
subgroup_generators: &[Word],
) -> Result<SubgroupPresentation, FpGroupError> {
self.subgroup_presentation_with_cap(subgroup_generators, default_max_cosets(self.rank()))
}
pub fn subgroup_presentation_with_cap(
&self,
subgroup_generators: &[Word],
max_cosets: usize,
) -> Result<SubgroupPresentation, FpGroupError> {
super::reidemeister::reidemeister_schreier(self, subgroup_generators, max_cosets)
}
pub fn relation_matrix(&self) -> Result<Vec<Vec<i64>>, FpGroupError> {
self.relators
.iter()
.map(|r| r.exponent_sums(self.rank()))
.collect()
}
pub fn abelian_invariants(&self) -> Result<AbelianInvariants, FpGroupError> {
let rank = self.rank();
if rank == 0 {
return Ok(AbelianInvariants::trivial());
}
let rows = self.relation_matrix()?;
let gens: Vec<Vec<Integer>> = (0..rank)
.map(|j| {
(0..rank)
.map(|i| Integer::from(usize::from(i == j)))
.collect()
})
.collect();
let relations: Vec<Vec<Integer>> = rows
.iter()
.map(|r| r.iter().map(|&e| Integer::from(e)).collect())
.collect();
quotient_invariants(rank, &gens, &relations)
}
pub fn cohomology(
&self,
degree: usize,
module: &GModule,
) -> Result<AbelianInvariants, FpGroupError> {
super::cohomology::cohomology(self, degree, module)
}
}
impl fmt::Display for FpGroup {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let gens = self.free.names().join(", ");
if self.relators.is_empty() {
return write!(f, "⟨{gens} | ⟩");
}
let rels: Vec<String> = self.relators.iter().map(|r| self.free.format(r)).collect();
write!(f, "⟨{gens} | {}⟩", rels.join(", "))
}
}