static bool makeParserAST(CompilerInstance &CI, StringRef Text,
                          CompilerInvocation Invocation) {
  Invocation.clearInputs();
  Invocation.setModuleName("main");
  Invocation.setInputKind(InputFileKind::IFK_Swift);

  std::unique_ptr<llvm::MemoryBuffer> Buf;
  Buf = llvm::MemoryBuffer::getMemBuffer(Text, "<module-interface>");
  Invocation.addInputBuffer(Buf.get());
  if (CI.setup(Invocation))
    return true;
  CI.performParseOnly();
  return false;
}

// This source file is part of the Swift.org open source project
// See http://example.com for the list of Swift project authors

#include "SwiftLangSupport.h"
#include "SwiftInterfaceGenContext.h"
#include "SwiftASTManager.h"

#include "swift/AST/ASTPrinter.h"
#include "swift/AST/ASTWalker.h"
#include "swift/Basic/Version.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
#include "swift/IDE/ModuleInterfacePrinting.h"
#include "swift/IDE/SyntaxModel.h"
#include "swift/IDE/Utils.h"
#include "swift/Strings.h"

#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/ConvertUTF.h"

using namespace SourceKit;
using namespace swift;
using namespace ide;

class SwiftInterfaceGenContext::Implementation {
public:
  struct TextRange {
    unsigned Offset;
    unsigned Length;
  };

  struct TextReference {
    /// The declaration from the module.
    const ValueDecl *Dcl = nullptr;
    const ModuleEntity Mod;
    TextRange Range;

    TextReference(const ValueDecl *D, unsigned Offset, unsigned Length)
      : Dcl(D), Mod(), Range{Offset, Length} {}
    TextReference(const ModuleEntity Mod, unsigned Offset, unsigned Length)
    : Mod(Mod), Range{Offset, Length} {}
  };

  struct TextDecl {
    /// The declaration from the module.
    const Decl *Dcl = nullptr;
    /// The range in the interface source.
    TextRange Range;

    TextDecl(const Decl *D, TextRange Range)
      : Dcl(D), Range(Range) {}
    TextDecl() = default;
  };

  struct SourceTextInfo {
    std::string Text;
    std::vector<TextReference> References;
    std::vector<TextDecl> Decls;
    llvm::StringMap<TextDecl> USRMap;
  };

  // Hold an AstUnit so that the Decl* we have are always valid.
  ASTUnitRef AstUnit;
  std::string DocumentName;
  bool IsModule = false;
  std::string ModuleOrHeaderName;
  CompilerInvocation Invocation;
  PrintingDiagnosticConsumer DiagConsumer;
  CompilerInstance Instance;
  ModuleDecl *Mod = nullptr;
  SourceTextInfo Info;
  // This is the non-typechecked AST for the generated interface source.
  CompilerInstance TextCI;
  // Synchronize access to the embedded compiler instance (if we don't have an
  WorkQueue Queue{WorkQueue::Dequeuing::Serial,
                  "sourcekit.swift.InterfaceGenContext"};
};

typedef SwiftInterfaceGenContext::Implementation::TextRange TextRange;
typedef SwiftInterfaceGenContext::Implementation::TextReference TextReference;
typedef SwiftInterfaceGenContext::Implementation::TextDecl TextDecl;
typedef SwiftInterfaceGenContext::Implementation::SourceTextInfo SourceTextInfo;

static ModuleDecl *getModuleByFullName(ASTContext &Ctx, StringRef ModuleName) {
  SmallVector<std::pair<Identifier, SourceLoc>, 4>
      AccessPath;
  while (!ModuleName.empty()) {
    StringRef SubModuleName;
    std::tie(SubModuleName, ModuleName) = ModuleName.split('.');
    AccessPath.push_back({ Ctx.getIdentifier(SubModuleName), SourceLoc() });
  }
  return Ctx.getModule(AccessPath);
}

static ModuleDecl *getModuleByFullName(ASTContext &Ctx, Identifier ModuleName) {
  return Ctx.getModule(std::make_pair(ModuleName, SourceLoc()));
}

namespace {
class AnnotatingPrinter : public StreamPrinter {
  SourceTextInfo &Info;

  struct DeclUSR {
    const Decl *Dcl = nullptr;
    std::string USR;
    DeclUSR(const Decl *D, StringRef USR) : Dcl(D), USR(USR) {}
  };
  std::vector<DeclUSR> DeclUSRs;

  // For members of a synthesized extension, we should append the USR of the
  // synthesize target to the original USR.
  std::string TargetUSR;

public:
  AnnotatingPrinter(SourceTextInfo &Info, llvm::raw_ostream &OS)
    : StreamPrinter(OS), Info(Info) { }

  ~AnnotatingPrinter() override {
    assert(DeclUSRs.empty() && "unmatched printDeclLoc call ?");
  }

  void printSynthesizedExtensionPre(const ExtensionDecl *ED,
                                    const NominalTypeDecl *Target,
                                    Optional<BracketOptions> Bracket) override {
    // When we start print a synthesized extension, record the target's USR.
    llvm::SmallString<64> Buf;
    llvm::raw_svector_ostream OS(Buf);
    if (!SwiftLangSupport::printUSR(Target, OS)) {
      TargetUSR = OS.str();
    }
  }

  void printSynthesizedExtensionPost(const ExtensionDecl *ED,
                                     const NominalTypeDecl *Target,
                                     Optional<BracketOptions> Bracket) override {
    // When we leave a synthesized extension, clear target's USR.
    TargetUSR = "";
  }

  void printDeclLoc(const Decl *D) override {
    unsigned LocOffset = OS.tell();
    TextDecl Entry(D, TextRange{LocOffset, 0});
    Info.Decls.emplace_back(Entry);

    if (auto VD = dyn_cast<ValueDecl>(D)) {
      // Only record non-local USRs.
      if (D->getDeclContext()->getLocalContext())
        return;

      llvm::SmallString<64> Buf;
      llvm::raw_svector_ostream OS(Buf);
      if (!SwiftLangSupport::printUSR(VD, OS)) {

        // Append target's USR if this is a member of a synthesized extension.
        if (!TargetUSR.empty()) {
          OS << LangSupport::SynthesizedUSRSeparator;
          OS << TargetUSR;
        }
        StringRef USR = OS.str();
        Info.USRMap[USR] = Entry;
        DeclUSRs.emplace_back(VD, USR);
      }
    }
  }

  void printDeclNameOrSignatureEndLoc(const Decl *D) override {
    unsigned Offset = OS.tell();

    if (!Info.Decls.empty() && Info.Decls.back().Dcl == D) {
      TextDecl &Entry = Info.Decls.back();
      Entry.Range.Length = Offset - Entry.Range.Offset;
    }

    if (!DeclUSRs.empty() && DeclUSRs.back().Dcl == D) {
      TextDecl &Entry = Info.USRMap[DeclUSRs.back().USR];
      assert(Entry.Dcl == D);
      Entry.Range.Length = Offset - Entry.Range.Offset;
      DeclUSRs.pop_back();
    }
  }

  void printTypeRef(Type T, const TypeDecl *TD, Identifier Name) override {
    unsigned StartOffset = OS.tell();
    Info.References.emplace_back(TD, StartOffset, Name.str().size());
    StreamPrinter::printTypeRef(T, TD, Name);
  }

  void printModuleRef(ModuleEntity Mod, Identifier Name) override {
    unsigned StartOffset = OS.tell();
    Info.References.emplace_back(Mod, StartOffset, Name.str().size());
    StreamPrinter::printModuleRef(Mod, Name);
  }
};

class DocSyntaxWalker : public SyntaxModelWalker {
  SourceManager &SM;
  unsigned BufferID;
  EditorConsumer &Consumer;

public:
  DocSyntaxWalker(SourceManager &SM, unsigned BufferID,
                  EditorConsumer &Consumer)
    : SM(SM), BufferID(BufferID), Consumer(Consumer) {}

  bool walkToNodePre(SyntaxNode Node) override {
    unsigned Offset = SM.getLocOffsetInBuffer(Node.Range.getStart(), BufferID);
    unsigned Length = Node.Range.getByteLength();

    UIdent UID = SwiftLangSupport::getUIDForSyntaxNodeKind(Node.Kind);
    if (UID.isValid())
      Consumer.handleSyntaxMap(Offset, Length, UID);
    return true;
  }
};

} // end anonymous namespace

static void reportSyntacticAnnotations(CompilerInstance &CI,
                                       EditorConsumer &Consumer) {
  auto SF = dyn_cast<SourceFile>(CI.getMainModule()->getFiles()[0]);
  SyntaxModelContext SyntaxContext(*SF);
  DocSyntaxWalker SyntaxWalker(CI.getSourceMgr(), *SF->getBufferID(),
                               Consumer);
  SyntaxContext.walk(SyntaxWalker);
}

static void reportDocumentStructure(CompilerInstance &CI,
                                    EditorConsumer &Consumer) {
  auto SF = dyn_cast<SourceFile>(CI.getMainModule()->getFiles()[0]);
  SwiftEditorDocument::reportDocumentStructure(*SF, Consumer);
}

static void reportSemanticAnnotations(const SourceTextInfo &IFaceInfo,
                                      EditorConsumer &Consumer) {
  for (auto &Ref : IFaceInfo.References) {
    UIdent Kind;
    bool IsSystem;
    if (Ref.Mod) {
      Kind = SwiftLangSupport::getUIDForModuleRef();
      IsSystem = Ref.Mod.isSystemModule();
    } else if (Ref.Dcl) {
      Kind = SwiftLangSupport::getUIDForDecl(Ref.Dcl, /*IsRef=*/true);
      IsSystem = Ref.Dcl->getModuleContext()->isSystemModule();
    }
    if (Kind.isInvalid())
      continue;
    unsigned Offset = Ref.Range.Offset;
    unsigned Length = Ref.Range.Length;
    Consumer.handleSemanticAnnotation(Offset, Length, Kind, IsSystem);
  }
}

static bool getModuleInterfaceInfo(ASTContext &Ctx,
                                   StringRef ModuleName,
                                   Optional<StringRef> Group,
                                 SwiftInterfaceGenContext::Implementation &Impl,
                                   std::string &ErrMsg,
                                   bool SynthesizedExtensions,
                                   Optional<StringRef> InterestedUSR) {
  ModuleDecl *&Mod = Impl.Mod;
  SourceTextInfo &Info = Impl.Info;

  if (ModuleName.empty()) {
    ErrMsg = "Module name is empty";
    return true;
  }

  // Get the (sub)module to generate.
  Mod = getModuleByFullName(Ctx, ModuleName);
  if (!Mod) {
    ErrMsg = "Could not load module: ";
    ErrMsg += ModuleName;
    return true;
  }

  std::vector<StringRef> SplitModuleName;
  while (!ModuleName.empty()) {
    StringRef SubModuleName;
    std::tie(SubModuleName, ModuleName) = ModuleName.split('.');
    SplitModuleName.push_back(SubModuleName);
  }
  assert(!SplitModuleName.empty());

  // FIXME: If this is a submodule, get its top-level module, which will be the
  // DeclContext for all of its Decls since we don't have first-class submodules.
  if (SplitModuleName.size() > 1) {
    Mod = getModuleByFullName(Ctx, SplitModuleName[0]);
    if (!Mod) {
      ErrMsg = "Could not load module: ";
      ErrMsg += ModuleName;
      return true;
    }
  }

  PrintOptions Options = PrintOptions::printInterface();
  ModuleTraversalOptions TraversalOptions = None; // Don't print submodules.
  SmallString<128> Text;
  llvm::raw_svector_ostream OS(Text);
  AnnotatingPrinter Printer(Info, OS);
  if (!Group && InterestedUSR) {
    Group = findGroupNameForUSR(Mod, InterestedUSR.getValue());
  }
  printSubmoduleInterface(Mod, SplitModuleName,
    Group.hasValue() ? llvm::makeArrayRef(Group.getValue()) : ArrayRef<StringRef>(),
                          TraversalOptions,
                          Printer, Options,
                          Group.hasValue() && SynthesizedExtensions);

  Info.Text = OS.str();
  return false;
}

static bool getHeaderInterfaceInfo(ASTContext &Ctx,
                                   StringRef HeaderName,
                                   SourceTextInfo &Info,
                                   std::string &ErrMsg) {
  if (HeaderName.empty()) {
    ErrMsg = "Header name is empty";
    return true;
  }

  PrintOptions Options = PrintOptions::printInterface();

  SmallString<128> Text;
  llvm::raw_svector_ostream OS(Text);
  AnnotatingPrinter Printer(Info, OS);
  printHeaderInterface(HeaderName, Ctx, Printer, Options);

  Info.Text = OS.str();
  return false;
}

SwiftInterfaceGenContextRef
SwiftInterfaceGenContext::createForSwiftSource(StringRef DocumentName,
                                               StringRef SourceFileName,
                                               ASTUnitRef AstUnit,
                                               CompilerInvocation Invocation,
                                               std::string &ErrMsg) {
  SwiftInterfaceGenContextRef IFaceGenCtx{ new SwiftInterfaceGenContext() };
  IFaceGenCtx->Impl.DocumentName = DocumentName;
  IFaceGenCtx->Impl.IsModule = true;
  IFaceGenCtx->Impl.ModuleOrHeaderName = SourceFileName;
  IFaceGenCtx->Impl.AstUnit = AstUnit;

  PrintOptions Options = PrintOptions::printSwiftFileInterface();
  SmallString<128> Text;
  llvm::raw_svector_ostream OS(Text);
  AnnotatingPrinter Printer(IFaceGenCtx->Impl.Info, OS);
  printSwiftSourceInterface(AstUnit->getPrimarySourceFile(), Printer, Options);
  IFaceGenCtx->Impl.Info.Text = OS.str();
  if (makeParserAST(IFaceGenCtx->Impl.TextCI, IFaceGenCtx->Impl.Info.Text,
                    Invocation)) {
    ErrMsg = "Error during syntactic parsing";
    return nullptr;
  }
  return IFaceGenCtx;
}

SwiftInterfaceGenContextRef
SwiftInterfaceGenContext::create(StringRef DocumentName,
                                 bool IsModule,
                                 StringRef ModuleOrHeaderName,
                                 Optional<StringRef> Group,
                                 CompilerInvocation Invocation,
                                 std::string &ErrMsg,
                                 bool SynthesizedExtensions,
                                 Optional<StringRef> InterestedUSR) {
  SwiftInterfaceGenContextRef IFaceGenCtx{ new SwiftInterfaceGenContext() };
  IFaceGenCtx->Impl.DocumentName = DocumentName;
  IFaceGenCtx->Impl.IsModule = IsModule;
  IFaceGenCtx->Impl.ModuleOrHeaderName = ModuleOrHeaderName;
  IFaceGenCtx->Impl.Invocation = Invocation;
  CompilerInstance &CI = IFaceGenCtx->Impl.Instance;

  // Display diagnostics to stderr.
  CI.addDiagnosticConsumer(&IFaceGenCtx->Impl.DiagConsumer);

  Invocation.clearInputs();
  if (CI.setup(Invocation)) {
    ErrMsg = "Error during invocation setup";
    return nullptr;
  }

  ASTContext &Ctx = CI.getASTContext();
  CloseClangModuleFiles scopedCloseFiles(*Ctx.getClangModuleLoader());

  // Load standard library so that Clang importer can use it.
  auto *Stdlib = getModuleByFullName(Ctx, Ctx.StdlibModuleName);
  if (!Stdlib) {
    ErrMsg = "Could not load the stdlib module";
    return nullptr;
  }

  if (IsModule) {
    if (getModuleInterfaceInfo(Ctx, ModuleOrHeaderName, Group, IFaceGenCtx->Impl,
                               ErrMsg, SynthesizedExtensions, InterestedUSR))
      return nullptr;
  } else {
    auto &FEOpts = Invocation.getFrontendOptions();
    if (FEOpts.ImplicitObjCHeaderPath.empty()) {
      ErrMsg = "Implicit ObjC header path is empty";
      return nullptr;
    }

    auto &Importer = static_cast<ClangImporter &>(*Ctx.getClangModuleLoader());
    Importer.importBridgingHeader(FEOpts.ImplicitObjCHeaderPath,
                                  CI.getMainModule(),
                                  /*diagLoc=*/{},
                                  /*trackParsedSymbols=*/true);
    if (getHeaderInterfaceInfo(Ctx, ModuleOrHeaderName,
                               IFaceGenCtx->Impl.Info, ErrMsg))
      return nullptr;
  }

  if (makeParserAST(IFaceGenCtx->Impl.TextCI, IFaceGenCtx->Impl.Info.Text,
                    Invocation)) {
    ErrMsg = "Error during syntactic parsing";
    return nullptr;
  }

  return IFaceGenCtx;
}

SwiftInterfaceGenContextRef
SwiftInterfaceGenContext::createForTypeInterface(CompilerInvocation Invocation,
                                                 StringRef TypeUSR,
                                                 std::string &ErrorMsg) {
  SwiftInterfaceGenContextRef IFaceGenCtx{ new SwiftInterfaceGenContext() };
  IFaceGenCtx->Impl.IsModule = false;
  IFaceGenCtx->Impl.ModuleOrHeaderName = TypeUSR;
  IFaceGenCtx->Impl.Invocation = Invocation;
  CompilerInstance &CI = IFaceGenCtx->Impl.Instance;
  SourceTextInfo &Info = IFaceGenCtx->Impl.Info;

  // Display diagnostics to stderr.
  CI.addDiagnosticConsumer(&IFaceGenCtx->Impl.DiagConsumer);

  if (CI.setup(Invocation)) {
    ErrorMsg = "Error during invocation setup";
    return nullptr;
  }
  CI.performSema();
  ASTContext &Ctx = CI.getASTContext();
  CloseClangModuleFiles scopedCloseFiles(*Ctx.getClangModuleLoader());

  // Load standard library so that Clang importer can use it.
  auto *Stdlib = getModuleByFullName(Ctx, Ctx.StdlibModuleName);
  if (!Stdlib) {
    ErrorMsg = "Could not load the stdlib module";
    return nullptr;
  }
  auto *Module = CI.getMainModule();
  if (!Module) {
    ErrorMsg = "Could not load the main module";
    return nullptr;
  }
  SmallString<128> Text;
  llvm::raw_svector_ostream OS(Text);
  AnnotatingPrinter Printer(Info, OS);
  if (ide::printTypeInterface(Module, TypeUSR, Printer,
                              IFaceGenCtx->Impl.DocumentName, ErrorMsg))
    return nullptr;
  IFaceGenCtx->Impl.Info.Text = OS.str();
  if (makeParserAST(IFaceGenCtx->Impl.TextCI, IFaceGenCtx->Impl.Info.Text,
                    Invocation)) {
    ErrorMsg = "Error during syntactic parsing";
    return nullptr;
  }
  return IFaceGenCtx;
}

SwiftInterfaceGenContext::SwiftInterfaceGenContext()
  : Impl(*new Implementation) {
}
SwiftInterfaceGenContext::~SwiftInterfaceGenContext() {
  delete &Impl;
}

StringRef SwiftInterfaceGenContext::getDocumentName() const {
  return Impl.DocumentName;
}

StringRef SwiftInterfaceGenContext::getModuleOrHeaderName() const {
  return Impl.ModuleOrHeaderName;
}

bool SwiftInterfaceGenContext::isModule() const {
  return Impl.IsModule;
}

bool SwiftInterfaceGenContext::matches(StringRef ModuleName,
                                       const swift::CompilerInvocation &Invok) {
  if (!Impl.IsModule)
    return false;
  if (ModuleName != Impl.ModuleOrHeaderName)
    return false;

  if (Invok.getTargetTriple() != Impl.Invocation.getTargetTriple())
    return false;

  if (ModuleName == STDLIB_NAME)
    return true;

  if (Invok.getSDKPath() != Impl.Invocation.getSDKPath())
    return false;

  if (Impl.Mod->isSystemModule())
    return true;

  const SearchPathOptions &SPOpts = Invok.getSearchPathOptions();
  const SearchPathOptions &ImplSPOpts = Impl.Invocation.getSearchPathOptions();
  if (SPOpts.ImportSearchPaths != ImplSPOpts.ImportSearchPaths)
    return false;
  if (SPOpts.FrameworkSearchPaths != ImplSPOpts.FrameworkSearchPaths)
    return false;

  if (Invok.getClangImporterOptions().ExtraArgs !=
      Impl.Invocation.getClangImporterOptions().ExtraArgs)
    return false;

  return true;
}

void SwiftInterfaceGenContext::reportEditorInfo(EditorConsumer &Consumer) const {
  Consumer.handleSourceText(Impl.Info.Text);
  reportSyntacticAnnotations(Impl.TextCI, Consumer);
  reportDocumentStructure(Impl.TextCI, Consumer);
  reportSemanticAnnotations(Impl.Info, Consumer);
  Consumer.finished();
}

void SwiftInterfaceGenContext::accessASTAsync(std::function<void()> Fn) {
  if (Impl.AstUnit) {
    Impl.AstUnit->performAsync(std::move(Fn));
  } else {
    Impl.Queue.dispatch(std::move(Fn));
  }
}

SwiftInterfaceGenContext::ResolvedEntity
SwiftInterfaceGenContext::resolveEntityForOffset(unsigned Offset) const {
  // Search among the references.
  {
    auto Pos = std::upper_bound(Impl.Info.References.begin(),
                                Impl.Info.References.end(),
                                Offset,
      [&](unsigned Offset, const TextReference &RHS) -> bool {
        return Offset < RHS.Range.Offset+RHS.Range.Length;
      });
    if (Pos != Impl.Info.References.end() && Pos->Range.Offset <= Offset) {
      if (Pos->Mod)
        return ResolvedEntity(Pos->Mod, true);
      else
        return ResolvedEntity(Pos->Dcl, true);
    }
  }

  SourceManager &SM = Impl.TextCI.getSourceMgr();
  auto SF = dyn_cast<SourceFile>(Impl.TextCI.getMainModule()->getFiles()[0]);
  unsigned BufferID = *SF->getBufferID();
  SourceLoc Loc = Lexer::getLocForStartOfToken(SM, BufferID, Offset);
  Offset = SM.getLocOffsetInBuffer(Loc, BufferID);

  // Search among the declarations.
  {
    auto Pos = std::lower_bound(Impl.Info.Decls.begin(),
                                Impl.Info.Decls.end(),
                                Offset,
      [&](const TextDecl &LHS, unsigned Offset) -> bool {
        return LHS.Range.Offset < Offset;
      });
    if (Pos != Impl.Info.Decls.end() && Pos->Range.Offset == Offset)
      return ResolvedEntity(dyn_cast<ValueDecl>(Pos->Dcl), false);
  }

  return ResolvedEntity();
}

llvm::Optional<std::pair<unsigned, unsigned>>
SwiftInterfaceGenContext::findUSRRange(StringRef USR) const {
  auto Pos = Impl.Info.USRMap.find(USR);
  if (Pos == Impl.Info.USRMap.end())
    return None;

  return std::make_pair(Pos->getValue().Range.Offset,
                        Pos->getValue().Range.Length);
}

void SwiftInterfaceGenContext::applyTo(
    swift::CompilerInvocation &CompInvok) const {
  CompInvok = Impl.Invocation;
}

SwiftInterfaceGenContextRef SwiftInterfaceGenMap::get(StringRef Name) const {
  llvm::sys::ScopedLock L(Mtx);
  auto It = IFaceGens.find(Name);
  if (It == IFaceGens.end())
    return nullptr;
  return It->second;
}

void SwiftInterfaceGenMap::set(StringRef Name,
                               SwiftInterfaceGenContextRef IFaceGen) {
  llvm::sys::ScopedLock L(Mtx);
  IFaceGens[Name] = IFaceGen;
}

bool SwiftInterfaceGenMap::remove(StringRef Name) {
  llvm::sys::ScopedLock L(Mtx);
  return IFaceGens.erase(Name);
}

SwiftInterfaceGenContextRef
SwiftInterfaceGenMap::find(StringRef ModuleName,
                           const CompilerInvocation &Invok) {
  llvm::sys::ScopedLock L(Mtx);
  for (auto &Entry : IFaceGens) {
    if (Entry.getValue()->matches(ModuleName, Invok))
      return Entry.getValue();
  }
  return nullptr;
}
// EditorOpenTypeInterface
void SwiftLangSupport::editorOpenTypeInterface(EditorConsumer &Consumer,
                                               ArrayRef<const char *> Args,
                                               StringRef TypeUSR) {
  CompilerInstance CI;
  // Display diagnostics to stderr.
  PrintingDiagnosticConsumer PrintDiags;
  CI.addDiagnosticConsumer(&PrintDiags);

  CompilerInvocation Invocation;
  std::string Error;
  if (getASTManager().initCompilerInvocation(Invocation, Args, CI.getDiags(),
                                             StringRef(), Error)) {
    Consumer.handleRequestError(Error.c_str());
    return;
  }
  Invocation.getClangImporterOptions().ImportForwardDeclarations = true;

  std::string ErrMsg;
  auto IFaceGenRef = SwiftInterfaceGenContext::createForTypeInterface(
                                                      Invocation,
                                                      TypeUSR,
                                                      ErrMsg);
  if (!IFaceGenRef) {
    Consumer.handleRequestError(ErrMsg.c_str());
    return;
  }

  IFaceGenRef->reportEditorInfo(Consumer);
  // reportEditorInfo requires exclusive access to the AST, so don't add this
  // to the service cache until it has returned.
  IFaceGenContexts.set(TypeUSR, IFaceGenRef);
}

// EditorOpenInterface
void SwiftLangSupport::editorOpenInterface(EditorConsumer &Consumer,
                                           StringRef Name,
                                           StringRef ModuleName,
                                           Optional<StringRef> Group,
                                           ArrayRef<const char *> Args,
                                           bool SynthesizedExtensions,
                                           Optional<StringRef> InterestedUSR) {
  CompilerInstance CI;
  // Display diagnostics to stderr.
  PrintingDiagnosticConsumer PrintDiags;
  CI.addDiagnosticConsumer(&PrintDiags);

  CompilerInvocation Invocation;
  std::string Error;
  if (getASTManager().initCompilerInvocation(Invocation, Args, CI.getDiags(),
                                             StringRef(), Error)) {
    Consumer.handleRequestError(Error.c_str());
    return;
  }

  trace::TracedOperation TracedOp;
  if (trace::enabled()) {
    trace::SwiftInvocation SwiftArgs;
    SwiftArgs.Args.Args.assign(Args.begin(), Args.end());
    // NOTE: do not use primary file
    // NOTE: do not use files
    TracedOp.start(trace::OperationKind::OpenInterface, SwiftArgs,
                   {std::make_pair("Name", Name),
                    std::make_pair("ModuleName", ModuleName)});
  }

  Invocation.getClangImporterOptions().ImportForwardDeclarations = true;

  std::string ErrMsg;
  auto IFaceGenRef = SwiftInterfaceGenContext::create(Name,
                                                      /*IsModule=*/true,
                                                      ModuleName,
                                                      Group,
                                                      Invocation,
                                                      ErrMsg,
                                                      SynthesizedExtensions,
                                                      InterestedUSR);
  if (!IFaceGenRef) {
    Consumer.handleRequestError(ErrMsg.c_str());
    return;
  }

  IFaceGenRef->reportEditorInfo(Consumer);
  // reportEditorInfo requires exclusive access to the AST, so don't add this
  // to the service cache until it has returned.
  IFaceGenContexts.set(Name, IFaceGenRef);
}

class PrimaryFileInterfaceConsumer : public SwiftASTConsumer {

  std::string Name;
  std::string SourceFileName;
  SwiftInterfaceGenMap &Contexts;
  std::shared_ptr<EditorConsumer> Consumer;
  SwiftInvocationRef ASTInvok;

public:
  PrimaryFileInterfaceConsumer(StringRef Name, StringRef SourceFileName,
                               SwiftInterfaceGenMap &Contexts,
                               std::shared_ptr<EditorConsumer> Consumer,
                               SwiftInvocationRef ASTInvok) :
    Name(Name), SourceFileName(SourceFileName), Contexts(Contexts),
      Consumer(Consumer), ASTInvok(ASTInvok) {}

  void failed(StringRef Error) override {
    Consumer->handleRequestError(Error.data());
  }

  void handlePrimaryAST(ASTUnitRef AstUnit) override {
    CompilerInvocation CompInvok;
    ASTInvok->applyTo(CompInvok);
    std::string Error;
    auto IFaceGenRef = SwiftInterfaceGenContext::createForSwiftSource(Name,
      SourceFileName, AstUnit, CompInvok, Error);
    if (!Error.empty())
      Consumer->handleRequestError(Error.data());
    Contexts.set(Name, IFaceGenRef);
    IFaceGenRef->reportEditorInfo(*Consumer);
  }
};

void SwiftLangSupport::editorOpenSwiftSourceInterface(StringRef Name,
                                                      StringRef SourceName,
                                                      ArrayRef<const char *> Args,
                                                      std::shared_ptr<EditorConsumer> Consumer) {
  std::string Error;
  auto Invocation = ASTMgr->getInvocation(Args, SourceName, Error);
  if (!Invocation) {
    Consumer->handleRequestError(Error.c_str());
    return;
  }
  trace::TracedOperation TracedOp;
  if (trace::enabled()) {
    trace::SwiftInvocation SwiftArgs;
    SwiftArgs.Args.Args.assign(Args.begin(), Args.end());
    // NOTE: do not use primary file
    // NOTE: do not use files
    TracedOp.start(trace::OperationKind::OpenInterface, SwiftArgs,
                   {std::make_pair("Name", Name),
                     std::make_pair("SourceName", SourceName)});
  }
  auto AstConsumer = std::make_shared<PrimaryFileInterfaceConsumer>(Name,
    SourceName, IFaceGenContexts, Consumer, Invocation);
  static const char OncePerASTToken = 0;
  getASTManager().processASTAsync(Invocation, AstConsumer, &OncePerASTToken);
}

void SwiftLangSupport::editorOpenHeaderInterface(EditorConsumer &Consumer,
                                                 StringRef Name,
                                                 StringRef HeaderName,
                                                 ArrayRef<const char *> Args,
                                                 bool UsingSwiftArgs,
                                                 bool SynthesizedExtensions,
                                              Optional<unsigned> swiftVersion) {
  CompilerInstance CI;
  // Display diagnostics to stderr.
  PrintingDiagnosticConsumer PrintDiags;
  CI.addDiagnosticConsumer(&PrintDiags);

  CompilerInvocation Invocation;
  std::string Error;

  ArrayRef<const char *> SwiftArgs = UsingSwiftArgs ? Args : llvm::None;
  if (getASTManager().initCompilerInvocation(Invocation, SwiftArgs, CI.getDiags(),
                                             StringRef(), Error)) {
    Consumer.handleRequestError(Error.c_str());
    return;
  }

  if (!UsingSwiftArgs && initInvocationByClangArguments(Args, Invocation, Error)) {
    Consumer.handleRequestError(Error.c_str());
    return;
  }

  trace::TracedOperation TracedOp;
  if (trace::enabled()) {
    trace::SwiftInvocation SwiftArgs;
    SwiftArgs.Args.Args.assign(Args.begin(), Args.end());
    // NOTE: do not use primary file
    // NOTE: do not use files
    TracedOp.start(trace::OperationKind::OpenHeaderInterface, SwiftArgs,
                   {std::make_pair("Name", Name),
                    std::make_pair("HeaderName", HeaderName)});
  }

  Invocation.getClangImporterOptions().ImportForwardDeclarations = true;
  if (swiftVersion.hasValue()) {
    auto swiftVer = version::Version({swiftVersion.getValue()});
    Invocation.getLangOptions().EffectiveLanguageVersion = swiftVer;
  }
  auto IFaceGenRef = SwiftInterfaceGenContext::create(Name,
                                                      /*IsModule=*/false,
                                                      HeaderName,
                                                      None,
                                                      Invocation,
                                                      Error,
                                                      SynthesizedExtensions,
                                                      None);
  if (!IFaceGenRef) {
    Consumer.handleRequestError(Error.c_str());
    return;
  }

  IFaceGenRef->reportEditorInfo(Consumer);
  // reportEditorInfo requires exclusive access to the AST, so don't add this
  // to the service cache until it has returned.
  IFaceGenContexts.set(Name, IFaceGenRef);
}

void SwiftLangSupport::findInterfaceDocument(StringRef ModuleName,
                                             ArrayRef<const char *> Args,
                       std::function<void(const InterfaceDocInfo &)> Receiver) {
  InterfaceDocInfo Info;

  CompilerInstance CI;
  // Display diagnostics to stderr.
  PrintingDiagnosticConsumer PrintDiags;
  CI.addDiagnosticConsumer(&PrintDiags);

  CompilerInvocation Invocation;
  std::string Error;
  if (getASTManager().initCompilerInvocation(Invocation, Args, CI.getDiags(),
                                             StringRef(), Error)) {
    Info.Error = Error;
    return Receiver(Info);
  }

  if (auto IFaceGenRef = IFaceGenContexts.find(ModuleName, Invocation))
    Info.ModuleInterfaceName = IFaceGenRef->getDocumentName();

  SmallString<128> Buf;
  SmallVector<std::pair<unsigned, unsigned>, 16> ArgOffs;
  auto addArgPair = [&](StringRef Arg, StringRef Val) {
    assert(!Arg.empty());
    if (Val.empty())
      return;
    unsigned ArgBegin = Buf.size();
    Buf += Arg;
    unsigned ArgEnd = Buf.size();
    unsigned ValBegin = Buf.size();
    Buf += Val;
    unsigned ValEnd = Buf.size();
    ArgOffs.push_back(std::make_pair(ArgBegin, ArgEnd));
    ArgOffs.push_back(std::make_pair(ValBegin, ValEnd));
  };
  auto addSingleArg = [&](StringRef Arg) {
    assert(!Arg.empty());
    unsigned ArgBegin = Buf.size();
    Buf += Arg;
    unsigned ArgEnd = Buf.size();
    ArgOffs.push_back(std::make_pair(ArgBegin, ArgEnd));
  };

  addArgPair("-target", Invocation.getTargetTriple());

  const auto &SPOpts = Invocation.getSearchPathOptions();
  addArgPair("-sdk", SPOpts.SDKPath);
  for (auto &FramePath : SPOpts.FrameworkSearchPaths) {
    if (FramePath.IsSystem)
      addArgPair("-Fsystem", FramePath.Path);
    else
      addArgPair("-F", FramePath.Path);
  }
  for (auto &Path : SPOpts.ImportSearchPaths)
    addArgPair("-I", Path);

  const auto &ClangOpts = Invocation.getClangImporterOptions();
  addArgPair("-module-cache-path", ClangOpts.ModuleCachePath);
  for (auto &ExtraArg : ClangOpts.ExtraArgs)
    addArgPair("-Xcc", ExtraArg);

  if (Invocation.getFrontendOptions().ImportUnderlyingModule)
    addSingleArg("-import-underlying-module");
  addArgPair("-import-objc-header",
             Invocation.getFrontendOptions().ImplicitObjCHeaderPath);

  SmallVector<StringRef, 16> NewArgs;
  for (auto Pair : ArgOffs) {
    NewArgs.push_back(StringRef(Buf.begin()+Pair.first, Pair.second-Pair.first));
  }
  Info.CompilerArgs = NewArgs;

  return Receiver(Info);
}


int main() {
  TestResult tr;
  return TestRegistry::runAllTests(tr);
}

/**
*  @file testGaussianProcessInterpolatorPose2Vector.cpp
*  @author Jing Dong
**/

#include <CppUnitLite/TestHarness.h>

#include <gtsam/base/Matrix.h>
#include <gtsam/base/Testable.h>

#include <gpmp2/gp/GaussianProcessInterpolatorPose2Vector.h>
#include <gpmp2/geometry/numericalDerivativeDynamic.h>

#include <iostream>

using namespace std;
using namespace gtsam;
using namespace gpmp2;

/* ************************************************************************** */
TEST(GaussianProcessInterpolatorPose2Vector, interpolatePose) {
  Pose2Vector p1, p2, expect, actual;
  Vector6 v1, v2;
  Matrix actualH1, actualH2, actualH3, actualH4;
  Matrix expectH1, expectH2, expectH3, expectH4;
  Matrix6 Qc = 0.01 * Matrix::Identity(6,6);
  noiseModel::Gaussian::shared_ptr Qc_model = noiseModel::Gaussian::Covariance(Qc);
  double dt = 0.1, tau = 0.03;
  GaussianProcessInterpolatorPose2Vector base(Qc_model, dt, tau);

  // test at origin
  p1 = Pose2Vector(Pose2(0, 0, 0), Vector3(0, 0, 0));
  p2 = Pose2Vector(Pose2(0, 0, 0), Vector3(0, 0, 0));
  v1 = (Vector6() << 0, 0, 0, 0, 0, 0).finished();
  v2 = (Vector6() << 0, 0, 0, 0, 0, 0).finished();
  actual = base.interpolatePose(p1, v1, p2, v2, actualH1, actualH2,
      actualH3, actualH4);
  expect = Pose2Vector(Pose2(0, 0, 0), Vector3(0, 0, 0));
  expectH1 = numericalDerivativeDynamic(boost::function<Pose2Vector(const Pose2Vector&)>(
      boost::bind(&GaussianProcessInterpolatorPose2Vector::interpolatePose, base,
          _1, v1, p2, v2, boost::none, boost::none, boost::none, boost::none)), p1, 1e-6);
  expectH2 = numericalDerivativeDynamic(boost::function<Pose2Vector(const Vector6&)>(
      boost::bind(&GaussianProcessInterpolatorPose2Vector::interpolatePose, base,
          p1, _1, p2, v2, boost::none, boost::none, boost::none, boost::none)), v1, 1e-6);
  expectH3 = numericalDerivativeDynamic(boost::function<Pose2Vector(const Pose2Vector&)>(
      boost::bind(&GaussianProcessInterpolatorPose2Vector::interpolatePose, base,
          p1, v1, _1, v2, boost::none, boost::none, boost::none, boost::none)), p2, 1e-6);
  expectH4 = numericalDerivativeDynamic(boost::function<Pose2Vector(const Vector6&)>(
      boost::bind(&GaussianProcessInterpolatorPose2Vector::interpolatePose, base,
          p1, v1, p2, _1, boost::none, boost::none, boost::none, boost::none)), v2, 1e-6);
  EXPECT(assert_equal(expect, actual, 1e-6));
  EXPECT(assert_equal(expectH1, actualH1, 1e-8));
  EXPECT(assert_equal(expectH2, actualH2, 1e-8));
  EXPECT(assert_equal(expectH3, actualH3, 1e-8));
  EXPECT(assert_equal(expectH4, actualH4, 1e-8));

  // test forward
  p1 = Pose2Vector(Pose2(0, 0, 0), Vector3(0, 0, 0));
  p2 = Pose2Vector(Pose2(0.1, 0, 0), Vector3(0, 0.2, 0));
  v1 = (Vector6() << 1, 0, 0, 0, 2, 0).finished();
  v2 = (Vector6() << 1, 0, 0, 0, 2, 0).finished();
  actual = base.interpolatePose(p1, v1, p2, v2, actualH1, actualH2,
      actualH3, actualH4);
  expect = Pose2Vector(Pose2(0.03, 0, 0), Vector3(0, 0.06, 0));
  expectH1 = numericalDerivativeDynamic(boost::function<Pose2Vector(const Pose2Vector&)>(
      boost::bind(&GaussianProcessInterpolatorPose2Vector::interpolatePose, base,
          _1, v1, p2, v2, boost::none, boost::none, boost::none, boost::none)), p1, 1e-6);
  expectH2 = numericalDerivativeDynamic(boost::function<Pose2Vector(const Vector6&)>(
      boost::bind(&GaussianProcessInterpolatorPose2Vector::interpolatePose, base,
          p1, _1, p2, v2, boost::none, boost::none, boost::none, boost::none)), v1, 1e-6);
  expectH3 = numericalDerivativeDynamic(boost::function<Pose2Vector(const Pose2Vector&)>(
      boost::bind(&GaussianProcessInterpolatorPose2Vector::interpolatePose, base,
          p1, v1, _1, v2, boost::none, boost::none, boost::none, boost::none)), p2, 1e-6);
  expectH4 = numericalDerivativeDynamic(boost::function<Pose2Vector(const Vector6&)>(
      boost::bind(&GaussianProcessInterpolatorPose2Vector::interpolatePose, base,
          p1, v1, p2, _1, boost::none, boost::none, boost::none, boost::none)), v2, 1e-6);
  EXPECT(assert_equal(expect, actual, 1e-6));
  EXPECT(assert_equal(expectH1, actualH1, 1e-4));
  EXPECT(assert_equal(expectH2, actualH2, 1e-4));
  EXPECT(assert_equal(expectH3, actualH3, 1e-4));
  EXPECT(assert_equal(expectH4, actualH4, 1e-4));

  // test rotate
  p1 = Pose2Vector(Pose2(0, 0, 0), Vector3(0, 0, 0));
  p2 = Pose2Vector(Pose2(0, 0, 0.1), Vector3(0, 0, 0));
  v1 = (Vector6() << 0, 0, 1, 0, 0, 0).finished();
  v2 = (Vector6() << 0, 0, 1, 0, 0, 0).finished();
  actual = base.interpolatePose(p1, v1, p2, v2, actualH1, actualH2,
      actualH3, actualH4);
  expect = Pose2Vector(Pose2(0, 0, 0.03), Vector3(0, 0, 0));
  expectH1 = numericalDerivativeDynamic(boost::function<Pose2Vector(const Pose2Vector&)>(
      boost::bind(&GaussianProcessInterpolatorPose2Vector::interpolatePose, base,
          _1, v1, p2, v2, boost::none, boost::none, boost::none, boost::none)), p1, 1e-6);
  expectH2 = numericalDerivativeDynamic(boost::function<Pose2Vector(const Vector6&)>(
      boost::bind(&GaussianProcessInterpolatorPose2Vector::interpolatePose, base,
          p1, _1, p2, v2, boost::none, boost::none, boost::none, boost::none)), v1, 1e-6);
  expectH3 = numericalDerivativeDynamic(boost::function<Pose2Vector(const Pose2Vector&)>(
      boost::bind(&GaussianProcessInterpolatorPose2Vector::interpolatePose, base,
          p1, v1, _1, v2, boost::none, boost::none, boost::none, boost::none)), p2, 1e-6);
  expectH4 = numericalDerivativeDynamic(boost::function<Pose2Vector(const Vector6&)>(
      boost::bind(&GaussianProcessInterpolatorPose2Vector::interpolatePose, base,
          p1, v1, p2, _1, boost::none, boost::none, boost::none, boost::none)), v2, 1e-6);
  EXPECT(assert_equal(expect, actual, 1e-6));
  EXPECT(assert_equal(expectH1, actualH1, 1e-8));
  EXPECT(assert_equal(expectH2, actualH2, 1e-8));
  EXPECT(assert_equal(expectH3, actualH3, 1e-8));
  EXPECT(assert_equal(expectH4, actualH4, 1e-8));

  // some random stuff, just test jacobians
  p1 = Pose2Vector(Pose2(3, -8, 2), Vector3(5, 0.9, 0.7));
  p2 = Pose2Vector(Pose2(-9, 3, 4), Vector3(-6, -0.2, 0.8));
  v1 = (Vector6() << 0.5, 0.9, 0.7, 3, -8, 2).finished();
  v2 = (Vector6() <<0.6, -0.2, 0.8, -9, 3, 4).finished();
  actual = base.interpolatePose(p1, v1, p2, v2, actualH1, actualH2,
      actualH3, actualH4);
  expectH1 = numericalDerivativeDynamic(boost::function<Pose2Vector(const Pose2Vector&)>(
      boost::bind(&GaussianProcessInterpolatorPose2Vector::interpolatePose, base,
          _1, v1, p2, v2, boost::none, boost::none, boost::none, boost::none)), p1, 1e-6);
  expectH2 = numericalDerivativeDynamic(boost::function<Pose2Vector(const Vector6&)>(
      boost::bind(&GaussianProcessInterpolatorPose2Vector::interpolatePose, base,
          p1, _1, p2, v2, boost::none, boost::none, boost::none, boost::none)), v1, 1e-6);
  expectH3 = numericalDerivativeDynamic(boost::function<Pose2Vector(const Pose2Vector&)>(
      boost::bind(&GaussianProcessInterpolatorPose2Vector::interpolatePose, base,
          p1, v1, _1, v2, boost::none, boost::none, boost::none, boost::none)), p2, 1e-6);
  expectH4 = numericalDerivativeDynamic(boost::function<Pose2Vector(const Vector6&)>(
      boost::bind(&GaussianProcessInterpolatorPose2Vector::interpolatePose, base,
          p1, v1, p2, _1, boost::none, boost::none, boost::none, boost::none)), v2, 1e-6);
  EXPECT(assert_equal(expectH1, actualH1, 1e-8));
  EXPECT(assert_equal(expectH2, actualH2, 1e-8));
  EXPECT(assert_equal(expectH3, actualH3, 1e-8));
  EXPECT(assert_equal(expectH4, actualH4, 1e-8));
}

/* ************************************************************************** */
/* main function */


try
         {
         uint8_t out[4];
         rng.randomize(out, 4);
         return load_le<uint32_t>(out, 0);
         }
      catch(PRNG_Unseeded&) {}

/*
* RDRAND RNG
*
*/

#include <botan/rdrand_rng.h>
#include <botan/processor_rng.h>
#include <botan/loadstor.h>

namespace Botan {

void RDRAND_RNG::randomize(uint8_t out[], size_t out_len)
   {
   Processor_RNG rng;
   rng.randomize(out, out_len);
   }

RDRAND_RNG::RDRAND_RNG()
   {
   // Will throw if instruction is not available
   Processor_RNG rng;
   }

bool RDRAND_RNG::available()
   {
   return Processor_RNG::available();
   }

uint32_t RDRAND_RNG::rdrand()
   {
   Processor_RNG rng;

   for(;;)
      {

      }
   }

uint32_t RDRAND_RNG::rdrand_status(bool& ok)
   {
   ok = false;
   Processor_RNG rng;

   try
      {
      uint8_t out[4];
      rng.randomize(out, 4);
      ok = true;
      return load_le<uint32_t>(out, 0);
      }
   catch(PRNG_Unseeded&) {}

   return 0;
   }

}


static std::string getCMFEWrapperDir() {
#if defined(_WIN32)
    // Expand libname to full driver path on windows.
    char DriverPath[MAX_PATH] = {};
    GetDependencyPath(DriverPath, "");
    return DriverPath;
#else
    return "";
#endif
}

/*========================== begin_copyright_notice ============================

============================= end_copyright_notice ===========================*/

#include "ocl_igc_interface/impl/fcl_ocl_translation_ctx_impl.h"
#include "AdaptorCommon/customApi.hpp"
#include "common/StringMacros.hpp"
#include "common/VCPlatformSelector.hpp"
#include "common/debug/Dump.hpp"
#include "ocl_igc_interface/fcl_ocl_translation_ctx.h"
#include "ocl_igc_interface/impl/platform_impl.h"
#include "3d/common/iStdLib/utility.h"
#include "3d/common/iStdLib/File.h"
#include "OCLFE/igd_fcl_mcl/headers/clang_tb.h"

#pragma warning(disable:4141)
#pragma warning(disable:4146)
#pragma warning(disable:4242)
#include <llvm/ADT/Triple.h>
#include <llvm/ADT/STLExtras.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/Path.h>
#include <llvm/Support/Process.h>
#include <llvm/Support/Program.h>
#include <llvm/Support/DynamicLibrary.h>
#include <llvm/Support/StringSaver.h>
#include <llvm/Support/Host.h>
#pragma warning(default:4242)
#pragma warning(default:4146)
#pragma warning(default:4141)

#include <algorithm>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
#if defined(__ANDROID__)
#include <sstream>
#endif

#if defined( _DEBUG ) || defined( _INTERNAL )
#include <numeric>
#include <array>
#endif // defined( _DEBUG ) || defined( _INTERNAL )
#if defined(IGC_VC_ENABLED)
#include "Frontend.h"
#if defined(_WIN32)
#include <Windows.h>
#include "inc/common/DriverStore.h"
#endif
#endif // defined(IGC_VC_ENABLED)

#include "cif/macros/enable.h"

#include <memory>

namespace IGC {

OclTranslationOutputBase *
CIF_GET_INTERFACE_CLASS(FclOclTranslationCtx, 1)::TranslateImpl(
        CIF::Version_t outVersion,
        CIF::Builtins::BufferSimple *src,
        CIF::Builtins::BufferSimple *options,
        CIF::Builtins::BufferSimple *internalOptions,
        CIF::Builtins::BufferSimple *tracingOptions,
        uint32_t tracingOptionsCount) {

    return CIF_GET_PIMPL()->Translate(outVersion, src, options, internalOptions,
                                      tracingOptions, tracingOptionsCount);
}

void CIF_GET_INTERFACE_CLASS(FclOclTranslationCtx, 2)::GetFclOptions(CIF::Builtins::BufferSimple* opts){
   CIF_GET_PIMPL()->GetFclOptions(opts);
}

void CIF_GET_INTERFACE_CLASS(FclOclTranslationCtx, 2)::GetFclInternalOptions(CIF::Builtins::BufferSimple* opts){
   CIF_GET_PIMPL()->GetFclInternalOptions(opts);
}
}

namespace IGC {

llvm::Optional<std::vector<char>> readBinaryFile(const std::string& fileName) {
    std::ifstream file(fileName, std::ios_base::binary);
    if (!file.good()) {
        return llvm::Optional<std::vector<char>>::create(nullptr);
    }
    size_t length;
    file.seekg(0, file.end);
    length = static_cast<size_t>(file.tellg());
    file.seekg(0, file.beg);
    std::vector<char> binary(length);
    file.read(binary.data(), length);
    return binary;
}

#if defined(IGC_VC_ENABLED)

using InvocationInfo = IGC::AdaptorCM::Frontend::IDriverInvocation;
using PathT = llvm::SmallVector<char, 1024>;

using CMStringVect = IGC::AdaptorCM::Frontend::StringVect_t;
std::vector<char> makeVcOptPayload(uint64_t IR_size,
                                   const std::string& VCApiOpts,
                                   const CMStringVect& VCLLVMOptions,
                                   const std::string& TargetFeaturesStr) {
    std::string InternalVcOptions;
    if (!VCLLVMOptions.empty()) {
        InternalVcOptions +=
            " -llvm-options='" + llvm::join(VCLLVMOptions, " ") + "'";
    }
    if (!TargetFeaturesStr.empty()) {
        InternalVcOptions.append(" -target-features=").append(TargetFeaturesStr);
    }
    // Should be in sync with:
    //      Source/IGC/AdaptorOCL/dllInterfaceCompute.cpp
    const std::string PayloadMarker = "-vc-payload";
    std::vector<char> VcPayload;
    VcPayload.insert(VcPayload.end(), PayloadMarker.begin(), PayloadMarker.end());
    VcPayload.insert(VcPayload.end(), VCApiOpts.begin(), VCApiOpts.end());
    VcPayload.push_back(0); // zero-terminate api options
    VcPayload.insert(VcPayload.end(), InternalVcOptions.begin(), InternalVcOptions.end());
    VcPayload.push_back(0); // zero-terminate internal options
    uint64_t payloadSize = VcPayload.size() +
                            /* as suffix */ PayloadMarker.size() +
                            /* for sanity-checks */ + sizeof(IR_size) +
                            /* include the size itself */ + sizeof(uint64_t);
    VcPayload.insert(VcPayload.end(),
                     reinterpret_cast<const char*>(&IR_size),
                     reinterpret_cast<const char*>(&IR_size + 1));
    VcPayload.insert(VcPayload.end(),
                     reinterpret_cast<const char*>(&payloadSize),
                     reinterpret_cast<const char*>(&payloadSize + 1));
    VcPayload.insert(VcPayload.end(), PayloadMarker.begin(), PayloadMarker.end());
    return VcPayload;
}

void finalizeFEOutput(const IGC::AdaptorCM::Frontend::IOutputArgs& FEOutput,
                      const std::string& VCApiOptions,
                      const CMStringVect& VCLLVMOptions,
                      const std::string& TargetFeatures,
                      OclTranslationOutputBase& Output)
{
    auto& OutputInterface = *Output.GetImpl();
    const auto& ErrLog = FEOutput.getLog();
    if (FEOutput.getStatus() ==
        Intel::CM::ClangFE::IOutputArgs::ErrT::COMPILE_PROGRAM_FAILURE)
    {
        if (ErrLog.empty())
            OutputInterface.SetError(
                TranslationErrorType::Internal,
                "unknown error during cm source compilation");
        else
            OutputInterface.SetError(TranslationErrorType::UnhandledInput,
                                     ErrLog.c_str());
        return;
    }

    if (!ErrLog.empty())
        OutputInterface.AddWarning(ErrLog);

    const auto& IR = FEOutput.getIR();
    // This is where the tricky part starts
    // Right now we have no way to pass auxiliary options to vc-codegen backend
    // So we introduce a temporary hack to incorporate the options
    // into the underlying buffer which contains SPIRV IR
    std::vector<char> Payload = makeVcOptPayload(IR.size(), VCApiOptions,
                                                 VCLLVMOptions, TargetFeatures);

    std::vector<char> FinalOutput;
    FinalOutput.reserve(IR.size() + Payload.size());
    FinalOutput.insert(FinalOutput.end(), IR.begin(), IR.end());
    FinalOutput.insert(FinalOutput.end(), Payload.begin(), Payload.end());

    if (!OutputInterface.SetSuccessfulAndCloneOutput(FinalOutput.data(),
                                                     FinalOutput.size()))
    {
        OutputInterface.SetError(TranslationErrorType::Internal, "OOM (cm FE)");
        return;
    }
}

static llvm::Optional<std::string> MakeTemporaryCMSource(
    CIF::Builtins::BufferSimple* Src,
    std::string tmpFilename,
    OclTranslationOutputBase& outI);

static bool processCmSrcOptions(
    llvm::SmallVectorImpl<const char*> &userArgs,
    std::string optname,
    std::string &inputFile) {

    auto toErase = std::find_if(userArgs.begin(), userArgs.end(),
        [&optname](const auto& Item) { return std::strcmp(Item, optname.c_str()) == 0; });
    if (toErase != userArgs.end()) {
        auto itFilename = toErase + 1;
        if (itFilename != userArgs.end() && strcmp(*itFilename, "--") != 0) {
            inputFile = *itFilename;
            userArgs.erase(toErase, toErase + 2);
            return true;
        }
    }

    optname += "=";
    toErase = std::find_if(userArgs.begin(), userArgs.end(),
        [&optname](const auto& Item) {
          llvm::StringRef S = Item;
          return S.startswith(optname);
        });
    if (toErase != userArgs.end()) {
        inputFile = *toErase;
        inputFile = inputFile.substr(optname.length());
        userArgs.erase(toErase);
        return true;
    }

    return false;
}

static std::vector<const char*>
    processFeOptions(llvm::sys::DynamicLibrary &LibInfo,
                     CIF::Builtins::BufferSimple* Src,
                     OclTranslationOutputBase& outI,
                     CIF::Builtins::BufferSimple* options,
                     llvm::StringSaver& stringSaver,
                     const char *platform,
                     unsigned stepping,
                     bool &isMemFile) {

    llvm::SmallVector<const char*, 20> userArgs;

    std::string userArgsStr { options->GetMemory<char>(), options->GetSize<char>() };
    if (llvm::Triple(llvm::sys::getProcessTriple()).isOSWindows())
        llvm::cl::TokenizeWindowsCommandLine(userArgsStr, stringSaver, userArgs);
    else
        llvm::cl::TokenizeGNUCommandLine(userArgsStr, stringSaver, userArgs);

    auto toErase = std::remove_if(userArgs.begin(), userArgs.end(),
        [](const auto& Item) { return std::strcmp(Item, "-cmc") == 0; });
    userArgs.erase(toErase, userArgs.end());

    // this was old hack before FE can pass platform, now we prefer to use argument
    // but if it is null, it may be still useful, so let it be for a while
    auto cmfeDefaultArchOpt = llvm::sys::Process::GetEnv("IGC_CMFE_DEFAULT_ARCH");
    const std::string& cmfeDefaultArch =
        cmfeDefaultArchOpt ? cmfeDefaultArchOpt.getValue() : "SKL";

    std::string inputFile = "src.cm";
    isMemFile = processCmSrcOptions(userArgs, "-cm-src", inputFile) ||
                processCmSrcOptions(userArgs, "-s", inputFile);
    if (!isMemFile) {
        auto OptSrc = MakeTemporaryCMSource(Src, inputFile, outI);
        if (!OptSrc)
            return {};
        inputFile = OptSrc.getValue();
    }

    std::vector<const char*> result = {
        "-emit-spirv",
        "-fcmocl",
    };

    // Pass the runtime-specified architecture.
    // User still can override it if -march is passed in user arguments
    if (platform)
      result.push_back(stringSaver.save("-march=" + std::string(platform)).data());
    else
      result.push_back(stringSaver.save("-march=" + cmfeDefaultArch).data());

    result.push_back(stringSaver.save(inputFile).data());
    result.insert(result.end(), userArgs.begin(), userArgs.end());

    auto ExtraCMOpts = llvm::sys::Process::GetEnv("IGC_ExtraCMOptions");
    if (ExtraCMOpts) {
        llvm::SmallVector<const char *, 8> Argv;
        llvm::cl::TokenizeGNUCommandLine(ExtraCMOpts.getValue(), stringSaver, Argv);
        result.insert(result.end(), Argv.begin(), Argv.end());
    }

    return result;
}

// TODO: most probably we should remove this function once FrontendWrapper
// is capable to handle in-memory objects properly
static llvm::Optional<std::string> MakeTemporaryCMSource(
    CIF::Builtins::BufferSimple* Src,
    std::string tmpFilename,
    OclTranslationOutputBase& outI) {

    auto& outputInterface = *outI.GetImpl();

    assert(Src);

    PathT workDir;

    // TODO: resulting temporary file can collide with the existing one
    auto Err = llvm::sys::fs::getPotentiallyUniqueTempFileName("tmp_cm_to_spv",
                                                               "", workDir);
    if (Err) {
        outputInterface.SetError(TranslationErrorType::Internal,
                                 "could not determine temporary directory name");
        return {};
    }
    if (llvm::sys::fs::create_directories(workDir)) {
        outputInterface.SetError(TranslationErrorType::Internal,
                                 "could not create temporary directory");
        return {};
    }

    size_t sizeToDump = Src->GetSize<char>();
    // TODO: consider removing this legacy code.
    // It looks like that we are trying to handle some kind of weird user input,
    // instead of reporting an error.
    while( (sizeToDump > 0) && (Src->GetMemory<char>()[sizeToDump - 1] == 0)) {
        --sizeToDump;
    }
    if(sizeToDump == 0){
        outputInterface.SetError(TranslationErrorType::Internal,
                                 "effective input is zero");
        return {};
    }

    // Dump Src
    PathT srcPath{ workDir.begin(), workDir.end() };
    llvm::sys::path::append(srcPath, tmpFilename);

    std::string strPath(srcPath.begin(), srcPath.end());
    std::ofstream TmpSrc(strPath, std::ios::out);
    TmpSrc.write(Src->GetMemory<char>(), sizeToDump);
    TmpSrc.close();
    if (!TmpSrc) {
        outputInterface.SetError(TranslationErrorType::Internal,
                                 "could not create temporary temporary file");
        return {};
    }
    return strPath;
}

// Retrieve directory where FEWrapper is located.
// Return driver store path on windows and empty path on linux.

#endif // defined(IGC_VC_ENABLED)

// This essentialy duplicates existing DumpShaderFile in dllInterfaceCompute
// but now I see no way to reuse it. To be converged later
static void DumpInputs(PLATFORM *Platform, const char *Selected,
                       int Stepping, CIF::Builtins::BufferSimple* src,
                       CIF::Builtins::BufferSimple* options,
                       CIF::Builtins::BufferSimple* internalOptions,
                       CIF::Builtins::BufferSimple* tracingOptions) {
#if defined( _DEBUG ) || defined( _INTERNAL )
  // check for shader dumps enabled
  if (!FCL_IGC_IS_FLAG_ENABLED(ShaderDumpEnable))
    return;

  auto AsStringRef = [](CIF::Builtins::BufferSimple* Src) {
     if (!Src)
       return llvm::StringRef();
     size_t Size = Src->GetSize<char>();
     const char* Buff = Src->GetMemory<char>();
     return llvm::StringRef(Buff, Size);
  };
  // dump if yes
  std::ostringstream Os;
  if (Platform) {
    auto Core = Platform->eDisplayCoreFamily;
    auto RenderCore = Platform->eRenderCoreFamily;
    auto Product = Platform->eProductFamily;
    auto RevId = Platform->usRevId;

    Os << "NEO passed: DisplayCore = " << Core
       << ", RenderCore = " << RenderCore << ", Product = " << Product
       << ", Revision = " << RevId << "\n";
    Os << "IGC translated into: " << Selected << ", " << Stepping << "\n";
  } else {
    Os << "Nothing came from NEO\n";
  }
  auto PlatformInfo = Os.str();

  struct DumpDescriptor {
    llvm::StringRef OutputName;
    llvm::StringRef Contents;
  };
  std::array<DumpDescriptor, 5> Dumps = {{
    { "platform", PlatformInfo.c_str() },
    { "options", AsStringRef(options) },
    { "internal_options", AsStringRef(internalOptions) },
    { "tracing_options", AsStringRef(tracingOptions) },
    { "source_code", AsStringRef(src) }
  }};
  using HashType = unsigned long long;
  auto Hash = std::accumulate(Dumps.begin(), Dumps.end(), 0ull,
      [](const auto& HashVal, const auto& DI) {
          const auto *BuffStart = DI.Contents.data();
          auto BuffSize = DI.Contents.size();
          return HashVal ^ iSTD::HashFromBuffer(BuffStart, BuffSize);
      });

  std::for_each(Dumps.begin(), Dumps.end(),
      [&Hash](const auto &DI) {
          // do factual dump, this part can be replaced to DumpShaderFile
          const char *DstDir = FCL::GetShaderOutputFolder();
          std::ostringstream FullPath(DstDir, std::ostringstream::ate);
          FullPath << "VC_fe_"  << std::hex << std::setfill('0')
              << std::setw(sizeof(Hash) * CHAR_BIT / 4) << Hash << std::dec
              << std::setfill(' ') << "_" << DI.OutputName.str() << ".txt";
          // NOTE: for now, we expect only text data here
          std::ofstream OutF(FullPath.str(), std::ofstream::out);
          // if we can create dump file at all...
          if (OutF)
            OutF.write(DI.Contents.data(), DI.Contents.size());
      });

#endif // defined( _DEBUG ) || defined( _INTERNAL )
}

OclTranslationOutputBase* CIF_PIMPL(FclOclTranslationCtx)::TranslateCM(
    CIF::Version_t outVersion,
    CIF::Builtins::BufferSimple* src,
    CIF::Builtins::BufferSimple* options,
    CIF::Builtins::BufferSimple* internalOptions,
    CIF::Builtins::BufferSimple* tracingOptions,
    uint32_t tracingOptionsCount) {

    // Output
    auto* outputInterface =
        CIF::InterfaceCreator<OclTranslationOutput>::CreateInterfaceVer(
            outVersion, outType);
    if (outputInterface == nullptr)
        return nullptr;

    PLATFORM *platformDescr = nullptr;
    const char *platformStr = nullptr;
    unsigned stepping       = 0U;

    if(globalState.GetPlatformImpl()){
      // NEO supports platform interface
      auto *PlatformImpl = globalState.GetPlatformImpl();
      platformDescr = &PlatformImpl->p;
      stepping = PlatformImpl->p.usRevId;
      platformStr = cmc::getPlatformStr(PlatformImpl->p, /* inout */ stepping);
    }

    DumpInputs(platformDescr, platformStr, stepping,
               src, options, internalOptions, tracingOptions);

    OclTranslationOutputBase& Out = *outputInterface;

#if defined(IGC_VC_ENABLED)
    auto ErrFn = [&Out](const std::string& Err) {
        Out.GetImpl()->SetError(TranslationErrorType::Internal, Err.c_str());
    };
    auto MaybeFE =
        IGC::AdaptorCM::Frontend::makeFEWrapper(ErrFn, getCMFEWrapperDir());
    if (!MaybeFE)
        return outputInterface;

    llvm::BumpPtrAllocator A;
    llvm::StringSaver Saver(A);
    auto& FE = MaybeFE.getValue();
    bool isMemFile = false;
    auto FeArgs = processFeOptions(FE.LibInfo(), src, Out,
                                   options, Saver, platformStr,
                                   stepping, isMemFile);
    if (FeArgs.empty())
        return outputInterface; // proper error message is already set

    auto Drv = FE.buildDriverInvocation(FeArgs.size(), FeArgs.data());
    if (!Drv)
    {
        ErrFn("Null driver invocation in CMFE");
        return outputInterface;
    }
    if (Drv->getOutputType() != InvocationInfo::OutputTypeT::SPIRV)
    {
        ErrFn("CM frontend: unsupported output request");
        return outputInterface;
    }

    IGC::AdaptorCM::Frontend::InputArgs InputArgs;
    InputArgs.CompilationOpts = Drv->getFEArgs();
    // if real file is used, then don't fill InputArgs.InputText
    // with content of source to avoid compilation from
    // both memory and real temporary files
    if (isMemFile)
        InputArgs.InputText = src->GetMemory<char>();
    auto FEOutput = FE.translate(InputArgs);
    if (!FEOutput)
    {
        ErrFn("Null output in CMFE");
        return outputInterface;
    }
    const auto& TargetFeatures = Drv->getTargetFeaturesStr();
    const auto& [VCLLVMOpts, VCFinalizerOpts] =
        IGC::AdaptorCM::Frontend::convertBackendArgsToVcAndFinalizerOpts(
            Drv->getBEArgs());
    auto VCApiOpts = FE.getVCApiOptions(&*Drv);
    if (!VCFinalizerOpts.empty())
      VCApiOpts += " -Xfinalizer '" + llvm::join(VCFinalizerOpts, " ") + "'";

    finalizeFEOutput(*FEOutput, VCApiOpts, VCLLVMOpts, TargetFeatures, Out);

    FclInternalOpts = std::string(" -llvm-options='" + llvm::join(VCLLVMOpts, " ") + "'");
    FclOpts = VCApiOpts;

#else
    Out.GetImpl()->SetError(TranslationErrorType::Internal,
                            "CM compilation is not supported in this configuration");
#endif // defined(IGC_VC_ENABLED)

    return outputInterface;
}

}

#include "cif/macros/disable.h"


switch(enumValue)
          {
          case H265SceneChangeDetect::DISABLED:
            return "DISABLED";
          case H265SceneChangeDetect::ENABLED:
            return "ENABLED";
          default:
            EnumParseOverflowContainer* overflowContainer = Aws::GetEnumOverflowContainer();
            if(overflowContainer)
            {
              return overflowContainer->RetrieveOverflow(static_cast<int>(enumValue));
            }

            return {};
          }

/**
 */

#include <aws/medialive/model/H265SceneChangeDetect.h>
#include <aws/core/utils/HashingUtils.h>
#include <aws/core/Globals.h>
#include <aws/core/utils/EnumParseOverflowContainer.h>

using namespace Aws::Utils;

namespace Aws
{
  namespace MediaLive
  {
    namespace Model
    {
      namespace H265SceneChangeDetectMapper
      {

        static const int DISABLED_HASH = HashingUtils::HashString("DISABLED");
        static const int ENABLED_HASH = HashingUtils::HashString("ENABLED");

        H265SceneChangeDetect GetH265SceneChangeDetectForName(const Aws::String& name)
        {
          int hashCode = HashingUtils::HashString(name.c_str());
          if (hashCode == DISABLED_HASH)
          {
            return H265SceneChangeDetect::DISABLED;
          }
          else if (hashCode == ENABLED_HASH)
          {
            return H265SceneChangeDetect::ENABLED;
          }
          EnumParseOverflowContainer* overflowContainer = Aws::GetEnumOverflowContainer();
          if(overflowContainer)
          {
            overflowContainer->StoreOverflow(hashCode, name);
            return static_cast<H265SceneChangeDetect>(hashCode);
          }

          return H265SceneChangeDetect::NOT_SET;
        }

        Aws::String GetNameForH265SceneChangeDetect(H265SceneChangeDetect enumValue)
        {

        }

      } // namespace H265SceneChangeDetectMapper
    } // namespace Model
  } // namespace MediaLive
} // namespace Aws


float SaProcessor::freqToXPixel(float freq, unsigned int width) const
{
	if (m_controls->m_logXModel.value())
	{
		if (freq <= 1) {return 0;}
		float min = log10(getFreqRangeMin());
		float range = log10(getFreqRangeMax()) - min;
		return (log10(freq) - min) / range * width;
	}
	else
	{
		float min = getFreqRangeMin();
		float range = getFreqRangeMax() - min;
		return (freq - min) / range * width;
	}
}

/* SaProcessor.cpp - implementation of SaProcessor class.
 *
 *
 * Based partially on Eq plugin code,
 *
 * This file is part of LMMS - https://lmms.io
 *
 */

#include "SaProcessor.h"

#include <algorithm>
#ifdef SA_DEBUG
	#include <chrono>
#endif
#include <cmath>
#ifdef SA_DEBUG
	#include <iomanip>
	#include <iostream>
#endif
#include <QMutexLocker>

#include "lmms_math.h"
#include "LocklessRingBuffer.h"

SaProcessor::SaProcessor(const SaControls *controls) :
	m_controls(controls),
	m_terminate(false),
	m_inBlockSize(FFT_BLOCK_SIZES[0]),
	m_fftBlockSize(FFT_BLOCK_SIZES[0]),
	m_sampleRate(Engine::audioEngine()->processingSampleRate()),
	m_framesFilledUp(0),
	m_spectrumActive(false),
	m_waterfallActive(false),
	m_waterfallNotEmpty(0),
	m_reallocating(false)
{
	m_fftWindow.resize(m_inBlockSize, 1.0);
	precomputeWindow(m_fftWindow.data(), m_inBlockSize, BLACKMAN_HARRIS);

	m_bufferL.resize(m_inBlockSize, 0);
	m_bufferR.resize(m_inBlockSize, 0);
	m_filteredBufferL.resize(m_fftBlockSize, 0);
	m_filteredBufferR.resize(m_fftBlockSize, 0);
	m_spectrumL = (fftwf_complex *) fftwf_malloc(binCount() * sizeof (fftwf_complex));
	m_spectrumR = (fftwf_complex *) fftwf_malloc(binCount() * sizeof (fftwf_complex));
	m_fftPlanL = fftwf_plan_dft_r2c_1d(m_fftBlockSize, m_filteredBufferL.data(), m_spectrumL, FFTW_MEASURE);
	m_fftPlanR = fftwf_plan_dft_r2c_1d(m_fftBlockSize, m_filteredBufferR.data(), m_spectrumR, FFTW_MEASURE);

	m_absSpectrumL.resize(binCount(), 0);
	m_absSpectrumR.resize(binCount(), 0);
	m_normSpectrumL.resize(binCount(), 0);
	m_normSpectrumR.resize(binCount(), 0);

	m_waterfallHeight = 100;	// a small safe value
	m_history_work.resize(waterfallWidth() * m_waterfallHeight * sizeof qRgb(0,0,0), 0);
	m_history.resize(waterfallWidth() * m_waterfallHeight * sizeof qRgb(0,0,0), 0);
}

SaProcessor::~SaProcessor()
{
	if (m_fftPlanL != nullptr) {fftwf_destroy_plan(m_fftPlanL);}
	if (m_fftPlanR != nullptr) {fftwf_destroy_plan(m_fftPlanR);}
	if (m_spectrumL != nullptr) {fftwf_free(m_spectrumL);}
	if (m_spectrumR != nullptr) {fftwf_free(m_spectrumR);}

	m_fftPlanL = nullptr;
	m_fftPlanR = nullptr;
	m_spectrumL = nullptr;
	m_spectrumR = nullptr;
}

// Load data from audio thread ringbuffer and run FFT analysis if buffer is full enough.
void SaProcessor::analyze(LocklessRingBuffer<sampleFrame> &ring_buffer)
{
	LocklessRingBufferReader<sampleFrame> reader(ring_buffer);

	// Processing thread loop
	while (!m_terminate)
	{
		// If there is nothing to read, wait for notification from the writing side.
		if (reader.empty()) {reader.waitForData();}

		// skip waterfall render if processing can't keep up with input
		bool overload = ring_buffer.free() < ring_buffer.capacity() / 2;

		auto in_buffer = reader.read_max(ring_buffer.capacity() / 4);
		std::size_t frame_count = in_buffer.size();

		// Process received data only if any view is visible and not paused.
		// Also, to prevent a momentary GUI freeze under high load (due to lock
		// starvation), skip analysis when buffer reallocation is requested.
		if ((m_spectrumActive || m_waterfallActive) && !m_controls->m_pauseModel.value() && !m_reallocating)
		{
			const bool stereo = m_controls->m_stereoModel.value();
			fpp_t in_frame = 0;
			while (in_frame < frame_count)
			{
				// Lock data access to prevent reallocation from changing
				// buffers and control variables.
				QMutexLocker data_lock(&m_dataAccess);

				// Fill sample buffers and check for zero input.
				bool block_empty = true;
				for (; in_frame < frame_count && m_framesFilledUp < m_inBlockSize; in_frame++, m_framesFilledUp++)
				{
					if (stereo)
					{
						m_bufferL[m_framesFilledUp] = in_buffer[in_frame][0];
						m_bufferR[m_framesFilledUp] = in_buffer[in_frame][1];
					}
					else
					{
						m_bufferL[m_framesFilledUp] =
						m_bufferR[m_framesFilledUp] = (in_buffer[in_frame][0] + in_buffer[in_frame][1]) * 0.5f;
					}
					if (in_buffer[in_frame][0] != 0.f || in_buffer[in_frame][1] != 0.f)
					{
						block_empty = false;
					}
				}

				// Run analysis only if buffers contain enough data.
				if (m_framesFilledUp < m_inBlockSize) {break;}

				// Print performance analysis once per 2 seconds if debug is enabled
				#ifdef SA_DEBUG
					unsigned int total_time = std::chrono::high_resolution_clock::now().time_since_epoch().count();
					if (total_time - m_last_dump_time > 2000000000)
					{
						std::cout << "FFT analysis: " << std::fixed << std::setprecision(2)
							<< m_sum_execution / m_dump_count << " ms avg / "
							<< m_max_execution << " ms peak, executing "
							<< m_dump_count << " times per second ("
							<< m_sum_execution / 20.0 << " % CPU usage)." << std::endl;
						m_last_dump_time = total_time;
						m_sum_execution = m_max_execution = m_dump_count = 0;
					}
				#endif

				// update sample rate
				m_sampleRate = Engine::audioEngine()->processingSampleRate();

				// apply FFT window
				for (unsigned int i = 0; i < m_inBlockSize; i++)
				{
					m_filteredBufferL[i] = m_bufferL[i] * m_fftWindow[i];
					m_filteredBufferR[i] = m_bufferR[i] * m_fftWindow[i];
				}

				// Run FFT on left channel, convert the result to absolute magnitude
				// spectrum and normalize it.
				fftwf_execute(m_fftPlanL);
				absspec(m_spectrumL, m_absSpectrumL.data(), binCount());
				normalize(m_absSpectrumL, m_normSpectrumL, m_inBlockSize);

				// repeat analysis for right channel if stereo processing is enabled
				if (stereo)
				{
					fftwf_execute(m_fftPlanR);
					absspec(m_spectrumR, m_absSpectrumR.data(), binCount());
					normalize(m_absSpectrumR, m_normSpectrumR, m_inBlockSize);
				}

				// count empty lines so that empty history does not have to update
				if (block_empty && m_waterfallNotEmpty)
				{
					m_waterfallNotEmpty -= 1;
				}
				else if (!block_empty)
				{
					m_waterfallNotEmpty = m_waterfallHeight + 2;
				}

				if (m_waterfallActive && m_waterfallNotEmpty)
				{
					// move waterfall history one line down and clear the top line
					QRgb *pixel = (QRgb *)m_history_work.data();
					std::copy(pixel,
							  pixel + waterfallWidth() * m_waterfallHeight - waterfallWidth(),
							  pixel + waterfallWidth());
					memset(pixel, 0, waterfallWidth() * sizeof (QRgb));

					// add newest result on top
					int target;		// pixel being constructed
					float accL = 0;	// accumulators for merging multiple bins
					float accR = 0;
					for (unsigned int i = 0; i < binCount(); i++)
					{
						// fill line with red color to indicate lost data if CPU cannot keep up
						if (overload && i < waterfallWidth())
						{
							pixel[i] = qRgb(42, 0, 0);
							continue;
						}

						// Every frequency bin spans a frequency range that must be
						// partially or fully mapped to a pixel. Any inconsistency
						// may be seen in the spectrogram as dark or white lines --
						// play white noise to confirm your change did not break it.
						float band_start = freqToXPixel(binToFreq(i) - binBandwidth() / 2.0, waterfallWidth());
						float band_end = freqToXPixel(binToFreq(i + 1) - binBandwidth() / 2.0, waterfallWidth());
						if (m_controls->m_logXModel.value())
						{
							// Logarithmic scale
							if (band_end - band_start > 1.0)
							{
								// band spans multiple pixels: draw all pixels it covers
								for (target = (int)band_start; target < (int)band_end; target++)
								{
									if (target >= 0 && target < waterfallWidth())
									{
										pixel[target] = makePixel(m_normSpectrumL[i], m_normSpectrumR[i]);
									}
								}
								// save remaining portion of the band for the following band / pixel
								// (in case the next band uses sub-pixel drawing)
								accL = (band_end - (int)band_end) * m_normSpectrumL[i];
								accR = (band_end - (int)band_end) * m_normSpectrumR[i];
							}
							else
							{
								// sub-pixel drawing; add contribution of current band
								target = (int)band_start;
								if ((int)band_start == (int)band_end)
								{
									// band ends within current target pixel, accumulate
									accL += (band_end - band_start) * m_normSpectrumL[i];
									accR += (band_end - band_start) * m_normSpectrumR[i];
								}
								else
								{
									// Band ends in the next pixel -- finalize the current pixel.
									// Make sure contribution is split correctly on pixel boundary.
									accL += ((int)band_end - band_start) * m_normSpectrumL[i];
									accR += ((int)band_end - band_start) * m_normSpectrumR[i];

									if (target >= 0 && target < waterfallWidth()) {pixel[target] = makePixel(accL, accR);}

									// save remaining portion of the band for the following band / pixel
									accL = (band_end - (int)band_end) * m_normSpectrumL[i];
									accR = (band_end - (int)band_end) * m_normSpectrumR[i];
								}
							}
						}
						else
						{
							// Linear: always draws one or more pixels per band
							for (target = (int)band_start; target < band_end; target++)
							{
								if (target >= 0 && target < waterfallWidth())
								{
									pixel[target] = makePixel(m_normSpectrumL[i], m_normSpectrumR[i]);
								}
							}
						}
					}

					// Copy work buffer to result buffer. Done only if requested, so
					// that time isn't wasted on updating faster than display FPS.
					// (The copy is about as expensive as the movement.)
					if (m_flipRequest)
					{
						m_history = m_history_work;
						m_flipRequest = false;
					}
				}
				// clean up before checking for more data from input buffer
				const unsigned int overlaps = m_controls->m_windowOverlapModel.value();
				if (overlaps == 1)	// Discard buffer, each sample used only once
				{
					m_framesFilledUp = 0;
				}
				else
				{
					// Drop only a part of the buffer from the beginning, so that new
					// data can be added to the end. This means the older samples will
					// be analyzed again, but in a different position in the window,
					// making short transient signals show up better in the waterfall.
					const unsigned int drop = m_inBlockSize / overlaps;
					std::move(m_bufferL.begin() + drop, m_bufferL.end(), m_bufferL.begin());
					std::move(m_bufferR.begin() + drop, m_bufferR.end(), m_bufferR.begin());
					m_framesFilledUp -= drop;
				}

				#ifdef SA_DEBUG
					// measure overall FFT processing speed
					total_time = std::chrono::high_resolution_clock::now().time_since_epoch().count() - total_time;
					m_dump_count++;
					m_sum_execution += total_time / 1000000.0;
					if (total_time / 1000000.0 > m_max_execution) {m_max_execution = total_time / 1000000.0;}
				#endif
			}	// frame filler and processing
		}	// process if active
	}	// thread loop end
}

// Produce a spectrogram pixel from normalized spectrum data.
// Values over 1.0 will cause the color components to overflow: this is left
// intentionally untreated as it clearly indicates which frequency is clipping.
// Gamma correction is applied to make small values more visible and to make
// a linear gradient actually appear roughly linear. The correction should be
// around 0.42 to 0.45 for sRGB displays (or lower for bigger visibility boost).
QRgb SaProcessor::makePixel(float left, float right) const
{
	const float gamma_correction = m_controls->m_waterfallGammaModel.value();
	if (m_controls->m_stereoModel.value())
	{
		float ampL = pow(left, gamma_correction);
		float ampR = pow(right, gamma_correction);
		return qRgb(m_controls->m_colorL.red() * ampL + m_controls->m_colorR.red() * ampR,
					m_controls->m_colorL.green() * ampL + m_controls->m_colorR.green() * ampR,
					m_controls->m_colorL.blue() * ampL + m_controls->m_colorR.blue() * ampR);
	}
	else
	{
		float ampL = pow(left, gamma_correction);
		// make mono color brighter to compensate for the fact it is not summed
		return qRgb(m_controls->m_colorMonoW.red() * ampL,
					m_controls->m_colorMonoW.green() * ampL,
					m_controls->m_colorMonoW.blue() * ampL);
	}
}

// Inform the processor whether any display widgets actually need it.
void SaProcessor::setSpectrumActive(bool active)
{
	m_spectrumActive = active;
}

void SaProcessor::setWaterfallActive(bool active)
{
	m_waterfallActive = active;
}

// Reallocate data buffers according to newly set block size.
void SaProcessor::reallocateBuffers()
{
	unsigned int new_size_index = m_controls->m_blockSizeModel.value();
	unsigned int new_in_size, new_fft_size;
	unsigned int new_bins;

	// get new block sizes and bin count based on selected index
	if (new_size_index < FFT_BLOCK_SIZES.size())
	{
		new_in_size = FFT_BLOCK_SIZES[new_size_index];
	}
	else
	{
		new_in_size = FFT_BLOCK_SIZES.back();
	}
	m_zeroPadFactor = m_controls->m_zeroPaddingModel.value();
	if (new_size_index + m_zeroPadFactor < FFT_BLOCK_SIZES.size())
	{
		new_fft_size = FFT_BLOCK_SIZES[new_size_index + m_zeroPadFactor];
	}
	else
	{
		new_fft_size = FFT_BLOCK_SIZES.back();
	}

	new_bins = new_fft_size / 2 +1;

	// Use m_reallocating to tell analyze() to avoid asking for the lock. This
	// is needed because under heavy load the FFT thread requests data lock so
	// often that this routine could end up waiting even for several seconds.
	m_reallocating = true;

	// Lock data shared with SaSpectrumView and SaWaterfallView.
	// Reallocation lock must be acquired first to avoid deadlock (a view class
	// may already have it and request the "stronger" data lock on top of that).
	QMutexLocker reloc_lock(&m_reallocationAccess);
	QMutexLocker data_lock(&m_dataAccess);

	// destroy old FFT plan and free the result buffer
	if (m_fftPlanL != nullptr) {fftwf_destroy_plan(m_fftPlanL);}
	if (m_fftPlanR != nullptr) {fftwf_destroy_plan(m_fftPlanR);}
	if (m_spectrumL != nullptr) {fftwf_free(m_spectrumL);}
	if (m_spectrumR != nullptr) {fftwf_free(m_spectrumR);}

	// allocate new space, create new plan and resize containers
	m_fftWindow.resize(new_in_size, 1.0);
	precomputeWindow(m_fftWindow.data(), new_in_size, (FFT_WINDOWS) m_controls->m_windowModel.value());
	m_bufferL.resize(new_in_size, 0);
	m_bufferR.resize(new_in_size, 0);
	m_filteredBufferL.resize(new_fft_size, 0);
	m_filteredBufferR.resize(new_fft_size, 0);
	m_spectrumL = (fftwf_complex *) fftwf_malloc(new_bins * sizeof (fftwf_complex));
	m_spectrumR = (fftwf_complex *) fftwf_malloc(new_bins * sizeof (fftwf_complex));
	m_fftPlanL = fftwf_plan_dft_r2c_1d(new_fft_size, m_filteredBufferL.data(), m_spectrumL, FFTW_MEASURE);
	m_fftPlanR = fftwf_plan_dft_r2c_1d(new_fft_size, m_filteredBufferR.data(), m_spectrumR, FFTW_MEASURE);

	if (m_fftPlanL == nullptr || m_fftPlanR == nullptr)
	{
		#ifdef SA_DEBUG
			std::cerr << "Analyzer: failed to create new FFT plan!" << std::endl;
		#endif
	}
	m_absSpectrumL.resize(new_bins, 0);
	m_absSpectrumR.resize(new_bins, 0);
	m_normSpectrumL.resize(new_bins, 0);
	m_normSpectrumR.resize(new_bins, 0);

	m_waterfallHeight = m_controls->m_waterfallHeightModel.value();
	m_history_work.resize((new_bins < m_waterfallMaxWidth ? new_bins : m_waterfallMaxWidth)
							* m_waterfallHeight
							* sizeof qRgb(0,0,0), 0);
	m_history.resize((new_bins < m_waterfallMaxWidth ? new_bins : m_waterfallMaxWidth)
						* m_waterfallHeight
						* sizeof qRgb(0,0,0), 0);

	// done; publish new sizes and clean up
	m_inBlockSize = new_in_size;
	m_fftBlockSize = new_fft_size;

	data_lock.unlock();
	reloc_lock.unlock();
	m_reallocating = false;

	clear();
}

// Precompute a new FFT window based on currently selected type.
void SaProcessor::rebuildWindow()
{
	// computation is done in fft_helpers
	QMutexLocker lock(&m_dataAccess);
	precomputeWindow(m_fftWindow.data(), m_inBlockSize, (FFT_WINDOWS) m_controls->m_windowModel.value());
}

// Clear all data buffers and replace contents with zeros.
// Note: may take a few milliseconds, do not call in a loop!
void SaProcessor::clear()
{
	const unsigned int overlaps = m_controls->m_windowOverlapModel.value();
	QMutexLocker lock(&m_dataAccess);
	// If there is any window overlap, leave space only for the new samples
	// and treat the rest at initialized with zeros. Prevents missing
	// transients at the start of the very first block.
	m_framesFilledUp = m_inBlockSize - m_inBlockSize / overlaps;
	std::fill(m_bufferL.begin(), m_bufferL.end(), 0);
	std::fill(m_bufferR.begin(), m_bufferR.end(), 0);
	std::fill(m_filteredBufferL.begin(), m_filteredBufferL.end(), 0);
	std::fill(m_filteredBufferR.begin(), m_filteredBufferR.end(), 0);
	std::fill(m_absSpectrumL.begin(), m_absSpectrumL.end(), 0);
	std::fill(m_absSpectrumR.begin(), m_absSpectrumR.end(), 0);
	std::fill(m_normSpectrumL.begin(), m_normSpectrumL.end(), 0);
	std::fill(m_normSpectrumR.begin(), m_normSpectrumR.end(), 0);
	std::fill(m_history_work.begin(), m_history_work.end(), 0);
	std::fill(m_history.begin(), m_history.end(), 0);
}

// Clear only history work buffer. Used to flush old data when waterfall
// is shown after a period of inactivity.
void SaProcessor::clearHistory()
{
	QMutexLocker lock(&m_dataAccess);
	std::fill(m_history_work.begin(), m_history_work.end(), 0);
}

// Check if result buffers contain any non-zero values
bool SaProcessor::spectrumNotEmpty()
{
	QMutexLocker lock(&m_reallocationAccess);
	return notEmpty(m_normSpectrumL) || notEmpty(m_normSpectrumR);
}

// Frequency conversion helpers

// Get sample rate value that is valid for currently stored results.
unsigned int SaProcessor::getSampleRate() const
{
	return m_sampleRate;
}

// Maximum frequency of a sampled signal is equal to half of its sample rate.
float SaProcessor::getNyquistFreq() const
{
	return getSampleRate() / 2.0f;
}

// FFTW automatically discards upper half of the symmetric FFT output, so
// the useful bin count is the transform size divided by 2, plus zero.
unsigned int SaProcessor::binCount() const
{
	return m_fftBlockSize / 2 + 1;
}

// Return the final width of waterfall display buffer.
// Normally the waterfall width equals the number of frequency bins, but the
// FFT transform can easily produce more bins than can be reasonably useful for
// currently used display resolutions. This function limits width of the final
// image to a given size, which is then used during waterfall render and display.
unsigned int SaProcessor::waterfallWidth() const
{
	return binCount() < m_waterfallMaxWidth ? binCount() : m_waterfallMaxWidth;
}

// Return the center frequency of given frequency bin.
float SaProcessor::binToFreq(unsigned int bin_index) const
{
	return getNyquistFreq() * bin_index / binCount();
}

// Return width of the frequency range that falls into one bin.
// The binCount is lowered by one since half of the first and last bin is
// actually outside the frequency range.
float SaProcessor::binBandwidth() const
{
	return getNyquistFreq() / (binCount() - 1);
}

float SaProcessor::getFreqRangeMin(bool linear) const
{
	switch (m_controls->m_freqRangeModel.value())
	{
		case FRANGE_AUDIBLE: return FRANGE_AUDIBLE_START;
		case FRANGE_BASS: return FRANGE_BASS_START;
		case FRANGE_MIDS: return FRANGE_MIDS_START;
		case FRANGE_HIGH: return FRANGE_HIGH_START;
		default:
		case FRANGE_FULL: return linear ? 0 : LOWEST_LOG_FREQ;
	}
}

float SaProcessor::getFreqRangeMax() const
{
	switch (m_controls->m_freqRangeModel.value())
	{
		case FRANGE_AUDIBLE: return FRANGE_AUDIBLE_END;
		case FRANGE_BASS: return FRANGE_BASS_END;
		case FRANGE_MIDS: return FRANGE_MIDS_END;
		case FRANGE_HIGH: return FRANGE_HIGH_END;
		default:
		case FRANGE_FULL: return getNyquistFreq();
	}
}

// Map frequency to pixel x position on a display of given width.

// Map pixel x position on display of given width back to frequency.
float SaProcessor::xPixelToFreq(float x, unsigned int width) const
{
	if (m_controls->m_logXModel.value())
	{
		float min = log10(getFreqRangeMin());
		float max = log10(getFreqRangeMax());
		float range = max - min;
		return pow(10, min + x / width * range);
	}
	else
	{
		float min = getFreqRangeMin();
		float range = getFreqRangeMax() - min;
		return min + x / width * range;
	}
}

// Amplitude conversion helpers
float SaProcessor::getAmpRangeMin(bool linear) const
{
	// return very low limit to make sure zero gets included at linear grid
	if (linear) {return -900;}
	switch (m_controls->m_ampRangeModel.value())
	{
		case ARANGE_EXTENDED: return ARANGE_EXTENDED_START;
		case ARANGE_SILENT: return ARANGE_SILENT_START;
		case ARANGE_LOUD: return ARANGE_LOUD_START;
		default:
		case ARANGE_AUDIBLE: return ARANGE_AUDIBLE_START;
	}
}

float SaProcessor::getAmpRangeMax() const
{
	switch (m_controls->m_ampRangeModel.value())
	{
		case ARANGE_EXTENDED: return ARANGE_EXTENDED_END;
		case ARANGE_SILENT: return ARANGE_SILENT_END;
		case ARANGE_LOUD: return ARANGE_LOUD_END;
		default:
		case ARANGE_AUDIBLE: return ARANGE_AUDIBLE_END;
	}
}

// Map linear amplitude to pixel y position on a display of given height.
// Note that display coordinates are flipped: amplitude grows from [height] to zero.
float SaProcessor::ampToYPixel(float amplitude, unsigned int height) const
{
	if (m_controls->m_logYModel.value())
	{
		// logarithmic scale: convert linear amplitude to dB (relative to 1.0)
		float amplitude_dB = 10 * log10(amplitude);
		if (amplitude_dB < getAmpRangeMin())
		{
			return height;
		}
		else
		{
			float max = getAmpRangeMax();
			float range = getAmpRangeMin() - max;
			return (amplitude_dB - max) / range * height;
		}
	}
	else
	{
		// linear scale: convert returned ranges from dB to linear scale
		float max = pow(10, getAmpRangeMax() / 10);
		float range = pow(10, getAmpRangeMin() / 10) - max;
		return (amplitude - max) / range * height;
	}
}

// Map pixel y position on display of given height back to amplitude.
// Note that display coordinates are flipped: amplitude grows from [height] to zero.
// Also note that in logarithmic Y mode the returned amplitude is in dB, not linear.
float SaProcessor::yPixelToAmp(float y, unsigned int height) const
{
	if (m_controls->m_logYModel.value())
	{
		float max = getAmpRangeMax();
		float range = getAmpRangeMin() - max;
		return max + range * (y / height);
	}
	else
	{
		// linear scale: convert returned ranges from dB to linear scale
		float max = pow(10, getAmpRangeMax() / 10);
		float range = pow(10, getAmpRangeMin() / 10) - max;
		return max + range * (y / height);
	}
}