1use ark_ff::PrimeField;
4use kimchi::{
5 circuits::{
6 argument::Argument,
7 expr,
8 polynomials::{
9 complete_add::CompleteAdd, endomul_scalar::EndomulScalar, endosclmul::EndosclMul,
10 poseidon::Poseidon, varbasemul::VarbaseMul,
11 },
12 },
13 curve::KimchiCurve,
14 prover_index::ProverIndex,
15};
16use poly_commitment::{commitment::CommitmentCurve, ipa::OpeningProof};
17use serde::Serialize;
18use std::{
19 collections::HashMap,
20 fmt::Display,
21 fs::{self, File},
22 io::Write,
23 path::Path,
24};
25use tinytemplate::TinyTemplate;
26
27pub mod witness;
28
29pub use witness::Witness;
30
31#[derive(Serialize)]
33struct Context {
34 js: String,
35 data: String,
36}
37
38trait LaTeX<F>: Argument<F>
40where
41 F: PrimeField,
42{
43 fn latex() -> Vec<Vec<String>> {
44 Self::constraints(&mut expr::Cache::default())
45 .iter()
46 .map(|c| c.latex_str())
47 .collect()
48 }
49}
50
51impl<T, F> LaTeX<F> for T
53where
54 T: Argument<F>,
55 F: PrimeField + Display,
56{
57}
58
59pub fn latex_constraints<G>() -> HashMap<&'static str, Vec<Vec<String>>>
60where
61 G: CommitmentCurve,
62{
63 let mut map = HashMap::new();
64 map.insert("Poseidon", Poseidon::<G::ScalarField>::latex());
65 map.insert("CompleteAdd", CompleteAdd::<G::ScalarField>::latex());
66 map.insert("VarBaseMul", VarbaseMul::<G::ScalarField>::latex());
67 map.insert("EndoMul", EndosclMul::<G::ScalarField>::latex());
68 map.insert("EndoMulScalar", EndomulScalar::<G::ScalarField>::latex());
69 map
70}
71
72pub fn visu<G: KimchiCurve>(
78 index: &ProverIndex<G, OpeningProof<G>>,
79 witness: Option<Witness<G::ScalarField>>,
80) where
81 G::BaseField: PrimeField,
82{
83 let index = serde_json::to_string(index).expect("couldn't serialize index");
85 let mut data = format!("const index = {index};");
86
87 if let Some(witness) = witness {
89 let witness = serde_json::to_string(&witness).expect("couldn't serialize witness");
90 data = format!("{data}const witness = {witness};");
91 } else {
92 data.push_str("const witness = null;");
93 }
94
95 let constraints = latex_constraints::<G>();
97 let constraints = serde_json::to_string(&constraints).expect("couldn't serialize constraints");
98 data = format!("{data}const constraints = {constraints};");
99
100 let template_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("src/assets/template.html");
102 let template = fs::read_to_string(&template_path).unwrap_or_else(|e| {
103 format!(
104 "could not read template file {}: {e}",
105 template_path.display()
106 )
107 });
108
109 let mut tt = TinyTemplate::new();
110 tt.set_default_formatter(&tinytemplate::format_unescaped);
111 tt.add_template("circuit", &template)
112 .expect("could not create template");
113
114 let html_output = std::env::current_dir()
116 .expect("no current directory?")
117 .join("circuit.html");
118
119 let js_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("src/assets/script.js");
120 let js = fs::read_to_string(&js_path)
121 .unwrap_or_else(|e| format!("could not read js file {}: {e}", js_path.display()));
122
123 let context = Context { js, data };
124
125 let rendered = tt
126 .render("circuit", &context)
127 .unwrap_or_else(|e| panic!("template file can't be rendered: {e}"));
128
129 let mut file = File::create(html_output).unwrap_or_else(|e| panic!("{e}"));
130 write!(&mut file, "{rendered}").expect("couldn't write the file on disk");
131}