qnaCrossTrainCore = function (luResource, qnaResource, fileName, interruptionIntentName, allLuEmpty) {
  let trainedLuResource = luResource
  let trainedQnaResource = qnaResource

  // extract questions
  const qnaSections = qnaResource.Sections.filter(s => s.SectionType === LUSectionTypes.QNASECTION)
  let questions = []
  qnaSections.forEach(q => questions = questions.concat(q.Questions))
  
  // remove dups of questions themselves
  questions = Array.from(new Set(questions))

  // extract lu utterances of all intents
  let utterances = extractIntentUtterances(luResource)
  utterances = Array.from(new Set(utterances))

  // extract lu utterances of interruption intent
  let utterancesOfInterruption = extractIntentUtterances(luResource, interruptionIntentName)

  // extract lu utterances except interruption
  let utterancesOfLocalIntents = utterances.filter(u => !utterancesOfInterruption.includes(u)).map(u => u.toLowerCase())

  // remove questions which are duplicated with local lu utterances
  let dedupedQuestions = questions.filter(q => !utterancesOfLocalIntents.includes(q.toLowerCase()))

  // update interruption intent if there are duplications with questions
  let dedupedQuestionsOfLowerCase = dedupedQuestions.map(u => u.toLowerCase())
  if (utterancesOfInterruption.some(u => dedupedQuestionsOfLowerCase.includes(u.toLowerCase()))) {
    utterancesOfInterruption = utterancesOfInterruption.filter(u => !dedupedQuestionsOfLowerCase.includes(u.toLowerCase()))

    // get section id
    const sectionId = trainedLuResource.Sections.filter(s => s.Name === interruptionIntentName)[0].Id

    // construct updated interruption intent content
    utterancesOfInterruption = utterancesOfInterruption.map(u => '- '.concat(u))
    let updatedSectionContent = utterancesOfInterruption.join(NEWLINE)
    if (updatedSectionContent && updatedSectionContent !== '') {
      trainedLuResource = new SectionOperator(trainedLuResource).updateSection(sectionId, `# ${interruptionIntentName}${NEWLINE}${updatedSectionContent}`)
    }
  }

  // construct questions content
  dedupedQuestions = dedupedQuestions.map(q => '- '.concat(q)).filter(i => !patternWithPrebuiltEntity(i))
  let questionsContent = dedupedQuestions.join(NEWLINE)

  // cross training comments
  const crossTrainingComments = '> Source: cross training. Please do not edit these directly!'

  // add questions from qna file to corresponding lu file with intent named DeferToRecognizer_QnA_${fileName}
  if (!allLuEmpty && questionsContent && questionsContent !== '') {
    const questionsToUtterances = `${NEWLINE}${crossTrainingComments}${NEWLINE}# DeferToRecognizer_QnA_${fileName}${NEWLINE}${questionsContent}`
    trainedLuResource = new SectionOperator(trainedLuResource).addSection(questionsToUtterances)
  }

  // update qna filters
  trainedQnaResource = qnaAddMetaData(qnaResource, fileName)

  // remove utterances with curly brackets
  const utterancesWithoutPatterns = utterances.filter(i => /{([^}]+)}/g.exec(i) === null)

  // remove utterances which are duplicated with local qna questions
  let questionsOfLowerCase = questions.map(q => q.toLowerCase())
  let dedupedUtterances = utterancesWithoutPatterns.filter(u => !questionsOfLowerCase.includes(u.toLowerCase()))

  // add utterances from lu file to corresponding qna file with question set to all utterances
  // split large QA pair to multiple smaller ones to overcome the limit that the maximum number of questions per answer is 300
  while (dedupedUtterances.length > 0) {
    let subDedupedUtterances = dedupedUtterances.splice(0, MAX_QUESTIONS_PER_ANSWER)
    // construct new question content for qna resource
    let utterancesContent = subDedupedUtterances.join(NEWLINE + '- ')
    let utterancesToQuestion = `${NEWLINE}${crossTrainingComments}${NEWLINE}> !# @qna.pair.source = crosstrained${NEWLINE}${NEWLINE}# ? ${utterancesContent}${NEWLINE}${NEWLINE}**Filters:**${NEWLINE}- dialogName=${fileName}${NEWLINE}${NEWLINE}\`\`\`${NEWLINE}intent=DeferToRecognizer_LUIS_${fileName}${NEWLINE}\`\`\``
    trainedQnaResource = new SectionOperator(trainedQnaResource).addSection(utterancesToQuestion)
  }

  return { luResource: trainedLuResource, qnaResource: trainedQnaResource }
}
qna cross training core function
@param {LUResource} luResource the lu resource
@param {LUResource} qnaResource the qna resource
@param {string} fileName file name
@param {string} interruptionIntentName interruption intent name
@param {boolean} allLuEmpty indicate if all lu files are section empty
@returns {luResource: LUResource, qnaResource: LUResource} cross trained lu resource and qna resource