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

Extended Galois field #38

Merged
merged 15 commits into from
Jan 9, 2019
Merged

Conversation

drskalman
Copy link
Collaborator

It is my first ever rust code for public consumption so it probably needs lots of improvement :-)

My goal at this stage was to write something fast so I can make the reed-solomon codes work for GF((2^8)^n) and then improve it afterward. So efficiency, etc wasn't on my mind at this stage.

Although it works for any extension degree n, the tests are written for GF((2^8)^2) extended by x^2 + a*x + a^7 (where a is the generator of GF(2^8) field used in this code).

@darrenldl
Copy link
Contributor

@drskalman Thanks very much for the work!

Since @rphmeier 's PR was merged into master before your PR, could you sync the branch from master to resolve the conflicts?

Thanks!

@drskalman
Copy link
Collaborator Author

Thanks for looking into this. Sure I can. I'm a rust newbie and I wasn't expecting merge without major change. So it was there more for seeking improvement advice rather than merge. But I'll do that. I'll close this and open a new one after rebase. It would be nice if you can add @rphmeier also as reviewer to the new PR.

@darrenldl
Copy link
Contributor

I am not sure if I have taken a look close enough to give proper advice, I do have some thoughts on the implementations however. I have not looked closely enough, so please do correct me if I got the wrong impression.

  • reed-solomon-erasure v1.X.X was following BackBlaze's original Java implementation, which considers things on value per value basis rather than thinking about things as byte slices. This seems to be what you're doing here. This was okay for very early implementation, but the later much faster implementation couldn't reuse any of the code, and required me to rewrite almost everything from scratch in v2.0.0 copying Klaus Post's Go implementation.
    • I'm concerned that something similar might happen here - the faster implementation ends up wasting the effort put in the first general implementation. Personally I feel it might be better to start with the faster implementation from the start, but I'm not very certain on that.
  • The other option I think might work out better would be to follow jerasure team's gf-complete, which is a C implementation that is mature and supports SIMD. They use the 3-clause BSD license (as per their COPYING file), which I think is compatible with MIT (license of this library). So the translated work should still be able to be distributed under MIT license.
    • I have not looked into their work closely either, so I am not certain if this is actually a good idea.
  • I'd rather add the uncheck stuff at the very end of the upgrade, since it can be very difficult to debug.
  • Testing wise, I would prefer quickcheck style tests to added along with unit tests as new code is added, or at least when the new code is stabilised, but we can think about this much later. The current quickcheck style tests are written using the quickcheck crate, and the names are prefixed with qc_, but I'm happy with proptest crate (or anything similarly robust) as well.

It would be nice if you can add @rphmeier also as reviewer to the new PR.

I can't add people as "reviewer" properly unless I add them as collaborator to this project, at which point I'd rather move this into an organisation project. But I'll mention him in the new PR.

}

pub fn from(original_rep_polynom: &Polynom) -> ExtendedFieldElement {

Copy link
Contributor

@rphmeier rphmeier Jan 5, 2019

Choose a reason for hiding this comment

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

extraneous whitespace -- also this is better as a From implementation

Copy link
Contributor

Choose a reason for hiding this comment

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

actually, I can see that there is also a From implementation and that it is equivalent to this function except for the fact that one reduces and one doesn't. What is the correct behavior? I would write to reduce to err on the side of caution.

@@ -0,0 +1,92 @@
use ::poly::Polynom;
use poly_math::*;
Copy link
Contributor

Choose a reason for hiding this comment

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

better not to use glob imports

pub fn inverse(self: &Self) -> ExtendedFieldElement {
if !self.is_zero() {
let (gcd, x, _) = self.rep_polynom.egcd(&EXT_POLY!());
//we still need to normilze it by divinig by the gcd
Copy link
Contributor

@rphmeier rphmeier Jan 5, 2019

Choose a reason for hiding this comment

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

quite a lot of typos in this function. it might also be better to document the fact that inverting zero panics

}


pub fn is_zero(self: &Self)-> bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

self: &Self equivalent to &self

@rphmeier
Copy link
Contributor

rphmeier commented Jan 5, 2019

I'm concerned that something similar might happen here - the faster implementation ends up wasting the effort put in the first general implementation

This is a legitimate concern, but I think as long as we abstract everything sufficiently via traits it shouldn't end up having non-local effects.

I'm pushing a few changes:

  • Make code more idiomatic
  • Quickcheck tests for polynomial math

src/macros.rs Outdated
}
}

macro_rules! EXT_POLY {
Copy link
Contributor

Choose a reason for hiding this comment

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

what is this for?

src/macros.rs Outdated
macro_rules! uncheck_mut {
($array:ident[$index:expr]) => {
* if cfg!(feature = "unsafe_indexing") {
unsafe {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would recommend removing the unsafe indexing entirely. rustc is good at eliding bounds checks where possible

src/poly.rs Outdated
*x = 0;
}
} else if new_len < old_len {
self.dirty = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see anywhere where the dirty flag is getting set to false again. Why not just ignore the bits further than length?

src/poly.rs Outdated
}

#[inline]
pub fn set_length(&mut self, new_len: usize) {
Copy link
Contributor

@rphmeier rphmeier Jan 5, 2019

Choose a reason for hiding this comment

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

what should happen if new_len > POLYNOMIAL_MAX_LENGTH?

src/poly.rs Outdated

#[inline]
pub fn reverse(mut self) -> Self {
(*self).reverse();
Copy link
Contributor

Choose a reason for hiding this comment

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

doesn't this need to take into account length?

@rphmeier rphmeier force-pushed the extended-galois-field branch from c1c98f5 to 2791287 Compare January 5, 2019 21:11
@rphmeier rphmeier force-pushed the extended-galois-field branch from 2791287 to cc374ef Compare January 6, 2019 14:02
@coveralls
Copy link

Coverage Status

Coverage decreased (-0.4%) to 96.355% when pulling cc374ef on paritytech:extended-galois-field into 0d64fd0 on darrenldl:master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage decreased (-0.4%) to 96.355% when pulling cc374ef on paritytech:extended-galois-field into 0d64fd0 on darrenldl:master.

@coveralls
Copy link

coveralls commented Jan 6, 2019

Coverage Status

Coverage decreased (-0.5%) to 96.227% when pulling f6cf735 on paritytech:extended-galois-field into 0d64fd0 on darrenldl:master.

@codecov
Copy link

codecov bot commented Jan 6, 2019

Codecov Report

Merging #38 into master will decrease coverage by 0.41%.
The diff coverage is 91.72%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #38      +/-   ##
==========================================
- Coverage   96.77%   96.35%   -0.42%     
==========================================
  Files           7        8       +1     
  Lines        2479     2771     +292     
==========================================
+ Hits         2399     2670     +271     
- Misses         80      101      +21
Impacted Files Coverage Δ
src/galois_8.rs 99.63% <ø> (ø) ⬆️
src/lib.rs 94.11% <ø> (ø) ⬆️
src/tests/mod.rs 98% <100%> (ø) ⬆️
src/matrix.rs 98.73% <100%> (ø) ⬆️
src/poly.rs 89.28% <89.28%> (ø)
src/galois_ext.rs 96.15% <96.15%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0d64fd0...cc374ef. Read the comment docs.

@codecov
Copy link

codecov bot commented Jan 6, 2019

Codecov Report

Merging #38 into master will decrease coverage by 0.9%.
The diff coverage is 77.62%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #38      +/-   ##
==========================================
- Coverage   96.77%   95.86%   -0.91%     
==========================================
  Files           7        7              
  Lines        2479     2612     +133     
==========================================
+ Hits         2399     2504     +105     
- Misses         80      108      +28
Impacted Files Coverage Δ
src/galois_8.rs 99.63% <ø> (ø) ⬆️
src/lib.rs 94.11% <ø> (ø) ⬆️
src/tests/mod.rs 98% <100%> (ø) ⬆️
src/matrix.rs 98.73% <100%> (ø) ⬆️
src/galois_16.rs 77.3% <77.3%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0d64fd0...f6cf735. Read the comment docs.

@rphmeier rphmeier force-pushed the extended-galois-field branch from dec113d to 6677c18 Compare January 7, 2019 17:15
@darrenldl
Copy link
Contributor

darrenldl commented Jan 8, 2019

Just want to confirm something in case I'm being stupid, so the .sage file is only for testing the formulas manually, right?

(Mainly want to ensure no static look up tables are/will be generated by sage/non-Rust code)

@rphmeier
Copy link
Contributor

rphmeier commented Jan 8, 2019

The sage formula is used to generate the irreducible polynomial we use as a modulus for GF(2^16), but not at build time.

In the future we will probably drop the polynomial logic and extend with another table of size 256, generated at build time.

@rphmeier
Copy link
Contributor

rphmeier commented Jan 8, 2019

Ok, I think this should be ready for review now.

I managed to get rid of the arbitrary length polynomials by porting over a couple specialized cases of polynomial division and the extended euclidean algorithm to the galois_16 module.

@darrenldl
Copy link
Contributor

Thanks very much @drskalman @rphmeier !

I'll try to get to reviewing it in a day or so, next 3 days are especially busy for me, so apologies if I don't end up reviewing it in time as suggested.

src/galois_16.rs Outdated
}

// either self is zero polynomial or is equivalent to 0
panic!("Cannot invert 0");
Copy link
Contributor

@darrenldl darrenldl Jan 8, 2019

Choose a reason for hiding this comment

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

Personally I feel it would be nicer to move the panic!(...) to top the top of the code, or change this function into if ... else .. branching, as these two options feel easier for audit and analysis.

Just to be more specific, moving it to top would be

if self.zero(...) {
    panic!("Cannot invert 0");
}

// first step of extended euclidean algorithm.
...

changing into if ... else ... branch would be

if self.zero(...) {
    panic!("Cannot invert 0");
} else {
    // first step of extended euclidean algorithm.
    ...
}

Largely my habit though, the main rationale would be regardless of function size, the panic!()s would always be visible at top of function, or at the top of one of the branches.

@darrenldl
Copy link
Contributor

darrenldl commented Jan 8, 2019

Seems best to also include tests for Element::inverse specifically. Though div, which is tested, invokes inverse directly, I'm not sure if the test cases invoke inverse sufficiently thoroughly.

I'll come back later to look over the remaining portion.

@darrenldl
Copy link
Contributor

The tests look good, I might add more tests later on just to safe guard future changes, but I feel the coverage should be solid enough for now.

Thanks for the work again @drskalman @rphmeier

@darrenldl darrenldl merged commit 0890891 into rust-rse:master Jan 9, 2019
@rphmeier
Copy link
Contributor

rphmeier commented Jan 9, 2019

More tests are always a bonus!

@rphmeier rphmeier deleted the extended-galois-field branch January 9, 2019 15:29
@drskalman
Copy link
Collaborator Author

drskalman commented Jan 9, 2019

Just want to confirm something in case I'm being stupid, so the .sage file is only for testing the formulas manually, right?
Right, I left it there in case if we need to generate higher degree irreducible polynomials and tests for them.

(Mainly want to ensure no static look up tables are/will be generated by sage/non-Rust code)
no static look up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants