class Array

  #
  # flatten.compact.uniq
  #
  def squash
    flatten.compact.uniq
  end

  #def to_hash
  #  Hash[self]
  #end

  #
  # Removes the elements from the array for which the block evaluates to true.
  # In addition, return the removed elements.
  #
  # For example, if you wanted to split an array into evens and odds:
  #
  #   nums = [1,2,3,4,5,6,7,8,9,10,11,12]
  #   even = nums.remove_if { |n| n.even? }   # remove all even numbers from the "nums" array and return them
  #   odd = nums                              # "nums" now only contains odd numbers
  #
  def remove_if(&block)
    removed = []

    delete_if do |x|
      if block.call(x)
        removed << x
        true
      else
        false
      end
    end

    removed
  end

  #
  # zip from the right (or reversed zip.)
  #
  # eg:
  #   >> [5,39].rzip([:hours, :mins, :secs])
  #   => [ [:mins, 5], [:secs, 39] ]
  #
  def rzip(other)
    # That's a lotta reverses!
    reverse.zip(other.reverse).reverse
  end

  #
  # Pick the middle element.
  #
  def middle
    self[(size-1) / 2]
  end

  #
  # XOR operator
  #
  def ^(other)
    (self | other) - (self & other)
  end

  #
  # Shuffle the array
  #
  unless instance_methods.include? :shuffle
    def shuffle
      sort_by{rand}
    end
  end

  #
  # Pick (a) random element(s).
  #
  unless instance_methods.include? :sample
    def sample(n=1)
      return self[rand sz] if n == 1

      sz      = size
      indices = []

      loop do
        indices += (0..n*1.2).map { rand sz }
        indices.uniq
        break if indices.size >= n
      end

      values_at(*indices[0...n])
    end
  end
  alias_method :pick, :sample

  #
  # Divide the array into n pieces.
  #
  def / pieces
    piece_size = (size.to_f / pieces).ceil
    each_slice(piece_size).to_a
  end

  alias_method :unzip, :transpose

  #
  # Convert the array to a hash
  #
  def to_h
    if self.first.is_a? Array
      Hash[self]
    else
      Hash[*self]
    end
  end

  #
  # Takes an array of numbers, puts them into equal-sized
  # buckets, and counts the buckets (aka. A Histogram!)
  #
  # Examples:
  #   [1,2,3,4,5,6,7,8,9].histogram(3) #=> [3,3,3]
  #   [1,2,3,4,5,6,7,8,9].histogram(2) #=> [4,5]
  #   [1,2,3,4,5,6,7,8,9].histogram(2, ranges: true)
  #      #=> {
  #            1.0...5.0 => 4,
  #            5.0...9.0 => 5
  #          }
  #
  def histogram(n_buckets=10, options={})
    
    use_ranges = options[:ranges] || options[:hash]

    min_val     = min
    max_val     = max
    range       = (max_val - min_val)
    bucket_size = range.to_f / n_buckets
    buckets     = [0]*n_buckets

    # p [range, bucket_size, buckets, min_val, max_val]

    each do |e|
      bucket = (e - min_val) / bucket_size
      bucket = n_buckets - 1 if bucket >= n_buckets
      # p [:e, e, :bucket, bucket]
      buckets[bucket] += 1
    end

    if use_ranges
      ranges = (0...n_buckets).map do |n|
        offset = n*bucket_size
        (min_val + offset) ... (min_val + offset + bucket_size)
      end
      Hash[ ranges.zip(buckets) ]
    else
      buckets
    end

  end

end