halo2_proofs/dev/failure/
emitter.rs
1use std::collections::BTreeMap;
2use std::iter;
3
4use group::ff::Field;
5
6use super::FailureLocation;
7use crate::{
8 dev::{metadata, util},
9 plonk::{Any, Expression},
10};
11
12fn padded(p: char, width: usize, text: &str) -> String {
13 let pad = width - text.len();
14 format!(
15 "{}{}{}",
16 iter::repeat(p).take(pad - pad / 2).collect::<String>(),
17 text,
18 iter::repeat(p).take(pad / 2).collect::<String>(),
19 )
20}
21
22pub(super) fn render_cell_layout(
28 prefix: &str,
29 location: &FailureLocation,
30 columns: &BTreeMap<metadata::Column, usize>,
31 layout: &BTreeMap<i32, BTreeMap<metadata::Column, String>>,
32 highlight_row: impl Fn(Option<i32>, i32),
33) {
34 let col_width = |cells: usize| cells.to_string().len() + 3;
35
36 let offset = match location {
39 FailureLocation::InRegion { region, offset } => {
40 eprintln!("{}Cell layout in region '{}':", prefix, region.name);
41 eprint!("{} | Offset |", prefix);
42 Some(*offset as i32)
43 }
44 FailureLocation::OutsideRegion { row } => {
45 eprintln!("{}Cell layout at row {}:", prefix, row);
46 eprint!("{} |Rotation|", prefix);
47 None
48 }
49 };
50
51 for (column, cells) in columns {
53 let width = col_width(*cells);
54 eprint!(
55 "{}|",
56 padded(
57 ' ',
58 width,
59 &format!(
60 "{}{}",
61 match column.column_type {
62 Any::Advice => "A",
63 Any::Fixed => "F",
64 Any::Instance => "I",
65 },
66 column.index,
67 )
68 )
69 );
70 }
71 eprintln!();
72 eprint!("{} +--------+", prefix);
73 for cells in columns.values() {
74 eprint!("{}+", padded('-', col_width(*cells), ""));
75 }
76 eprintln!();
77 for (rotation, row) in layout {
78 eprint!(
79 "{} |{}|",
80 prefix,
81 padded(' ', 8, &(offset.unwrap_or(0) + rotation).to_string())
82 );
83 for (col, cells) in columns {
84 let width = col_width(*cells);
85 eprint!(
86 "{}|",
87 padded(
88 ' ',
89 width,
90 row.get(col).map(|s| s.as_str()).unwrap_or_default()
91 )
92 );
93 }
94 highlight_row(offset, *rotation);
95 eprintln!();
96 }
97}
98
99pub(super) fn expression_to_string<F: Field>(
100 expr: &Expression<F>,
101 layout: &BTreeMap<i32, BTreeMap<metadata::Column, String>>,
102) -> String {
103 expr.evaluate(
104 &util::format_value,
105 &|_| panic!("virtual selectors are removed during optimization"),
106 &|query, column, rotation| {
107 if let Some(label) = layout
108 .get(&rotation.0)
109 .and_then(|row| row.get(&(Any::Fixed, column).into()))
110 {
111 label.clone()
112 } else if rotation.0 == 0 {
113 format!("S{}", query)
115 } else {
116 format!("F{}@{}", column, rotation.0)
118 }
119 },
120 &|_, column, rotation| {
121 layout
122 .get(&rotation.0)
123 .unwrap()
124 .get(&(Any::Advice, column).into())
125 .unwrap()
126 .clone()
127 },
128 &|_, column, rotation| {
129 layout
130 .get(&rotation.0)
131 .unwrap()
132 .get(&(Any::Instance, column).into())
133 .unwrap()
134 .clone()
135 },
136 &|a| {
137 if a.contains(' ') {
138 format!("-({})", a)
139 } else {
140 format!("-{}", a)
141 }
142 },
143 &|a, b| {
144 if let Some(b) = b.strip_prefix('-') {
145 format!("{} - {}", a, b)
146 } else {
147 format!("{} + {}", a, b)
148 }
149 },
150 &|a, b| match (a.contains(' '), b.contains(' ')) {
151 (false, false) => format!("{} * {}", a, b),
152 (false, true) => format!("{} * ({})", a, b),
153 (true, false) => format!("({}) * {}", a, b),
154 (true, true) => format!("({}) * ({})", a, b),
155 },
156 &|a, s| {
157 if a.contains(' ') {
158 format!("({}) * {}", a, util::format_value(s))
159 } else {
160 format!("{} * {}", a, util::format_value(s))
161 }
162 },
163 )
164}