All real numbers form the uncountable set ℝ. Among its subsets, relatively simple are the convex sets, each expressed as a range between two real numbers a and b where a ≤ b. There are actually four cases for the meaning of "between", depending on open or closed boundary:

 [a, b]: {x | a ≤ x and x ≤ b }
 (a, b): {x | a < x and x < b }
 [a, b): {x | a ≤ x and x < b }
 (a, b]: {x | a < x and x ≤ b }
Note that if a = b, of the four only [a, a] would be non-empty.

Task

 Devise a way to represent any set of real numbers, for the definition of 'any' in the implementation notes below.
 Provide methods for these common set operations (x is a real number; A and B are sets):
 x ∈ A: determine if x is an element of A
 example: 1 is in [1, 2), while 2, 3, ... are not.
 A ∪ B: union of A and B, i.e. {x | x ∈ A or x ∈ B}
 example: [0, 2) ∪ (1, 3) = [0, 3); [0, 1) ∪ (2, 3] = well, [0, 1) ∪ (2, 3]
 A ∩ B: intersection of A and B, i.e. {x | x ∈ A and x ∈ B}
 example: [0, 2) ∩ (1, 3) = (1, 2); [0, 1) ∩ (2, 3] = empty set
 A - B: difference between A and B, also written as A \ B, i.e. {x | x ∈ A and x ∉ B}
 example: [0, 2) − (1, 3) = [0, 1]
 Test your implementation by checking if numbers 0, 1, and 2 are in any of the following sets:
 (0, 1] ∪ [0, 2)
 [0, 2) ∩ (1, 2]
 [0, 3) − (0, 1)
 [0, 3) − [0, 1]
Implementation notes

 'Any' real set means 'sets that can be expressed as the union of a finite number of convex real sets'. Cantor's set needs not apply.
 Infinities should be handled gracefully; indeterminate numbers (NaN) can be ignored.
 You can use your machine's native real number representation, which is probably IEEE floating point, and assume it's good enough (it usually is).
Optional work

 Create a function to determine if a given set is empty (contains no element).
 Define A = {x | 0 < x < 10 and |sin(π x²)| > 1/2 }, B = {x | 0 < x < 10 and |sin(π x)| > 1/2}, calculate the length of the real axis covered by the set A − B. Note that 
|sin(π x)| > 1/2 is the same as n + 1/6 < x < n + 5/6 for all integers n; your program does not need to derive this by itself.

class Rset
  Set = Struct.new(:lo, :hi, :inc_lo, :inc_hi) do
    def include?(x)
      (inc_lo ? lo<=x : lo<x) and (inc_hi ? x<=hi : x<hi)
    end
    def length
      hi - lo
    end
    def to_s
      "#{inc_lo ? '[' : '('}#{lo},#{hi}#{inc_hi ? ']' : ')'}"
    end
  end
 
  def initialize(lo=nil, hi=nil, inc_lo=false, inc_hi=false)
    if lo.nil? and hi.nil?
      @sets = []            # empty set
    else
      raise TypeError      unless lo.is_a?(Numeric) and hi.is_a?(Numeric)
      raise ArgumentError  unless valid?(lo, hi, inc_lo, inc_hi)
      @sets = [Set[lo, hi, !!inc_lo, !!inc_hi]]         # !! -> Boolean values
    end
  end
 
  def self.[](lo, hi, inc_hi=true)
    self.new(lo, hi, true, inc_hi)
  end
 
  def self.parse(str)
    raise ArgumentError  unless str =~ /(\[|\()(.+),(.+)(\]|\))/
    b0, lo, hi, b1 = $~.captures        # $~ : Regexp.last_match
    lo = Rational(lo)
    lo = lo.numerator  if lo.denominator == 1
    hi = Rational(hi)
    hi = hi.numerator  if hi.denominator == 1
    self.new(lo, hi, b0=='[', b1==']')
  end
 
  def initialize_copy(obj)
    super
    @sets = @sets.map(&:dup)
  end
 
  def include?(x)
    @sets.any?{|set| set.include?(x)}
  end
 
  def empty?
    @sets.empty?
  end
 
  def union(other)
    sets = (@sets+other.sets).map(&:dup).sort_by{|set| [set.lo, set.hi]}
    work = []
    pre = sets.shift
    sets.each do |post|
      if valid?(pre.hi, post.lo, !pre.inc_hi, !post.inc_lo)
        work << pre
        pre = post
      else
        pre.inc_lo |= post.inc_lo  if pre.lo == post.lo
        if pre.hi < post.hi
          pre.hi = post.hi
          pre.inc_hi = post.inc_hi
        elsif pre.hi == post.hi
          pre.inc_hi |= post.inc_hi
        end
      end
    end
    work << pre  if pre
    new_Rset(work)
  end
  alias | union
 
  def intersection(other)
    sets = @sets.map(&:dup)
    work = []
    other.sets.each do |oset|
      sets.each do |set|
        if set.hi < oset.lo or oset.hi < set.lo
          # ignore
        elsif oset.lo < set.lo and set.hi < oset.hi
          work << set
        else
          lo = [set.lo, oset.lo].max
          if set.lo == oset.lo
            inc_lo = set.inc_lo && oset.inc_lo
          else
            inc_lo = (set.lo < oset.lo) ? oset.inc_lo : set.inc_lo
          end
          hi = [set.hi, oset.hi].min
          if set.hi == oset.hi
            inc_hi = set.inc_hi && oset.inc_hi
          else
            inc_hi = (set.hi < oset.hi) ? set.inc_hi : oset.inc_hi
          end
          work << Set[lo, hi, inc_lo, inc_hi]  if valid?(lo, hi, inc_lo, inc_hi)
        end
      end
    end
    new_Rset(work)
  end
  alias & intersection
 
  def difference(other)
    sets = @sets.map(&:dup)
    other.sets.each do |oset|
      work = []
      sets.each do |set|
        if set.hi < oset.lo or oset.hi < set.lo
          work << set
        elsif oset.lo < set.lo and set.hi < oset.hi
          # delete
        else
          if set.lo < oset.lo
            inc_hi = (set.hi==oset.lo and !set.inc_hi) ? false : !oset.inc_lo
            work << Set[set.lo, oset.lo, set.inc_lo, inc_hi]
          elsif valid?(set.lo, oset.lo, set.inc_lo, !oset.inc_lo)
            work << Set[set.lo, set.lo, true, true]
          end
          if oset.hi < set.hi
            inc_lo = (oset.hi==set.lo and !set.inc_lo) ? false : !oset.inc_hi
            work << Set[oset.hi, set.hi, inc_lo, set.inc_hi]
          elsif valid?(oset.hi, set.hi, !oset.inc_hi, set.inc_hi)
            work << Set[set.hi, set.hi, true, true]
          end
        end
      end
      sets = work
    end
    new_Rset(sets)
  end
  alias - difference
 
  # symmetric difference
  def ^(other)
    (self - other) | (other - self)
  end
 
  def ==(other)
    self.class == other.class and @sets == other.sets
  end
 
  def length
    @sets.inject(0){|len, set| len + set.length}
  end
 
  def to_s
    "#{self.class}#{@sets.join}"
  end
  alias inspect to_s
 
  protected
 
  attr_accessor :sets
 
  private
 
  def new_Rset(sets)
    rset = self.class.new          # empty set
    rset.sets = sets
    rset
  end
 
  def valid?(lo, hi, inc_lo, inc_hi)
    lo < hi or (lo==hi and inc_lo and inc_hi)
  end
end
 
def Rset(lo, hi, inc_hi=false)
  Rset.new(lo, hi, false, inc_hi)
end