use hugr::{
Direction, IncomingPort, OutgoingPort, Port, PortIndex, Wire, core::HugrNode, types::Signature,
};
use itertools::Itertools;
use num_rational::Rational64;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ResourceId(usize);
impl ResourceId {
pub(crate) fn new(id: usize) -> Self {
Self(id)
}
pub fn as_usize(self) -> usize {
self.0
}
}
#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Position(Rational64);
impl std::fmt::Debug for Position {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Position({})", self.0)
}
}
impl Position {
#[expect(unused)]
pub(super) fn new_integer(i: i64) -> Self {
Self(Rational64::from_integer(i))
}
pub fn to_f64(&self, precision: usize) -> f64 {
let big = self.0 * Rational64::from_integer(10).pow(precision as i32);
big.round().to_integer() as f64 / 10f64.powi(precision as i32)
}
pub fn increment(&self) -> Self {
Self(self.0 + 1)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CircuitUnit<N: HugrNode> {
Resource(ResourceId),
Copyable(Wire<N>),
}
impl<N: HugrNode> CircuitUnit<N> {
pub fn is_resource(&self) -> bool {
matches!(self, CircuitUnit::Resource(..))
}
pub fn is_copyable(&self) -> bool {
matches!(self, CircuitUnit::Copyable(..))
}
pub fn as_resource(&self) -> Option<ResourceId> {
match self {
CircuitUnit::Resource(id) => Some(*id),
CircuitUnit::Copyable(..) => None,
}
}
pub fn as_copyable_wire(&self) -> Option<Wire<N>> {
match self {
CircuitUnit::Resource(..) => None,
CircuitUnit::Copyable(wire) => Some(*wire),
}
}
pub(super) fn sentinel() -> Self {
CircuitUnit::Resource(ResourceId::new(usize::MAX))
}
pub(super) fn is_sentinel(&self) -> bool {
self == &Self::sentinel()
}
}
#[derive(Debug, Clone)]
pub(super) struct PortMap<T> {
vec: Vec<T>,
num_inputs: usize,
}
impl<T> PortMap<T> {
pub(super) fn with_default(default: T, signature: &Signature) -> Self
where
T: Clone,
{
let num_inputs = signature.input_count();
let num_outputs = signature.output_count();
debug_assert!(
signature.input_ports().all(|p| p.index() < num_inputs),
"dataflow in ports are not in range 0..num_inputs"
);
debug_assert!(
signature.output_ports().all(|p| p.index() < num_outputs),
"dataflow out ports are not in range 0..num_outputs"
);
Self {
vec: vec![default; num_inputs + num_outputs],
num_inputs,
}
}
fn index(&self, port: impl Into<Port>) -> usize {
let port = port.into();
match port.direction() {
Direction::Incoming => port.index(),
Direction::Outgoing => self.num_inputs + port.index(),
}
}
pub(super) fn get(&self, port: impl Into<Port>) -> &T {
let port = port.into();
let index = self.index(port);
&self.vec[index]
}
pub(super) fn get_slice(&self, dir: Direction) -> &[T] {
match dir {
Direction::Incoming => &self.vec[..self.num_inputs],
Direction::Outgoing => &self.vec[self.num_inputs..],
}
}
pub(super) fn set(&mut self, port: impl Into<Port>, value: impl Into<T>) {
let port = port.into();
let index = self.index(port);
self.vec[index] = value.into();
}
#[allow(unused, clippy::allow_attributes, reason = "used only in tests")]
pub(super) fn iter(&self) -> impl Iterator<Item = (Port, &T)> {
let (inp_slice, out_slice) = self.vec.split_at(self.num_inputs);
let inp_ports = (0..).map(IncomingPort::from).map_into();
let out_ports = (0..).map(OutgoingPort::from).map_into();
let inp = inp_ports.zip(inp_slice);
let out = out_ports.zip(out_slice);
inp.chain(out)
}
#[expect(unused)]
pub(super) fn values(&self) -> impl Iterator<Item = &T> {
self.vec.iter()
}
}
#[derive(Debug, Clone, Default)]
pub struct ResourceAllocator {
next_id: usize,
}
impl ResourceAllocator {
pub fn new() -> Self {
Self::default()
}
pub fn allocate(&mut self) -> ResourceId {
let id = ResourceId::new(self.next_id);
self.next_id += 1;
id
}
}