diff --git a/zixy/src/fermion/operator/mod.rs b/zixy/src/fermion/operator/mod.rs
index 68b4627..0e24fd5 100644
--- a/zixy/src/fermion/operator/mod.rs
+++ b/zixy/src/fermion/operator/mod.rs
@@ -5,3 +5,6 @@ pub mod cmpnt_list;
pub mod cre_or_ann;
pub mod products;
pub mod springs;
+pub mod term;
+pub mod term_set;
+pub mod terms;
diff --git a/zixy/src/fermion/operator/term.rs b/zixy/src/fermion/operator/term.rs
new file mode 100644
index 0000000..86d3a8b
--- /dev/null
+++ b/zixy/src/fermion/operator/term.rs
@@ -0,0 +1,52 @@
+//! Single-term fermion utilities.
+
+use std::fmt::Display;
+
+use crate::container::coeffs::traits::NumRepr;
+use crate::container::traits::proj::Borrow;
+use crate::container::word_iters::terms;
+use crate::container::word_iters::terms::AsViewMut;
+use crate::fermion::mode::Modes;
+use crate::fermion::operator::cmpnt_list::CmpntList;
+use crate::fermion::operator::terms::Terms;
+use crate::fermion::traits::ModesBased;
+
+/// A single fermion operator with a generically-typed coefficient.
+pub type Term = terms::Term;
+
+impl Term {
+ /// Create a single fermion term with unit coefficient on the given mode space.
+ pub fn new(modes: Modes) -> Self {
+ let mut terms = Terms::::new(modes);
+ terms.push_clear();
+ Self {
+ word_iters: terms.word_iters,
+ coeffs: terms.coeffs,
+ }
+ }
+}
+
+impl Display for Term {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.borrow())
+ }
+}
+
+impl ModesBased for Term {
+ fn modes(&self) -> &Modes {
+ self.word_iters.modes()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::container::coeffs::unity::Unity;
+ use crate::container::traits::Elements;
+
+ #[test]
+ fn test_new_has_one_term() {
+ let term = Term::::new(Modes::from_count(4));
+ assert_eq!(term.coeffs.len(), 1);
+ }
+}
diff --git a/zixy/src/fermion/operator/term_set.rs b/zixy/src/fermion/operator/term_set.rs
new file mode 100644
index 0000000..c210fbd
--- /dev/null
+++ b/zixy/src/fermion/operator/term_set.rs
@@ -0,0 +1,68 @@
+//! Stores fermion terms with the help of a `Map` to ensure each string appears at most once.
+
+use crate::container::coeffs::traits::NumRepr;
+use crate::container::map::Map;
+use crate::container::word_iters;
+use crate::fermion::mode::Modes;
+use crate::fermion::operator::cmpnt_list::CmpntList;
+use crate::fermion::operator::terms::Terms;
+use crate::fermion::traits::ModesBased;
+
+pub type TermSet = word_iters::term_set::TermSet;
+pub type View<'a, C /*: NumRepr*/> = word_iters::term_set::View<'a, CmpntList, C>;
+pub type ViewMut<'a, C /*: NumRepr*/> = word_iters::term_set::ViewMut<'a, CmpntList, C>;
+
+/// Trait for structs that immutably view a [`TermSet`].
+pub trait AsView: word_iters::term_set::AsView {}
+
+/// Trait for structs that mutably view a [`TermSet`].
+pub trait AsViewMut: word_iters::term_set::AsViewMut {}
+
+impl AsView for TermSet {}
+impl<'a, C: NumRepr> AsView for View<'a, C> {}
+impl<'a, C: NumRepr> AsView for ViewMut<'a, C> {}
+
+impl AsViewMut for TermSet {}
+impl<'a, C: NumRepr> AsViewMut for ViewMut<'a, C> {}
+
+impl TermSet {
+ /// Create a new instance.
+ pub fn new(modes: Modes) -> Self {
+ Self {
+ terms: Terms::new(modes),
+ map: Map::default(),
+ }
+ }
+}
+
+impl ModesBased for TermSet {
+ fn modes(&self) -> &Modes {
+ self.terms.word_iters.modes()
+ }
+}
+
+impl<'a, C: NumRepr> ModesBased for View<'a, C> {
+ fn modes(&self) -> &Modes {
+ self.word_iters.modes()
+ }
+}
+
+impl<'a, C: NumRepr> ModesBased for ViewMut<'a, C> {
+ fn modes(&self) -> &Modes {
+ self.word_iters.modes()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::container::coeffs::unity::Unity;
+ use crate::container::traits::Elements;
+
+ #[test]
+ fn test_empty() {
+ let set = TermSet::::new(Modes::from_count(4));
+ assert_eq!(set.len(), 0);
+ assert!(set.is_empty());
+ }
+}
diff --git a/zixy/src/fermion/operator/terms.rs b/zixy/src/fermion/operator/terms.rs
new file mode 100644
index 0000000..928de79
--- /dev/null
+++ b/zixy/src/fermion/operator/terms.rs
@@ -0,0 +1,79 @@
+//! Extends fermion `CmpntList` fermion with associated coefficients.
+
+use crate::container::coeffs::traits::NumRepr;
+use crate::container::word_iters::terms;
+use crate::fermion::mode::Modes;
+use crate::fermion::operator::cmpnt_list::CmpntList;
+use crate::fermion::traits::ModesBased;
+
+/// A Fermion `CmpntList` paired with one coefficient per componenet, plus
+// immutable and mutable views into one.
+pub type Terms = terms::Terms;
+pub type View<'a, C /*: NumRepr*/> = terms::View<'a, CmpntList, C>;
+pub type ViewMut<'a, C /*: NumRepr*/> = terms::ViewMut<'a, CmpntList, C>;
+
+//Borrowed handles to a single fermion term inside a `Terms` container.
+pub type TermRef<'a, C /*: NumRepr*/> = terms::TermRef<'a, CmpntList, C>;
+pub type TermMutRef<'a, C /*: NumRepr*/> = terms::TermMutRef<'a, CmpntList, C>;
+
+/// Trait for structs that immutably view a fermion [`Terms`].
+/// Currently unused, but may be useful for future extensions.
+pub trait AsView: terms::AsView {}
+pub trait AsViewMut: terms::AsViewMut {}
+
+impl AsView for Terms {}
+impl<'a, C: NumRepr> AsView for View<'a, C> {}
+
+impl AsViewMut for Terms {}
+impl<'a, C: NumRepr> AsViewMut for ViewMut<'a, C> {}
+
+impl Terms {
+ /// Create a new (empty) list of fermion terms on the given mode space.
+ pub fn new(modes: Modes) -> Self {
+ use crate::container::traits::EmptyFrom;
+ Self::empty_from(&CmpntList::new(modes))
+ }
+}
+
+impl ModesBased for Terms {
+ fn modes(&self) -> &Modes {
+ self.word_iters.modes()
+ }
+}
+
+impl<'a, C: NumRepr> ModesBased for View<'a, C> {
+ fn modes(&self) -> &Modes {
+ self.word_iters.modes()
+ }
+}
+
+impl<'a, C: NumRepr> ModesBased for ViewMut<'a, C> {
+ fn modes(&self) -> &Modes {
+ self.word_iters.modes()
+ }
+}
+
+impl<'a, C: NumRepr> ModesBased for TermRef<'a, C> {
+ fn modes(&self) -> &Modes {
+ self.word_iters.modes()
+ }
+}
+
+impl<'a, C: NumRepr> ModesBased for TermMutRef<'a, C> {
+ fn modes(&self) -> &Modes {
+ self.word_iters.modes()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::container::coeffs::unity::Unity;
+ use crate::container::traits::Elements;
+
+ #[test]
+ fn test_empty() {
+ let list = Terms::::new(Modes::from_count(3));
+ assert!(list.is_empty());
+ }
+}