Skip to content
This repository has been archived by the owner on Apr 9, 2024. It is now read-only.

chore!: refactor ToRadix to ToRadixLe and ToRadixBe #58

Merged
merged 20 commits into from
Feb 16, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions acir/src/circuit/directives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,12 @@ pub enum Directive {
bit_size: u32,
},

//decomposition of a: a=\sum b[i]*radix^i where b is an array of witnesses < radix
//decomposition of a: a=\sum b[i]*radix^i where b is an array of witnesses < radix in either little endian or big endian form
ToRadix {
a: Expression,
b: Vec<Witness>,
radix: u32,
is_little_endian: bool,
},
}

Expand Down Expand Up @@ -108,13 +109,19 @@ impl Directive {
write_u32(&mut writer, r.witness_index())?;
write_u32(&mut writer, *bit_size)?;
}
Directive::ToRadix { a, b, radix } => {
Directive::ToRadix {
a,
b,
radix,
is_little_endian,
} => {
a.write(&mut writer)?;
write_u32(&mut writer, b.len() as u32)?;
for bit in b {
write_u32(&mut writer, bit.witness_index())?;
}
write_u32(&mut writer, *radix)?;
write_u32(&mut writer, *is_little_endian as u32)?;
}
};

Expand Down Expand Up @@ -175,8 +182,14 @@ impl Directive {
}

let radix = read_u32(&mut reader)?;
let is_little_endian = read_u32(&mut reader)?;

Ok(Directive::ToRadix { a, b, radix })
Ok(Directive::ToRadix {
a,
b,
radix,
is_little_endian: is_little_endian == 1,
})
}

_ => Err(std::io::ErrorKind::InvalidData.into()),
Expand Down Expand Up @@ -228,10 +241,18 @@ fn serialisation_roundtrip() {
bit_size: 32,
};

let to_radix = Directive::ToRadix {
let to_radix_le = Directive::ToRadix {
a: Expression::default(),
b: vec![Witness(1u32), Witness(2u32), Witness(3u32), Witness(4u32)],
radix: 4,
is_little_endian: true,
};

let to_radix_be = Directive::ToRadix {
a: Expression::default(),
b: vec![Witness(1u32), Witness(2u32), Witness(3u32), Witness(4u32)],
radix: 4,
is_little_endian: false,
};

let directives = vec![
Expand All @@ -240,7 +261,8 @@ fn serialisation_roundtrip() {
quotient_predicate,
truncate,
odd_range,
to_radix,
to_radix_le,
to_radix_be,
];

for directive in directives {
Expand Down
10 changes: 8 additions & 2 deletions acir/src/circuit/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,16 +152,22 @@ impl std::fmt::Display for Opcode {
)
}
Opcode::BlackBoxFuncCall(g) => write!(f, "{g}"),
Opcode::Directive(Directive::ToRadix { a, b, radix: _ }) => {
Opcode::Directive(Directive::ToRadix {
a,
b,
radix: _,
is_little_endian,
}) => {
write!(f, "DIR::TORADIX ")?;
write!(
f,
// TODO (Note): this assumes that the decomposed bits have contiguous witness indices
// This should be the case, however, we can also have a function which checks this
"(_{}, [_{}..._{}])",
"(_{}, [_{}..._{}], endianness: {})",
a,
b.first().unwrap().witness_index(),
b.last().unwrap().witness_index(),
if *is_little_endian { "little" } else { "big" }
)
}
}
Expand Down
23 changes: 23 additions & 0 deletions acvm/src/pwg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,26 @@ pub fn get_value(

Ok(result)
}

// Inserts `value` into the initial witness map
// under the key of `witness`.
// Returns an error, if there was already a value in the map
// which does not match the value that one is about to insert
fn insert_value(
witness: &Witness,
value_to_insert: FieldElement,
initial_witness: &mut BTreeMap<Witness, FieldElement>,
) -> Result<(), OpcodeResolutionError> {
let optional_old_value = initial_witness.insert(*witness, value_to_insert);

let old_value = match optional_old_value {
Some(old_value) => old_value,
None => return Ok(()),
};

if old_value != value_to_insert {
return Err(OpcodeResolutionError::UnsatisfiedConstrain);
}

Ok(())
}
88 changes: 62 additions & 26 deletions acvm/src/pwg/directives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use num_traits::{One, Zero};

use crate::OpcodeResolutionError;

use super::{get_value, witness_to_value};
use super::{get_value, insert_value, witness_to_value};

pub fn solve_directives(
initial_witness: &mut BTreeMap<Witness, FieldElement>,
Expand Down Expand Up @@ -64,33 +64,24 @@ pub fn solve_directives(

Ok(())
}
Directive::ToRadix { a, b, radix } => {
let val_a = get_value(a, initial_witness)?;
Directive::ToRadix {
a,
b,
radix,
is_little_endian,
} => {
let value_a = get_value(a, initial_witness)?;

let a_big = BigUint::from_bytes_be(&val_a.to_be_bytes());
let a_dec = a_big.to_radix_le(*radix);
if b.len() < a_dec.len() {
return Err(OpcodeResolutionError::UnsatisfiedConstrain);
}
for i in 0..b.len() {
let v = if i < a_dec.len() {
FieldElement::from_be_bytes_reduce(&[a_dec[i]])
} else {
FieldElement::zero()
};
match initial_witness.entry(b[i]) {
std::collections::btree_map::Entry::Vacant(e) => {
e.insert(v);
}
std::collections::btree_map::Entry::Occupied(e) => {
if e.get() != &v {
return Err(OpcodeResolutionError::UnsatisfiedConstrain);
}
}
}
}
let big_integer = BigUint::from_bytes_be(&value_a.to_be_bytes());

Ok(())
// Decompose the integer into its radix digits
let decomposed_integer = if *is_little_endian {
big_integer.to_radix_le(*radix)
} else {
big_integer.to_radix_be(*radix)
};
kevaundray marked this conversation as resolved.
Show resolved Hide resolved

to_radix_outcome(b, decomposed_integer, initial_witness, is_little_endian)
}
Directive::OddRange { a, b, r, bit_size } => {
let val_a = witness_to_value(initial_witness, *a)?;
Expand All @@ -112,3 +103,48 @@ pub fn solve_directives(
}
}
}

fn to_radix_outcome(
witnesses: &Vec<Witness>,
decomposed_integer: Vec<u8>,
initial_witness: &mut BTreeMap<Witness, FieldElement>,
is_little_endian: &bool,
) -> Result<(), OpcodeResolutionError> {
if witnesses.len() < decomposed_integer.len() {
return Err(OpcodeResolutionError::UnsatisfiedConstrain);
}

if *is_little_endian {
for (i, witness) in witnesses.into_iter().enumerate() {
// Fetch the `i'th` digit from the decomposed integer list
// and convert it to a field element.
// If it is not available, which can happen when the decomposed integer
// list is shorter than the witness list, we return 0.
let value = match decomposed_integer.get(i) {
Some(digit) => FieldElement::from_be_bytes_reduce(&[*digit]),
None => FieldElement::zero(),
};

insert_value(witness, value, initial_witness)?
}
} else {
// if it is big endian and the decompoased integer list is shorter
// than the witness list, pad the extra part with 0 first then
// add the decompsed interger list to the witness list.
let padding_len = witnesses.len() - decomposed_integer.len();
for (i, witness) in witnesses.into_iter().enumerate() {
if i < padding_len {
kevaundray marked this conversation as resolved.
Show resolved Hide resolved
insert_value(witness, FieldElement::zero(), initial_witness)?
} else {
let value = match decomposed_integer.get(i - padding_len) {
Some(digit) => FieldElement::from_be_bytes_reduce(&[*digit]),
None => FieldElement::zero(),
};

insert_value(witness, value, initial_witness)?
}
}
}

Ok(())
}
1 change: 1 addition & 0 deletions stdlib/src/fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub(crate) fn bit_decomposition(
a: gate.clone(),
b: bit_vector.clone(),
radix: 2,
is_little_endian: true,
}));

// Now apply constraints to the bits such that they are the bit decomposition
Expand Down