Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid Clone in FenwickTree #104

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion examples/library-checker-static-range-sum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ fn main() {
lrs: [(usize, usize); q],
}

let mut fenwick = FenwickTree::new(n, 0);
let mut fenwick = FenwickTree::<u64>::new(n);
for (i, a) in r#as.into_iter().enumerate() {
fenwick.add(i, a);
}
Expand Down
24 changes: 13 additions & 11 deletions src/fenwicktree.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,37 @@
use std::iter::repeat_with;

use crate::num_traits::Zero;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These appear to be changes that need not specifically rely on #102. Check out the application examples below.

https://github.com/rust-lang-ja/ac-library-rs/compare/7e99fd2941976764b4e7925898fae7cc77890885...mizar:ac-library-rs:4be604494341a86242b77120e5685740dc41791f?expand=1

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think requiring using Default is not always a good idea because the semantics are different. Zero traits is clearly for the "additive identity", while Default trait is for a general "default" instance. The latter is not guaranteed to be an additive identity, and that restriction shall be stated in the doc instead of as a type restriction. I prefer type restrictions to other document constraints, and thus thought introducing Zero trait would be reasonable.


// Reference: https://en.wikipedia.org/wiki/Fenwick_tree
pub struct FenwickTree<T> {
n: usize,
ary: Vec<T>,
e: T,
}

impl<T: Clone + std::ops::AddAssign<T>> FenwickTree<T> {
pub fn new(n: usize, e: T) -> Self {
impl<T: for<'a> std::ops::AddAssign<&'a T> + Zero> FenwickTree<T> {
pub fn new(n: usize) -> Self {
FenwickTree {
n,
ary: vec![e.clone(); n],
e,
ary: repeat_with(T::zero).take(n).collect(),
}
}
pub fn accum(&self, mut idx: usize) -> T {
let mut sum = self.e.clone();
let mut sum = T::zero();
while idx > 0 {
sum += self.ary[idx - 1].clone();
sum += &self.ary[idx - 1];
idx &= idx - 1;
}
sum
}
/// performs data[idx] += val;
pub fn add<U: Clone>(&mut self, mut idx: usize, val: U)
pub fn add<U>(&mut self, mut idx: usize, val: U)
where
T: std::ops::AddAssign<U>,
T: for<'a> std::ops::AddAssign<&'a U>,
{
let n = self.n;
idx += 1;
while idx <= n {
self.ary[idx - 1] += val.clone();
self.ary[idx - 1] += &val;
idx += idx & idx.wrapping_neg();
}
}
Expand All @@ -48,7 +50,7 @@ mod tests {

#[test]
fn fenwick_tree_works() {
let mut bit = FenwickTree::new(5, 0i64);
let mut bit = FenwickTree::<i64>::new(5);
// [1, 2, 3, 4, 5]
for i in 0..5 {
bit.add(i, i as i64 + 1);
Expand Down
30 changes: 2 additions & 28 deletions src/internal_type_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,25 +52,13 @@ pub trait Integral:
+ fmt::Debug
+ fmt::Binary
+ fmt::Octal
+ Zero
+ One
+ crate::num_traits::Zero
+ crate::num_traits::One
+ BoundedBelow
+ BoundedAbove
{
}

/// Class that has additive identity element
pub trait Zero {
/// The additive identity element
fn zero() -> Self;
}

/// Class that has multiplicative identity element
pub trait One {
/// The multiplicative identity element
fn one() -> Self;
}

pub trait BoundedBelow {
fn min_value() -> Self;
}
Expand All @@ -82,20 +70,6 @@ pub trait BoundedAbove {
macro_rules! impl_integral {
($($ty:ty),*) => {
$(
impl Zero for $ty {
#[inline]
fn zero() -> Self {
0
}
}

impl One for $ty {
#[inline]
fn one() -> Self {
1
}
}

impl BoundedBelow for $ty {
#[inline]
fn min_value() -> Self {
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ pub mod segtree;
pub mod string;
pub mod twosat;

pub mod num_traits;

pub(crate) mod internal_bit;
pub(crate) mod internal_math;
pub(crate) mod internal_queue;
Expand Down
43 changes: 43 additions & 0 deletions src/num_traits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/// A type that has an additive identity element.
pub trait Zero {
/// The additive identity element.
fn zero() -> Self;
}

/// A type that has a multiplicative identity element.
pub trait One {
/// The multiplicative identity element.
fn one() -> Self;
}

macro_rules! impl_zero {
($zero: literal, $one: literal: $($t: ty),*) => {
$(
impl Zero for $t {
fn zero() -> Self {
$zero
}
}

impl One for $t {
fn one() -> Self {
$one
}
}
)*
};
}
impl_zero!(0, 1: usize, u8, u16, u32, u64, u128);
impl_zero!(0, 1: isize, i8, i16, i32, i64, i128);
impl_zero!(0.0, 1.0: f32, f64);

impl<T: Zero> Zero for core::num::Wrapping<T> {
fn zero() -> Self {
Self(T::zero())
}
}
impl<T: One> One for core::num::Wrapping<T> {
fn one() -> Self {
Self(T::one())
}
}
3 changes: 2 additions & 1 deletion src/segtree.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::internal_bit::ceil_pow2;
use crate::internal_type_traits::{BoundedAbove, BoundedBelow, One, Zero};
use crate::internal_type_traits::{BoundedAbove, BoundedBelow};
use crate::num_traits::{One, Zero};
use std::cmp::{max, min};
use std::convert::Infallible;
use std::marker::PhantomData;
Expand Down