A \$Z^m\$ number system includes integers in the interval \$[0, m)\$ when \$m > 0\$ or \$(m, 0]\$ when \$m < 0\$. The code defines a trait Mod
to represent numbers in this system and the +
, -
, and *
operator for it.
use std::ops::Add;
use std::ops::Mul;
use std::ops::Sub;
use std::ops::Rem;
struct Mod<T>
where T: Modulo<T> + Mul<Output=T> + Sub<Output=T> + Add<Output=T> + Rem<Output=T> + Copy + Clone
{
modulo: T,
i: T,
}
trait Modulo<T>
where T: Add<Output=T> + Rem<Output=T> + Copy + Clone
{
fn modulo(self, n: T) -> T;
}
impl<T> Modulo<T> for T
where T: Add<Output=T> + Rem<Output=T> + Copy + Clone
{
fn modulo(self, n: T) -> T {
((self % n) + n) % n
}
}
impl<T> Mod<T>
where T: Modulo<T> + Mul<Output=T> + Sub<Output=T> + Add<Output=T> + Rem<Output=T> + Copy + Clone
{
fn new(modulo: T, i: T) -> Mod<T> {
let n = i.modulo(modulo);
Mod {
modulo: modulo,
i: n,
}
}
}
impl<T> Add for Mod<T>
where T: Modulo<T> + Mul<Output=T> + Sub<Output=T> + Add<Output=T> + Rem<Output=T> + Copy + Clone
{
type Output = Mod<T>;
fn add(self, other: Mod<T>) -> Mod<T> {
Mod::new(self.modulo, self.i + other.i)
}
}
impl<T> Sub for Mod<T>
where T: Modulo<T> + Mul<Output=T> + Sub<Output=T> + Add<Output=T> + Rem<Output=T> + Copy + Clone
{
type Output = Mod<T>;
fn sub(self, other: Mod<T>) -> Mod<T> {
Mod::new(self.modulo, self.i - other.i)
}
}
impl<T> Mul for Mod<T>
where T: Modulo<T> + Mul<Output=T> + Sub<Output=T> + Add<Output=T> + Rem<Output=T> + Copy + Clone
{
type Output = Mod<T>;
fn mul(self, other: Mod<T>) -> Mod<T> {
Mod::new(self.modulo, self.i * other.i)
}
}
fn main() {
let x = Mod::new(-5i8, 3i8);
let y = Mod::new(-5i8, 8i8);
println!("{}", (x + y).i);
let x = Mod::new(-5i8, 3i8);
let y = Mod::new(-5i8, 8i8);
println!("{}", (x - y).i);
let x = Mod::new(-5i8, 3i8);
let y = Mod::new(-5i8, 8i8);
println!("{}", (x * y).i);
let x = Mod::new(-5i16, 3i16);
let y = Mod::new(-5i16, 8i16);
println!("{}", (x + y).i);
let x = Mod::new(-5i16, 3i16);
let y = Mod::new(-5i16, 8i16);
println!("{}", (x - y).i);
let x = Mod::new(-5i16, 3i16);
let y = Mod::new(-5i16, 8i16);
println!("{}", (x * y).i);
let x = Mod::new(-5, 3);
let y = Mod::new(-5, 8);
println!("{}", (x + y).i);
let x = Mod::new(-5, 3);
let y = Mod::new(-5, 8);
println!("{}", (x - y).i);
let x = Mod::new(-5, 3);
let y = Mod::new(-5, 8);
println!("{}", (x * y).i);
let x = Mod::new(5u8, 3u8);
let y = Mod::new(5u8, 8u8);
println!("{}", (x + y).i);
let x = Mod::new(5u8, 3u8);
let y = Mod::new(5u8, 8u8);
println!("{}", (x - y).i);
let x = Mod::new(5u8, 3u8);
let y = Mod::new(5u8, 8u8);
println!("{}", (x * y).i);
}
I tried to achieve the goals laid out in the previous version:
- Changed the modulo method for better performance.
- Generalized over a bunch of types other than
i32
.
All suggestions are still welcome. In particular, requiring T
to implement Copy
and Clone
might be a bit too restrictive, I would like to relax that.
1 Answer 1
Place multiple imports from the same module on one line.
I would heartily recommend against interleaving a trait definition (
Modulo
) and a struct definition (Mod
).I prefer to not put trait bounds on the trait definition. Those usually want to be a supertrait or just on a trait implementation.
Similarly, I prefer not put trait bounds on a struct definition; just on on implementation.
There's no need for the
Clone
bound — it's never used.I'd make your new "operator" trait match others: Parameterize the right-hand side (
Rhs
) and theOutput
as an associated type.Then you can DRY up your duplicated trait bounds by creating a new trait that has all the other traits as a supertrait.
While you are at it, remove the type parameter
T
because you always use it in the same fashion; it might as well be hardcoded.Place a space around
=
when restricting associated types in traits.There's no need for the temp var in the
Mod
constructor.Tests. TESTS. TESTS You are so close to already having tests! Your
main
function can be basically transformed into a sequence of tests. Then the computer can verify them, instead of having to read the output each time. Please give them useful names that describe what each tests, not my stupid sequential names.
use std::ops::{Add, Mul, Sub, Rem};
trait Modulo<Rhs = Self> {
type Output;
fn modulo(self, n: Rhs) -> Self::Output;
}
impl<T> Modulo<T> for T
where T: Add<Output = T> + Rem<Output = T> + Copy
{
type Output = T;
fn modulo(self, n: T) -> T {
((self % n) + n) % n
}
}
trait ZMod: Modulo<Output = Self> + Mul<Output = Self> + Sub<Output = Self> + Add<Output = Self> + Rem<Output = Self> + Copy {}
impl<T> ZMod for T
where T: Modulo<Output = T> + Mul<Output = T> + Sub<Output = T> + Add<Output = T> + Rem<Output = T> + Copy
{}
struct Mod<T> {
modulo: T,
i: T,
}
impl<T> Mod<T>
where T: ZMod
{
fn new(modulo: T, i: T) -> Mod<T> {
Mod {
modulo: modulo,
i: i.modulo(modulo),
}
}
}
impl<T> Add for Mod<T>
where T: ZMod
{
type Output = Mod<T>;
fn add(self, other: Mod<T>) -> Mod<T> {
Mod::new(self.modulo, self.i + other.i)
}
}
impl<T> Sub for Mod<T>
where T: ZMod
{
type Output = Mod<T>;
fn sub(self, other: Mod<T>) -> Mod<T> {
Mod::new(self.modulo, self.i - other.i)
}
}
impl<T> Mul for Mod<T>
where T: ZMod
{
type Output = Mod<T>;
fn mul(self, other: Mod<T>) -> Mod<T> {
Mod::new(self.modulo, self.i * other.i)
}
}
#[test]
fn t0() {
let x = Mod::new(-5i8, 3i8);
let y = Mod::new(-5i8, 8i8);
assert_eq!(-4, (x + y).i);
}
#[test]
fn t1() {
let x = Mod::new(-5i8, 3i8);
let y = Mod::new(-5i8, 8i8);
assert_eq!(-4, (x + y).i);
}
#[test]
fn t2() {
let x = Mod::new(-5i8, 3i8);
let y = Mod::new(-5i8, 8i8);
assert_eq!(0, (x - y).i);
}
#[test]
fn t3() {
let x = Mod::new(-5i8, 3i8);
let y = Mod::new(-5i8, 8i8);
assert_eq!(-1, (x * y).i);
}
#[test]
fn t4() {
let x = Mod::new(-5i16, 3i16);
let y = Mod::new(-5i16, 8i16);
assert_eq!(-4, (x + y).i);
}
#[test]
fn t5() {
let x = Mod::new(-5i16, 3i16);
let y = Mod::new(-5i16, 8i16);
assert_eq!(0, (x - y).i);
}
#[test]
fn t6() {
let x = Mod::new(-5i16, 3i16);
let y = Mod::new(-5i16, 8i16);
assert_eq!(-1, (x * y).i);
}
#[test]
fn t7() {
let x = Mod::new(-5, 3);
let y = Mod::new(-5, 8);
assert_eq!(-4, (x + y).i);
}
#[test]
fn t8() {
let x = Mod::new(-5, 3);
let y = Mod::new(-5, 8);
assert_eq!(0, (x - y).i);
}
#[test]
fn t9() {
let x = Mod::new(-5, 3);
let y = Mod::new(-5, 8);
assert_eq!(-1, (x * y).i);
}
#[test]
fn t10() {
let x = Mod::new(5u8, 3u8);
let y = Mod::new(5u8, 8u8);
assert_eq!(1, (x + y).i);
}
#[test]
fn t11() {
let x = Mod::new(5u8, 3u8);
let y = Mod::new(5u8, 8u8);
assert_eq!(0, (x - y).i);
}
#[test]
fn t12() {
let x = Mod::new(5u8, 3u8);
let y = Mod::new(5u8, 8u8);
assert_eq!(4, (x * y).i);
}
-
\$\begingroup\$ Great, the code looks much better now. \$\endgroup\$qed– qed2016年09月16日 18:32:31 +00:00Commented Sep 16, 2016 at 18:32