def run
      n = opt[:min_blast_hits] < 10 ? 10 : opt[:min_blast_hits]
      n = 50 if n > 50

      raise NotEnoughHitsError if hits.length < n
      raise unless prediction.is_a?(Query) && hits[0].is_a?(Query)

      start = Time.new
      # get the first n hits
      n_hits = [n - 1, @hits.length].min
      less_hits = @hits[0..n_hits]
      # get raw sequences for less_hits
      less_hits.delete_if do |hit|
        if hit.raw_sequence.nil?
          hit.raw_sequence = FetchRawSequences.run(hit.identifier,
                                                   hit.accession_no)
        end
        hit.raw_sequence.nil? ? true : false
      end

      raise NoInternetError if less_hits.length.zero?
      # in case of nucleotide prediction sequence translate into protein
      # translate with the reading frame of all hits considered for alignment
      reading_frames = less_hits.map(&:reading_frame).uniq
      raise ReadingFrameError if reading_frames.length != 1

      if @type == :nucleotide
        s = Bio::Sequence::NA.new(prediction.raw_sequence)
        prediction.protein_translation = s.translate(reading_frames[0])
      end

      # multiple align sequences from less_hits with the prediction
      # the prediction is the last sequence in the vector
      @multiple_alignment = multiple_align_mafft(prediction, less_hits)

      out = get_sm_pssm(@multiple_alignment[0..@multiple_alignment.length - 2])
      sm = out[0]
      freq = out[1]

      # remove isolated residues from the predicted sequence
      index          = @multiple_alignment.length - 1
      prediction_raw = remove_isolated_residues(@multiple_alignment[index])
      # remove isolated residues from the statistical model
      sm = remove_isolated_residues(sm)

      a1 = get_consensus(@multiple_alignment[0..@multiple_alignment.length - 2])

      plot1     = plot_alignment(freq)
      gaps      = gap_validation(prediction_raw, sm)
      extra_seq = extra_sequence_validation(prediction_raw, sm)
      consensus = consensus_validation(prediction_raw, a1)

      @validation_report = AlignmentValidationOutput.new(@short_header, @header,
                                                         @description, gaps,
                                                         extra_seq, consensus)
      @validation_report.plot_files.push(plot1)
      @validation_report.run_time = Time.now - start
      @validation_report
    rescue NotEnoughHitsError
      @validation_report = ValidationReport.new('Not enough evidence',
                                                :warning, @short_header,
                                                @header, @description)
    rescue NoMafftInstallationError
      @validation_report = ValidationReport.new('Mafft error', :error,
                                                @short_header, @header,
                                                @description)
      @validation_report.errors.push NoMafftInstallationError
    rescue NoInternetError
      @validation_report = ValidationReport.new('Internet error', :error,
                                                @short_header, @header,
                                                @description)
      @validation_report.errors.push NoInternetError
    rescue ReadingFrameError
      @validation_report = ValidationReport.new('Multiple reading frames',
                                                :error, @short_header,
                                                @header, @description)
      @validation_report.errors.push 'Multiple reading frames Error'
    rescue StandardError
      @validation_report = ValidationReport.new('Unexpected error', :error,
                                                @short_header, @header,
                                                @description)
      @validation_report.errors.push 'Unexpected Error'
    end
def run
      n = opt[:min_blast_hits] < 10 ? 10 : opt[:min_blast_hits]
      n = 50 if n > 50

      raise NotEnoughHitsError if hits.length < n
      raise unless prediction.is_a?(Query) && hits[0].is_a?(Query)

      start = Time.new
      # get the first n hits
      n_hits = [n - 1, @hits.length].min
      less_hits = @hits[0..n_hits]
      # get raw sequences for less_hits
      less_hits.delete_if do |hit|
        if hit.raw_sequence.nil?
          hit.raw_sequence = FetchRawSequences.run(hit.identifier,
                                                   hit.accession_no)
        end
        hit.raw_sequence.nil? ? true : false
      end

      raise NoInternetError if less_hits.length.zero?
      # in case of nucleotide prediction sequence translate into protein
      # translate with the reading frame of all hits considered for alignment
      reading_frames = less_hits.map(&:reading_frame).uniq
      raise ReadingFrameError if reading_frames.length != 1

      if @type == :nucleotide
        s = Bio::Sequence::NA.new(prediction.raw_sequence)
        prediction.protein_translation = s.translate(reading_frames[0])
      end

      # multiple align sequences from less_hits with the prediction
      # the prediction is the last sequence in the vector
      @multiple_alignment = multiple_align_mafft(prediction, less_hits)

      out = get_sm_pssm(@multiple_alignment[0..@multiple_alignment.length - 2])
      sm = out[0]
      freq = out[1]

      # remove isolated residues from the predicted sequence
      index          = @multiple_alignment.length - 1
      prediction_raw = remove_isolated_residues(@multiple_alignment[index])
      # remove isolated residues from the statistical model
      sm = remove_isolated_residues(sm)

      a1 = get_consensus(@multiple_alignment[0..@multiple_alignment.length - 2])

      plot1     = plot_alignment(freq)
      gaps      = gap_validation(prediction_raw, sm)
      extra_seq = extra_sequence_validation(prediction_raw, sm)
      consensus = consensus_validation(prediction_raw, a1)

      @validation_report = AlignmentValidationOutput.new(@short_header, @header,
                                                         @description, gaps,
                                                         extra_seq, consensus)
      @validation_report.plot_files.push(plot1)
      @validation_report.run_time = Time.now - start
      @validation_report
    rescue NotEnoughHitsError
      @validation_report = ValidationReport.new('Not enough evidence',
                                                :warning, @short_header,
                                                @header, @description)
    rescue NoMafftInstallationError
      @validation_report = ValidationReport.new('Mafft error', :error,
                                                @short_header, @header,
                                                @description)
      @validation_report.errors.push NoMafftInstallationError
    rescue NoInternetError
      @validation_report = ValidationReport.new('Internet error', :error,
                                                @short_header, @header,
                                                @description)
      @validation_report.errors.push NoInternetError
    rescue ReadingFrameError
      @validation_report = ValidationReport.new('Multiple reading frames',
                                                :error, @short_header,
                                                @header, @description)
      @validation_report.errors.push 'Multiple reading frames Error'
    rescue StandardError
      @validation_report = ValidationReport.new('Unexpected error', :error,
                                                @short_header, @header,
                                                @description)
      @validation_report.errors.push 'Unexpected Error'
    end
Initilizes the object
 Params:
 +prediction+: a +Sequence+ object representing the blast query
 +hits+: a vector of +Sequence+ objects (representing blast hits)
 +plot_path+: name of the fasta file

 Find gaps/extra regions based on the multiple alignment
 of the first n hits
 Output:
 +AlignmentValidationOutput+ object