text:
require "formula"
require "utils/bottles"
require "tab"
require "keg"
require "formula_versions"
require "cli/parser"
require "utils/inreplace"
require "erb"
require "zlib"
require "api"

BOTTLE_ERB = <<-EOS
  bottle do
    <% if [HOMEBREW_BOTTLE_DEFAULT_DOMAIN.to_s,
           "#{HOMEBREW_BOTTLE_DEFAULT_DOMAIN}/bottles"].exclude?(root_url) %>
    root_url "<%= root_url %>"<% if root_url_using.present? %>,
      using: <%= root_url_using %>
    <% end %>
    <% end %>
    <% if rebuild.positive? %>
    rebuild <%= rebuild %>
    <% end %>
    <% sha256_lines.each do |line| %>
    <%= line %>
    <% end %>
  end
EOS

MAXIMUM_STRING_MATCHES = 100
GZIP_BUFFER_SIZE = 64 * 1024

ALLOWABLE_HOMEBREW_REPOSITORY_LINKS = [
  %r{#{Regexp.escape(HOMEBREW_LIBRARY)}/Homebrew/os/(mac|linux)/pkgconfig},
].freeze

module Homebrew
  extend T::Sig

  module_function

  sig { returns(CLI::Parser) }
  def bottle_args
    Homebrew::CLI::Parser.new do
      description <<~EOS
        Generate a bottle (binary package) from a formula that was installed with
        `--build-bottle`.
        If the formula specifies a rebuild version, it will be incremented in the
        generated DSL. Passing `--keep-old` will attempt to keep it at its original
        value, while `--no-rebuild` will remove it.
      EOS
      switch "--skip-relocation",
             description: "Do not check if the bottle can be marked as relocatable."
      switch "--force-core-tap",
             description: "Build a bottle even if <formula> is not in `homebrew/core` or any installed taps."
      switch "--no-rebuild",
             description: "If the formula specifies a rebuild version, remove it from the generated DSL."
      switch "--keep-old",
             description: "If the formula specifies a rebuild version, attempt to preserve its value in the " \
                          "generated DSL."
      switch "--json",
             description: "Write bottle information to a JSON file, which can be used as the value for " \
                          "`--merge`."
      switch "--merge",
             description: "Generate an updated bottle block for a formula and optionally merge it into the " \
                          "formula file. Instead of a formula name, requires the path to a JSON file generated " \
                          "with `brew bottle --json` <formula>."
      switch "--write",
             depends_on:  "--merge",
             description: "Write changes to the formula file. A new commit will be generated unless " \
                          "`--no-commit` is passed."
      switch "--no-commit",
             depends_on:  "--write",
             description: "When passed with `--write`, a new commit will not generated after writing changes " \
                          "to the formula file."
      switch "--only-json-tab",
             depends_on:  "--json",
             description: "When passed with `--json`, the tab will be written to the JSON file but not the bottle."
      flag   "--committer=",
             description: "Specify a committer name and email in `git`'s standard author format."
      flag   "--root-url=",
             description: "Use the specified <URL> as the root of the bottle's URL instead of Homebrew's default."
      flag   "--root-url-using=",
             description: "Use the specified download strategy class for downloading the bottle's URL instead of " \
                          "Homebrew's default."

      conflicts "--no-rebuild", "--keep-old"

      named_args [:installed_formula, :file], min: 1
    end
  end

  def bottle
    args = bottle_args.parse

    if args.merge?
      Homebrew.install_bundler_gems!
      return merge(args: args)
    end

    args.named.to_resolved_formulae(uniq: false).each do |f|
      bottle_formula f, args: args
    end
  end

  def keg_contain?(string, keg, ignores, formula_and_runtime_deps_names = nil, args:)
    @put_string_exists_header, @put_filenames = nil

    print_filename = lambda do |str, filename|
      unless @put_string_exists_header
        opoo "String '#{str}' still exists in these files:"
        @put_string_exists_header = true
      end

      @put_filenames ||= []

      return if @put_filenames.include?(filename)

      puts Formatter.error(filename.to_s)
      @put_filenames << filename
    end

    result = false

    keg.each_unique_file_matching(string) do |file|
      next if Metafiles::EXTENSIONS.include?(file.extname) # Skip document files.

      linked_libraries = Keg.file_linked_libraries(file, string)
      result ||= !linked_libraries.empty?

      if args.verbose?
        print_filename.call(string, file) unless linked_libraries.empty?
        linked_libraries.each do |lib|
          puts " #{Tty.bold}-->#{Tty.reset} links to #{lib}"
        end
      end

      text_matches = Keg.text_matches_in_file(file, string, ignores, linked_libraries, formula_and_runtime_deps_names)
      result = true if text_matches.any?

      next if !args.verbose? || text_matches.empty?

      print_filename.call(string, file)
      text_matches.first(MAXIMUM_STRING_MATCHES).each do |match, offset|
        puts " #{Tty.bold}-->#{Tty.reset} match '#{match}' at offset #{Tty.bold}0x#{offset}#{Tty.reset}"
      end

      if text_matches.size > MAXIMUM_STRING_MATCHES
        puts "Only the first #{MAXIMUM_STRING_MATCHES} matches were output."
      end
    end

    keg_contain_absolute_symlink_starting_with?(string, keg, args: args) || result
  end

  def keg_contain_absolute_symlink_starting_with?(string, keg, args:)
    absolute_symlinks_start_with_string = []
    keg.find do |pn|
      next if !pn.symlink? || !(link = pn.readlink).absolute?

      absolute_symlinks_start_with_string << pn if link.to_s.start_with?(string)
    end

    if args.verbose? && absolute_symlinks_start_with_string.present?
      opoo "Absolute symlink starting with #{string}:"
      absolute_symlinks_start_with_string.each do |pn|
        puts "  #{pn} -> #{pn.resolved_path}"
      end
    end

    !absolute_symlinks_start_with_string.empty?
  end

  def cellar_parameter_needed?(cellar)
    default_cellars = [
      Homebrew::DEFAULT_MACOS_CELLAR,
      Homebrew::DEFAULT_MACOS_ARM_CELLAR,
      Homebrew::DEFAULT_LINUX_CELLAR,
    ]
    cellar.present? && default_cellars.exclude?(cellar)
  end

  def generate_sha256_line(tag, digest, cellar, tag_column, digest_column)
    line = "sha256 "
    tag_column += line.length
    digest_column += line.length
    if cellar.is_a?(Symbol)
      line += "cellar: :#{cellar},"
    elsif cellar_parameter_needed?(cellar)
      line += %Q(cellar: "#{cellar}",)
    end
    line += " " * (tag_column - line.length)
    line += "#{tag}:"
    line += " " * (digest_column - line.length)
    %Q(#{line}"#{digest}")
  end

  def bottle_output(bottle, root_url_using)
    cellars = bottle.checksums.map do |checksum|
      cellar = checksum["cellar"]
      next unless cellar_parameter_needed? cellar

      case cellar
      when String
        %Q("#{cellar}")
      when Symbol
        ":#{cellar}"
      end
    end.compact
    tag_column = cellars.empty? ? 0 : "cellar: #{cellars.max_by(&:length)}, ".length

    tags = bottle.checksums.map { |checksum| checksum["tag"] }
    # Start where the tag ends, add the max length of the tag, add two for the `: `
    digest_column = tag_column + tags.max_by(&:length).length + 2

    sha256_lines = bottle.checksums.map do |checksum|
      generate_sha256_line(checksum["tag"], checksum["digest"], checksum["cellar"], tag_column, digest_column)
    end
    erb_binding = bottle.instance_eval { binding }
    erb_binding.local_variable_set(:sha256_lines, sha256_lines)
    erb_binding.local_variable_set(:root_url_using, root_url_using)
    erb = ERB.new BOTTLE_ERB
    erb.result(erb_binding).gsub(/^\s*$\n/, "")
  end

  def sudo_purge
    return unless ENV["HOMEBREW_BOTTLE_SUDO_PURGE"]

    system "/usr/bin/sudo", "--non-interactive", "/usr/sbin/purge"
  end

  def setup_tar_and_args!(args)
    # Without --only-json-tab bottles are never reproducible
    default_tar_args = ["tar", [].freeze].freeze
    return default_tar_args unless args.only_json_tab?

    # Ensure tar is set up for reproducibility.
    # https://reproducible-builds.org/docs/archives/
    gnutar_args = [
      "--format", "pax", "--owner", "0", "--group", "0", "--sort", "name",
      # Set exthdr names to exclude PID (for GNU tar <1.33). Also don't store atime and ctime.
      "--pax-option", "globexthdr.name=/GlobalHead.%n,exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime"
    ].freeze

    return ["tar", gnutar_args].freeze if OS.linux?

    # Use gnu-tar on macOS as it can be set up for reproducibility better than libarchive.
    begin
      gnu_tar = Formula["gnu-tar"]
    rescue FormulaUnavailableError
      return default_tar_args
    end

    ensure_formula_installed!(gnu_tar, reason: "bottling")

    ["#{gnu_tar.opt_bin}/gtar", gnutar_args].freeze
  end

  def formula_ignores(f)
    ignores = []
    cellar_regex = Regexp.escape(HOMEBREW_CELLAR)
    prefix_regex = Regexp.escape(HOMEBREW_PREFIX)

    # Ignore matches to go keg, because all go binaries are statically linked.
    any_go_deps = f.deps.any? do |dep|
      dep.name =~ Version.formula_optionally_versioned_regex(:go)
    end
    if any_go_deps
      go_regex = Version.formula_optionally_versioned_regex(:go, full: false)
      ignores << %r{#{cellar_regex}/#{go_regex}/[\d.]+/libexec}
    end

    ignores << case f.name
    # On Linux, GCC installation can be moved so long as the whole directory tree is moved together:
    # https://gcc-help.gcc.gnu.narkive.com/GnwuCA7l/moving-gcc-from-the-installation-path-is-it-allowed.
    when Version.formula_optionally_versioned_regex(:gcc)
      Regexp.union(%r{#{cellar_regex}/gcc}, %r{#{prefix_regex}/opt/gcc}) if OS.linux?
    # binutils is relocatable for the same reason: https://github.com/Homebrew/brew/pull/11899#issuecomment-906804451.
    when Version.formula_optionally_versioned_regex(:binutils)
      %r{#{cellar_regex}/binutils} if OS.linux?
    end

    ignores.compact
  end

  def bottle_formula(f, args:)
    local_bottle_json = args.json? && f.local_bottle_path.present?

    unless local_bottle_json
      return ofail "Formula not installed or up-to-date: #{f.full_name}" unless f.latest_version_installed?
      return ofail "Formula was not installed with --build-bottle: #{f.full_name}" unless Utils::Bottles.built_as? f
    end

    tap = f.tap
    if tap.nil?
      return ofail "Formula not from core or any installed taps: #{f.full_name}" unless args.force_core_tap?

      tap = CoreTap.instance
    end

    return ofail "Formula has no stable version: #{f.full_name}" unless f.stable

    bottle_tag, rebuild = if local_bottle_json
      _, tag_string, rebuild_string = Utils::Bottles.extname_tag_rebuild(f.local_bottle_path.to_s)
      [tag_string.to_sym, rebuild_string.to_i]
    end

    bottle_tag = if bottle_tag
      Utils::Bottles::Tag.from_symbol(bottle_tag)
    else
      Utils::Bottles.tag
    end

    rebuild ||= if args.no_rebuild? || !tap
      0
    elsif args.keep_old?
      f.bottle_specification.rebuild
    else
      ohai "Determining #{f.full_name} bottle rebuild..."
      FormulaVersions.new(f).formula_at_revision("origin/HEAD") do |upstream_f|
        if f.pkg_version == upstream_f.pkg_version
          upstream_f.bottle_specification.rebuild + 1
        else
          0
        end
      end || 0
    end

    filename = Bottle::Filename.create(f, bottle_tag.to_sym, rebuild)
    local_filename = filename.to_s
    bottle_path = Pathname.pwd/filename

    tab = nil
    keg = nil

    tap_path = tap.path
    tap_git_revision = tap.git_head
    tap_git_remote = tap.remote

    root_url = args.root_url

    relocatable = T.let(false, T::Boolean)
    skip_relocation = T.let(false, T::Boolean)

    prefix = HOMEBREW_PREFIX.to_s
    cellar = HOMEBREW_CELLAR.to_s

    if local_bottle_json
      bottle_path = f.local_bottle_path
      local_filename = bottle_path.basename.to_s

      tab_path = Utils::Bottles.receipt_path(bottle_path)
      raise "This bottle does not contain the file INSTALL_RECEIPT.json: #{bottle_path}" unless tab_path

      tab_json = Utils::Bottles.file_from_bottle(bottle_path, tab_path)
      tab = Tab.from_file_content(tab_json, tab_path)

      tag_spec = Formula[f.name].bottle_specification.tag_specification_for(bottle_tag, no_older_versions: true)
      relocatable = [:any, :any_skip_relocation].include?(tag_spec.cellar)
      skip_relocation = tag_spec.cellar == :any_skip_relocation

      prefix = bottle_tag.default_prefix
      cellar = bottle_tag.default_cellar
    else
      tar_filename = filename.to_s.sub(/.gz$/, "")
      tar_path = Pathname.pwd/tar_filename

      keg = Keg.new(f.prefix)
    end

    ohai "Bottling #{local_filename}..."

    formula_and_runtime_deps_names = [f.name] + f.runtime_dependencies.map(&:name)

    # this will be nil when using a local bottle
    keg&.lock do
      original_tab = nil
      changed_files = nil

      begin
        keg.delete_pyc_files!

        changed_files = keg.replace_locations_with_placeholders unless args.skip_relocation?

        Formula.clear_cache
        Keg.clear_cache
        Tab.clear_cache
        Dependency.clear_cache
        Requirement.clear_cache
        tab = Tab.for_keg(keg)
        original_tab = tab.dup
        tab.poured_from_bottle = false
        tab.HEAD = nil
        tab.time = nil
        tab.changed_files = changed_files.dup
        if args.only_json_tab?
          tab.changed_files.delete(Pathname.new(Tab::FILENAME))
          tab.tabfile.unlink
        else
          tab.write
        end

        keg.find do |file|
          # Set the times for reproducible bottles.
          if file.symlink?
            # Need to make symlink permissions consistent on macOS and Linux
            File.lchmod 0777, file if OS.mac?
            File.lutime(tab.source_modified_time, tab.source_modified_time, file)
          else
            file.utime(tab.source_modified_time, tab.source_modified_time)
          end
        end

        cd cellar do
          sudo_purge
          # Tar then gzip for reproducible bottles.
          tar, tar_args = setup_tar_and_args!(args)
          safe_system tar, "--create", "--numeric-owner",
                      *tar_args,
                      "--file", tar_path, "#{f.name}/#{f.pkg_version}"
          sudo_purge
          # Set more times for reproducible bottles.
          tar_path.utime(tab.source_modified_time, tab.source_modified_time)
          relocatable_tar_path = "#{f}-bottle.tar"
          mv tar_path, relocatable_tar_path
          # Use gzip, faster to compress than bzip2, faster to uncompress than bzip2
          # or an uncompressed tarball (and more bandwidth friendly).
          gz = Zlib::GzipWriter.open(bottle_path)
          gz.mtime = tab.source_modified_time
          gz.orig_name = relocatable_tar_path
          File.open(relocatable_tar_path, "rb") do |tarfile|
            gz.write(tarfile.read(GZIP_BUFFER_SIZE)) until tarfile.eof?
          end
          gz.close
          rm_f relocatable_tar_path
          sudo_purge
        end

        ohai "Detecting if #{local_filename} is relocatable..." if bottle_path.size > 1 * 1024 * 1024

        prefix_check = if Homebrew.default_prefix?(prefix)
          File.join(prefix, "opt")
        else
          prefix
        end

        # Ignore matches to source code, which is not required at run time.
        # These matches may be caused by debugging symbols.
        ignores = [%r{/include/|\.(c|cc|cpp|h|hpp)$}]

        # Add additional workarounds to ignore
        ignores += formula_ignores(f)

        repository_reference = if HOMEBREW_PREFIX == HOMEBREW_REPOSITORY
          HOMEBREW_LIBRARY
        else
          HOMEBREW_REPOSITORY
        end.to_s
        if keg_contain?(repository_reference, keg, ignores + ALLOWABLE_HOMEBREW_REPOSITORY_LINKS, args: args)
          odie "Bottle contains non-relocatable reference to #{repository_reference}!"
        end

        relocatable = true
        if args.skip_relocation?
          skip_relocation = true
        else
          relocatable = false if keg_contain?(prefix_check, keg, ignores, formula_and_runtime_deps_names, args: args)
          relocatable = false if keg_contain?(cellar, keg, ignores, formula_and_runtime_deps_names, args: args)
          if keg_contain?(HOMEBREW_LIBRARY.to_s, keg, ignores, formula_and_runtime_deps_names, args: args)
            relocatable = false
          end
          if prefix != prefix_check
            relocatable = false if keg_contain_absolute_symlink_starting_with?(prefix, keg, args: args)
            relocatable = false if keg_contain?("#{prefix}/etc", keg, ignores, args: args)
            relocatable = false if keg_contain?("#{prefix}/var", keg, ignores, args: args)
            relocatable = false if keg_contain?("#{prefix}/share/vim", keg, ignores, args: args)
          end
          skip_relocation = relocatable && !keg.require_relocation?
        end
        puts if !relocatable && args.verbose?
      rescue Interrupt
        ignore_interrupts { bottle_path.unlink if bottle_path.exist? }
        raise
      ensure
        ignore_interrupts do
          original_tab&.write
          keg.replace_placeholders_with_locations changed_files unless args.skip_relocation?
        end
      end
    end

    bottle = BottleSpecification.new
    bottle.tap = tap
    bottle.root_url(root_url) if root_url
    bottle_cellar = if relocatable
      if skip_relocation
        :any_skip_relocation
      else
        :any
      end
    else
      cellar
    end
    bottle.rebuild rebuild
    sha256 = bottle_path.sha256
    bottle.sha256 cellar: bottle_cellar, bottle_tag.to_sym => sha256

    old_spec = f.bottle_specification
    if args.keep_old? && !old_spec.checksums.empty?
      mismatches = [:root_url, :rebuild].reject do |key|
        old_spec.send(key) == bottle.send(key)
      end
      unless mismatches.empty?
        bottle_path.unlink if bottle_path.exist?

        mismatches.map! do |key|
          old_value = old_spec.send(key).inspect
          value = bottle.send(key).inspect
          "#{key}: old: #{old_value}, new: #{value}"
        end

        odie <<~EOS
          `--keep-old` was passed but there are changes in:
          #{mismatches.join("\n")}
        EOS
      end
    end

    output = bottle_output(bottle, args.root_url_using)

    puts "./#{local_filename}"
    puts output

    return unless args.json?

    json = {
      f.full_name => {
        "formula" => {
          "name"             => f.name,
          "pkg_version"      => f.pkg_version.to_s,
          "path"             => f.path.to_s.delete_prefix("#{HOMEBREW_REPOSITORY}/"),
          "tap_git_path"     => f.path.to_s.delete_prefix("#{tap_path}/"),
          "tap_git_revision" => tap_git_revision,
          "tap_git_remote"   => tap_git_remote,
          # descriptions can contain emoji. sigh.
          "desc"             => f.desc.to_s.encode(
            Encoding.find("ASCII"),
            invalid: :replace, undef: :replace, replace: "",
          ).strip,
          "license"          => SPDX.license_expression_to_string(f.license),
          "homepage"         => f.homepage,
        },
        "bottle"  => {
          "root_url" => bottle.root_url,
          "cellar"   => bottle_cellar.to_s,
          "rebuild"  => bottle.rebuild,
          "date"     => Pathname(filename.to_s).mtime.strftime("%F"),
          "tags"     => {
            bottle_tag.to_s => {
              "filename"              => filename.url_encode,
              "local_filename"        => filename.to_s,
              "sha256"                => sha256,
              "formulae_brew_sh_path" => Homebrew::API::Formula.formula_api_path,
              "tab"                   => tab.to_bottle_hash,
            },
          },
        },
      },
    }

    puts "Writing #{filename.json}" if args.verbose?
    json_path = Pathname(filename.json)
    json_path.unlink if json_path.exist?
    json_path.write(JSON.pretty_generate(json))
  end

  def parse_json_files(filenames)
    filenames.map do |filename|
      JSON.parse(File.read(filename))
    end
  end

  def merge_json_files(json_files)
    json_files.reduce({}) do |hash, json_file|
      json_file.each_value do |json_hash|
        json_bottle = json_hash["bottle"]
        cellar = json_bottle.delete("cellar")
        json_bottle["tags"].each_value do |json_platform|
          json_platform["cellar"] ||= cellar
        end
      end
      hash.deep_merge(json_file)
    end
  end

  def merge(args:)
    bottles_hash = merge_json_files(parse_json_files(args.named))

    any_cellars = ["any", "any_skip_relocation"]
    bottles_hash.each do |formula_name, bottle_hash|
      ohai formula_name

      bottle = BottleSpecification.new
      bottle.root_url bottle_hash["bottle"]["root_url"]
      bottle.rebuild bottle_hash["bottle"]["rebuild"]

      path = HOMEBREW_REPOSITORY/bottle_hash["formula"]["path"]
      formula = Formulary.factory(path)

      old_bottle_spec = formula.bottle_specification
      old_pkg_version = formula.pkg_version
      FormulaVersions.new(formula).formula_at_revision("origin/HEAD") do |upstream_f|
        old_pkg_version = upstream_f.pkg_version
      end

      old_bottle_spec_matches = old_bottle_spec &&
                                bottle_hash["formula"]["pkg_version"] == old_pkg_version.to_s &&
                                bottle.root_url == old_bottle_spec.root_url &&
                                old_bottle_spec.collector.tags.present?

      # if all the cellars and checksums are the same: we can create an
      # `all: $SHA256` bottle.
      tag_hashes = bottle_hash["bottle"]["tags"].values
      all_bottle = (!old_bottle_spec_matches || bottle.rebuild != old_bottle_spec.rebuild) &&
                   tag_hashes.count > 1 &&
                   tag_hashes.uniq { |tag_hash| "#{tag_hash["cellar"]}-#{tag_hash["sha256"]}" }.count == 1

      bottle_hash["bottle"]["tags"].each do |tag, tag_hash|
        cellar = tag_hash["cellar"]
        cellar = cellar.to_sym if any_cellars.include?(cellar)

        tag_sym = if all_bottle
          :all
        else
          tag.to_sym
        end

        sha256_hash = { cellar: cellar, tag_sym => tag_hash["sha256"] }
        bottle.sha256 sha256_hash

        break if all_bottle
      end

      unless args.write?
        puts bottle_output(bottle, args.root_url_using)
        next
      end

      no_bottle_changes = if old_bottle_spec_matches && bottle.rebuild != old_bottle_spec.rebuild
        bottle.collector.tags.all? do |tag|
          tag_spec = bottle.collector.specification_for(tag)
          next false if tag_spec.blank?

          old_tag_spec = old_bottle_spec.collector.specification_for(tag)
          next false if old_tag_spec.blank?

          next false if tag_spec.cellar != old_tag_spec.cellar

          tag_spec.checksum.hexdigest == old_tag_spec.checksum.hexdigest
        end
      end

      all_bottle_hash = nil
      bottle_hash["bottle"]["tags"].each do |tag, tag_hash|
        filename = Bottle::Filename.new(
          formula_name,
          bottle_hash["formula"]["pkg_version"],
          tag,
          bottle_hash["bottle"]["rebuild"],
        )

        if all_bottle && all_bottle_hash.nil?
          all_bottle_tag_hash = tag_hash.dup

          all_filename = Bottle::Filename.new(
            formula_name,
            bottle_hash["formula"]["pkg_version"],
            "all",
            bottle_hash["bottle"]["rebuild"],
          )

          all_bottle_tag_hash["filename"] = all_filename.url_encode
          all_bottle_tag_hash["local_filename"] = all_filename.to_s
          cellar = all_bottle_tag_hash.delete("cellar")

          all_bottle_formula_hash = bottle_hash.dup
          all_bottle_formula_hash["bottle"]["cellar"] = cellar
          all_bottle_formula_hash["bottle"]["tags"] = { all: all_bottle_tag_hash }

          all_bottle_hash = { formula_name => all_bottle_formula_hash }

          puts "Copying #{filename} to #{all_filename}" if args.verbose?
          FileUtils.cp filename.to_s, all_filename.to_s

          puts "Writing #{all_filename.json}" if args.verbose?
          all_local_json_path = Pathname(all_filename.json)
          all_local_json_path.unlink if all_local_json_path.exist?
          all_local_json_path.write(JSON.pretty_generate(all_bottle_hash))
        end

        if all_bottle || no_bottle_changes
          puts "Removing #{filename} and #{filename.json}" if args.verbose?
          FileUtils.rm_f [filename.to_s, filename.json]
        end
      end

      next if no_bottle_changes

      require "utils/ast"
      formula_ast = Utils::AST::FormulaAST.new(path.read)
      checksums = old_checksums(formula, formula_ast, bottle_hash, args: args)
      update_or_add = checksums.nil? ? "add" : "update"

      checksums&.each(&bottle.method(:sha256))
      output = bottle_output(bottle, args.root_url_using)
      puts output

      case update_or_add
      when "update"
        formula_ast.replace_bottle_block(output)
      when "add"
        formula_ast.add_bottle_block(output)
      end
      path.atomic_write(formula_ast.process)

      next if args.no_commit?

      Utils::Git.set_name_email!(committer: args.committer.blank?)
      Utils::Git.setup_gpg!

      if (committer = args.committer)
        committer = Utils.parse_author!(committer)
        ENV["GIT_COMMITTER_NAME"] = committer[:name]
        ENV["GIT_COMMITTER_EMAIL"] = committer[:email]
      end

      short_name = formula_name.split("/", -1).last
      pkg_version = bottle_hash["formula"]["pkg_version"]

      path.parent.cd do
        safe_system "git", "commit", "--no-edit", "--verbose",
                    "--message=#{short_name}: #{update_or_add} #{pkg_version} bottle.",
                    "--", path
      end
    end
  end

  def merge_bottle_spec(old_keys, old_bottle_spec, new_bottle_hash)
    mismatches = []
    checksums = []

    new_values = {
      root_url: new_bottle_hash["root_url"],
      rebuild:  new_bottle_hash["rebuild"],
    }

    skip_keys = [:sha256, :cellar]
    old_keys.each do |key|
      next if skip_keys.include?(key)

      old_value = old_bottle_spec.send(key).to_s
      new_value = new_values[key].to_s

      next if old_value.present? && new_value == old_value

      mismatches << "#{key}: old: #{old_value.inspect}, new: #{new_value.inspect}"
    end

    return [mismatches, checksums] if old_keys.exclude? :sha256

    old_bottle_spec.collector.each_tag do |tag|
      old_tag_spec = old_bottle_spec.collector.specification_for(tag)
      old_hexdigest = old_tag_spec.checksum.hexdigest
      old_cellar = old_tag_spec.cellar
      new_value = new_bottle_hash.dig("tags", tag.to_s)
      if new_value.present? && new_value["sha256"] != old_hexdigest
        mismatches << "sha256 #{tag}: old: #{old_hexdigest.inspect}, new: #{new_value["sha256"].inspect}"
      elsif new_value.present? && new_value["cellar"] != old_cellar.to_s
        mismatches << "cellar #{tag}: old: #{old_cellar.to_s.inspect}, new: #{new_value["cellar"].inspect}"
      else
        checksums << { cellar: old_cellar, tag.to_sym => old_hexdigest }
      end
    end

    [mismatches, checksums]
  end

  def old_checksums(formula, formula_ast, bottle_hash, args:)
    bottle_node = formula_ast.bottle_block
    return if bottle_node.nil?
    return [] unless args.keep_old?

    old_keys = Utils::AST.body_children(bottle_node.body).map(&:method_name)
    old_bottle_spec = formula.bottle_specification
    mismatches, checksums = merge_bottle_spec(old_keys, old_bottle_spec, bottle_hash["bottle"])
    if mismatches.present?
      odie <<~EOS
        `--keep-old` was passed but there are changes in:
        #{mismatches.join("\n")}
      EOS
    end
    checksums
  end
end


text:
package tectonicus.configuration;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.util.HashMap;
import java.util.Map;

@RequiredArgsConstructor
public enum Dimension {
	OVERWORLD("minecraft:overworld"),
	NETHER("minecraft:the_nether"),
	END("minecraft:the_end");

	private static final Map<String, Dimension> ID_LOOKUP = new HashMap<>(values().length);

	@Getter
	private final String id;

	static {
		for (Dimension dimension : values()) {
			ID_LOOKUP.put(dimension.id, dimension);
		}
	}

	public static Dimension byId(String id) {
		return ID_LOOKUP.getOrDefault(id, OVERWORLD);
	}
}

redpajama_set_name: RedPajamaGithub


text:
package com.jpaquery.core.impl;

import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jpaquery.core.facade.Order;
import com.jpaquery.core.facade.OrderPath;
import com.jpaquery.core.facade.QueryAppender;
import com.jpaquery.core.vo.QueryContent;

public class OrderImpl implements Order {

	static Logger log = LoggerFactory.getLogger(OrderImpl.class);

	JpaQueryHandler finderHandler;
	JpaQueryImpl finderImpl;

	/**
	 * 路径
	 */
	List<Object> paths = new ArrayList<>();

	public JpaQueryHandler getFinderHandler() {
		return finderHandler;
	}

	public JpaQueryImpl getFinderImpl() {
		return finderImpl;
	}

	public List<Object> getPaths() {
		return paths;
	}

	public OrderImpl(JpaQueryHandler finderHandler, JpaQueryImpl finderImpl) {
		super();
		this.finderHandler = finderHandler;
		this.finderImpl = finderImpl;
	}

	public QueryContent toQueryContent() {
		return finderImpl.finderRender.toOrder(finderImpl, this);
	}

	public OrderPath get(Object obj) {
		Object arg = finderHandler.getPathInfo();
		arg = arg == null ? obj : arg;
		OrderPath path = new OrderPathImpl(finderHandler, finderImpl, this, arg);
		paths.add(path);
		return path;
	}

	public QueryAppender append(String queryString, Object... args) {
		QueryAppenderImpl queryAppenderImpl = new QueryAppenderImpl(finderImpl, queryString, args);
		paths.add(queryAppenderImpl);
		return queryAppenderImpl;
	}

}


text:
// @flow
import type { Server } from 'http';
declare module 'express' {
  declare type RouterOptions = {
    caseSensitive?: boolean,
    mergeParams?: boolean,
    strict?: boolean
  };
  declare class RequestResponseBase {
    app: Application;
    get(field: string): string | void;
  }
  declare class Request extends http$IncomingMessage mixins RequestResponseBase {
    baseUrl: string;
    body: mixed;
    cookies: {[cookie: string]: string};
    fresh: boolean;
    hostname: boolean;
    ip: string;
    ips: Array<string>;
    method: string;
    originalUrl: string;
    params: {[param: string]: string};
    path: string;
    protocol: 'https' | 'http';
    query: {[name: string]: string};
    route: string;
    secure: boolean;
    signedCookies: {[signedCookie: string]: string};
    stale: boolean;
    subdomains: Array<string>;
    xhr: boolean;
    accepts(types: string): string | false;
    acceptsCharsets(...charsets: Array<string>): string | false;
    acceptsEncodings(...encoding: Array<string>): string | false;
    acceptsLanguages(...lang: Array<string>): string | false;
    header(field: string): string | void;
    is(type: string): boolean;
    param(name: string, defaultValue?: string): string | void;
  }

  declare type CookieOptions = {
    domain?: string,
    encode?: (value: string) => string,
    expires?: Date,
    httpOnly?: boolean,
    maxAge?: string,
    path?: string,
    secure?: boolean,
    signed?: boolean
  };

  declare type RenderCallback = (err: Error | null, html?: string) => mixed;

  declare type SendFileOptions = {
    maxAge?: number,
    root?: string,
    lastModified?: boolean,
    headers?: {[name: string]: string},
    dotfiles?: 'allow' | 'deny' | 'ignore'
  };
  declare class Response extends http$ClientRequest mixins RequestResponseBase {
    headersSent: boolean;
    locals: {[name: string]: mixed};
    append(field: string, value?: string): this;
    attachment(filename?: string): this;
    cookie(name: string, value: string, options?: CookieOptions): this;
    clearCookie(name: string, options?: CookieOptions): this;
    download(path: string, filename?: string, callback?: (err?: ?Error) => void): this;
    format(typesObject: {[type: string]: Function}): this;
    json(body?: mixed): this;
    jsonp(body?: mixed): this;
    links(links: {[name: string]: string}): this;
    location(path: string): this;
    redirect(url: string, ...args: Array<void>): this;
    redirect(status: number, url: string, ...args: Array<void>): this;
    render(view: string, locals?: mixed, callback: RenderCallback): this;
    send(body?: mixed): this;
    sendFile(path: string, options?: SendFileOptions, callback?: (err?: ?Error) => mixed): this;
    sendStatus(statusCode: number): this;
    set(field: string, value?: string): this;
    status(statusCode: number): this;
    type(type: string): this;
    vary(field: string): this;
  }
  declare type $Response = Response;
  declare type $Request = Request;
  declare type NextFunction = (err?: ?Error) => mixed;
  declare type Middleware =
    ((req: Request, res: Response, next: NextFunction) => mixed) |
    ((error: ?Error, req : Request, res: Response, next: NextFunction) => mixed);
  declare interface RouteMethodType<T> {
    (middleware: Middleware): T;
    (...middleware: Array<Middleware>): T;
    (path: string|RegExp|string[], ...middleware: Array<Middleware>): T;
  }
  declare interface RouterMethodType<T> {
    (middleware: Middleware): T;
    (...middleware: Array<Middleware>): T;
    (path: string|RegExp|string[], ...middleware: Array<Middleware>): T;
    (path: string, router: Router): T;
  }
  declare class Route {
    get: RouteMethodType<this>;
    post: RouteMethodType<this>;
    put: RouteMethodType<this>;
    head: RouteMethodType<this>;
    delete: RouteMethodType<this>;
    options: RouteMethodType<this>;
    trace: RouteMethodType<this>;
    copy: RouteMethodType<this>;
    lock: RouteMethodType<this>;
    mkcol: RouteMethodType<this>;
    move: RouteMethodType<this>;
    purge: RouteMethodType<this>;
    propfind: RouteMethodType<this>;
    proppatch: RouteMethodType<this>;
    unlock: RouteMethodType<this>;
    report: RouteMethodType<this>;
    mkactivity: RouteMethodType<this>;
    checkout: RouteMethodType<this>;
    merge: RouteMethodType<this>;

    // @TODO Missing 'm-search' but get flow illegal name error.

    notify: RouteMethodType<this>;
    subscribe: RouteMethodType<this>;
    unsubscribe: RouteMethodType<this>;
    patch: RouteMethodType<this>;
    search: RouteMethodType<this>;
    connect: RouteMethodType<this>;
  }
  declare class Router extends Route {
    constructor(options?: RouterOptions): void;

    use: RouterMethodType<this>;

    route(path: string): Route;
  }

  declare function serveStatic(root: string, options?: Object): Middleware;

  declare class Application extends Router mixins events$EventEmitter {
    constructor(): void;
    locals: {[name: string]: mixed};
    mountpath: string;
    listen(port: number, hostname?: string, backlog?: number, callback?: (err?: ?Error) => mixed): Server;
    listen(port: number, hostname?: string, callback?: (err?: ?Error) => mixed): Server;
    listen(port: number, callback?: (err?: ?Error) => mixed): Server;
    listen(path: string, callback?: (err?: ?Error) => mixed): Server;
    listen(handle: Object, callback?: (err?: ?Error) => mixed): Server;
    disable(name: string): void;
    disabled(name: string): boolean;
    enable(name: string): void;
    enabled(name: string): boolean;
    engine(name: string, callback: Function): void;
    /**
     * Mixed will not be taken as a value option. Issue around using the GET http method name and the get for settings.
     */
    //   get(name: string): mixed;
    set(name: string, value: mixed): mixed;
    render(name: string, optionsOrFunction: {[name: string]: mixed}, callback: RenderCallback): void;
  }

  declare type $Application = Application;

  declare module.exports: {
    (): Application, // If you try to call like a function, it will use this signature
    static: serveStatic, // `static` property on the function
    Router: typeof Router, // `Router` property on the function
  };
}


text:
package io.datarouter.model.index.multi.base;

import io.datarouter.model.databean.BaseDatabean;
import io.datarouter.model.databean.Databean;
import io.datarouter.model.index.multi.MultiIndexEntry;
import io.datarouter.model.key.primary.PrimaryKey;

public abstract class BaseMultiIndexEntry<
		IK extends PrimaryKey<IK>,
		IE extends Databean<IK,IE>,
		PK extends PrimaryKey<PK>,
		D extends Databean<PK,D>>
extends BaseDatabean<IK,IE>
implements MultiIndexEntry<IK,IE,PK,D>{

	public BaseMultiIndexEntry(IK key){
		super(key);
	}

}


text:
require 'acts_as_paranoid_dag'

# This is the required database structure.
ActiveRecord::Schema.define( :version => 1 ) do

  create_table :users, force: true do |t|
    t.string :name
  end

  create_table :groups, force: true do |t|
    t.string :name
  end

  create_table :dag_links, force: true do |t|
    t.integer :ancestor_id
    t.string  :ancestor_type
    t.integer :descendant_id
    t.string  :descendant_type
    t.boolean :direct
    t.integer :count
    t.datetime :deleted_at
    t.timestamps
  end

end

# This is how to include the functionality in the models.
class DagLink < ActiveRecord::Base
  acts_as_dag_links polymorphic: true, paranoid: true
end

class User < ActiveRecord::Base
  has_dag_links link_class_name: "DagLink", ancestor_class_names: %w(Group)
end

class Group < ActiveRecord::Base
  has_dag_links link_class_name: "DagLink", ancestor_class_names: %w(Group), descendant_class_names: %w(Group User)
end


text:
.PHONY: test publish

test:
	meteor test-packages --velocity --driver-package respondly:test-reporter ./

publish:
	meteor publish


text:
package org.apache.shindig.gadgets.oauth;

import junitx.extensions.EqualsHashCodeTestCase;
import static junitx.framework.Assert.assertNotEquals;

public class BasicOAuthStoreTokenIndexTest extends EqualsHashCodeTestCase {
  public BasicOAuthStoreTokenIndexTest() { super("TestHashCodeEquals");}

  protected Object createInstance() throws Exception {
    BasicOAuthStoreTokenIndex eq =  new BasicOAuthStoreTokenIndex();
    eq.setGadgetUri("http://www.example.com/foo");
    eq.setModuleId(100000000);
    eq.setServiceName("test");
    eq.setUserId("abc");
    return eq;
  }

  protected Object createNotEqualInstance() throws Exception {
    return new BasicOAuthStoreTokenIndex();
  }

  public void testHashCode() {
    BasicOAuthStoreTokenIndex eq1 = new BasicOAuthStoreTokenIndex();
    BasicOAuthStoreTokenIndex eq2 = new BasicOAuthStoreTokenIndex();

    // just be sure that our new hashcode method works
    eq1.setModuleId(100);
    eq2.setModuleId(200);
    assertNotEquals(eq1.hashCode(), eq2.hashCode());

  }
}


text:
<?php

namespace Sgpg\BackendBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;

use Sgpg\DocenteBundle\Entity\Docente;
use Sgpg\BackendBundle\Form\DocenteModelType;
use Sgpg\BackendBundle\Model\DocenteModel;

class DocenteController extends Controller
{
    /**
     * @Route("/docentes", name="backend_docente_portada")
     * @Template()
     */
    public function indexAction()
    {
        $em = $this->getDoctrine()->getManager();
        $usuario = $this->get('security.context')->getToken()->getUser();
        $docentes = $em->getRepository('DocenteBundle:Docente')->findAll();

        return array(   'docentes' => $docentes,
                        'usuario' => $usuario
        );
    }

    /**
     * @Route("/docentes/nuevo", name="backend_docente_nuevo")
     * @Template()
     */
    public function nuevoAction(Request $request)
    {
        $usuario = $this->get('security.context')->getToken()->getUser();
        $entity = new DocenteModel();
        $form   = $this->createForm(new DocenteModelType(), $entity);

        if ($request->isMethod('POST'))
        {
            $form->handleRequest($request);
            if ($form->isValid())
            {
                $em = $this->getDoctrine()->getManager();
                $bdocente = $em->getRepository('DocenteBundle:Docente')->findOneByCi($entity->ci);
                if ($bdocente == null)
                {
                    $docente = new Docente();
                    $docente->setCarrera($entity->carrera);
                    $docente->setNombres($entity->nombres);
                    $docente->setApPaterno($entity->apPaterno);
                    $docente->setApMaterno($entity->apMaterno);
                    $docente->setCi($entity->ci);
                    $docente->setSalt(md5(time()));
                    $encoder = $this->get('security.encoder_factory')->getEncoder($docente);
                    $passwordCodificado = $encoder->encodePassword(
                        $docente->getCi(),
                        $docente->getSalt()
                    );
                    $docente->setPassword($passwordCodificado);

                    $em->persist($docente);
                    $em->flush();
                    $flashBag = $this->get('session')->getFlashBag();
                    $flashBag->add('smtc_success', 'Se ha creado un Docente con exito');

                    return $this->redirect($this->generateUrl('backend_docente_portada'));
                }
                else
                {
                    $flashBag = $this->get('session')->getFlashBag();
                    $flashBag->add('smtc_warning', 'El Docente ya esta Registrado');
                }
            }
        }

        return array(
            'entity' => $entity,
            'form'   => $form->createView(),
            'usuario' => $usuario
        );
    }

    public function verAction($docente_id)
    {
        $usuario = $this->get('security.context')->getToken()->getUser();
        $em = $this->getDoctrine()->getManager();
        $docente = $em->getRepository('DocenteBundle:Docente')->findOneById($docente_id);

        return $this->render('BackendBundle:Docente:ver.html.twig', array(
            'docente'   => $docente,
            'usuario' => $usuario
            ));
    }

    public function editarAction($docente_id)
    {
        $request = $this->getRequest();
        $em = $this->getDoctrine()->getManager();

        $usuario = $this->get('security.context')->getToken()->getUser();
        $docente = $em->getRepository('DocenteBundle:Docente')->findOneById($docente_id);
        $entity = new DocenteModel();

        $entity->carrera = $docente->getCarrera();
        $entity->nombres = $docente->getNombres();
        $entity->apPaterno = $docente->getApPaterno();
        $entity->apMaterno = $docente->getApMaterno();
        $entity->ci = $docente->getCi();

        $form = $this->createForm(new DocenteModelType(), $entity);

        if ($request->isMethod('POST'))
        {
            $form->handleRequest($request);
            if ($form->isValid())
            {
                $docente->setCarrera($entity->carrera);
                $docente->setNombres($entity->nombres);
                $docente->setApPaterno($entity->apPaterno);
                $docente->setApMaterno($entity->apMaterno);
                $docente->setCi($entity->ci);
                $docente->setSalt(md5(time()));
                $encoder = $this->get('security.encoder_factory')->getEncoder($docente);
                $passwordCodificado = $encoder->encodePassword(
                    $docente->getCi(),
                    $docente->getSalt()
                );
                $docente->setPassword($passwordCodificado);

                $em->persist($docente);
                $em->flush();
                $flashBag = $this->get('session')->getFlashBag();
                $flashBag->add('smtc_success', 'Se han editado los datos del Docente con exito');

                return $this->redirect($this->generateUrl('backend_docente_portada'));
            }
        }

        return $this->render('BackendBundle:Docente:editar.html.twig',array(
            'entity' => $entity,
            'form'   => $form->createView(),
            'usuario' => $usuario,
            'docente' => $docente
        ));
    }

    public function eliminarAction($docente_id)
    {
        $em = $this->getDoctrine()->getManager();

        $entity = $em->getRepository('DocenteBundle:Docente')->findOneById($docente_id);

        if ($entity->getActivo())
        {
            $entity->setActivo(false);

            $em->persist($entity);
            $em->flush();
            $flashBag = $this->get('session')->getFlashBag();
            $flashBag->add('smtc_success', 'Se ha Eliminado al Docente: '.$entity.' con exito');

            return $this->redirect($this->generateUrl('backend_docente_portada'));
        }
        else
        {
            $entity->setActivo(true);

            $em->persist($entity);
            $em->flush();
            $flashBag = $this->get('session')->getFlashBag();
            $flashBag->add('smtc_success', 'Se ha Activado al Docente '.$entity.' con exito');

            return $this->redirect($this->generateUrl('backend_docente_portada'));
        }
    }
}


text:
require "fiddle"

module KeyControl

  class System

    # Public: Execute the requested action in keyctl.
    #
    # action - The action to perform.
    # args   - A list of arguments which should be passed to the action.
    #
    # Returns the stdout value returned by the call.
    def run(action, *args)
      send(action).call(*args)
    end

    # Public: Execute the requested action in keyctl, raising an error on
    # failure.
    #
    # action - The action to perform.
    # args   - A list of arguments which should be passed to the action.
    #
    # Returns the stdout value returned by the call.
    # Raises a SystemCallError if the requested system call fails.
    def run!(action, *args)
      result = run(action, *args)
      raise SystemCallError.new(action.to_s, Fiddle.last_error) if result == -1

      result
    end

    # Public: Execute the requested keyctl action, buffering the output into a
    # string in multiple passes.
    #
    # action - The action to perform.
    # args   - A list of arguments which should be passed to the action.
    #
    # Returns a String containing the buffered output.
    def get(action, *args)
      length = run(action, *args, "", 0)
      buffer = "\x00" * length
      run(action, *args, buffer, length)

      buffer
    end

    # Public: Get a symbol representing the reason for the last error set by a
    # system call.
    #
    # Returns a Symbol or nil.
    def error
      Errno.constants.detect do |error_name|
        errno = Errno.const_get(error_name)
        errno::Errno == Fiddle.last_error
      end
    end

    private

    # Private: Get a handle representing the system calls available through
    # libkeyutils.so.
    #
    # Returns a Fiddle::Handle.
    def keyutils
      @keyutils ||= KeyControl::LIBRARIES.detect do |library|
        begin
          break Fiddle::Handle.new(library)
        rescue Fiddle::DLError
          nil
        end
      end
    end

    # Private: Get a proc representing the add_key system call.
    #
    # Returns a Fiddle::Function.
    def add
      @add ||= Fiddle::Function.new(
        keyutils["add_key"],
        [ Fiddle::ALIGN_CHAR,
          Fiddle::ALIGN_CHAR,
          Fiddle::TYPE_VOIDP,
          Fiddle::TYPE_SIZE_T,
          Fiddle::TYPE_INT ],
        Fiddle::TYPE_INT )
    end

    # Private: Get a proc representing the keyctl_read system call.
    #
    # Returns a Fiddle::Function.
    def read
      @read ||= Fiddle::Function.new(
        keyutils["keyctl_read"],
        [ Fiddle::TYPE_INT,
          Fiddle::ALIGN_CHAR,
          Fiddle::TYPE_SIZE_T ],
        Fiddle::TYPE_LONG )
    end

    # Private: Get a proc representing the request_key system call.
    #
    # Returns a Fiddle::Function.
    def search
      @search ||= Fiddle::Function.new(
        keyutils["request_key"],
        [ Fiddle::ALIGN_CHAR,
          Fiddle::ALIGN_CHAR,
          Fiddle::ALIGN_CHAR,
          Fiddle::TYPE_INT ],
        Fiddle::TYPE_INT )
    end

    # Private: Get a proc representing the keyctl_set_timeout system call.
    #
    # Returns a Fiddle::Function.
    def set_timeout
      @set_timeout ||= Fiddle::Function.new(
        keyutils["keyctl_set_timeout"],
        [ Fiddle::TYPE_INT,
          Fiddle::TYPE_INT ],
        Fiddle::TYPE_LONG )
    end

    # Private: Get a proc representing the keyctl_unlink system call.
    #
    # Returns a Fiddle::Function.
    def unlink
      @unlink ||= Fiddle::Function.new(
        keyutils["keyctl_unlink"],
        [ Fiddle::ALIGN_INT,
          Fiddle::ALIGN_INT ],
        Fiddle::TYPE_LONG )
    end
  end
end


text:
Building on Windows
===================

Installing Dependencies
-----------------------

Follow the setup steps for building Mango simulations in C++ described
in :ref:`setup-windows-cpp` (it is not necessary to run the Mango installer as
described in the first section). Make sure to install an appropriate
version of Python.

Obtaining the Source
--------------------
Mango's source code can be obtained by downloading an archived copy
or by cloning the git repository. For the latest archive or repository
address check the home page. At the time of writing the following
command clones the repository in the current directory:

  .. code-block:: bash

     $ git clone https://volund@github.com/volund/mango.git .

Configuring, Building and Installing
------------------------------------

Open a MinGW shell (for instance by selecting "MinGW > MinGW Shell"
from the Program Files menu) and cd into the directory containing the
source, which will be assumed to be */c/src/mango*. Although not strictly
necessary we will also create a build directory and configure/build
the project there (if you prefer not to do this simply skip those
steps and adjust commands appropriately):

  .. code-block:: bash

     $ cd C:\\src\\mango
     $ mkdir build-windows
     $ cd build-windows

Next CMake must be run. If during installation it was added to the
path CMake may be invoked simply as *cmake* - otherwise the full
path to the executable must be used. We will assume it was not added
to the path and that it was installed to *C:/Program Files/CMake 2.8*.
Then once we have changed into the
build directory running CMake may usually be accomplished by running:

  .. code-block:: bash

     $ /c/Program\ Files/Cmake\ 2.8/bin/cmake -G "MSYS Makefiles" ..

CMake will attempt to find the relevant Python, OpenGL and GLUT
dependencies. If this works, you should see output similar to:

  .. code-block:: bash

     ...
     -- Detecting CXX compiler ABI info
     -- Detecting CXX compiler ABI info - done
     -- Found OpenGL: opengl32
     -- Found GLUT: c:/WINDOWS/system32/freeglut.dll
     -- Appending 'freeglut' to GLUT_LIBRARIES for correct WIN32 linking against free
     glut.dll (pass -DDISABLE_FREEGLUT_DLL_FIX=1 to disable)
     -- New GLUT_LIBRARIES value: c:/WINDOWS/system32/freeglut.dll;freeglut
     -- Found PythonLibs: C:/Python32/libs/libpython32.a (found suitable version "3.2
     .3", required is "3.2")
     -- Configuring done
     -- Generating done
     -- Build files have been written to: C:/src/mango/build-windows

If this fails, you will see output similar to (in this case due to a failure
to find GLUT):

  .. code-block:: bash

     ..
     -- Found OpenGL: opengl32
     -- Could NOT find GLUT (missing:  GLUT_INCLUDE_DIR)
     GLUT not found, not building mango_onglut
     -- Found PythonLibs: C:/Python32/libs/libpython32.a (found suitable version "3.
     .3", required is "3.2")
     GLUT not found, not building the mango executable

     -- Configuring incomplete, errors occurred!

In cases such as these whichever problem is preventing correct configuration must
be resolved before proceeding. For instance, on a particular Windows XP build
machine the following expanded command is used to resolve the above inability to
find the GLUT include directory:

  .. code-block:: bash

     $ /c/Program\ Files/Cmake\ 2.8/bin/cmake -G "MSYS Makefiles" -DGLUT_ROOT_PATH="C:/MinGW/" ..

Once configured successfully, Mango can be built with:

  .. code-block:: bash

     $ make


text:
ValidatorContext.prototype.validateBasic = function validateBasic(data, schema, dataPointerPath) {
	var error;
	if (error = this.validateType(data, schema, dataPointerPath)) {
		return error.prefixWith(null, "type");
	}
	if (error = this.validateEnum(data, schema, dataPointerPath)) {
		return error.prefixWith(null, "type");
	}
	return null;
};

ValidatorContext.prototype.validateType = function validateType(data, schema) {
	if (schema.type === undefined) {
		return null;
	}
	var dataType = typeof data;
	if (data === null) {
		dataType = "null";
	} else if (Array.isArray(data)) {
		dataType = "array";
	}
	var allowedTypes = schema.type;
	if (!Array.isArray(allowedTypes)) {
		allowedTypes = [allowedTypes];
	}

	for (var i = 0; i < allowedTypes.length; i++) {
		var type = allowedTypes[i];
		if (type === dataType || (type === "integer" && dataType === "number" && (data % 1 === 0))) {
			return null;
		}
	}
	return this.createError(ErrorCodes.INVALID_TYPE, {type: dataType, expected: allowedTypes.join("/")}, '', '', null, data, schema);
};

ValidatorContext.prototype.validateEnum = function validateEnum(data, schema) {
	if (schema["enum"] === undefined) {
		return null;
	}
	for (var i = 0; i < schema["enum"].length; i++) {
		var enumVal = schema["enum"][i];
		if (recursiveCompare(data, enumVal)) {
			return null;
		}
	}
	return this.createError(ErrorCodes.ENUM_MISMATCH, {value: (typeof JSON !== 'undefined') ? JSON.stringify(data) : data}, '', '', null, data, schema);
};


text:
#include <numeric>

#include "mlir-hlo/Dialect/mhlo/IR/chlo_ops.h"
#include "mlir-hlo/Dialect/mhlo/IR/hlo_ops.h"
#include "mlir-hlo/Dialect/mhlo/transforms/rewriters.h"
#include "mlir-hlo/utils/broadcast_utils.h"
#include "mlir/Dialect/SCF/SCF.h"
#include "mlir/Dialect/Shape/IR/Shape.h"
#include "mlir/Dialect/StandardOps/IR/Ops.h"
#include "mlir/IR/Attributes.h"
#include "mlir/IR/MLIRContext.h"
#include "mlir/IR/OperationSupport.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/IR/StandardTypes.h"
#include "mlir/Transforms/DialectConversion.h"

namespace mlir {
namespace chlo {
namespace {

struct ConvertConstantLikeOp : public OpConversionPattern<ConstantLikeOp> {
  using OpConversionPattern<ConstantLikeOp>::OpConversionPattern;
  LogicalResult matchAndRewrite(
      ConstantLikeOp op, ArrayRef<Value> operands,
      ConversionPatternRewriter &rewriter) const override {
    auto result_ty = op.getType().cast<ShapedType>();

    // Unranked uses are not supported.  Consider `transform-unranked-hlo`.
    if (!result_ty.hasRank()) return failure();

    // Lower to MHLO constant if statically shaped.
    if (result_ty.hasStaticShape()) {
      rewriter.replaceOpWithNewOp<mhlo::ConstOp>(
          op, DenseElementsAttr::get(result_ty, op.value()));
      return success();
    }

    // Lower to broadcasted constant.
    ConstantLikeOp::Adaptor transformed(operands);
    auto loc = op.getLoc();
    Type extent_tensor_type = shape::getExtentTensorType(op.getContext());
    Value constant = rewriter.create<mhlo::ConstOp>(loc, op.value());
    Value uncasted_shape = rewriter.create<shape::ShapeOfOp>(
        loc, extent_tensor_type, transformed.operand());
    Type shape_ty =
        RankedTensorType::get({result_ty.getRank()}, rewriter.getIndexType());
    Value shape = rewriter.create<TensorCastOp>(loc, shape_ty, uncasted_shape);
    rewriter.replaceOpWithNewOp<mhlo::DynamicBroadcastInDimOp>(
        op, result_ty, constant, shape, rewriter.getI64TensorAttr({}));
    return success();
  }
};

// Converts binary ops that statically are determined to not broadcast directly
// to the corresponding mhlo non-broadcasting op.
template <typename ChloOpTy, typename HloOpTy, typename Adaptor>
struct ConvertTrivialNonBroadcastBinaryOp : public OpRewritePattern<ChloOpTy> {
  using OpRewritePattern<ChloOpTy>::OpRewritePattern;
  LogicalResult matchAndRewrite(ChloOpTy op,
                                PatternRewriter &rewriter) const override {
    // Only rewrite for statically determinable non-broadcasting cases.
    auto lhs_type = op.lhs().getType().template dyn_cast<RankedTensorType>();
    auto rhs_type = op.rhs().getType().template dyn_cast<RankedTensorType>();
    if (!lhs_type || !rhs_type) return failure();

    // Requires rank broadcast.
    if (lhs_type.getRank() != rhs_type.getRank()) return failure();
    // Any dynamic dimension may require broadcasting and requires more
    // analysis.
    if (!lhs_type.hasStaticShape() || !rhs_type.hasStaticShape())
      return failure();

    for (auto extents : llvm::zip(lhs_type.getShape(), rhs_type.getShape())) {
      auto lhs_extent = std::get<0>(extents);
      auto rhs_extent = std::get<1>(extents);
      if (lhs_extent != rhs_extent) {
        return failure();
      }
    }

    rewriter.replaceOp(op, {Adaptor::CreateOp(op, op.getResult().getType(),
                                              op.lhs(), op.rhs(), rewriter)});
    return success();
  }
};

// Converts a binary op with ranked broadcasting operands to explicitly
// broadcast and invoke the corresponding mhlo non-broadcasting op.
// Note that dynamic broadcasting supported by this pattern is only valid for
// "numpy" broadcasting semantics as defined here:
//   https://docs.scipy.org/doc/numpy/reference/ufuncs.html
// Specifically, this includes the following cases:
//   - Same rank broadcast (operands have the same static rank).
//   - Different-rank broadcast, either without a broadcast_dims attribte or
//     with the broadcast_dims attribute set to map to a prefix padding.
//   - Legal combinations of degenerate (1-dim) implicit broadcasting.
// The restriction on broadcast_dims derives from the definition of the
// `shape.broadcast` op, which only supports prefix-padding.
template <typename ChloOpTy, typename HloOpTy, typename Adaptor>
struct ConvertRankedDynamicBroadcastBinaryOp
    : public OpRewritePattern<ChloOpTy> {
  using OpRewritePattern<ChloOpTy>::OpRewritePattern;
  LogicalResult matchAndRewrite(ChloOpTy op,
                                PatternRewriter &rewriter) const override {
    // Only support ranked operands.
    Value lhs = op.lhs();
    Value rhs = op.rhs();
    auto lhs_type = lhs.getType().dyn_cast<RankedTensorType>();
    auto rhs_type = rhs.getType().dyn_cast<RankedTensorType>();
    auto result_type =
        op.getResult().getType().template dyn_cast<RankedTensorType>();
    if (!lhs_type || !rhs_type || !result_type) return failure();

    // Check for "numpy"-style rank broadcast.
    auto broadcast_dimensions = op.broadcast_dimensions();
    if (broadcast_dimensions &&
        !hlo::IsLegalNumpyRankedBroadcast(lhs, rhs, *broadcast_dimensions)) {
      // Note: It is unclear whether the general specification of explicit
      // broadcast_dimensions on binary ops is a feature we want to carry
      // forward. While it can technically be implemented for ranked-dynamic,
      // it is incompatible with unranked inputs. If this warning is emitted
      // in real programs, it is an indication that the feature should be
      // implemented versus just falling back on the more standard definition
      // of numpy-like prefix-padding.
      op.emitWarning() << "unsupported non prefix-padded dynamic rank "
                       << "broadcast_dimensions = " << *broadcast_dimensions;
      return failure();
    }

    // Compute result shape.
    auto loc = op.getLoc();

    // Insert a constraint on the shapes being broadcastable and insert all
    // future code into an assuming block reliant on the constraint.
    Value lhs_shape = rewriter.create<shape::ShapeOfOp>(loc, lhs);
    Value rhs_shape = rewriter.create<shape::ShapeOfOp>(loc, rhs);
    auto broadcastable_cstr =
        rewriter.create<shape::CstrBroadcastableOp>(loc, lhs_shape, rhs_shape);
    auto assuming_op = rewriter.create<shape::AssumingOp>(
        loc, ArrayRef<Type>{result_type}, broadcastable_cstr.result());

    OpBuilder::InsertionGuard guard(rewriter);
    rewriter.createBlock(&assuming_op.doRegion());

    int64_t result_rank = std::max(lhs_type.getRank(), rhs_type.getRank());
    Value result_extents =
        hlo::ComputeBinaryElementwiseBroadcastingResultExtents(
            loc, lhs, rhs, rewriter, /*unsafe_as_extent_tensor=*/true);

    // Note that we unconditionally emit DynamicBroadcastInDim ops and let
    // downstream canonicalizations fold them away if possible. This is
    // because, in the dynamic case, there are many corner cases regarding
    // when it is safe to omit, and some of them require analysis to prove
    // properly.
    auto lhs_broadcast_dimensions = llvm::to_vector<4>(
        llvm::seq<int64_t>(result_rank - lhs_type.getRank(), result_rank));
    Value broadcasted_lhs = rewriter.create<mhlo::DynamicBroadcastInDimOp>(
        loc,
        RankedTensorType::get(result_type.getShape(),
                              lhs_type.getElementType()),
        lhs, result_extents,
        rewriter.getI64TensorAttr(lhs_broadcast_dimensions));
    auto rhs_broadcast_dimensions = llvm::to_vector<4>(
        llvm::seq<int64_t>(result_rank - rhs_type.getRank(), result_rank));
    Value broadcasted_rhs = rewriter.create<mhlo::DynamicBroadcastInDimOp>(
        loc,
        RankedTensorType::get(result_type.getShape(),
                              rhs_type.getElementType()),
        rhs, result_extents,
        rewriter.getI64TensorAttr(rhs_broadcast_dimensions));

    // And generate the final non-broadcasted binary op.
    Value final_result = Adaptor::CreateOp(op, result_type, broadcasted_lhs,
                                           broadcasted_rhs, rewriter);
    rewriter.create<shape::AssumingYieldOp>(loc, final_result);
    rewriter.replaceOp(op, {assuming_op.getResult(0)});
    return success();
  }
};

// Converts a broadcasting binary operation with a scalar operand and an
// unranked operand to a ranked broadcasting operation by dynamically reshaping
// the unranked operand to a 1D tensor. This will always be safe because
// broadcasting from a scalar to another shape always works.
template <typename ChloOpTy, typename HloOpTy>
struct ConvertUnrankedScalarDynamicBroadcastBinaryOp
    : public OpRewritePattern<ChloOpTy> {
  using OpRewritePattern<ChloOpTy>::OpRewritePattern;
  LogicalResult matchAndRewrite(ChloOpTy op,
                                PatternRewriter &rewriter) const override {
    auto loc = op.getLoc();
    Value lhs = op.lhs();
    Value rhs = op.rhs();

    auto lhs_ranked_type = lhs.getType().dyn_cast<RankedTensorType>();
    auto lhs_unranked_type = lhs.getType().dyn_cast<UnrankedTensorType>();

    auto rhs_ranked_type = rhs.getType().dyn_cast<RankedTensorType>();
    auto rhs_unranked_type = rhs.getType().dyn_cast<UnrankedTensorType>();

    bool lhs_is_scalar = lhs_ranked_type &&
                         lhs_ranked_type.getShape().empty() &&
                         rhs_unranked_type;
    bool rhs_is_scalar = rhs_ranked_type &&
                         rhs_ranked_type.getShape().empty() &&
                         lhs_unranked_type;

    // Only support the case where exactly one operand is scalar and the other
    // is unranked. Other patterns in this file will create more efficient
    // lowerings for cases where both ranks are known or will handle the more
    // generic case of both inputs being unranked.
    if (!(lhs_is_scalar ^ rhs_is_scalar)) return failure();

    auto result_type = op.getResult().getType().template dyn_cast<TensorType>();

    // Reshape the non-scalar value into a dynamically sized, rank-1 tensor
    Value shape =
        rewriter.create<shape::ShapeOfOp>(loc, lhs_is_scalar ? rhs : lhs);
    Value num_elements = rewriter.create<shape::NumElementsOp>(loc, shape);
    Value size_tensor =
        rewriter.create<TensorFromElementsOp>(loc, num_elements);
    Value reshaped = rewriter.create<mhlo::DynamicReshapeOp>(
        loc, RankedTensorType::get({-1}, result_type.getElementType()),
        lhs_is_scalar ? rhs : lhs, size_tensor);

    // Create a new ranked Chlo op that will be further lowered by other
    // patterns into Mhlo.
    SmallVector<Value, 2> operands{lhs_is_scalar ? lhs : reshaped,
                                   rhs_is_scalar ? rhs : reshaped};
    Value computed = rewriter.create<ChloOpTy>(
        loc, SmallVector<Type, 1>{reshaped.getType()}, operands, op.getAttrs());

    // Reshape the result back into an unranked tensor.
    rewriter.replaceOpWithNewOp<mhlo::DynamicReshapeOp>(op, result_type,
                                                        computed, shape);

    return success();
  }
};

// Handles lowering of the following pattern to patterns that will be further
// matched by other patterns until they result in LHLO:
//   %result = "chlo.op"(%lhs, %rhs) : (<*xTy>, <*xTy>) -> <*xTy>
//
// The sequence of specializations this handles is:
//   - Either operand being scalar
//   - Operands having equal shapes
//   - The resulting value being any of ranks [2,6]
template <typename ChloOpTy, typename HloOpTy, typename Adaptor>
struct ConvertUnrankedDynamicBroadcastBinaryOp
    : public OpRewritePattern<ChloOpTy> {
  using OpRewritePattern<ChloOpTy>::OpRewritePattern;

  LogicalResult matchAndRewrite(ChloOpTy op,
                                PatternRewriter &rewriter) const override {
    auto loc = op.getLoc();
    Value lhs = op.lhs();
    Value rhs = op.rhs();
    auto lhs_type = lhs.getType().dyn_cast<UnrankedTensorType>();
    auto rhs_type = rhs.getType().dyn_cast<UnrankedTensorType>();
    auto result_type = op.getResult().getType().template dyn_cast<TensorType>();

    // Only support unranked operands. If either operand is ranked, another
    // pattern will handle the lowering.
    if (!lhs_type || !rhs_type) return failure();

    // If lhs is scalar
    auto if_op = rewriter.create<scf::IfOp>(
        loc, result_type, IsScalarTensor(rewriter, op, lhs), true);
    OpBuilder if_lhs_scalar_builder = if_op.getThenBodyBuilder();
    Value reshaped_lhs = if_lhs_scalar_builder.create<TensorCastOp>(
        loc, RankedTensorType::get({}, lhs_type.getElementType()), lhs);
    Value if_lhs_scalar_result = if_lhs_scalar_builder.create<ChloOpTy>(
        loc, ArrayRef<Type>{result_type}, ArrayRef<Value>{reshaped_lhs, rhs},
        op.getAttrs());
    if_lhs_scalar_builder.create<scf::YieldOp>(loc, if_lhs_scalar_result);

    // If lhs is NOT scalar
    //
    // See if rhs is scalar
    OpBuilder else_lhs_scalar_builder = if_op.getElseBodyBuilder();
    auto if_rhs_scalar_op = else_lhs_scalar_builder.create<scf::IfOp>(
        loc, result_type, IsScalarTensor(else_lhs_scalar_builder, op, rhs),
        true);
    else_lhs_scalar_builder.create<scf::YieldOp>(loc,
                                                 if_rhs_scalar_op.getResult(0));
    OpBuilder if_rhs_scalar_builder = if_rhs_scalar_op.getThenBodyBuilder();
    Value reshaped_rhs = if_rhs_scalar_builder.create<TensorCastOp>(
        loc, RankedTensorType::get({}, lhs_type.getElementType()), rhs);
    Value if_rhs_scalar_result = if_rhs_scalar_builder.create<ChloOpTy>(
        loc, ArrayRef<Type>{result_type}, ArrayRef<Value>{lhs, reshaped_rhs},
        op.getAttrs());
    if_rhs_scalar_builder.create<scf::YieldOp>(loc, if_rhs_scalar_result);

    // If NEITHER shape is scalar
    //
    // See if shapes are equal.
    OpBuilder else_no_scalars_builder = if_rhs_scalar_op.getElseBodyBuilder();
    Value shape_of_lhs =
        else_no_scalars_builder.create<shape::ShapeOfOp>(loc, lhs);
    Value shape_of_rhs =
        else_no_scalars_builder.create<shape::ShapeOfOp>(loc, rhs);
    Value equal_shapes = else_no_scalars_builder.create<shape::ShapeEqOp>(
        loc, shape_of_lhs, shape_of_rhs);

    auto if_eq_shapes_op = else_no_scalars_builder.create<scf::IfOp>(
        loc, result_type, equal_shapes, true);
    else_no_scalars_builder.create<scf::YieldOp>(loc,
                                                 if_eq_shapes_op.getResult(0));

    OpBuilder if_eq_shapes_builder = if_eq_shapes_op.getThenBodyBuilder();
    Value non_broadcast_op =
        Adaptor::CreateOp(op, result_type, lhs, rhs, if_eq_shapes_builder);
    if_eq_shapes_builder.create<scf::YieldOp>(loc, non_broadcast_op);

    // If shapes are not scalar, nor equal
    //
    // See if values are of a rank that we support.
    OpBuilder if_neq_shapes_builder = if_eq_shapes_op.getElseBodyBuilder();
    if_neq_shapes_builder.create<scf::YieldOp>(
        loc, HandleBroadcastAndOp(if_neq_shapes_builder, op, lhs, rhs));

    rewriter.replaceOp(op, {if_op.getResult(0)});
    return success();
  }

 private:
  // Returns the dyanamic result of checking the given value is a scalar
  // tensor.
  Value IsScalarTensor(OpBuilder &rewriter, ChloOpTy op, Value tensor) const {
    auto loc = op.getLoc();

    Value shape_of_tensor = rewriter.create<shape::ShapeOfOp>(loc, tensor);
    Value rank_tensor = rewriter.create<shape::RankOp>(
        loc, rewriter.getIndexType(), shape_of_tensor);
    return rewriter.create<CmpIOp>(loc, rewriter.getI1Type(), CmpIPredicate::eq,
                                   rank_tensor,
                                   rewriter.create<ConstantIndexOp>(loc, 0));
  }

  // Create the if statement and code for a broadcasting op with a result of a
  // given rank.
  scf::IfOp createRankSpecializedBroadcastAndOp(OpBuilder &builder, ChloOpTy op,
                                                Value lhs, Value rhs,
                                                Value actual_rank,
                                                int targeted_rank) const {
    auto loc = op.getLoc();

    // Create the if block to place the current specialized logic in.
    Value greater_rank_is_n = builder.create<CmpIOp>(
        loc, CmpIPredicate::eq, actual_rank,
        builder.create<ConstantIndexOp>(loc, targeted_rank));
    auto if_op =
        builder.create<scf::IfOp>(loc, lhs.getType(), greater_rank_is_n, true);
    OpBuilder if_builder = if_op.getThenBodyBuilder();

    // Handle shape broadcasting and inferrence.
    Value lhs_shape = if_builder.create<shape::ShapeOfOp>(loc, lhs);
    Value rhs_shape = if_builder.create<shape::ShapeOfOp>(loc, rhs);
    SmallVector<int64_t, 6> ranked_shape(targeted_rank, 1);
    auto unknown_rank_extent_tensor_type = RankedTensorType::get(
        {RankedTensorType::kDynamicSize}, builder.getIndexType());
    auto known_rank_extent_tensor_type =
        RankedTensorType::get({targeted_rank}, builder.getIndexType());
    auto reshaped_type = RankedTensorType::get(
        llvm::SmallVector<int64_t, 6>(targeted_rank,
                                      RankedTensorType::kDynamicSize),
        lhs.getType().template dyn_cast<TensorType>().getElementType());
    Value ranked_shape_val = if_builder.create<shape::ConstShapeOp>(
        loc, known_rank_extent_tensor_type,
        mlir::DenseIntElementsAttr::get(known_rank_extent_tensor_type,
                                        ranked_shape));
    Value extended_lhs = if_builder.create<shape::BroadcastOp>(
        loc, unknown_rank_extent_tensor_type, lhs_shape, ranked_shape_val,
        nullptr);
    Value extended_lhs_casted = if_builder.create<TensorCastOp>(
        loc, known_rank_extent_tensor_type, extended_lhs);
    Value extended_rhs = if_builder.create<shape::BroadcastOp>(
        loc, unknown_rank_extent_tensor_type, rhs_shape, ranked_shape_val,
        nullptr);
    Value extended_rhs_casted = if_builder.create<TensorCastOp>(
        loc, known_rank_extent_tensor_type, extended_rhs);

    // 1. Reshape operands to the given rank (with the same number of elements)
    // 2. Compute the ranked-broadcasted ChloOp (which will assert that the ops
    //    can be broadcasted and do the actual broadcasting)
    // 3. Type erase the output back to unranked
    Value reshaped_lhs = if_builder.create<mhlo::DynamicReshapeOp>(
        loc, reshaped_type, lhs, extended_lhs_casted);
    Value reshaped_rhs = if_builder.create<mhlo::DynamicReshapeOp>(
        loc, reshaped_type, rhs, extended_rhs_casted);
    Value result = if_builder.create<ChloOpTy>(
        loc, ArrayRef<Type>{reshaped_type},
        ArrayRef<Value>{reshaped_lhs, reshaped_rhs}, op.getAttrs());
    Value reshaped_result = if_builder.create<TensorCastOp>(
        loc, UnrankedTensorType::get(reshaped_type.getElementType()), result);
    if_builder.create<scf::YieldOp>(loc, reshaped_result);

    // Return the if_op, so the result can be used and the else block can be
    // used for the next rank specialized step.
    return if_op;
  }

  // Iterates over the desired ranks to be specialized and generates the code
  // snippet for each case.
  Value HandleBroadcastAndOp(OpBuilder &rewriter, ChloOpTy op, Value lhs,
                             Value rhs) const {
    constexpr int max_rank_specialization = 7;
    auto loc = op.getLoc();

    // Find the larger rank of the 2 operands.
    auto extent_tensor_type = RankedTensorType::get({ShapedType::kDynamicSize},
                                                    rewriter.getIndexType());
    Value lhs_shape =
        rewriter.create<shape::ShapeOfOp>(loc, extent_tensor_type, lhs);
    Value rhs_shape =
        rewriter.create<shape::ShapeOfOp>(loc, extent_tensor_type, rhs);
    Value lhs_rank =
        rewriter.create<RankOp>(loc, rewriter.getIndexType(), lhs_shape);
    Value rhs_rank =
        rewriter.create<RankOp>(loc, rewriter.getIndexType(), rhs_shape);
    Value greater_rank_lhs =
        rewriter.create<CmpIOp>(loc, CmpIPredicate::sgt, lhs_rank, rhs_rank);
    Value greater_rank =
        rewriter.create<SelectOp>(loc, greater_rank_lhs, lhs_rank, rhs_rank);

    // Generate a list of nested if/else statements to handle rank
    // specializations from 2-6.
    scf::IfOp if_op = createRankSpecializedBroadcastAndOp(rewriter, op, lhs,
                                                          rhs, greater_rank, 2);

    // Put each subsequent rank specialization inside the else statement of the
    // previous one.
    OpBuilder else_builder = if_op.getElseBodyBuilder();
    for (int i = 3; i < max_rank_specialization; i++) {
      auto inner_if = createRankSpecializedBroadcastAndOp(else_builder, op, lhs,
                                                          rhs, greater_rank, i);

      else_builder.create<scf::YieldOp>(loc, inner_if.getResult(0));
      else_builder = inner_if.getElseBodyBuilder();
    }

    // Fire an assertion if none of the rank specializations applied (one of the
    // ranks was greater than 6).
    else_builder.create<AssertOp>(
        loc, else_builder.create<ConstantIntOp>(loc, 0, 1),
        "Input for dynamic binary op lowering was of a rank greater than 6");
    else_builder.create<scf::YieldOp>(loc, lhs);

    // Return the result of the outermost if statement.
    return if_op.getResult(0);
  }
};

template <typename ChloOpTy, typename HloOpTy, typename Adaptor>
void PopulateForBinaryOp(MLIRContext *context,
                         OwningRewritePatternList *patterns) {
  patterns
      ->insert<ConvertTrivialNonBroadcastBinaryOp<ChloOpTy, HloOpTy, Adaptor>>(
          context, 10);
  patterns->insert<
      ConvertRankedDynamicBroadcastBinaryOp<ChloOpTy, HloOpTy, Adaptor>>(
      context, 5);
  patterns->insert<
      ConvertUnrankedScalarDynamicBroadcastBinaryOp<ChloOpTy, HloOpTy>,
      ConvertUnrankedDynamicBroadcastBinaryOp<ChloOpTy, HloOpTy, Adaptor>>(
      context);
}

template <typename FromOpTy, typename ToOpTy>
struct HloBinaryElementwiseAdaptor {
  static ToOpTy CreateOp(FromOpTy from_op, Type result_type,
                         Value broadcasted_lhs, Value broadcasted_rhs,
                         OpBuilder &builder) {
    return builder.create<ToOpTy>(from_op.getLoc(), result_type,
                                  broadcasted_lhs, broadcasted_rhs);
  }
};

struct HloComplexAdaptor {
  static mhlo::ComplexOp CreateOp(BroadcastComplexOp from_op, Type result_type,
                                  Value broadcasted_lhs, Value broadcasted_rhs,
                                  OpBuilder &builder) {
    return builder.create<mhlo::ComplexOp>(from_op.getLoc(), result_type,
                                           broadcasted_lhs, broadcasted_rhs);
  }
};

struct HloCompareAdaptor {
  static mhlo::CompareOp CreateOp(BroadcastCompareOp from_op, Type result_type,
                                  Value broadcasted_lhs, Value broadcasted_rhs,
                                  OpBuilder &builder) {
    return builder.create<mhlo::CompareOp>(
        from_op.getLoc(), result_type, broadcasted_lhs, broadcasted_rhs,
        from_op.comparison_direction(), from_op.compare_typeAttr());
  }
};

#include "generated_chlo_legalize_to_hlo.inc"
}  // namespace

void PopulateLegalizeChloToHloPatterns(MLIRContext *context,
                                       OwningRewritePatternList *patterns) {
  populateWithGenerated(context, *patterns);

  // Instantiate conversion templates for conforming binary elementwise ops
  // that do not have different dtypes between operands and results and do
  // not have special attributes that need to be preserved.
#define POPULATE_BCAST(ChloOp, HloOp)                                      \
  PopulateForBinaryOp<ChloOp, HloOp,                                       \
                      HloBinaryElementwiseAdaptor<ChloOp, HloOp>>(context, \
                                                                  patterns);

  POPULATE_BCAST(BroadcastAddOp, mhlo::AddOp);
  POPULATE_BCAST(BroadcastAndOp, mhlo::AndOp);
  POPULATE_BCAST(BroadcastAtan2Op, mhlo::Atan2Op);
  POPULATE_BCAST(BroadcastDivOp, mhlo::DivOp);
  POPULATE_BCAST(BroadcastMaxOp, mhlo::MaxOp);
  POPULATE_BCAST(BroadcastMinOp, mhlo::MinOp);
  POPULATE_BCAST(BroadcastMulOp, mhlo::MulOp);
  POPULATE_BCAST(BroadcastOrOp, mhlo::OrOp);
  POPULATE_BCAST(BroadcastPowOp, mhlo::PowOp);
  POPULATE_BCAST(BroadcastRemOp, mhlo::RemOp);
  POPULATE_BCAST(BroadcastShiftLeftOp, mhlo::ShiftLeftOp);
  POPULATE_BCAST(BroadcastShiftRightArithmeticOp, mhlo::ShiftRightArithmeticOp);
  POPULATE_BCAST(BroadcastShiftRightLogicalOp, mhlo::ShiftRightLogicalOp);
  POPULATE_BCAST(BroadcastSubOp, mhlo::SubOp);
  POPULATE_BCAST(BroadcastXorOp, mhlo::XorOp);

  // Broadcasting ops requiring special construction.
  PopulateForBinaryOp<BroadcastComplexOp, mhlo::ComplexOp, HloComplexAdaptor>(
      context, patterns);
  PopulateForBinaryOp<BroadcastCompareOp, mhlo::CompareOp, HloCompareAdaptor>(
      context, patterns);

  // Other patterns.
  patterns->insert<ConvertConstantLikeOp>(context);
}

}  // namespace chlo
}  // namespace mlir