Target addition and subtraction. A little more doc.

This commit is contained in:
Ignotus Peverell 2016-11-15 12:44:19 -08:00
parent 3a54df15f3
commit 44545525f4
No known key found for this signature in database
GPG key ID: 99CD25F39F8F8211

View file

@ -1,4 +1,4 @@
// Copyright 2016 The Developers // Copyright 2016 The Grin Developers
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -17,13 +17,17 @@
use byteorder::{ByteOrder, BigEndian}; use byteorder::{ByteOrder, BigEndian};
use std::fmt; use std::fmt;
use std::ops::{Shl, Shr, Index, IndexMut}; use std::ops::{Add, Sub, Shl, Shr, Index, IndexMut};
use tiny_keccak::Keccak; use tiny_keccak::Keccak;
use ser::{self, Reader, Writer, Writeable, Readable}; use ser::{self, Reader, Writer, Writeable, Readable};
/// A Bitcoin-style target, implemented as a 32 bytes positive big number that
/// can be compared against a proof of work. Serializes in a compact format
/// composed of a one-byte exponent and a 4 bytes mantissa. Hence a target will
/// only ever have a u32 worth of significant numbers.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord)] #[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct Target([u8; 32]); pub struct Target(pub [u8; 32]);
impl fmt::Display for Target { impl fmt::Display for Target {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -96,7 +100,7 @@ impl IndexMut<usize> for Target {
} }
/// Implements shift left to break the target down into an exponent and a /// Implements shift left to break the target down into an exponent and a
/// mantissa /// mantissa and provide simple multiplication by a power of 2.
impl Shl<usize> for Target { impl Shl<usize> for Target {
type Output = Target; type Output = Target;
@ -122,6 +126,7 @@ impl Shl<usize> for Target {
} }
/// Implements shift right to build a target from an exponent and a mantissa /// Implements shift right to build a target from an exponent and a mantissa
/// and provide simple division by a power of 2.
impl Shr<usize> for Target { impl Shr<usize> for Target {
type Output = Target; type Output = Target;
@ -146,6 +151,63 @@ impl Shr<usize> for Target {
} }
} }
/// Implement addition between targets. Overflow is truncated.
impl Add for Target {
type Output = Target;
fn add(self, other: Target) -> Target {
let mut sum = [0; 32];
let mut carry = 0;
for i in (0..32).rev() {
let (sum_i, carry_i) = add_with_carryover(self[i], other[i], carry);
sum[i] = sum_i;
carry = carry_i;
}
Target(sum)
}
}
fn add_with_carryover(a: u8, b: u8, carry: u8) -> (u8, u8) {
let mut new_carry = 0;
let (a_carry, over) = a.overflowing_add(carry);
if over {
new_carry += 1;
}
let (sum, over) = a_carry.overflowing_add(b);
if over {
new_carry += 1;
}
(sum, new_carry)
}
/// Implement subtractions between targets. Underflow is truncated.
impl Sub for Target {
type Output = Target;
fn sub(self, other: Target) -> Target {
let mut diff = [0; 32];
let mut carry = 0;
for i in (0..32).rev() {
let (diff_i, carry_i) = sub_with_carryover(self[i], other[i], carry);
diff[i] = diff_i;
carry = carry_i;
}
Target(diff)
}
}
fn sub_with_carryover(a: u8, b: u8, carry: u8) -> (u8, u8) {
let mut new_carry = 0;
let (a_carry, under) = a.overflowing_sub(carry);
if under {
new_carry += 1;
}
let (diff, under) = a_carry.overflowing_sub(b);
if under {
new_carry += 1;
}
(diff, new_carry)
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
@ -180,6 +242,11 @@ mod test {
assert_eq!(rsl[31], 0); assert_eq!(rsl[31], 0);
} }
#[test]
fn shift_truncate() {
assert_eq!((Target::join(0, 0xffff).unwrap() >> 8) << 8, Target::join(0, 0xff00).unwrap());
}
#[test] #[test]
fn split_fit() { fn split_fit() {
let t = Target::join(10 * 8, ::std::u32::MAX).unwrap(); let t = Target::join(10 * 8, ::std::u32::MAX).unwrap();
@ -197,4 +264,33 @@ mod test {
assert_eq!(exp, 220); assert_eq!(exp, 220);
assert_eq!(mant, 10 << 28); assert_eq!(mant, 10 << 28);
} }
#[test]
fn addition() {
assert_eq!(Target::join(0, 10).unwrap() + Target::join(0, 20).unwrap(),
Target::join(0, 30).unwrap());
// single overflow
assert_eq!(Target::join(0, 250).unwrap() + Target::join(0, 250).unwrap(),
Target::join(0, 500).unwrap());
// multiple overflows
assert_eq!(Target::join(0, 300).unwrap() + Target::join(0, 300).unwrap(),
Target::join(0, 600).unwrap());
assert_eq!(Target::join(10, 300).unwrap() + Target::join(10, 300).unwrap(),
Target::join(10, 600).unwrap());
// cascading overflows
assert_eq!(Target::join(8, 0xffff).unwrap() + Target::join(8, 0xffff).unwrap(),
Target::join(8, 0x1fffe).unwrap());
}
#[test]
fn subtraction() {
assert_eq!(Target::join(0, 40).unwrap() - Target::join(0, 10).unwrap(),
Target::join(0, 30).unwrap());
assert_eq!(Target::join(0, 300).unwrap() - Target::join(0, 100).unwrap(),
Target::join(0, 200).unwrap());
assert_eq!(Target::join(0, 0xffff).unwrap() - Target::join(0, 0xffff).unwrap(),
Target::join(0, 0).unwrap());
assert_eq!(Target::join(0, 0xffff).unwrap() - Target::join(0, 0xff01).unwrap(),
Target::join(0, 0xfe).unwrap());
}
} }