Merge pull request #72 from ebfull/little-endian
Switch to little endian
This commit is contained in:
commit
eb409fa3cf
|
@ -9,7 +9,7 @@ repository = "https://github.com/zcash-hackworks/sapling"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
|
|
||||||
[dependencies.pairing]
|
[dependencies.pairing]
|
||||||
version = "0.14"
|
version = "0.14.2"
|
||||||
features = ["expose-arith"]
|
features = ["expose-arith"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
|
@ -343,7 +343,7 @@ mod test {
|
||||||
|
|
||||||
let mut out = out.into_iter();
|
let mut out = out.into_iter();
|
||||||
for b in expected.into_iter() {
|
for b in expected.into_iter() {
|
||||||
for i in (0..8).rev() {
|
for i in 0..8 {
|
||||||
let c = out.next().unwrap().get_value().unwrap();
|
let c = out.next().unwrap().get_value().unwrap();
|
||||||
|
|
||||||
assert_eq!(c, (b >> i) & 1u8 == 1u8);
|
assert_eq!(c, (b >> i) & 1u8 == 1u8);
|
||||||
|
@ -405,7 +405,7 @@ mod test {
|
||||||
let mut input_bits = vec![];
|
let mut input_bits = vec![];
|
||||||
|
|
||||||
for (byte_i, input_byte) in data.into_iter().enumerate() {
|
for (byte_i, input_byte) in data.into_iter().enumerate() {
|
||||||
for bit_i in (0..8).rev() {
|
for bit_i in 0..8 {
|
||||||
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
|
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
|
||||||
|
|
||||||
input_bits.push(AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)).unwrap().into());
|
input_bits.push(AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)).unwrap().into());
|
||||||
|
@ -417,7 +417,7 @@ mod test {
|
||||||
assert!(cs.is_satisfied());
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
let mut s = hash_result.as_ref().iter()
|
let mut s = hash_result.as_ref().iter()
|
||||||
.flat_map(|&byte| (0..8).rev().map(move |i| (byte >> i) & 1u8 == 1u8));
|
.flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8));
|
||||||
|
|
||||||
for b in r {
|
for b in r {
|
||||||
match b {
|
match b {
|
||||||
|
|
|
@ -45,6 +45,13 @@ pub fn bytes_to_bits(bytes: &[u8]) -> Vec<bool>
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn bytes_to_bits_le(bytes: &[u8]) -> Vec<bool>
|
||||||
|
{
|
||||||
|
bytes.iter()
|
||||||
|
.flat_map(|&v| (0..8).map(move |i| (v >> i) & 1 == 1))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn compute_multipacking<E: Engine>(
|
pub fn compute_multipacking<E: Engine>(
|
||||||
bits: &[bool]
|
bits: &[bool]
|
||||||
) -> Vec<E::Fr>
|
) -> Vec<E::Fr>
|
||||||
|
|
|
@ -226,11 +226,6 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
|
||||||
constants::CRH_IVK_PERSONALIZATION
|
constants::CRH_IVK_PERSONALIZATION
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Swap bit-endianness in each byte
|
|
||||||
for ivk_byte in ivk.chunks_mut(8) {
|
|
||||||
ivk_byte.reverse();
|
|
||||||
}
|
|
||||||
|
|
||||||
// drop_5 to ensure it's in the field
|
// drop_5 to ensure it's in the field
|
||||||
ivk.truncate(E::Fs::CAPACITY as usize);
|
ivk.truncate(E::Fs::CAPACITY as usize);
|
||||||
|
|
||||||
|
@ -697,7 +692,7 @@ fn test_input_circuit_with_bls12_381() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected_nf = note.nf(&viewing_key, position, params);
|
let expected_nf = note.nf(&viewing_key, position, params);
|
||||||
let expected_nf = multipack::bytes_to_bits(&expected_nf);
|
let expected_nf = multipack::bytes_to_bits_le(&expected_nf);
|
||||||
let expected_nf = multipack::compute_multipacking::<Bls12>(&expected_nf);
|
let expected_nf = multipack::compute_multipacking::<Bls12>(&expected_nf);
|
||||||
assert_eq!(expected_nf.len(), 2);
|
assert_eq!(expected_nf.len(), 2);
|
||||||
|
|
||||||
|
@ -718,7 +713,7 @@ fn test_input_circuit_with_bls12_381() {
|
||||||
|
|
||||||
assert!(cs.is_satisfied());
|
assert!(cs.is_satisfied());
|
||||||
assert_eq!(cs.num_constraints(), 98777);
|
assert_eq!(cs.num_constraints(), 98777);
|
||||||
assert_eq!(cs.hash(), "499305e409599a3e4fe0a885f6adf674e9f49ba4a21e47362356d2a89f15dc1f");
|
assert_eq!(cs.hash(), "d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89");
|
||||||
|
|
||||||
assert_eq!(cs.get("randomization of note commitment/x3/num"), cm);
|
assert_eq!(cs.get("randomization of note commitment/x3/num"), cm);
|
||||||
|
|
||||||
|
@ -795,7 +790,7 @@ fn test_output_circuit_with_bls12_381() {
|
||||||
|
|
||||||
assert!(cs.is_satisfied());
|
assert!(cs.is_satisfied());
|
||||||
assert_eq!(cs.num_constraints(), 7827);
|
assert_eq!(cs.num_constraints(), 7827);
|
||||||
assert_eq!(cs.hash(), "d18e83255220328a688134038ba4f82d5ce67ffe9f97b2ae2678042da0efad43");
|
assert_eq!(cs.hash(), "c26d5cdfe6ccd65c03390902c02e11393ea6bb96aae32a7f2ecb12eb9103faee");
|
||||||
|
|
||||||
let expected_cm = payment_address.create_note(
|
let expected_cm = payment_address.create_note(
|
||||||
value_commitment.value,
|
value_commitment.value,
|
||||||
|
|
|
@ -114,10 +114,7 @@ impl UInt32 {
|
||||||
|
|
||||||
/// Turns this `UInt32` into its little-endian byte order representation.
|
/// Turns this `UInt32` into its little-endian byte order representation.
|
||||||
pub fn into_bits(&self) -> Vec<Boolean> {
|
pub fn into_bits(&self) -> Vec<Boolean> {
|
||||||
self.bits.chunks(8)
|
self.bits.clone()
|
||||||
.flat_map(|v| v.iter().rev())
|
|
||||||
.cloned()
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a little-endian byte order representation of bits into a
|
/// Converts a little-endian byte order representation of bits into a
|
||||||
|
@ -126,10 +123,7 @@ impl UInt32 {
|
||||||
{
|
{
|
||||||
assert_eq!(bits.len(), 32);
|
assert_eq!(bits.len(), 32);
|
||||||
|
|
||||||
let new_bits = bits.chunks(8)
|
let new_bits = bits.to_vec();
|
||||||
.flat_map(|v| v.iter().rev())
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let mut value = Some(0u32);
|
let mut value = Some(0u32);
|
||||||
for b in new_bits.iter().rev() {
|
for b in new_bits.iter().rev() {
|
||||||
|
|
|
@ -5,8 +5,7 @@ use jubjub::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use pairing::{
|
use pairing::{
|
||||||
PrimeField,
|
PrimeField
|
||||||
PrimeFieldRepr
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use blake2_rfc::blake2s::Blake2s;
|
use blake2_rfc::blake2s::Blake2s;
|
||||||
|
@ -29,20 +28,11 @@ pub fn group_hash<E: JubjubEngine>(
|
||||||
let mut h = Blake2s::with_params(32, &[], &[], personalization);
|
let mut h = Blake2s::with_params(32, &[], &[], personalization);
|
||||||
h.update(constants::GH_FIRST_BLOCK);
|
h.update(constants::GH_FIRST_BLOCK);
|
||||||
h.update(tag);
|
h.update(tag);
|
||||||
let mut h = h.finalize().as_ref().to_vec();
|
let h = h.finalize().as_ref().to_vec();
|
||||||
assert!(h.len() == 32);
|
assert!(h.len() == 32);
|
||||||
|
|
||||||
// Take first/unset first bit of hash
|
match edwards::Point::<E, _>::read(&h[..], params) {
|
||||||
let s = h[0] >> 7 == 1; // get s
|
Ok(p) => {
|
||||||
h[0] &= 0b0111_1111; // unset s from h
|
|
||||||
|
|
||||||
// cast to prime field representation
|
|
||||||
let mut y0 = <E::Fr as PrimeField>::Repr::default();
|
|
||||||
y0.read_be(&h[..]).expect("hash is sufficiently large");
|
|
||||||
|
|
||||||
if let Ok(y0) = E::Fr::from_repr(y0) {
|
|
||||||
if let Some(p) = edwards::Point::<E, _>::get_for_y(y0, s, params) {
|
|
||||||
// Enter into the prime order subgroup
|
|
||||||
let p = p.mul_by_cofactor(params);
|
let p = p.mul_by_cofactor(params);
|
||||||
|
|
||||||
if p != edwards::Point::zero() {
|
if p != edwards::Point::zero() {
|
||||||
|
@ -50,10 +40,7 @@ pub fn group_hash<E: JubjubEngine>(
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
} else {
|
},
|
||||||
None
|
Err(_) => None
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,6 @@ use std::io::{
|
||||||
Read
|
Read
|
||||||
};
|
};
|
||||||
|
|
||||||
use util::swap_bits_u64;
|
|
||||||
|
|
||||||
// Represents the affine point (X/Z, Y/Z) via the extended
|
// Represents the affine point (X/Z, Y/Z) via the extended
|
||||||
// twisted Edwards coordinates.
|
// twisted Edwards coordinates.
|
||||||
//
|
//
|
||||||
|
@ -97,21 +95,8 @@ impl<E: JubjubEngine> Point<E, Unknown> {
|
||||||
params: &E::Params
|
params: &E::Params
|
||||||
) -> io::Result<Self>
|
) -> io::Result<Self>
|
||||||
{
|
{
|
||||||
// Jubjub points are encoded least significant bit first.
|
|
||||||
// The most significant bit (bit 254) encodes the parity
|
|
||||||
// of the x-coordinate.
|
|
||||||
|
|
||||||
let mut y_repr = <E::Fr as PrimeField>::Repr::default();
|
let mut y_repr = <E::Fr as PrimeField>::Repr::default();
|
||||||
|
y_repr.read_le(reader)?;
|
||||||
// This reads in big-endian, so we perform a swap of the
|
|
||||||
// limbs in the representation and swap the bit order.
|
|
||||||
y_repr.read_be(reader)?;
|
|
||||||
|
|
||||||
y_repr.as_mut().reverse();
|
|
||||||
|
|
||||||
for b in y_repr.as_mut() {
|
|
||||||
*b = swap_bits_u64(*b);
|
|
||||||
}
|
|
||||||
|
|
||||||
let x_sign = (y_repr.as_ref()[3] >> 63) == 1;
|
let x_sign = (y_repr.as_ref()[3] >> 63) == 1;
|
||||||
y_repr.as_mut()[3] &= 0x7fffffffffffffff;
|
y_repr.as_mut()[3] &= 0x7fffffffffffffff;
|
||||||
|
@ -216,13 +201,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
|
||||||
y_repr.as_mut()[3] |= 0x8000000000000000u64;
|
y_repr.as_mut()[3] |= 0x8000000000000000u64;
|
||||||
}
|
}
|
||||||
|
|
||||||
y_repr.as_mut().reverse();
|
y_repr.write_le(writer)
|
||||||
|
|
||||||
for b in y_repr.as_mut() {
|
|
||||||
*b = swap_bits_u64(*b);
|
|
||||||
}
|
|
||||||
|
|
||||||
y_repr.write_be(writer)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert from a Montgomery point
|
/// Convert from a Montgomery point
|
||||||
|
|
|
@ -363,7 +363,7 @@ fn test_jubjub_bls12() {
|
||||||
|
|
||||||
tests::test_suite::<Bls12>(¶ms);
|
tests::test_suite::<Bls12>(¶ms);
|
||||||
|
|
||||||
let test_repr = hex!("b9481dd1103b7d1f8578078eb429d3c476472f53e88c0eaefdf51334c7c8b98c");
|
let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139d31");
|
||||||
let p = edwards::Point::<Bls12, _>::read(&test_repr[..], ¶ms).unwrap();
|
let p = edwards::Point::<Bls12, _>::read(&test_repr[..], ¶ms).unwrap();
|
||||||
let q = edwards::Point::<Bls12, _>::get_for_y(
|
let q = edwards::Point::<Bls12, _>::get_for_y(
|
||||||
Fr::from_str("22440861827555040311190986994816762244378363690614952020532787748720529117853").unwrap(),
|
Fr::from_str("22440861827555040311190986994816762244378363690614952020532787748720529117853").unwrap(),
|
||||||
|
@ -372,4 +372,15 @@ fn test_jubjub_bls12() {
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
assert!(p == q);
|
assert!(p == q);
|
||||||
|
|
||||||
|
// Same thing, but sign bit set
|
||||||
|
let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139db1");
|
||||||
|
let p = edwards::Point::<Bls12, _>::read(&test_repr[..], ¶ms).unwrap();
|
||||||
|
let q = edwards::Point::<Bls12, _>::get_for_y(
|
||||||
|
Fr::from_str("22440861827555040311190986994816762244378363690614952020532787748720529117853").unwrap(),
|
||||||
|
true,
|
||||||
|
¶ms
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
assert!(p == q);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ use pedersen_hash::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use byteorder::{
|
use byteorder::{
|
||||||
BigEndian,
|
LittleEndian,
|
||||||
WriteBytesExt
|
WriteBytesExt
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,8 +28,6 @@ use jubjub::{
|
||||||
|
|
||||||
use blake2_rfc::blake2s::Blake2s;
|
use blake2_rfc::blake2s::Blake2s;
|
||||||
|
|
||||||
use util::swap_bits_u64;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ValueCommitment<E: JubjubEngine> {
|
pub struct ValueCommitment<E: JubjubEngine> {
|
||||||
pub value: u64,
|
pub value: u64,
|
||||||
|
@ -96,14 +94,11 @@ impl<E: JubjubEngine> ViewingKey<E> {
|
||||||
h.update(&preimage);
|
h.update(&preimage);
|
||||||
let mut h = h.finalize().as_ref().to_vec();
|
let mut h = h.finalize().as_ref().to_vec();
|
||||||
|
|
||||||
// Reverse the bytes to interpret it in little-endian byte order
|
// Drop the most significant five bits, so it can be interpreted as a scalar.
|
||||||
h.reverse();
|
h[31] &= 0b0000_0111;
|
||||||
|
|
||||||
// Drop the first five bits, so it can be interpreted as a scalar.
|
|
||||||
h[0] &= 0b0000_0111;
|
|
||||||
|
|
||||||
let mut e = <E::Fs as PrimeField>::Repr::default();
|
let mut e = <E::Fs as PrimeField>::Repr::default();
|
||||||
e.read_be(&h[..]).unwrap();
|
e.read_le(&h[..]).unwrap();
|
||||||
|
|
||||||
E::Fs::from_repr(e).expect("should be a valid scalar")
|
E::Fs::from_repr(e).expect("should be a valid scalar")
|
||||||
}
|
}
|
||||||
|
@ -198,13 +193,8 @@ impl<E: JubjubEngine> Note<E> {
|
||||||
// Calculate the note contents, as bytes
|
// Calculate the note contents, as bytes
|
||||||
let mut note_contents = vec![];
|
let mut note_contents = vec![];
|
||||||
|
|
||||||
// Write the value in little-endian bit order
|
// Writing the value in little endian
|
||||||
// swapping the bits to ensure the order is
|
(&mut note_contents).write_u64::<LittleEndian>(self.value).unwrap();
|
||||||
// correct (LittleEndian byte order would
|
|
||||||
// be incorrect here.)
|
|
||||||
(&mut note_contents).write_u64::<BigEndian>(
|
|
||||||
swap_bits_u64(self.value)
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
// Write g_d
|
// Write g_d
|
||||||
self.g_d.write(&mut note_contents).unwrap();
|
self.g_d.write(&mut note_contents).unwrap();
|
||||||
|
@ -219,7 +209,7 @@ impl<E: JubjubEngine> Note<E> {
|
||||||
Personalization::NoteCommitment,
|
Personalization::NoteCommitment,
|
||||||
note_contents.into_iter()
|
note_contents.into_iter()
|
||||||
.flat_map(|byte| {
|
.flat_map(|byte| {
|
||||||
(0..8).rev().map(move |i| ((byte >> i) & 1) == 1)
|
(0..8).map(move |i| ((byte >> i) & 1) == 1)
|
||||||
}),
|
}),
|
||||||
params
|
params
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,18 +6,11 @@ use rand::Rng;
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
|
|
||||||
use jubjub::{FixedGenerators, JubjubEngine, JubjubParams, Unknown, edwards::Point};
|
use jubjub::{FixedGenerators, JubjubEngine, JubjubParams, Unknown, edwards::Point};
|
||||||
use util::{hash_to_scalar, swap_bits_u64};
|
use util::{hash_to_scalar};
|
||||||
|
|
||||||
fn read_scalar<E: JubjubEngine, R: Read>(reader: R) -> io::Result<E::Fs> {
|
fn read_scalar<E: JubjubEngine, R: Read>(reader: R) -> io::Result<E::Fs> {
|
||||||
let mut s_repr = <E::Fs as PrimeField>::Repr::default();
|
let mut s_repr = <E::Fs as PrimeField>::Repr::default();
|
||||||
|
s_repr.read_le(reader)?;
|
||||||
// This reads in big-endian, so we perform a swap of the
|
|
||||||
// limbs in the representation and swap the bit order.
|
|
||||||
s_repr.read_be(reader)?;
|
|
||||||
s_repr.as_mut().reverse();
|
|
||||||
for b in s_repr.as_mut() {
|
|
||||||
*b = swap_bits_u64(*b);
|
|
||||||
}
|
|
||||||
|
|
||||||
match E::Fs::from_repr(s_repr) {
|
match E::Fs::from_repr(s_repr) {
|
||||||
Ok(s) => Ok(s),
|
Ok(s) => Ok(s),
|
||||||
|
@ -29,15 +22,7 @@ fn read_scalar<E: JubjubEngine, R: Read>(reader: R) -> io::Result<E::Fs> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_scalar<E: JubjubEngine, W: Write>(s: &E::Fs, writer: W) -> io::Result<()> {
|
fn write_scalar<E: JubjubEngine, W: Write>(s: &E::Fs, writer: W) -> io::Result<()> {
|
||||||
let mut s_repr = s.into_repr();
|
s.into_repr().write_le(writer)
|
||||||
|
|
||||||
// This writes in big-endian, so we perform a swap of the
|
|
||||||
// limbs in the representation and swap the bit order.
|
|
||||||
s_repr.as_mut().reverse();
|
|
||||||
for b in s_repr.as_mut() {
|
|
||||||
*b = swap_bits_u64(*b);
|
|
||||||
}
|
|
||||||
s_repr.write_be(writer)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn h_star<E: JubjubEngine>(a: &[u8], b: &[u8]) -> E::Fs {
|
fn h_star<E: JubjubEngine>(a: &[u8], b: &[u8]) -> E::Fs {
|
||||||
|
|
28
src/util.rs
28
src/util.rs
|
@ -2,15 +2,6 @@ use blake2_rfc::blake2b::Blake2b;
|
||||||
|
|
||||||
use jubjub::{JubjubEngine, ToUniform};
|
use jubjub::{JubjubEngine, ToUniform};
|
||||||
|
|
||||||
pub fn swap_bits_u64(x: u64) -> u64
|
|
||||||
{
|
|
||||||
let mut tmp = 0;
|
|
||||||
for i in 0..64 {
|
|
||||||
tmp |= ((x >> i) & 1) << (63 - i);
|
|
||||||
}
|
|
||||||
tmp
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hash_to_scalar<E: JubjubEngine>(persona: &[u8], a: &[u8], b: &[u8]) -> E::Fs {
|
pub fn hash_to_scalar<E: JubjubEngine>(persona: &[u8], a: &[u8], b: &[u8]) -> E::Fs {
|
||||||
let mut hasher = Blake2b::with_params(64, &[], &[], persona);
|
let mut hasher = Blake2b::with_params(64, &[], &[], persona);
|
||||||
hasher.update(a);
|
hasher.update(a);
|
||||||
|
@ -18,22 +9,3 @@ pub fn hash_to_scalar<E: JubjubEngine>(persona: &[u8], a: &[u8], b: &[u8]) -> E:
|
||||||
let ret = hasher.finalize();
|
let ret = hasher.finalize();
|
||||||
E::Fs::to_uniform(ret.as_ref())
|
E::Fs::to_uniform(ret.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_swap_bits_u64() {
|
|
||||||
assert_eq!(swap_bits_u64(17182120934178543809), 0b1000001100011011110000011000111000101111111001001100111001110111);
|
|
||||||
assert_eq!(swap_bits_u64(15135675916470734665), 0b1001001011110010001101010010001110110000100111010011000001001011);
|
|
||||||
assert_eq!(swap_bits_u64(6724233301461108393), 0b1001010101100000100011100001010111110001011000101000101010111010);
|
|
||||||
assert_eq!(swap_bits_u64(206708183275952289), 0b1000010100011010001010100011101011111111111110100111101101000000);
|
|
||||||
assert_eq!(swap_bits_u64(12712751566144824320), 0b0000000000100110010110111000001110001100001000110011011000001101);
|
|
||||||
|
|
||||||
let mut a = 15863238721320035327u64;
|
|
||||||
for _ in 0..1000 {
|
|
||||||
a = a.wrapping_mul(a);
|
|
||||||
|
|
||||||
let swapped = swap_bits_u64(a);
|
|
||||||
let unswapped = swap_bits_u64(swapped);
|
|
||||||
|
|
||||||
assert_eq!(a, unswapped);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue