<commit_before>module Gisele
  class VM
    class ProgList
      class Memory < ProgList

        def initialize(progs = [])
          @progs = progs
        end

        def fetch(puid)
          @progs[is_a_valid_puid!(puid)].dup
        end

        def save(prog)
          if Array===prog
            prog.map{|p| save(p)}
          else
            prog = is_a_prog!(prog)
            prog.puid ? save_prog(prog) : register_prog(prog)
          end
        end

        def pick(waitfor, &bl)
          @progs.find{|p| p.waitfor == waitfor}.tap{|found|
            bl.call if bl && !found
          }
        end

        def empty?
          @progs.empty?
        end

        def to_relation
          Alf::Relation(@progs.map{|p| p.to_hash})
        end

      private

        def save_prog(prog)
          prog.puid.tap{|puid|
            @progs[is_a_valid_puid!(puid)] = prog.dup
          }
        end

        def register_prog(prog)
          @progs << prog.dup.tap{|d|
            d.puid   = @progs.size
            d.parent = d.puid if d.parent.nil?
          }
          @progs.last.puid
        end

        def is_a_prog!(prog)
          raise ArgumentError, "Invalid prog: #{prog}", caller unless Prog===prog
          prog
        end

        def is_a_valid_puid!(puid)
          case puid
          when /^\d+$/
            is_a_valid_puid! Integer(puid)
          else
            unless valid_puid?(puid)
              raise InvalidPUIDError, "Invalid puid: `#{puid.inspect}`"
            end
            puid
          end
        end

        def valid_puid?(puid)
          (Integer===puid) and (0 <= puid) and (puid < @progs.size)
        end

      end # class Memory
    end # class ProgList
  end # class VM
end # module Gisele
<commit_msg>ProgList::Memory#pick picks one Prog at random to favor fairness.<commit_after>module Gisele
  class VM
    class ProgList
      class Memory < ProgList

        def initialize(progs = [])
          @progs = progs
        end

        def fetch(puid)
          @progs[is_a_valid_puid!(puid)].dup
        end

        def save(prog)
          if Array===prog
            prog.map{|p| save(p)}
          else
            prog = is_a_prog!(prog)
            prog.puid ? save_prog(prog) : register_prog(prog)
          end
        end

        def pick(waitfor, &bl)
          candidate = @progs.select{|p| p.waitfor == waitfor}.sample
          bl.call if bl and candidate.nil?
          candidate
        end

        def empty?
          @progs.empty?
        end

        def to_relation
          Alf::Relation(@progs.map{|p| p.to_hash})
        end

      private

        def save_prog(prog)
          prog.puid.tap{|puid|
            @progs[is_a_valid_puid!(puid)] = prog.dup
          }
        end

        def register_prog(prog)
          @progs << prog.dup.tap{|d|
            d.puid   = @progs.size
            d.parent = d.puid if d.parent.nil?
          }
          @progs.last.puid
        end

        def is_a_prog!(prog)
          raise ArgumentError, "Invalid prog: #{prog}", caller unless Prog===prog
          prog
        end

        def is_a_valid_puid!(puid)
          case puid
          when /^\d+$/
            is_a_valid_puid! Integer(puid)
          else
            unless valid_puid?(puid)
              raise InvalidPUIDError, "Invalid puid: `#{puid.inspect}`"
            end
            puid
          end
        end

        def valid_puid?(puid)
          (Integer===puid) and (0 <= puid) and (puid < @progs.size)
        end

      end # class Memory
    end # class ProgList
  end # class VM
end # module Gisele

{'fraction_non_alphanumeric': 0.09933774834437085, 'fraction_numerical': 0.0005518763796909492, 'mean_word_length': 1.3927392739273927, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4648532', 'n_tokens_mistral': 1230, 'n_tokens_neox': 1146, 'n_words': 261}