Last active
August 29, 2015 14:05
-
-
Save bravoecho/73ea750acae37ac2a29d to your computer and use it in GitHub Desktop.
Week bitmask / bitarray
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
require 'set' | |
class Week | |
DAYS = %w[Mon Tue Wed Thu Fri Sat Sun] | |
REVERTED_DAYS = DAYS.reverse | |
attr_reader :day_bitmask | |
def self.new(*days) | |
from_days(*days) | |
end | |
def self.from_days(*days) | |
instance = allocate | |
instance.instance_eval { @day_bitmask = days_to_bitmask(*days) } | |
instance | |
end | |
def self.from_bitmask(int) | |
instance = allocate | |
instance.instance_eval { @day_bitmask = int } | |
instance | |
end | |
def to_bitmask | |
@day_bitmask | |
end | |
# An integer acts like a bitarray. | |
# This first transforms the bitmask to a bitarray, and then takes the | |
# days whose corresponsing bit is set to 1. | |
def to_a | |
DAYS.select.with_index { |_, i| day_bitmask[i] == 1 } | |
end | |
def days | |
to_a | |
end | |
private | |
def days_to_bitmask(*days) | |
bitarray = days_to_bitarray(*days) | |
bitarray_to_bitmask(bitarray) | |
end | |
def days_to_bitarray(*days) | |
# This is probably super yagni | |
days = Set.new(days) | |
REVERTED_DAYS.map { |day| days.include?(day) ? 1 : 0 } | |
end | |
def bitarray_to_bitmask(ba) | |
ba.join.to_i(2) | |
end | |
end | |
Week.from_days('Mon', 'Thu', 'Fri', 'Sat').to_bitmask # => 57 | |
Week.from_days('Mon', 'Thu', 'Sun').to_bitmask # => 73 | |
Week.from_days('Mon', 'Thu', 'Sat').to_bitmask # => 41 | |
Week.from_days('Fri', 'Sat', 'Sun').to_bitmask # => 112 | |
Week.from_days('Mon', 'Thu').to_bitmask # => 9 | |
Week.from_days('Thu').to_bitmask # => 8 | |
week = Week.from_bitmask(112) # => #<Week:0x007f7c7db5e428 @day_bitmask=112> | |
week.to_a # => ["Fri", "Sat", "Sun"] | |
# Has all? => & operator | |
# Yes => value & target == value | |
41 & 57 # => 41 | |
9 & 57 # => 9 | |
8 & 57 # => 8 | |
# No => value & target != value | |
41 & 73 # => 9 | |
9 & 112 # => 0 | |
# Has any? | |
# Yes => value & target > 0 | |
9 & 57 # => 9 | |
112 & 57 # => 48 | |
# No => value & target == 0 | |
9 & 112 # => 0 | |
8 & 112 # => 0 | |
# | |
# Old version | |
# def to_a | |
# day_bitmask | |
# .to_s(2) | |
# .rjust(7, '0') | |
# .reverse | |
# .each_char | |
# .with_index | |
# .with_object([]) { |(bit, i), result| | |
# result << DAYS[i] if bit == '1' | |
# } | |
# end | |
# Old version | |
# DAYS_WITH_INDEX = Hash[DAYS.map.with_index { |day, idx| [day, idx] }] | |
# def days_to_bitmask(*days) | |
# # compact because sadly values_at(missing_key) returns [nil] | |
# DAYS_WITH_INDEX.values_at(*days).compact.reduce(0) do |result, day_idx| | |
# result + 2**day_idx | |
# end | |
# end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment