<commit_before>module Transrate

  class SnapError < StandardError
  end

  class Snap

    attr_reader :index_name, :sam, :read_count

    def initialize
      which_snap = Cmd.new('which snap')
      which_snap.run
      if !which_snap.status.success?
        raise SnapError.new("could not find snap in the path")
      end
      @snap = which_snap.stdout.split("\n").first

      @index_built = false
      @index_name = ""
    end

    def build_paired_cmd l, r, threads
      cmd = "#{@snap} paired #{@index_name}"
      l.split(",").zip(r.split(",")).each do |left, right|
        cmd << " #{left} #{right}"
      end
      # NOTE: do NOT turn on the -so flag (sort bam output)
      # it violates the basic assumption of eXpress's streaming
      # algorithm: that the fragments are observed in approximately
      # random order.
      cmd << " -o #{@bam}"
      cmd << " -s 0 1000" # min and max distance between paired-read starts
      cmd << " -H 300000" # max seed hits to consider in paired mode
      cmd << " -h 2000" # max seed hits to consider when reverting to single
      cmd << " -I"   # ignore read IDs
      cmd << " -d 30" # max edit distance (function of read length?)
      cmd << " -t #{threads}"
      cmd << " -b" # bind threads to cores
      cmd << " -M"  # format cigar string
      cmd << " -sa" # keep all alignments, don't discard 0x100
      # cmd << " -C++" # trim low-quality bases from front and back of reads
      cmd
    end

    def map_reads(file, left, right, insertsize: 200,
                  insertsd: 50, outputname: nil, threads: 8)
      raise SnapError.new("Index not built") if !@index_built

      lbase = File.basename(left.split(",").first)
      rbase = File.basename(right.split(",").first)
      index = File.basename(@index_name)
      @bam = File.expand_path("#{lbase}.#{rbase}.#{index}.bam")
      @read_count_file = "#{lbase}-#{rbase}-read_count.txt"

      unless File.exists? @bam
        snapcmd = build_paired_cmd(left, right, threads)
        runner = Cmd.new snapcmd
        runner.run
        save_readcount runner.stdout
        unless runner.status.success?
          raise SnapError.new("Snap failed\n#{runner.stderr}")
        end
      else
        load_readcount left
      end
      @bam<commit_msg>include captured stdout in SNAP error<commit_after>module Transrate

  class SnapError < StandardError
  end

  class Snap

    attr_reader :index_name, :sam, :read_count

    def initialize
      which_snap = Cmd.new('which snap')
      which_snap.run
      if !which_snap.status.success?
        raise SnapError.new("could not find snap in the path")
      end
      @snap = which_snap.stdout.split("\n").first

      @index_built = false
      @index_name = ""
    end

    def build_paired_cmd l, r, threads
      cmd = "#{@snap} paired #{@index_name}"
      l.split(",").zip(r.split(",")).each do |left, right|
        cmd << " #{left} #{right}"
      end
      # NOTE: do NOT turn on the -so flag (sort bam output)
      # it violates the basic assumption of eXpress's streaming
      # algorithm: that the fragments are observed in approximately
      # random order.
      cmd << " -o #{@bam}"
      cmd << " -s 0 1000" # min and max distance between paired-read starts
      cmd << " -H 300000" # max seed hits to consider in paired mode
      cmd << " -h 2000" # max seed hits to consider when reverting to single
      cmd << " -I"   # ignore read IDs
      cmd << " -d 30" # max edit distance (function of read length?)
      cmd << " -t #{threads}"
      cmd << " -b" # bind threads to cores
      cmd << " -M"  # format cigar string
      cmd << " -sa" # keep all alignments, don't discard 0x100
      # cmd << " -C++" # trim low-quality bases from front and back of reads
      cmd
    end

    def map_reads(file, left, right, insertsize: 200,
                  insertsd: 50, outputname: nil, threads: 8)
      raise SnapError.new("Index not built") if !@index_built

      lbase = File.basename(left.split(",").first)
      rbase = File.basename(right.split(",").first)
      index = File.basename(@index_name)
      @bam = File.expand_path("#{lbase}.#{rbase}.#{index}.bam")
      @read_count_file = "#{lbase}-#{rbase}-read_count.txt"

      unless File.exists? @bam
        snapcmd = build_paired_cmd(left, right, threads)
        runner = Cmd.new snapcmd
        runner.run
        save_readcount runner.stdout
        unless runner.status.success?
          raise SnapError.new("Snap failed\n#{runner.stderr}\n#{runner.stdout}")
        end
      else
        load_readcount left
      end
      @bam
{'fraction_non_alphanumeric': 0.12244454110482819, 'fraction_numerical': 0.0117442366246194, 'mean_word_length': 2.356934306569343, 'pattern_counts': {'":': 0, '<': 53, '<?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': '1266889', 'n_tokens_mistral': 1494, 'n_tokens_neox': 1398, 'n_words': 444}