-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Add Range overlap and set operations #17081
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
Open
mamantoha
wants to merge
13
commits into
crystal-lang:master
Choose a base branch
from
mamantoha:range-set-operations
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+329
−0
Open
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
90be6f1
Add Range overlap and set operations
mamantoha 4524a7c
Merge branch 'master' into range-set-operations
mamantoha 536114b
Update spec/std/range_spec.cr
mamantoha 59acacd
Update spec/std/range_spec.cr
mamantoha f4fabb2
Update spec/std/range_spec.cr
mamantoha 56759bd
Update spec/std/range_spec.cr
mamantoha 45bf0ac
Update spec/std/range_spec.cr
mamantoha 73f4ab5
Update spec/std/range_spec.cr
mamantoha d142095
Update spec/std/range_spec.cr
mamantoha 97d9cd4
Update spec/std/range_spec.cr
mamantoha 608c262
Update src/range.cr
mamantoha 63888e8
Update src/range.cr
mamantoha 6c03268
Merge branch 'master' into range-set-operations
mamantoha File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -283,6 +283,121 @@ struct Range(B, E) | |
| (@exclusive ? value < end_value : value <= end_value)) | ||
| end | ||
|
|
||
| # Returns `true` if this range and *other* have at least one value in common. | ||
| # | ||
| # ``` | ||
| # (1..5).overlaps?(5..10) # => true | ||
| # (1...5).overlaps?(5..10) # => false | ||
| # (1...1).overlaps?(1..1) # => false | ||
| # ``` | ||
| def overlaps?(other : Range) : Bool | ||
| return false if empty_without_iterating? | ||
| return false if other.empty_without_iterating? | ||
|
|
||
| begin_value = @begin | ||
| end_value = @end | ||
| other_begin = other.begin | ||
| other_end = other.end | ||
|
|
||
| if end_value && other_begin | ||
| return false if @exclusive ? end_value <= other_begin : end_value < other_begin | ||
| end | ||
|
|
||
| if other_end && begin_value | ||
| return false if other.excludes_end? ? other_end <= begin_value : other_end < begin_value | ||
| end | ||
|
|
||
| true | ||
| end | ||
|
|
||
| # Returns a range that covers all values in this range and *other*. | ||
| # | ||
| # Returns `nil` if the ranges cannot be represented as a single continuous range. | ||
| # | ||
| # ``` | ||
| # (1..5).union(3..7) # => 1..7 | ||
| # (1...10).union(10..20) # => 1..20 | ||
| # (1..5).union(10..20) # => nil | ||
| # (1..5).union(6..10) # => 1..10 | ||
| # ``` | ||
| def union(other : Range(OB, OE)) : Range(B | OB, E | OE)? forall OB, OE | ||
| self_empty = empty_without_iterating? | ||
| other_empty = other.empty_without_iterating? | ||
|
|
||
| return nil if self_empty && other_empty | ||
| return Range(B | OB, E | OE).new(other.begin, other.end, other.excludes_end?) if self_empty | ||
| return Range(B | OB, E | OE).new(@begin, @end, @exclusive) if other_empty | ||
| return nil unless overlaps?(other) || adjacent_to?(other) | ||
|
|
||
| begin_value = @begin | ||
| end_value = @end | ||
| other_begin = other.begin | ||
| other_end = other.end | ||
|
|
||
| union_begin = | ||
| if begin_value.nil? || (other_begin && begin_value <= other_begin) | ||
| begin_value | ||
| else | ||
| other_begin | ||
| end | ||
|
|
||
| union_end, exclusive = | ||
| if end_value.nil? | ||
| {end_value, @exclusive} | ||
| elsif other_end.nil? | ||
| {other_end, other.excludes_end?} | ||
| elsif end_value < other_end | ||
| {other_end, other.excludes_end?} | ||
| elsif end_value > other_end | ||
| {end_value, @exclusive} | ||
| else | ||
| {end_value, @exclusive && other.excludes_end?} | ||
| end | ||
|
|
||
| Range(B | OB, E | OE).new(union_begin, union_end, exclusive) | ||
| end | ||
|
|
||
| # Returns a range that covers all values common to this range and *other*. | ||
| # | ||
| # Returns `nil` if the ranges do not overlap. | ||
| # | ||
| # ``` | ||
| # (1..5).intersection(3..7) # => 3..5 | ||
| # (1...10).intersection(7..12) # => 7...10 | ||
| # (1..5).intersection(10..20) # => nil | ||
| # (1...10).intersection(10..) # => nil | ||
| # ``` | ||
| def intersection(other : Range(OB, OE)) : Range(B | OB, E | OE)? forall OB, OE | ||
| return nil unless overlaps?(other) | ||
|
|
||
| begin_value = @begin | ||
| end_value = @end | ||
| other_begin = other.begin | ||
| other_end = other.end | ||
|
|
||
| intersection_begin = | ||
| if begin_value.nil? || (other_begin && other_begin >= begin_value) | ||
| other_begin | ||
| else | ||
| begin_value | ||
| end | ||
|
|
||
| intersection_end, exclusive = | ||
| if end_value.nil? | ||
| {other_end, other.excludes_end?} | ||
| elsif other_end.nil? | ||
| {end_value, @exclusive} | ||
| elsif other_end < end_value | ||
| {other_end, other.excludes_end?} | ||
| elsif end_value < other_end | ||
| {end_value, @exclusive} | ||
| else | ||
| {end_value, @exclusive || other.excludes_end?} | ||
| end | ||
|
|
||
| Range(B | OB, E | OE).new(intersection_begin, intersection_end, exclusive) | ||
| end | ||
|
|
||
| # Same as `includes?`. | ||
| def covers?(value) | ||
| includes?(value) | ||
|
|
@@ -319,6 +434,35 @@ struct Range(B, E) | |
| to_s(io) | ||
| end | ||
|
|
||
| protected def empty_without_iterating? : Bool | ||
| return false unless end_value = @end | ||
| return false unless begin_value = @begin | ||
|
|
||
| @exclusive ? begin_value >= end_value : begin_value > end_value | ||
| end | ||
|
|
||
| private def adjacent_to?(other : Range) : Bool | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How bad an idea would it be to make this method public? |
||
| end_value = @end | ||
| other_begin = other.begin | ||
|
|
||
| return true if end_value && other_begin && end_value == other_begin | ||
| unless excludes_end? | ||
| return true if end_value && other_begin && end_value.responds_to?(:succ) && end_value.succ == other_begin | ||
| return true if end_value && other_begin && other_begin.responds_to?(:pred) && other_begin.pred == end_value | ||
| end | ||
|
|
||
| other_end = other.end | ||
| begin_value = @begin | ||
|
|
||
| return true if other_end && begin_value && other_end == begin_value | ||
| unless other.excludes_end? | ||
| return true if other_end && begin_value && other_end.responds_to?(:succ) && other_end.succ == begin_value | ||
| return true if other_end && begin_value && begin_value.responds_to?(:pred) && begin_value.pred == other_end | ||
| end | ||
|
|
||
| false | ||
| end | ||
|
|
||
| # Optimized version of `Enumerable#sum` that runs in O(1) time when `self` is | ||
| # an `Int` range. | ||
| def sum(initial) | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.