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

fix(inclusion): fix integer overflow+inefficient Round*PowerOfTwo #118

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

odeke-em
Copy link
Contributor

This code fixes an integer flow using finer bit twiddling and even makes it much more efficient to always run deterministically in O(1) time essentially and not O(k) where k=log2(input) due to the prior k iterations that then caused the overflow when result became > maxInt.

While here also added some benchmarks and more test cases.

Fixes #117

@odeke-em odeke-em requested review from a team as code owners October 17, 2024 11:37
@odeke-em odeke-em requested review from rootulp, rach-id, cristaloleg and vgonkivs and removed request for a team October 17, 2024 11:37
@celestia-bot celestia-bot requested review from a team October 17, 2024 11:37
@odeke-em odeke-em force-pushed the inclusion-fix-RoundDownPowerOfTwo+RoundUpPowerOfTwo branch 4 times, most recently from 9f9516b to d838e40 Compare October 17, 2024 12:48
…und*PowerOfTwo

This code fixes an integer flow using finer bit twiddling
and even makes it much more efficient to always run
deterministically in O(1) time essentially and not O(k) where
k=log2(input) due to the prior k iterations that then caused
the overflow when result became > maxInt.

While here also added some benchmarks and more test cases.

Fixes celestiaorg#117
@odeke-em odeke-em force-pushed the inclusion-fix-RoundDownPowerOfTwo+RoundUpPowerOfTwo branch from d838e40 to 8d14642 Compare October 17, 2024 12:49
Copy link
Member

@rach-id rach-id left a comment

Choose a reason for hiding this comment

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

LGTM

if input&(input-1) == 0 { // It is already a power of 2
return input
}
var powUp I = 1 << bits.Len64(uint64(input))
Copy link
Member

Choose a reason for hiding this comment

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

[Optional]
I would add a comment here explaining why this works

want: 2,
},
{
shareCount: (defaultSubtreeRootThreshold * 2) + 1,
Copy link
Member

Choose a reason for hiding this comment

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

[Optional]
I would add something like:

Suggested change
shareCount: (defaultSubtreeRootThreshold * 2) + 1,
shareCount: (defaultSubtreeRootThreshold * 2) + 1, // 129

to avoid needing to calculate the numbers when reading the tests

@celestia-bot celestia-bot requested review from a team and cmwaters and removed request for a team October 17, 2024 15:12
Comment on lines +57 to +59
if input&(input-1) == 0 { // It is already a power of 2
return input
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

[nit] turn the comment into self-documenting code

Suggested change
if input&(input-1) == 0 { // It is already a power of 2
return input
}
if isPowerOfTwo(input) {
return input
}

by extracting a function

func RoundUpPowerOfTwo[I constraints.Integer](input I) bool {
    return input&(input-1) == 0
}

Comment on lines +61 to +65
if powUp < input {
// An overflow occurred due to a very large size
// of input and we should return a positive power of 2.
powUp = 1
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

[blocking] this is unexpected behavior. I would rather this function return an error here than quietly return 1.

Comment on lines +74 to 76
if input&(input-1) == 0 { // It is already a power of 2
return input, nil
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

same nit: extract a function and delete the comment

{input: math.MaxInt32 + 1, want: 1 << 31},
{input: math.MaxInt32, want: 1 << 31},
{input: math.MaxInt >> 1, want: 1 << 62},
{input: math.MaxInt, want: 1},
Copy link
Collaborator

Choose a reason for hiding this comment

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

[blocking] this seems like unexpected behavior. I think we should return an error in this case

Comment on lines +261 to +281
type roundUpTestCase struct {
input int
want int
}

var roundUpTestCases = []roundUpTestCase{
{input: -1, want: 1},
{input: 0, want: 1},
{input: 1, want: 1},
{input: 2, want: 2},
{input: 4, want: 4},
{input: 5, want: 8},
{input: 8, want: 8},
{input: 11, want: 16},
{input: 511, want: 512},
{input: math.MaxInt32 - 1, want: 1 << 31},
{input: math.MaxInt32 + 1, want: 1 << 31},
{input: math.MaxInt32, want: 1 << 31},
{input: math.MaxInt >> 1, want: 1 << 62},
{input: math.MaxInt, want: 1},
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

[blocking] these were previously defined inside the function TestRoundUpPowerOfTwo. Can you please move them back there so that their scope is confined to that function

Comment on lines +340 to +394
type subtreeWidthTestCase struct {
shareCount int
want int
}

var subtreeWidthTestCases = []subtreeWidthTestCase{
{
shareCount: 0,
want: 1,
},
{
shareCount: 1,
want: 1,
},
{
shareCount: 2,
want: 1,
},
{
shareCount: defaultSubtreeRootThreshold,
want: 1,
},
{
shareCount: defaultSubtreeRootThreshold + 1,
want: 2,
},
{
shareCount: defaultSubtreeRootThreshold - 1,
want: 1,
},
{
shareCount: defaultSubtreeRootThreshold * 2,
want: 2,
},
{
shareCount: (defaultSubtreeRootThreshold * 2) + 1,
want: 4,
},
{
shareCount: (defaultSubtreeRootThreshold * 3) - 1,
want: 4,
},
{
shareCount: (defaultSubtreeRootThreshold * 4),
want: 4,
},
{
shareCount: (defaultSubtreeRootThreshold * 5),
want: 8,
},
{
shareCount: (defaultSubtreeRootThreshold * defaultMaxSquareSize) - 1,
want: 128,
},
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

[blocking] please define subtreeWidthTestCase and subtreeWidthTestCases inside the function TestSubTreeWidth to confine their scope.

Comment on lines +458 to +475
type roundDownTestCase struct {
input int
want int
}

var roundDownTestCases = []roundDownTestCase{
{input: 1, want: 1},
{input: 2, want: 2},
{input: 4, want: 4},
{input: 5, want: 4},
{input: 8, want: 8},
{input: 11, want: 8},
{input: 511, want: 256},
{input: math.MaxInt32 - 1, want: 1 << 30},
{input: math.MaxInt32, want: 1 << 30},
{input: math.MaxInt32 + 1, want: 1 << 31},
{input: math.MaxInt, want: 1 << 62},
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please define these inside TestRoundDownPowerOfTwo

type testCase struct {
input int
want int
var sink any
Copy link
Collaborator

Choose a reason for hiding this comment

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

[question] should this be defined inside each Benchmark* test so that one benchmark does not impact another?

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