use open_hypergraphs::lax::functor::*;
use open_hypergraphs::lax::var;
use open_hypergraphs::lax::*;
use std::fs::File;
use std::hash::Hash;
use std::io::Write;
use std::process::Command;
#[derive(PartialEq, Clone, Debug, Hash)]
pub struct Bit;
#[derive(PartialEq, Clone, Debug)]
pub enum Gate {
Not,
Xor,
Zero, Or,
And,
One,
Copy, }
impl var::HasVar for Gate {
fn var() -> Gate {
Gate::Copy
}
}
impl var::HasBitXor<Bit, Gate> for Gate {
fn bitxor(_: Bit, _: Bit) -> (Bit, Gate) {
(Bit, Gate::Xor)
}
}
impl var::HasBitAnd<Bit, Gate> for Gate {
fn bitand(_: Bit, _: Bit) -> (Bit, Gate) {
(Bit, Gate::And)
}
}
impl var::HasBitOr<Bit, Gate> for Gate {
fn bitor(_: Bit, _: Bit) -> (Bit, Gate) {
(Bit, Gate::Or)
}
}
impl var::HasNot<Bit, Gate> for Gate {
fn not(_: Bit) -> (Bit, Gate) {
(Bit, Gate::Not)
}
}
use std::cell::RefCell;
use std::rc::Rc;
type Term = OpenHypergraph<Bit, Gate>;
type Builder = Rc<RefCell<Term>>;
type Var = var::Var<Bit, Gate>;
fn zero(state: Builder) -> Var {
var::fn_operation(&state, &[], Bit, Gate::Zero)
}
fn full_adder(a: Var, b: Var, cin: Var) -> (Var, Var) {
let a_xor_b = a.clone() ^ b.clone();
let sum = a_xor_b.clone() ^ cin.clone();
let cout = (a & b) | (cin & a_xor_b.clone());
(sum, cout)
}
fn ripple_carry_adder(state: Builder, a: &[Var], b: &[Var]) -> (Vec<Var>, Var) {
let n = a.len();
assert_eq!(n, b.len(), "Input bit arrays must have the same length");
let mut sum = Vec::with_capacity(n);
let mut carry = zero(state);
for i in 0..n {
let (s, c) = full_adder(a[i].clone(), b[i].clone(), carry);
sum.push(s);
carry = c;
}
(sum, carry)
}
fn n_bit_adder(n: usize) -> Term {
var::build(|state| {
let xs = (0..2 * n)
.map(|_| Var::new(state.clone(), Bit))
.collect::<Vec<_>>();
let (mut zs, cout) = ripple_carry_adder(state.clone(), &xs[..n], &xs[n..]);
zs.push(cout);
(xs, zs)
})
.unwrap()
}
#[allow(dead_code)]
fn xor() -> Term {
var::build(|state| {
let xs = vec![Var::new(state.clone(), Bit); 2];
let y = xs[0].clone() ^ xs[1].clone();
(xs, vec![y])
})
.unwrap()
}
use graphviz_rust::dot_structures::Graph;
use graphviz_rust::printer::{DotPrinter, PrinterContext};
use open_hypergraphs_dot::{generate_dot_with, Options, Orientation};
pub fn render_dot(graph: &Graph) -> String {
let mut ctx = PrinterContext::default();
graph.print(&mut ctx)
}
pub fn edge_label(g: &Gate) -> String {
use Gate::*;
println!("{:?}", g);
match g {
Not => "!",
Xor => "+",
Zero => "0",
Or => "∨",
And => "∧",
One => "1",
Copy => "Δ",
}
.to_string()
}
fn render_adder(graph: &Term, file_slug: String) -> std::io::Result<()> {
let opts = Options {
orientation: Orientation::TB,
node_label: Box::new(|_n| "".to_string()),
edge_label: Box::new(edge_label),
..Default::default()
};
let dot_graph = generate_dot_with(graph, &opts);
let dot_string = render_dot(&dot_graph);
println!("Generated DOT representation:");
println!("{}", dot_string);
let output_path = format!("examples/{}.dot", file_slug);
let mut file = File::create(&output_path)?;
file.write_all(dot_string.as_bytes())?;
println!("DOT file saved to {}", &output_path);
let output_png = format!("examples/{}.png", file_slug);
match Command::new("dot")
.args(["-Tpng", &output_path, "-o", &output_png])
.status()
{
Ok(status) if status.success() => println!("PNG image rendered to {}", &output_png),
_ => println!("Note: Install GraphViz to render the DOT file as an image."),
}
Ok(())
}
fn main() -> std::io::Result<()> {
let graph = n_bit_adder(1);
render_adder(&graph, "adder".to_string())?;
let graph = var::forget::Forget.map_arrow(&graph);
render_adder(&graph, "adder_novar".to_string())
}