Full Code of leanprover/std4 for AI

main 48ff08a0e30a cached
270 files
1.2 MB
437.3k tokens
1 requests
Download .txt
Showing preview only (1,386K chars total). Download the full file or copy to clipboard to get everything.
Repository: leanprover/std4
Branch: main
Commit: 48ff08a0e30a
Files: 270
Total size: 1.2 MB

Directory structure:
gitextract_6ram4obd/

├── .docker/
│   └── gitpod/
│       └── Dockerfile
├── .github/
│   └── workflows/
│       ├── build.yml
│       ├── docs-deploy.yml
│       ├── docs-release.yml
│       ├── labels-from-comments.yml
│       ├── labels-from-status.yml
│       ├── merge_conflicts.yml
│       ├── nightly_bump_and_merge.yml
│       ├── nightly_detect_failure.yml
│       ├── nightly_merge_master.yml
│       └── test_mathlib.yml
├── .gitignore
├── .gitpod.yml
├── .vscode/
│   ├── copyright.code-snippets
│   └── settings.json
├── Batteries/
│   ├── Classes/
│   │   ├── Cast.lean
│   │   ├── Deprecated.lean
│   │   ├── Order.lean
│   │   ├── RatCast.lean
│   │   └── SatisfiesM.lean
│   ├── CodeAction/
│   │   ├── Attr.lean
│   │   ├── Basic.lean
│   │   ├── Deprecated.lean
│   │   ├── Match.lean
│   │   └── Misc.lean
│   ├── CodeAction.lean
│   ├── Control/
│   │   ├── AlternativeMonad.lean
│   │   ├── ForInStep/
│   │   │   ├── Basic.lean
│   │   │   └── Lemmas.lean
│   │   ├── ForInStep.lean
│   │   ├── LawfulMonadState.lean
│   │   ├── Lemmas.lean
│   │   ├── Monad.lean
│   │   ├── Nondet/
│   │   │   └── Basic.lean
│   │   └── OptionT.lean
│   ├── Data/
│   │   ├── Array/
│   │   │   ├── Basic.lean
│   │   │   ├── Init/
│   │   │   │   └── Lemmas.lean
│   │   │   ├── Lemmas.lean
│   │   │   ├── Match.lean
│   │   │   ├── Merge.lean
│   │   │   ├── Monadic.lean
│   │   │   ├── Pairwise.lean
│   │   │   └── Scan.lean
│   │   ├── Array.lean
│   │   ├── AssocList.lean
│   │   ├── BinaryHeap/
│   │   │   └── Basic.lean
│   │   ├── BinaryHeap.lean
│   │   ├── BinomialHeap/
│   │   │   ├── Basic.lean
│   │   │   └── Lemmas.lean
│   │   ├── BinomialHeap.lean
│   │   ├── BitVec/
│   │   │   ├── Basic.lean
│   │   │   └── Lemmas.lean
│   │   ├── BitVec.lean
│   │   ├── Bool.lean
│   │   ├── ByteArray.lean
│   │   ├── ByteSlice.lean
│   │   ├── Char/
│   │   │   ├── AsciiCasing.lean
│   │   │   └── Basic.lean
│   │   ├── Char.lean
│   │   ├── DList/
│   │   │   ├── Basic.lean
│   │   │   └── Lemmas.lean
│   │   ├── DList.lean
│   │   ├── Fin/
│   │   │   ├── Basic.lean
│   │   │   ├── Fold.lean
│   │   │   ├── Lemmas.lean
│   │   │   └── OfBits.lean
│   │   ├── Fin.lean
│   │   ├── FloatArray.lean
│   │   ├── HashMap/
│   │   │   └── Basic.lean
│   │   ├── HashMap.lean
│   │   ├── Int.lean
│   │   ├── List/
│   │   │   ├── ArrayMap.lean
│   │   │   ├── Basic.lean
│   │   │   ├── Count.lean
│   │   │   ├── Init/
│   │   │   │   └── Lemmas.lean
│   │   │   ├── Lemmas.lean
│   │   │   ├── Matcher.lean
│   │   │   ├── Monadic.lean
│   │   │   ├── Pairwise.lean
│   │   │   ├── Perm.lean
│   │   │   └── Scan.lean
│   │   ├── List.lean
│   │   ├── MLList/
│   │   │   ├── Basic.lean
│   │   │   ├── Heartbeats.lean
│   │   │   └── IO.lean
│   │   ├── MLList.lean
│   │   ├── NameSet.lean
│   │   ├── Nat/
│   │   │   ├── Basic.lean
│   │   │   ├── Bisect.lean
│   │   │   ├── Bitwise/
│   │   │   │   └── Lemmas.lean
│   │   │   ├── Bitwise.lean
│   │   │   ├── Gcd.lean
│   │   │   └── Lemmas.lean
│   │   ├── Nat.lean
│   │   ├── PairingHeap.lean
│   │   ├── RBMap/
│   │   │   ├── Alter.lean
│   │   │   ├── Basic.lean
│   │   │   ├── Depth.lean
│   │   │   ├── Lemmas.lean
│   │   │   └── WF.lean
│   │   ├── RBMap.lean
│   │   ├── Random/
│   │   │   └── MersenneTwister.lean
│   │   ├── Random.lean
│   │   ├── Range/
│   │   │   └── Lemmas.lean
│   │   ├── Range.lean
│   │   ├── Rat/
│   │   │   └── Float.lean
│   │   ├── Rat.lean
│   │   ├── RunningStats.lean
│   │   ├── Stream.lean
│   │   ├── String/
│   │   │   ├── AsciiCasing.lean
│   │   │   ├── Basic.lean
│   │   │   ├── Legacy.lean
│   │   │   ├── Lemmas.lean
│   │   │   └── Matcher.lean
│   │   ├── String.lean
│   │   ├── UInt.lean
│   │   ├── UnionFind/
│   │   │   ├── Basic.lean
│   │   │   └── Lemmas.lean
│   │   ├── UnionFind.lean
│   │   ├── Vector/
│   │   │   ├── Basic.lean
│   │   │   ├── Lemmas.lean
│   │   │   └── Monadic.lean
│   │   └── Vector.lean
│   ├── Lean/
│   │   ├── AttributeExtra.lean
│   │   ├── EStateM.lean
│   │   ├── Except.lean
│   │   ├── Expr.lean
│   │   ├── Float.lean
│   │   ├── HashMap.lean
│   │   ├── HashSet.lean
│   │   ├── IO/
│   │   │   └── Process.lean
│   │   ├── Json.lean
│   │   ├── LawfulMonad.lean
│   │   ├── LawfulMonadLift.lean
│   │   ├── Meta/
│   │   │   ├── Basic.lean
│   │   │   ├── DiscrTree.lean
│   │   │   ├── Expr.lean
│   │   │   ├── Inaccessible.lean
│   │   │   ├── InstantiateMVars.lean
│   │   │   ├── SavedState.lean
│   │   │   ├── Simp.lean
│   │   │   └── UnusedNames.lean
│   │   ├── MonadBacktrack.lean
│   │   ├── NameMapAttribute.lean
│   │   ├── PersistentHashMap.lean
│   │   ├── PersistentHashSet.lean
│   │   ├── Position.lean
│   │   ├── SatisfiesM.lean
│   │   ├── Syntax.lean
│   │   ├── System/
│   │   │   └── IO.lean
│   │   ├── TagAttribute.lean
│   │   └── Util/
│   │       └── EnvSearch.lean
│   ├── Linter/
│   │   ├── UnnecessarySeqFocus.lean
│   │   └── UnreachableTactic.lean
│   ├── Linter.lean
│   ├── Logic.lean
│   ├── Tactic/
│   │   ├── Alias.lean
│   │   ├── Basic.lean
│   │   ├── Case.lean
│   │   ├── Congr.lean
│   │   ├── Exact.lean
│   │   ├── GeneralizeProofs.lean
│   │   ├── HelpCmd.lean
│   │   ├── Init.lean
│   │   ├── Instances.lean
│   │   ├── Lemma.lean
│   │   ├── Lint/
│   │   │   ├── Basic.lean
│   │   │   ├── Frontend.lean
│   │   │   ├── Misc.lean
│   │   │   ├── Simp.lean
│   │   │   └── TypeClass.lean
│   │   ├── Lint.lean
│   │   ├── NoMatch.lean
│   │   ├── OpenPrivate.lean
│   │   ├── PermuteGoals.lean
│   │   ├── PrintDependents.lean
│   │   ├── PrintOpaques.lean
│   │   ├── PrintPrefix.lean
│   │   ├── SeqFocus.lean
│   │   ├── ShowUnused.lean
│   │   ├── SqueezeScope.lean
│   │   ├── Trans.lean
│   │   └── Unreachable.lean
│   └── Util/
│       ├── Cache.lean
│       ├── ExtendedBinder.lean
│       ├── LibraryNote.lean
│       ├── Panic.lean
│       ├── Pickle.lean
│       └── ProofWanted.lean
├── Batteries.lean
├── BatteriesTest/
│   ├── ArrayMap.lean
│   ├── Char.lean
│   ├── GeneralizeProofs.lean
│   ├── Internal/
│   │   ├── DummyLabelAttr.lean
│   │   ├── DummyLibraryNote.lean
│   │   └── DummyLibraryNote2.lean
│   ├── MLList.lean
│   ├── OpenPrivateDefs.lean
│   ├── String.lean
│   ├── absurd.lean
│   ├── alias.lean
│   ├── array.lean
│   ├── array_scan.lean
│   ├── by_contra.lean
│   ├── case.lean
│   ├── congr.lean
│   ├── conv_equals.lean
│   ├── except.lean
│   ├── exfalso.lean
│   ├── float.lean
│   ├── help_cmd.lean
│   ├── import_lean.lean
│   ├── instances.lean
│   ├── isIndependentOf.lean
│   ├── kmp_matcher.lean
│   ├── lemma_cmd.lean
│   ├── library_note.lean
│   ├── lintTC.lean
│   ├── lintTrace.lean
│   ├── lint_coinductive.lean
│   ├── lint_docBlame.lean
│   ├── lint_docBlameThm.lean
│   ├── lint_dupNamespace.lean
│   ├── lint_lean.lean
│   ├── lint_simpNF.lean
│   ├── lint_simpNF_respectTransparency.lean
│   ├── lint_unreachableTactic.lean
│   ├── linterVisibility.lean
│   ├── lintsimp.lean
│   ├── lintunused.lean
│   ├── list_enumeration.lean
│   ├── list_sublists.lean
│   ├── mersenne_twister.lean
│   ├── nondet.lean
│   ├── norm_cast.lean
│   ├── omega/
│   │   └── benchmark.lean
│   ├── on_goal.lean
│   ├── openPrivate.lean
│   ├── print_opaques.lean
│   ├── print_prefix.lean
│   ├── proof_wanted.lean
│   ├── register_label_attr.lean
│   ├── rfl.lean
│   ├── satisfying.lean
│   ├── seq_focus.lean
│   ├── show_term.lean
│   ├── show_unused.lean
│   ├── simp_trace.lean
│   ├── simpa.lean
│   ├── solve_by_elim.lean
│   ├── trans.lean
│   ├── tryThis.lean
│   ├── vector.lean
│   └── where.lean
├── LICENSE
├── README.md
├── Shake/
│   └── Main.lean
├── bors.toml
├── docs/
│   └── lakefile.toml
├── lake-manifest.json
├── lakefile.toml
├── lean-toolchain
└── scripts/
    ├── check_imports.lean
    ├── create-adaptation-pr.sh
    ├── lintWhitespace.sh
    ├── merge-lean-testing-pr.sh
    ├── nolints.json
    ├── noshake.json
    ├── runLinter.lean
    └── updateBatteries.sh

================================================
FILE CONTENTS
================================================

================================================
FILE: .docker/gitpod/Dockerfile
================================================
# This is the Dockerfile for leanprover-community/batteries
# This file is mostly copied from [mathlib4](https://github.com/leanprover-community/mathlib4/blob/master/.docker/gitpod/Dockerfile)

# gitpod doesn't support multiple FROM statements, (or rather, you can't copy from one to another)
# so we just install everything in one go
FROM ubuntu:jammy

USER root

RUN apt-get update && apt-get install sudo git curl bash-completion python3-requests gcc make -y && apt-get clean

RUN useradd -l -u 33333 -G sudo -md /home/gitpod -s /bin/bash -p gitpod gitpod \
    # passwordless sudo for users in the 'sudo' group
    && sed -i.bkp -e 's/%sudo\s\+ALL=(ALL\(:ALL\)\?)\s\+ALL/%sudo ALL=NOPASSWD:ALL/g' /etc/sudoers
USER gitpod
WORKDIR /home/gitpod

SHELL ["/bin/bash", "-c"]

# gitpod bash prompt
RUN { echo && echo "PS1='\[\033[01;32m\]\u\[\033[00m\] \[\033[01;34m\]\w\[\033[00m\]\$(__git_ps1 \" (%s)\") $ '" ; } >> .bashrc

# install elan
RUN curl https://raw.githubusercontent.com/leanprover/elan/master/elan-init.sh -sSf | sh -s -- -y --default-toolchain none

# install whichever toolchain batteries is currently using
RUN . ~/.profile && elan toolchain install $(curl https://raw.githubusercontent.com/leanprover-community/batteries/main/lean-toolchain)

# install neovim (for any lean.nvim user), via tarball since the appimage doesn't work for some reason, and jammy's version is ancient
RUN curl -s -L https://github.com/neovim/neovim/releases/download/stable/nvim-linux64.tar.gz | tar xzf - && sudo mv nvim-linux64 /opt/nvim

ENV PATH="/home/gitpod/.local/bin:/home/gitpod/.elan/bin:/opt/nvim/bin:${PATH}"

# fix the infoview when the container is used on gitpod:
ENV VSCODE_API_VERSION="1.50.0"

# ssh to github once to bypass the unknown fingerprint warning
RUN ssh -o StrictHostKeyChecking=no github.com || true

# run sudo once to suppress usage info
RUN sudo echo finished


================================================
FILE: .github/workflows/build.yml
================================================
on:
  push:
    branches-ignore:
      # ignore tmp branches used by bors
      - 'staging.tmp*'
      - 'trying.tmp*'
      - 'staging*.tmp'
  pull_request:

name: ci

concurrency:
  group: build-${{ github.sha }}
  cancel-in-progress: true

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - id: lean-action
        name: build, test, and lint batteries
        uses: leanprover/lean-action@v1
        with:
          build-args: '--wfail'

      - name: Check that all files are imported
        run: lake env lean scripts/check_imports.lean

      - name: Check for forbidden character ↦
        if: always()
        run: |
          if grep -r -n --include=\*.lean -e '↦' . ; then
            echo "Error: Found forbidden character ↦"
            exit 1
          fi

      - name: Check for 'namespace Mathlib'
        if: always()
        run: |
          if grep -r -n --include=\*.lean -e 'namespace Mathlib' . ; then
            echo "Error: Found 'namespace Mathlib'"
            exit 1
          fi

      - name: Check for long lines
        if: always()
        run: |
          ! (find Batteries -name "*.lean" -type f -exec grep -E -H -n '^.{101,}$' {} \; | grep -v -E 'https?://')

      - name: Check for trailing whitespace
        if: always()
        run: |
          scripts/lintWhitespace.sh

      - name: Don't 'import Lean', use precise imports
        if: always()
        run: |
          ! (find . -name "*.lean" ! -path "./BatteriesTest/import_lean.lean" -type f -print0 | xargs -0 grep -E -n '^import Lean$')


================================================
FILE: .github/workflows/docs-deploy.yml
================================================
name: Deploy Docs

on:
  workflow_dispatch:
  schedule:
    - cron: '0 10 * * *' # daily (UTC 10:00)

permissions:
  contents: write

jobs:
  deploy-docs:
    runs-on: ubuntu-latest
    if: github.repository_owner == 'leanprover-community'
    steps:

      - name: Checkout
        uses: actions/checkout@v4

      - name: Install Lean
        uses: leanprover/lean-action@v1
        with:
          test: false
          lint: false
          use-github-cache: true

      - name: Build Docs
        working-directory: docs
        run: lake build --keep-toolchain -q Batteries:docs

      - name: Deploy Docs
        run: |
          git config user.name "leanprover-community-batteries-bot"
          git config user.email "leanprover-community-batteries-bot@users.noreply.github.com"
          git checkout -b docs
          git add docs/doc docs/doc-data
          git commit -m "chore: generate docs"
          git push origin docs --force


================================================
FILE: .github/workflows/docs-release.yml
================================================
name: Release Docs

on:
  push:
    tags:
      - "v[0-9]+.[0-9]+.[0-9]+"
      - "v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+"

permissions:
  contents: write

jobs:
  build-docs:
    runs-on: ubuntu-latest
    if: github.repository_owner == 'leanprover-community'
    steps:

      - name: Checkout
        uses: actions/checkout@v4

      - name: Install Lean
        uses: leanprover/lean-action@v1
        with:
          test: false
          lint: false
          use-github-cache: true

      - name: Build Docs
        working-directory: docs
        run: lake build --keep-toolchain -q Batteries:docs

      - name: Compress Docs
        working-directory: docs
        env:
          TAG_NAME: ${{ github.ref_name }}
        run: |
          tar -czf docs-${TAG_NAME}.tar.gz doc doc-data
          zip -rq docs-${TAG_NAME}.zip doc doc-data

      - name: Release Docs
        uses: softprops/action-gh-release@v2
        with:
          prerelease: ${{ contains(github.ref, 'rc') }}
          make_latest: ${{ !contains(github.ref, 'rc') }}
          files: |
            docs/docs-${{ github.ref_name }}.tar.gz
            docs/docs-${{ github.ref_name }}.zip
          fail_on_unmatched_files: true


================================================
FILE: .github/workflows/labels-from-comments.yml
================================================
# This workflow allows any user to add one of the `awaiting-review`, `awaiting-author`, or `WIP` labels,
# by commenting on the PR or issue.
# Other labels from this set are removed automatically at the same time.

name: Label PR based on Comment

on:
  issue_comment:
    types: [created]

jobs:
  update-label:
    if: github.event.issue.pull_request != null && (github.event.comment.body == 'awaiting-review' || github.event.comment.body == 'awaiting-author' || github.event.comment.body == 'WIP')
    runs-on: ubuntu-latest

    steps:
    - name: Remove all relevant labels
      uses: actions/github-script@v6
      with:
        github-token: ${{ secrets.GITHUB_TOKEN }}
        script: |
          const { owner, repo, number: issue_number } = context.issue;

          // Remove the labels if they exist
          await github.rest.issues.removeLabel({ owner, repo, issue_number, name: 'awaiting-review' }).catch(() => {});
          await github.rest.issues.removeLabel({ owner, repo, issue_number, name: 'awaiting-author' }).catch(() => {});
          await github.rest.issues.removeLabel({ owner, repo, issue_number, name: 'WIP' }).catch(() => {});

    - name: Add label based on comment
      uses: actions/github-script@v6
      with:
        github-token: ${{ secrets.GITHUB_TOKEN }}
        script: |
          const { owner, repo, number: issue_number  } = context.issue;
          const commentBody = context.payload.comment.body;

          if (commentBody == 'awaiting-review') {
            await github.rest.issues.addLabels({ owner, repo, issue_number, labels: ['awaiting-review'] });
          } else if (commentBody == 'awaiting-author') {
            await github.rest.issues.addLabels({ owner, repo, issue_number, labels: ['awaiting-author'] });
          } else if (commentBody == 'WIP') {
            await github.rest.issues.addLabels({ owner, repo, issue_number, labels: ['WIP'] });
          }

    # - name: Delete the comment
    #   uses: actions/github-script@v6
    #   with:
    #     github-token: ${{ secrets.GITHUB_TOKEN }}
    #     script: |
    #       const { owner, repo } = context.repo;

    #       await github.rest.issues.deleteComment({ owner, repo, comment_id: context.payload.comment.id });


================================================
FILE: .github/workflows/labels-from-status.yml
================================================
# This workflow assigns `awaiting-review` or `WIP` labels to new PRs, and it removes
# `awaiting-review`, `awaiting-author`, or `WIP` label from closed PRs.
# It does not modify labels for open PRs that already have one of the `awaiting-review`,
# `awaiting-author`, or `WIP` labels.

name: Label PR from status change

permissions:
  contents: read
  pull-requests: write

on:
  pull_request_target:
    types:
      - closed
      - opened
      - reopened
      - converted_to_draft
      - ready_for_review
    branches:
      - main

jobs:
  auto-label:
    if: github.repository_owner == 'leanprover-community'
    runs-on: ubuntu-latest
    steps:

    - uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - name: Unlabel closed PR
      if: github.event.pull_request.state == 'closed'
      uses: actions-ecosystem/action-remove-labels@v1
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        labels: |
          WIP
          awaiting-author
          awaiting-review

    - name: Label unlabeled draft PR as WIP
      if: |
        github.event.pull_request.state == 'open' &&
        github.event.pull_request.draft &&
        ! contains(github.event.pull_request.labels.*.name, 'awaiting-author') &&
        ! contains(github.event.pull_request.labels.*.name, 'awaiting-review') &&
        ! contains(github.event.pull_request.labels.*.name, 'WIP')
      uses: actions-ecosystem/action-add-labels@v1
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        labels: WIP

    - name: Label unlabeled other PR as awaiting-review
      if: |
        github.event.pull_request.state == 'open' &&
        ! github.event.pull_request.draft &&
        ! contains(github.event.pull_request.labels.*.name, 'awaiting-author') &&
        ! contains(github.event.pull_request.labels.*.name, 'awaiting-review') &&
        ! contains(github.event.pull_request.labels.*.name, 'WIP')
      uses: actions-ecosystem/action-add-labels@v1
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        labels: awaiting-review


================================================
FILE: .github/workflows/merge_conflicts.yml
================================================
name: Merge conflicts

on:
  schedule:
    - cron: '*/60 * * * *' # run every 60 minutes

jobs:
  main:
    if: github.repository_owner == 'leanprover-community'
    runs-on: ubuntu-latest
    steps:
      - name: Generate app token
        id: app-token
        uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
        with:
          app-id: ${{ secrets.MATHLIB_MERGE_CONFLICTS_APP_ID }}
          private-key: ${{ secrets.MATHLIB_MERGE_CONFLICTS_PRIVATE_KEY }}
      # The create-github-app-token README states that this token is masked and will not be logged accidentally.
      - name: check if prs are dirty
        uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 # v3.0.3
        with:
          dirtyLabel: "merge-conflict"
          repoToken: ${{ steps.app-token.outputs.token }}


================================================
FILE: .github/workflows/nightly_bump_and_merge.yml
================================================
name: Bump toolchain and merge pr-testing branches

# This workflow combines the former `nightly_bump_toolchain.yml` and `discover-lean-pr-testing.yml`
# into a single workflow. This ensures that when the toolchain is bumped, any relevant
# lean-pr-testing branches are merged in the same push, avoiding spurious CI failures
# on the intermediate state (bumped toolchain without the adaptations).

on:
  schedule:
    - cron: '45 9/3 * * *'
    # 10:45AM CET/1:45AM PT (and then every 3 hours thereafter),
    # This should be 2 hours and 45 minutes after lean4 starts building the nightly.
    # Mathlib's `nightly-testing` branch is bumped 15 minutes later.
  workflow_dispatch:

jobs:
  bump-and-merge:
    runs-on: ubuntu-latest
    if: github.repository_owner == 'leanprover-community'
    steps:
    - name: Generate app token
      id: app-token
      uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
      with:
        app-id: ${{ secrets.MATHLIB_NIGHTLY_TESTING_APP_ID }}
        private-key: ${{ secrets.MATHLIB_NIGHTLY_TESTING_PRIVATE_KEY }}
    # The create-github-app-token README states that this token is masked and will not be logged accidentally.

    - name: Checkout nightly-testing branch
      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
      with:
        ref: nightly-testing
        fetch-depth: 0  # Fetch all branches and history
        token: ${{ steps.app-token.outputs.token }}

    - name: Set up Git
      run: |
        git config --global user.name "mathlib-nightly-testing[bot]"
        git config --global user.email "mathlib-nightly-testing[bot]@users.noreply.github.com"

    - name: Configure Lean
      uses: leanprover/lean-action@f807b338d95de7813c5c50d018f1c23c9b93b4ec # 2025-04-24
      with:
        auto-config: false
        use-github-cache: false
        use-mathlib-cache: false

    # ============================================================================
    # Phase 1: Bump the toolchain (commit locally, don't push yet)
    # ============================================================================

    - name: Get old toolchain version
      id: old-toolchain
      run: |
        # Capture the current toolchain BEFORE we modify anything
        OLD=$(cut -f2 -d: lean-toolchain)
        echo "old=$OLD"
        echo "old=$OLD" >> "$GITHUB_OUTPUT"

    - name: Get latest release tag from leanprover/lean4-nightly
      id: get-latest-release
      env:
        GH_TOKEN: ${{ steps.app-token.outputs.token }}
      run: |
        RELEASE_TAG=$(gh api -X GET repos/leanprover/lean4-nightly/releases \
          -f per_page=1 --jq '.[0].tag_name')
        if [ -z "$RELEASE_TAG" ] || [ "$RELEASE_TAG" = "null" ]; then
          echo "::error::Could not determine latest lean4-nightly release"
          exit 1
        fi
        echo "RELEASE_TAG=$RELEASE_TAG"
        echo "RELEASE_TAG=$RELEASE_TAG" >> "$GITHUB_ENV"
        echo "new=$RELEASE_TAG" >> "$GITHUB_OUTPUT"

    - name: Update lean-toolchain file
      run: |
        echo "leanprover/lean4:${RELEASE_TAG}" > lean-toolchain

    - name: Commit toolchain bump (without pushing)
      id: commit-bump
      run: |
        git add lean-toolchain
        # Don't fail if there's nothing to commit (toolchain already up to date)
        if git commit -m "chore: bump to ${RELEASE_TAG}"; then
          echo "bumped=true" >> "$GITHUB_OUTPUT"
        else
          echo "bumped=false" >> "$GITHUB_OUTPUT"
          echo "Toolchain already at ${RELEASE_TAG}, no bump needed"
        fi

    # ============================================================================
    # Phase 2: Find and merge pr-testing branches
    # ============================================================================

    - name: Clone lean4-nightly and get PRs
      id: get-prs
      if: steps.commit-bump.outputs.bumped == 'true'
      run: |
        OLD="${{ steps.old-toolchain.outputs.old }}"
        NEW="${{ steps.get-latest-release.outputs.new }}"

        echo "Finding PRs between $OLD and $NEW"

        NIGHTLY_URL="https://github.com/leanprover/lean4-nightly.git"

        # Create a temporary directory for cloning
        cd "$(mktemp -d)" || exit 1

        # Clone the repository with a depth of 1
        git clone --depth 1 "$NIGHTLY_URL"

        # Navigate to the cloned repository
        cd lean4-nightly || exit 1

        # Fetch the $OLD tag
        git fetch --depth=1 origin tag "$OLD" --no-tags
        # Fetch the $NEW tag
        git fetch origin tag "$NEW" --no-tags

        # Get all commit SHAs between the $OLD and $NEW toolchains
        COMMIT_SHAS=$(git log --format="%H" "$OLD..$NEW")

        # Initialize an empty string to collect PR numbers
        PRS=""

        # For each commit, query the GitHub API to get associated PRs
        for commit_sha in $COMMIT_SHAS; do
          echo "Checking commit $commit_sha for associated PRs..."

          # Query GitHub API for PRs associated with this commit
          pr_numbers=$(curl -s -H "Accept: application/vnd.github.v3+json" \
            "https://api.github.com/repos/leanprover/lean4/commits/$commit_sha/pulls" | \
            jq -r '.[] | select(.merged_at != null) | .number | tostring' 2>/dev/null || echo "")

          # Add each PR number to our list (duplicates will be handled later)
          for pr_num in $pr_numbers; do
            if [[ "$pr_num" =~ ^[0-9]+$ ]]; then
              PRS="$PRS $pr_num"
              echo "Found PR #$pr_num associated with commit $commit_sha"
            fi
          done
        done

        # Remove duplicates and trim whitespace
        PRS=$(echo "$PRS" | tr ' ' '\n' | sort -u | tr '\n' ' ' | xargs)

        # Output the PRs
        echo "Found PRs: $PRS"
        printf "prs<<EOF\n%s\nEOF" "$PRS" >> "$GITHUB_OUTPUT"

    - name: Find matching pr-testing branches
      id: find-branches
      if: steps.commit-bump.outputs.bumped == 'true'
      run: |
        PRS="${{ steps.get-prs.outputs.prs }}"
        echo "=== PRS ========================="
        echo "$PRS"

        # CRITICAL: If no PRs were found, skip branch matching entirely.
        if [ -z "$PRS" ]; then
          echo "No PRs found between old and new nightlies. Skipping branch discovery."
          echo "branches_exist=false" >> "$GITHUB_ENV"
          printf "branches<<EOF\n\nEOF" >> "$GITHUB_OUTPUT"
          exit 0
        fi

        echo "$PRS" | tr ' ' '\n' > prs.txt
        echo "=== prs.txt ====================="
        cat prs.txt
        MATCHING_BRANCHES=$(git branch -r | grep -f prs.txt | grep "lean-pr-testing" || true)
        echo "=== MATCHING_BRANCHES ==========="
        echo "$MATCHING_BRANCHES"
        echo "================================="

        # Initialize an empty variable to store branches with relevant diffs
        RELEVANT_BRANCHES=""

        # Loop through each matching branch
        for BRANCH in $MATCHING_BRANCHES; do
            echo " === Testing $BRANCH for relevance."
            # Get the diff filenames
            DIFF_FILES=$(git diff --name-only "origin/nightly-testing...$BRANCH")

            # Check if the diff contains files other than the specified ones
            # Note: Batteries uses lakefile.toml, not lakefile.lean
            if echo "$DIFF_FILES" | grep -v -e 'lake-manifest.json' -e 'lakefile.toml' -e 'lean-toolchain'; then
                # Extract the actual branch name
                ACTUAL_BRANCH=${BRANCH#origin/}

                # Append the branch details to RELEVANT_BRANCHES
                RELEVANT_BRANCHES="$RELEVANT_BRANCHES""$ACTUAL_BRANCH"$' '
            fi
        done

        # Output the relevant branches
        echo "=== RELEVANT_BRANCHES ==========="
        echo "'$RELEVANT_BRANCHES'"
        printf "branches<<EOF\n%s\nEOF" "$RELEVANT_BRANCHES" >> "$GITHUB_OUTPUT"

        # Check if there are relevant branches
        if [ -z "${RELEVANT_BRANCHES}" ]; then
          echo "branches_exist=false" >> "$GITHUB_ENV"
        else
          echo "branches_exist=true" >> "$GITHUB_ENV"
        fi

    - name: Execute merge script for each branch
      id: execute-merges
      if: steps.commit-bump.outputs.bumped == 'true' && env.branches_exist == 'true'
      run: |
        BRANCHES="${{ steps.find-branches.outputs.branches }}"

        # Initialize arrays to track results
        SUCCESSFUL_MERGES=""
        FAILED_MERGES=""

        # Ensure the merge script is executable
        chmod +x scripts/merge-lean-testing-pr.sh

        # Process each branch
        for BRANCH in $BRANCHES; do
          # Extract PR number from branch name
          PR_NUMBER=$(echo "$BRANCH" | grep -oP '\d+$')

          # Make sure we're on nightly-testing branch before doing fetch operations
          git checkout nightly-testing

          # Fetch all tags in the repository
          git fetch --tags

          # Fetch the PR branch
          git fetch origin "$BRANCH"

          # Find the most recent nightly-testing-YYYY-MM-DD tag that is an ancestor of the branch
          git checkout origin/"$BRANCH" || {
            echo "Failed to checkout branch origin/$BRANCH, skipping"
            continue
          }

          # Find tags that are ancestors of this branch with the right format
          LATEST_TAG=$(git tag --merged HEAD | grep "nightly-testing-[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}" | sort -r | head -n 1)
          echo "Latest tag found for $BRANCH: ${LATEST_TAG:-none}"

          # Return to nightly-testing branch
          git checkout nightly-testing

          # Default to nightly-testing if no tag is found
          if [ -z "$LATEST_TAG" ]; then
            COMPARE_BASE="nightly-testing"
          else
            COMPARE_BASE="$LATEST_TAG"
          fi

          GITHUB_DIFF="https://github.com/leanprover-community/batteries/compare/$COMPARE_BASE...lean-pr-testing-$PR_NUMBER"

          echo "Attempting to merge branch: $BRANCH (PR #$PR_NUMBER)"
          echo "Using diff URL: $GITHUB_DIFF (comparing with $COMPARE_BASE)"

          # Reset to a clean state before running merge script
          git reset --hard HEAD

          # Run the merge script and capture exit code
          # Note: The merge script does its own commit but NOT push
          if ./scripts/merge-lean-testing-pr.sh "$PR_NUMBER"; then
            echo "Successfully merged $BRANCH"
            SUCCESSFUL_MERGES="$SUCCESSFUL_MERGES$PR_NUMBER|$GITHUB_DIFF|$BRANCH "
          else
            echo "Failed to merge $BRANCH"
            FAILED_MERGES="$FAILED_MERGES$PR_NUMBER|$GITHUB_DIFF|$BRANCH "

            # Clean up - reset to a clean state
            git reset --hard HEAD
            git checkout nightly-testing
          fi
        done

        # Output the results
        echo "successful_merges=$SUCCESSFUL_MERGES" >> "$GITHUB_OUTPUT"
        echo "failed_merges=$FAILED_MERGES" >> "$GITHUB_OUTPUT"

    # ============================================================================
    # Phase 3: Push everything and notify
    # ============================================================================

    - name: Push all changes
      if: steps.commit-bump.outputs.bumped == 'true'
      run: |
        # This pushes the toolchain bump commit plus any successful merge commits
        git push origin nightly-testing

    - name: Prepare Zulip message
      id: zulip-message
      if: steps.commit-bump.outputs.bumped == 'true' && env.branches_exist == 'true'
      run: |
        SUCCESSFUL_MERGES="${{ steps.execute-merges.outputs.successful_merges }}"
        FAILED_MERGES="${{ steps.execute-merges.outputs.failed_merges }}"

        # Start building the message
        MESSAGE=""

        # Report successful merges
        if [ -n "$SUCCESSFUL_MERGES" ]; then
          MESSAGE+=$'### Successfully merged branches into Batteries\' \'nightly-testing\':\n\n'
          for MERGE_INFO in $SUCCESSFUL_MERGES; do
            IFS='|' read -r PR_NUMBER GITHUB_DIFF _ <<< "$MERGE_INFO"
            MESSAGE+=$(printf -- '- [lean-pr-testing-%s](%s) (adaptations for lean#%s)' "$PR_NUMBER" "$GITHUB_DIFF" "$PR_NUMBER")$'\n\n'
          done
          MESSAGE+=$'\n'
        else
          MESSAGE+=$'No branches were successfully merged into Batteries\' \'nightly-testing\'. \n\n'
        fi

        # Report failed merges
        if [ -n "$FAILED_MERGES" ]; then
          MESSAGE+=$'### Failed merges:\n\nThe following branches need to be merged manually into Batteries\' \'nightly-testing\':\n\n'
          for MERGE_INFO in $FAILED_MERGES; do
            IFS='|' read -r PR_NUMBER GITHUB_DIFF _ <<< "$MERGE_INFO"
            MESSAGE+=$(printf '- [lean-pr-testing-%s](%s) (adaptations for lean#%s)' "$PR_NUMBER" "$GITHUB_DIFF" "$PR_NUMBER")$'\n\n'
            MESSAGE+=$'```bash\n'
            MESSAGE+=$(printf 'scripts/merge-lean-testing-pr.sh %s' "$PR_NUMBER")$'\n'
            MESSAGE+=$'```\n\n'
          done
        else
          MESSAGE+=$'All branches were successfully merged!\n'
        fi

        # Output the message using the correct GitHub Actions syntax
        printf 'msg<<EOF\n%s\nEOF' "$MESSAGE" | tee "$GITHUB_OUTPUT"

    - name: Send message on Zulip
      if: steps.commit-bump.outputs.bumped == 'true' && env.branches_exist == 'true'
      uses: zulip/github-actions-zulip/send-message@e4c8f27c732ba9bd98ac6be0583096dea82feea5 # v1.0.2
      with:
        api-key: ${{ secrets.ZULIP_API_KEY }}
        email: 'github-mathlib4-bot@leanprover.zulipchat.com'
        organization-url: 'https://leanprover.zulipchat.com'
        to: 'nightly-testing-batteries'
        type: 'stream'
        topic: 'Mergeable lean testing PRs (Batteries)'
        content: |
          ${{ steps.zulip-message.outputs.msg }}


================================================
FILE: .github/workflows/nightly_detect_failure.yml
================================================
name: Post to zulip if the nightly-testing branch is failing.

on:
  workflow_run:
    workflows: ["ci"]
    types:
      - completed

jobs:
  handle_failure:
    if: ${{ github.event.workflow_run.conclusion == 'failure' && github.event.workflow_run.head_branch == 'nightly-testing' }}
    runs-on: ubuntu-latest

    steps:
    - name: Send message on Zulip
      uses: zulip/github-actions-zulip/send-message@e4c8f27c732ba9bd98ac6be0583096dea82feea5 # v1.0.2
      with:
        api-key: ${{ secrets.ZULIP_API_KEY }}
        email: 'github-mathlib4-bot@leanprover.zulipchat.com'
        organization-url: 'https://leanprover.zulipchat.com'
        to: 'nightly-testing-batteries'
        type: 'stream'
        topic: 'Batteries status updates'
        content: |
          ❌ The latest CI for Batteries' [nightly-testing branch](https://github.com/${{ github.repository }}/tree/nightly-testing) has [failed](https://github.com/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}) ([${{ github.sha }}](https://github.com/${{ github.repository }}/commit/${{ github.sha }})).
          You can `git fetch; git checkout nightly-testing` and push a fix.

  handle_success:
    if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'nightly-testing' }}
    runs-on: ubuntu-latest

    steps:
    - name: Generate app token
      id: app-token
      uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
      with:
        app-id: ${{ secrets.MATHLIB_NIGHTLY_TESTING_APP_ID }}
        private-key: ${{ secrets.MATHLIB_NIGHTLY_TESTING_PRIVATE_KEY }}
    # The create-github-app-token README states that this token is masked and will not be logged accidentally.

    - name: Checkout code
      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
      with:
        ref: nightly-testing # checkout nightly-testing branch
        fetch-depth: 0 # checkout all branches so that we can push from `nightly-testing` to `nightly-testing-YYYY-MM-DD`
        token: ${{ steps.app-token.outputs.token }}
    - name: Update the nightly-testing-YYYY-MM-DD branch
      run: |
        toolchain="$(<lean-toolchain)"
        if [[ $toolchain =~ leanprover/lean4:nightly-([a-zA-Z0-9_-]+) ]]; then
          version=${BASH_REMATCH[1]}
          printf 'NIGHTLY=%s\n' "${version}" >> "${GITHUB_ENV}"
          # Check if the remote tag exists
          if git ls-remote --tags --exit-code origin "nightly-testing-$version" >/dev/null; then
              printf 'Tag nightly-testing-%s already exists on the remote.' "${version}"
          else
              # If the tag does not exist, create and push the tag to remote
              printf 'Creating tag %s from the current state of the nightly-testing branch.' "nightly-testing-${version}"
              git tag "nightly-testing-${version}"
              git push origin "nightly-testing-${version}"
          fi
          hash="$(git rev-parse "nightly-testing-${version}")"
          printf 'SHA=%s\n' "${hash}" >> "${GITHUB_ENV}"
        else
          echo "Error: The file lean-toolchain does not contain the expected pattern."
          exit 1
        fi

    # Now post a success message to zulip, if the last message there is not a success message.
    # https://chat.openai.com/share/87656d2c-c804-4583-91aa-426d4f1537b3
    - name: Install Zulip API client
      run: pip install zulip

    - name: Check last message and post if necessary
      env:
        ZULIP_EMAIL: 'github-mathlib4-bot@leanprover.zulipchat.com'
        ZULIP_API_KEY: ${{ secrets.ZULIP_API_KEY }}
        ZULIP_SITE: 'https://leanprover.zulipchat.com'
        SHA: ${{ env.SHA }}
      run: |
        import os
        import zulip
        client = zulip.Client(email=os.getenv('ZULIP_EMAIL'), api_key=os.getenv('ZULIP_API_KEY'), site=os.getenv('ZULIP_SITE'))

        # Get the last message from the bot in the 'status updates' topic.
        # We narrow by sender to ignore human replies in between.
        bot_email = 'github-mathlib4-bot@leanprover.zulipchat.com'
        request = {
          'anchor': 'newest',
          'num_before': 1,
          'num_after': 0,
          'narrow': [
            {'operator': 'stream', 'operand': 'nightly-testing-batteries'},
            {'operator': 'topic', 'operand': 'Batteries status updates'},
            {'operator': 'sender', 'operand': bot_email}
          ],
          'apply_markdown': False    # Otherwise the content test below fails.
        }
        response = client.get_messages(request)
        messages = response['messages']
        if not messages or messages[0]['content'] != f"✅ The latest CI for Batteries' [nightly-testing branch](https://github.com/${{ github.repository }}/tree/nightly-testing) has succeeded! ([{os.getenv('SHA')}](https://github.com/${{ github.repository }}/commit/{os.getenv('SHA')}))":
            # Post the success message
            request = {
                'type': 'stream',
                'to': 'nightly-testing-batteries',
                'topic': 'Batteries status updates',
                'content': f"✅ The latest CI for Batteries' [nightly-testing branch](https://github.com/${{ github.repository }}/tree/nightly-testing) has succeeded! ([{os.getenv('SHA')}](https://github.com/${{ github.repository }}/commit/{os.getenv('SHA')}))"
            }
            result = client.send_message(request)
            print(result)
      shell: python

    # Next, determine if we should remind the humans to create a new PR to the `bump/v4.X.0` branch.

    - name: Check for matching bump/nightly-YYYY-MM-DD branch
      id: check_branch
      uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
      with:
        script: |
          const branchName = `bump/nightly-${process.env.NIGHTLY}`;
          console.log(`Looking for branch: ${branchName}`);

          // Use paginate to get all branches
          const branches = await github.paginate(github.rest.repos.listBranches, {
            owner: context.repo.owner,
            repo: context.repo.repo
          });

          const exists = branches.some(branch => branch.name === branchName);
          if (exists) {
            console.log(`Branch ${branchName} exists.`);
            return true;
          } else {
            console.log(`Branch ${branchName} does not exist.`);
            return false;
          }
        result-encoding: string

    - name: Exit if matching branch exists
      if: steps.check_branch.outputs.result == 'true'
      run: |
        echo "Matching bump/nightly-YYYY-MM-DD branch found, no further action needed."
        exit 0

    - name: Fetch latest bump branch name
      id: latest_bump_branch
      uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
      with:
        result-encoding: string
        script: |
          const branches = await github.paginate(github.rest.repos.listBranches, {
            owner: context.repo.owner,
            repo: context.repo.repo
          });
          const bumpBranches = branches
            .map(branch => branch.name)
            .filter(name => name.match(/^bump\/v4\.\d+\.0$/))
            .sort((a, b) => b.localeCompare(a, undefined, {numeric: true, sensitivity: 'base'}));
          if (!bumpBranches.length) {
            throw new Exception("Did not find any bump/v4.x.0 branch")
          }
          const latestBranch = bumpBranches[0];
          return latestBranch;

    - name: Fetch lean-toolchain from latest bump branch
      id: bump_version
      uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
      with:
        script: |
          try {
            const response = await github.rest.repos.getContent({
              owner: context.repo.owner,
              repo: context.repo.repo,
              path: 'lean-toolchain',
              ref: '${{ steps.latest_bump_branch.outputs.result }}'
            });
            const content = Buffer.from(response.data.content, 'base64').toString();
            const match = content.match(/leanprover\/lean4:nightly-(\d{4}-\d{2}-\d{2})/);
            if (!match) {
              core.setFailed('Toolchain pattern did not match');
              core.setOutput('toolchain_content', content);
              return null;
            }
            return match[1];
          } catch (error) {
            core.setFailed(error.message);
            return null;
          }

    - name: Send warning message on Zulip if pattern doesn't match
      if: failure()
      uses: zulip/github-actions-zulip/send-message@e4c8f27c732ba9bd98ac6be0583096dea82feea5 # v1.0.2
      with:
        api-key: ${{ secrets.ZULIP_API_KEY }}
        email: 'github-mathlib4-bot@leanprover.zulipchat.com'
        organization-url: 'https://leanprover.zulipchat.com'
        to: 'nightly-testing-batteries'
        type: 'stream'
        topic: 'Batteries status updates'
        content: |
          ⚠️ Warning: The lean-toolchain file in the latest bump branch does not match the expected pattern 'leanprover/lean4:nightly-YYYY-MM-DD'.
          Current content: ${{ steps.bump_version.outputs.toolchain_content }}
          This needs to be fixed for the nightly testing process to work correctly.

    - name: Setup for automatic PR creation
      if: steps.check_branch.outputs.result == 'false'
      env:
        BUMP_VERSION: ${{ steps.bump_version.outputs.result }}
        BUMP_BRANCH: ${{ steps.latest_bump_branch.outputs.result }}
        SHA: ${{ env.SHA }}
      run: |
        echo "Installing zulip CLI..."
        pip install zulip
        echo "Configuring git identity for mathlib4-bot..."
        git config --global user.name "mathlib4-bot"
        git config --global user.email "github-mathlib4-bot@leanprover.zulipchat.com"
        echo "Setting up zulip credentials..."
        {
          echo "[api]"
          echo "email=github-mathlib4-bot@leanprover.zulipchat.com"
          echo "key=${{ secrets.ZULIP_API_KEY }}"
          echo "site=https://leanprover.zulipchat.com"
        } > ~/.zuliprc
        chmod 600 ~/.zuliprc
        echo "Setup complete"

    - name: Clean workspace and checkout Batteries
      if: steps.check_branch.outputs.result == 'false'
      run: |
        sudo rm -rf -- *
    # Regenerate the app token just before use.
    # GitHub App tokens expire after 1 hour, and the preceding steps can take longer than that.
    - name: Regenerate app token for Batteries checkout
      if: steps.check_branch.outputs.result == 'false'
      id: app-token-2
      uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
      with:
        app-id: ${{ secrets.MATHLIB_NIGHTLY_TESTING_APP_ID }}
        private-key: ${{ secrets.MATHLIB_NIGHTLY_TESTING_PRIVATE_KEY }}
    - name: Checkout Batteries repository
      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
      if: steps.check_branch.outputs.result == 'false'
      with:
        ref: nightly-testing # checkout nightly-testing branch (shouldn't matter which)
        fetch-depth: 0 # checkout all branches
        token: ${{ steps.app-token-2.outputs.token }}

    - name: Attempt automatic PR creation
      id: auto_pr
      if: steps.check_branch.outputs.result == 'false'
      continue-on-error: true
      env:
        BUMP_VERSION: ${{ steps.bump_version.outputs.result }}
        BUMP_BRANCH: ${{ steps.latest_bump_branch.outputs.result }}
        SHA: ${{ env.SHA }}
        GH_TOKEN: ${{ steps.app-token-2.outputs.token }}
        ZULIP_API_KEY: ${{ secrets.ZULIP_API_KEY }}
      run: |
        echo "Current version: ${NIGHTLY}"
        echo "Target bump branch: ${BUMP_BRANCH}"
        echo "Using commit SHA: ${SHA}"
        current_version="${NIGHTLY}"
        bump_branch_suffix="${BUMP_BRANCH#bump/}"
        echo "Running create-adaptation-pr.sh with:"
        echo "  bumpversion: ${bump_branch_suffix}"
        echo "  nightlydate: ${current_version}"
        echo "  nightlysha: ${SHA}"
        ./scripts/create-adaptation-pr.sh --bumpversion="${bump_branch_suffix}" --nightlydate="${current_version}" --nightlysha="${SHA}" --auto=yes

    - name: Fallback to manual instructions
      if: steps.auto_pr.outcome == 'failure' && steps.check_branch.outputs.result == 'false'
      env:
        BUMP_VERSION: ${{ steps.bump_version.outputs.result }}
        BUMP_BRANCH: ${{ steps.latest_bump_branch.outputs.result }}
        SHA: ${{ env.SHA }}
        ZULIP_API_KEY: ${{ secrets.ZULIP_API_KEY }}
        REPOSITORY: ${{ github.repository }}
        CURRENT_RUN_ID: ${{ github.run_id }}
      shell: python
      run: |
        import os
        import re
        import zulip
        client = zulip.Client(config_file="~/.zuliprc")
        current_version = os.getenv('NIGHTLY')
        bump_version = os.getenv('BUMP_VERSION')
        bump_branch = os.getenv('BUMP_BRANCH')
        sha = os.getenv('SHA')
        repository = os.getenv('REPOSITORY')
        current_run_id = os.getenv('CURRENT_RUN_ID')
        print(f'Current version: {current_version}, Bump version: {bump_version}, SHA: {sha}')
        if current_version > bump_version:
            print('Lean toolchain in `nightly-testing` is ahead of the bump branch.')
            # Get the last message from the bot in the 'Batteries bump branch reminders' topic.
            # We narrow by sender to ignore human replies in between.
            bot_email = 'github-mathlib4-bot@leanprover.zulipchat.com'
            request = {
              'anchor': 'newest',
              'num_before': 1,
              'num_after': 0,
              'narrow': [
                {'operator': 'stream', 'operand': 'nightly-testing-batteries'},
                {'operator': 'topic', 'operand': 'Batteries bump branch reminders'},
                {'operator': 'sender', 'operand': bot_email}
              ],
              'apply_markdown': False    # Otherwise the content test below fails.
            }
            response = client.get_messages(request)
            messages = response['messages']
            last_bot_message = messages[0] if messages else None
            bump_branch_suffix = bump_branch.replace('bump/', '')
            failed_link = f"https://github.com/{repository}/actions/runs/{current_run_id}"
            payload = f"🛠️: Automatic PR creation [failed]({failed_link}). Please create a new bump/nightly-{current_version} branch from nightly-testing (specifically {sha}), and then PR that to {bump_branch}. "
            payload += "To do so semi-automatically, run the following script from Batteries root:\n\n"
            payload += f"```bash\n./scripts/create-adaptation-pr.sh --bumpversion={bump_branch_suffix} --nightlydate={current_version} --nightlysha={sha}\n```\n"
            # Check if we already posted a message for this nightly date and bump branch.
            # We extract these fields from the last bot message rather than comparing substrings,
            # since the message also contains a run ID that differs between workflow runs.
            should_post = True
            if last_bot_message:
                last_content = last_bot_message['content']
                # Extract nightly date and bump branch from last bot message
                date_match = re.search(r'bump/nightly-(\d{4}-\d{2}-\d{2})', last_content)
                branch_match = re.search(r'PR that to (bump/v[\d.]+)', last_content)
                if date_match and branch_match:
                    last_date = date_match.group(1)
                    last_branch = branch_match.group(1)
                    if last_date == current_version and last_branch == bump_branch:
                        should_post = False
                        print(f'Already posted for nightly {current_version} and {bump_branch}')
            if should_post:
                if last_bot_message:
                    print("###### Last bot message:")
                    print(last_bot_message['content'])
                    print("###### Current message:")
                    print(payload)
                # Post the reminder message
                request = {
                    'type': 'stream',
                    'to': 'nightly-testing-batteries',
                    'topic': 'Batteries bump branch reminders',
                    'content': payload
                }
                result = client.send_message(request)
                print(result)
        else:
            print('No action needed.')


================================================
FILE: .github/workflows/nightly_merge_master.yml
================================================
# This job merges every commit to `main` into `nightly-testing`, resolving merge conflicts in favor of `nightly-testing`.

name: Merge main to nightly

on:
  push:
    branches:
      - main

jobs:
  merge-to-nightly:
    if: github.repository_owner == 'leanprover-community'
    runs-on: ubuntu-latest
    steps:
      - name: Generate app token
        id: app-token
        uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
        with:
          app-id: ${{ secrets.MATHLIB_NIGHTLY_TESTING_APP_ID }}
          private-key: ${{ secrets.MATHLIB_NIGHTLY_TESTING_PRIVATE_KEY }}
      # The create-github-app-token README states that this token is masked and will not be logged accidentally.

      - name: Checkout repository
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          fetch-depth: 0
          token: ${{ steps.app-token.outputs.token }}

      - name: Configure Git User
        run: |
          git config user.name "mathlib-nightly-testing[bot]"
          git config user.email "mathlib-nightly-testing[bot]@users.noreply.github.com"

      - name: Merge main to nightly favoring nightly changes
        run: |
          git checkout nightly-testing
          git merge main --strategy-option ours --no-commit --allow-unrelated-histories
          git commit -m "Merge main into nightly-testing"
          git push origin nightly-testing


================================================
FILE: .github/workflows/test_mathlib.yml
================================================
# Test Mathlib against a Batteries PR

name: Test Mathlib

on:
  workflow_run:
    workflows: [ci]
    types: [completed]

jobs:
  on-success:
    runs-on: ubuntu-latest
    if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request' && github.repository == 'leanprover-community/batteries'
    steps:
      - name: Checkout PR
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          fetch-depth: 0

      - name: Get PR info
        id: pr-info
        run: |
          echo "pullRequestNumber=$(gh pr list --search $SHA --json number -q '.[0].number' || echo '')" >> $GITHUB_OUTPUT
          echo "targetBranch=$(gh pr list --search $SHA --json baseRefName -q '.[0].baseRefName' || echo '')" >> $GITHUB_OUTPUT
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SHA: ${{ github.event.workflow_run.head_sha }}

      - name: Generate app token
        if: steps.pr-info.outputs.pullRequestNumber != '' && steps.pr-info.outputs.targetBranch == 'main'
        id: app-token
        uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
        with:
          app-id: ${{ secrets.MATHLIB_NIGHTLY_TESTING_APP_ID }}
          private-key: ${{ secrets.MATHLIB_NIGHTLY_TESTING_PRIVATE_KEY }}
          owner: leanprover-community
          repositories: mathlib4,mathlib4-nightly-testing

      - name: Checkout mathlib4 repository
        if: steps.pr-info.outputs.pullRequestNumber != '' && steps.pr-info.outputs.targetBranch == 'main'
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          repository: leanprover-community/mathlib4
          token: ${{ steps.app-token.outputs.token }}
          ref: master
          fetch-depth: 0

      - name: Add nightly-testing remote
        if: steps.pr-info.outputs.pullRequestNumber != '' && steps.pr-info.outputs.targetBranch == 'main'
        run: |
          git remote add nightly-testing https://github.com/leanprover-community/mathlib4-nightly-testing.git
          git fetch nightly-testing

      - name: Install elan
        if: steps.pr-info.outputs.pullRequestNumber != '' && steps.pr-info.outputs.targetBranch == 'main'
        run: |
          set -o pipefail
          curl -sSfL https://github.com/leanprover/elan/releases/download/v3.0.0/elan-x86_64-unknown-linux-gnu.tar.gz | tar xz
          ./elan-init -y --default-toolchain none
          echo "$HOME/.elan/bin" >> "${GITHUB_PATH}"

      - name: Check if branch exists
        if: steps.pr-info.outputs.pullRequestNumber != '' && steps.pr-info.outputs.targetBranch == 'main'
        id: check_mathlib_tag
        env:
          PR_NUMBER: ${{ steps.pr-info.outputs.pullRequestNumber }}
          HEAD_REPO: ${{ github.event.workflow_run.head_repository.full_name }}
          HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
        run: |
          git config user.name "mathlib-nightly-testing[bot]"
          git config user.email "mathlib-nightly-testing[bot]@users.noreply.github.com"

          echo "PR info: $HEAD_REPO $HEAD_BRANCH"

          BASE=master
          echo "Using base tag: $BASE"

          EXISTS="$(git ls-remote --heads nightly-testing batteries-pr-testing-$PR_NUMBER | wc -l)"
          echo "Branch exists: $EXISTS"
          if [ "$EXISTS" = "0" ]; then
            echo "Branch does not exist, creating it."
            git switch -c batteries-pr-testing-$PR_NUMBER "$BASE"

            # Modify the lakefile.lean with the fork and branch name
            sed -i "s,require \"leanprover-community\" / \"batteries\" @ git \".\+\",require \"leanprover-community\" / \"batteries\" from git \"https://github.com/$HEAD_REPO\" @ \"$HEAD_BRANCH\",g" lakefile.lean

            lake update batteries
            git add lakefile.lean lake-manifest.json
            git commit -m "Update Batteries branch for testing https://github.com/leanprover-community/batteries/pull/$PR_NUMBER"
          else
            echo "Branch already exists, merging $BASE and bumping Batteries."
            git switch batteries-pr-testing-$PR_NUMBER
            git merge "$BASE" --strategy-option ours --no-commit --allow-unrelated-histories
            lake update batteries
            git add lake-manifest.json
            git commit --allow-empty -m "Trigger CI for https://github.com/leanprover-community/batteries/pull/$PR_NUMBER"
          fi

      - name: Push changes
        if: steps.pr-info.outputs.pullRequestNumber != '' && steps.pr-info.outputs.targetBranch == 'main'
        env:
          PR_NUMBER: ${{ steps.pr-info.outputs.pullRequestNumber }}
        run: |
          git push nightly-testing batteries-pr-testing-$PR_NUMBER


================================================
FILE: .gitignore
================================================
# Prior to v4.3.0-rc2 lake stored files in these locations.
# We'll leave them in the `.gitignore` for a while for users switching between toolchains.
/build/
/lake-packages/
/lakefile.olean
# After v4.3.0-rc2 lake stores its files here:
/.lake/


================================================
FILE: .gitpod.yml
================================================
image:
  file: .docker/gitpod/Dockerfile

vscode:
  extensions:
    - leanprover.lean4

tasks:
  - init: |
      elan self update
      lake build


================================================
FILE: .vscode/copyright.code-snippets
================================================
{
	"Copyright header for batteries": {
		"scope": "lean4",
		"prefix": "copyright",
		"body": [
			"/-",
			"Copyright (c) ${CURRENT_YEAR} $1. All rights reserved.",
			"Released under Apache 2.0 license as described in the file LICENSE.",
			"Authors: $1",
			"-/"
		]
	}
}


================================================
FILE: .vscode/settings.json
================================================
{
  "editor.insertSpaces": true,
  "editor.tabSize": 2,
  "editor.rulers" : [100],
  "files.encoding": "utf8",
  "files.eol": "\n",
  "files.insertFinalNewline": true,
  "files.trimFinalNewlines": true,
  "files.trimTrailingWhitespace": true,
  "search.usePCRE2": true
}


================================================
FILE: Batteries/Classes/Cast.lean
================================================
/-
Copyright (c) 2014 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Gabriel Ebner
-/
module

public import Batteries.Util.LibraryNote

@[expose] public section

library_note «coercion into rings» /--
Coercions such as `Nat.castCoe` that go from a concrete structure such as
`Nat` to an arbitrary ring `R` should be set up as follows:
```lean
instance : CoeTail Nat R where coe := ...
instance : CoeHTCT Nat R where coe := ...
```

It needs to be `CoeTail` instead of `Coe` because otherwise type-class
inference would loop when constructing the transitive coercion `Nat → Nat → Nat → ...`.
Sometimes we also need to declare the `CoeHTCT` instance
if we need to shadow another coercion
(e.g. `Nat.cast` should be used over `Int.ofNat`).
-/


================================================
FILE: Batteries/Classes/Deprecated.lean
================================================
/-
Copyright (c) 2022 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
module

public import Batteries.Classes.Order

@[expose] public section

/-! Deprecated Batteries comparison classes

Examples are to ensure that old instances have equivalent new instances.
-/

set_option linter.deprecated false

namespace Batteries

/-- `OrientedCmp cmp` asserts that `cmp` is determined by the relation `cmp x y = .lt`. -/
@[deprecated Std.OrientedCmp (since := "2025-07-01")]
class OrientedCmp (cmp : α → α → Ordering) : Prop where
  /-- The comparator operation is symmetric, in the sense that if `cmp x y` equals `.lt` then
  `cmp y x = .gt` and vice versa. -/
  symm (x y) : (cmp x y).swap = cmp y x

attribute [deprecated Std.OrientedOrd.eq_swap (since := "2025-07-01")] OrientedCmp.symm

namespace OrientedCmp

@[deprecated Std.OrientedCmp.gt_iff_lt (since := "2025-07-01")]
theorem cmp_eq_gt [OrientedCmp cmp] : cmp x y = .gt ↔ cmp y x = .lt := by
  rw [← Ordering.swap_inj, symm]; exact .rfl

@[deprecated Std.OrientedCmp.le_iff_ge (since := "2025-07-01")]
theorem cmp_ne_gt [OrientedCmp cmp] : cmp x y ≠ .gt ↔ cmp y x ≠ .lt := not_congr cmp_eq_gt

@[deprecated Std.OrientedCmp.eq_comm (since := "2025-07-01")]
theorem cmp_eq_eq_symm [OrientedCmp cmp] : cmp x y = .eq ↔ cmp y x = .eq := by
  rw [← Ordering.swap_inj, symm]; exact .rfl

@[deprecated Std.ReflCmp.compare_self (since := "2025-07-01")]
theorem cmp_refl [OrientedCmp cmp] : cmp x x = .eq :=
  match e : cmp x x with
  | .lt => nomatch e.symm.trans (cmp_eq_gt.2 e)
  | .eq => rfl
  | .gt => nomatch (cmp_eq_gt.1 e).symm.trans e

@[deprecated Std.OrientedCmp.not_lt_of_lt  (since := "2025-07-01")]
theorem lt_asymm [OrientedCmp cmp] (h : cmp x y = .lt) : cmp y x ≠ .lt :=
  fun h' => nomatch h.symm.trans (cmp_eq_gt.2 h')

@[deprecated Std.OrientedCmp.not_gt_of_gt  (since := "2025-07-01")]
theorem gt_asymm [OrientedCmp cmp] (h : cmp x y = .gt) : cmp y x ≠ .gt :=
  mt cmp_eq_gt.1 <| lt_asymm <| cmp_eq_gt.1 h

end OrientedCmp

/-- `TransCmp cmp` asserts that `cmp` induces a transitive relation. -/
@[deprecated Std.TransCmp (since := "2025-07-01")]
class TransCmp (cmp : α → α → Ordering) : Prop extends OrientedCmp cmp where
  /-- The comparator operation is transitive. -/
  le_trans : cmp x y ≠ .gt → cmp y z ≠ .gt → cmp x z ≠ .gt

attribute [deprecated Std.TransCmp.le_trans (since := "2025-07-01")] TransCmp.le_trans

namespace TransCmp
variable [TransCmp cmp]
open OrientedCmp Decidable

@[deprecated Std.TransCmp.ge_trans (since := "2025-07-01")]
theorem ge_trans (h₁ : cmp x y ≠ .lt) (h₂ : cmp y z ≠ .lt) : cmp x z ≠ .lt := by
  have := @TransCmp.le_trans _ cmp _ z y x
  simp [cmp_eq_gt] at *; exact this h₂ h₁

@[deprecated Std.TransCmp.lt_of_le_of_lt (since := "2025-07-01")]
theorem le_lt_trans (h₁ : cmp x y ≠ .gt) (h₂ : cmp y z = .lt) : cmp x z = .lt :=
  byContradiction fun h₃ => ge_trans (mt cmp_eq_gt.2 h₁) h₃ h₂

@[deprecated Std.TransCmp.lt_of_lt_of_le (since := "2025-07-01")]
theorem lt_le_trans (h₁ : cmp x y = .lt) (h₂ : cmp y z ≠ .gt) : cmp x z = .lt :=
  byContradiction fun h₃ => ge_trans h₃ (mt cmp_eq_gt.2 h₂) h₁

@[deprecated Std.TransCmp.lt_trans (since := "2025-07-01")]
theorem lt_trans (h₁ : cmp x y = .lt) (h₂ : cmp y z = .lt) : cmp x z = .lt :=
  le_lt_trans (gt_asymm <| cmp_eq_gt.2 h₁) h₂

@[deprecated Std.TransCmp.gt_trans (since := "2025-07-01")]
theorem gt_trans (h₁ : cmp x y = .gt) (h₂ : cmp y z = .gt) : cmp x z = .gt := by
  rw [cmp_eq_gt] at h₁ h₂ ⊢; exact lt_trans h₂ h₁

@[deprecated Std.TransCmp.congr_left (since := "2025-07-01")]
theorem cmp_congr_left (xy : cmp x y = .eq) : cmp x z = cmp y z :=
  match yz : cmp y z with
  | .lt => byContradiction (ge_trans (nomatch ·.symm.trans (cmp_eq_eq_symm.1 xy)) · yz)
  | .gt => byContradiction (le_trans (nomatch ·.symm.trans (cmp_eq_eq_symm.1 xy)) · yz)
  | .eq => match xz : cmp x z with
    | .lt => nomatch ge_trans (nomatch ·.symm.trans xy) (nomatch ·.symm.trans yz) xz
    | .gt => nomatch le_trans (nomatch ·.symm.trans xy) (nomatch ·.symm.trans yz) xz
    | .eq => rfl

@[deprecated Std.TransCmp.congr_left (since := "2025-07-01")]
theorem cmp_congr_left' (xy : cmp x y = .eq) : cmp x = cmp y :=
  funext fun _ => cmp_congr_left xy

@[deprecated Std.TransCmp.congr_right (since := "2025-07-01")]
theorem cmp_congr_right (yz : cmp y z = .eq) : cmp x y = cmp x z := by
  rw [← Ordering.swap_inj, symm, symm, cmp_congr_left yz]

end TransCmp

instance [inst : OrientedCmp cmp] : OrientedCmp (flip cmp) where
  symm _ _ := inst.symm ..

example [inst : Std.OrientedCmp cmp] : Std.OrientedCmp (flip cmp) := inferInstance

instance [inst : TransCmp cmp] : TransCmp (flip cmp) where
  le_trans h1 h2 := inst.le_trans h2 h1

example [inst : Std.TransCmp cmp] : Std.TransCmp (flip cmp) := inferInstance

/-- `BEqCmp cmp` asserts that `cmp x y = .eq` and `x == y` coincide. -/
@[deprecated Std.LawfulBEqCmp (since := "2025-07-01")]
class BEqCmp [BEq α] (cmp : α → α → Ordering) : Prop where
  /-- `cmp x y = .eq` holds iff `x == y` is true. -/
  cmp_iff_beq : cmp x y = .eq ↔ x == y

attribute [deprecated Std.LawfulBEqCmp.compare_eq_iff_beq
  (since := "2025-07-01")] BEqCmp.cmp_iff_beq

@[deprecated Std.LawfulEqCmp.compare_eq_iff_eq (since := "2025-07-01")]
theorem BEqCmp.cmp_iff_eq [BEq α] [LawfulBEq α] [BEqCmp (α := α) cmp] : cmp x y = .eq ↔ x = y := by
  simp [BEqCmp.cmp_iff_beq]

/-- `LTCmp cmp` asserts that `cmp x y = .lt` and `x < y` coincide. -/
@[deprecated Std.LawfulLTCmp (since := "2025-07-01")]
class LTCmp [LT α] (cmp : α → α → Ordering) : Prop extends OrientedCmp cmp where
  /-- `cmp x y = .lt` holds iff `x < y` is true. -/
  cmp_iff_lt : cmp x y = .lt ↔ x < y

attribute [deprecated Std.LawfulLTCmp.eq_lt_iff_lt (since := "2025-07-01")] LTCmp.cmp_iff_lt

@[deprecated Std.LawfulLTCmp.eq_gt_iff_gt (since := "2025-07-01")]
theorem LTCmp.cmp_iff_gt [LT α] [LTCmp (α := α) cmp] : cmp x y = .gt ↔ y < x := by
  rw [OrientedCmp.cmp_eq_gt, LTCmp.cmp_iff_lt]

/-- `LECmp cmp` asserts that `cmp x y ≠ .gt` and `x ≤ y` coincide. -/
@[deprecated Std.LawfulLECmp (since := "2025-07-01")]
class LECmp [LE α] (cmp : α → α → Ordering) : Prop extends OrientedCmp cmp where
  /-- `cmp x y ≠ .gt` holds iff `x ≤ y` is true. -/
  cmp_iff_le : cmp x y ≠ .gt ↔ x ≤ y

attribute [deprecated Std.LawfulLECmp.ne_gt_iff_le (since := "2025-07-01")] LECmp.cmp_iff_le

@[deprecated Std.LawfulLECmp.ne_lt_iff_ge (since := "2025-07-01")]
theorem LECmp.cmp_iff_ge [LE α] [LECmp (α := α) cmp] : cmp x y ≠ .lt ↔ y ≤ x := by
  rw [← OrientedCmp.cmp_ne_gt, LECmp.cmp_iff_le]

/-- `LawfulCmp cmp` asserts that the `LE`, `LT`, `BEq` instances are all coherent with each other
and with `cmp`, describing a strict weak order (a linear order except for antisymmetry). -/
@[deprecated Std.LawfulBCmp (since := "2025-07-01")]
class LawfulCmp [LE α] [LT α] [BEq α] (cmp : α → α → Ordering) : Prop extends
  TransCmp cmp, BEqCmp cmp, LTCmp cmp, LECmp cmp

/-- `OrientedOrd α` asserts that the `Ord` instance satisfies `OrientedCmp`. -/
@[deprecated Std.OrientedOrd (since := "2025-07-01")]
abbrev OrientedOrd (α) [Ord α] := OrientedCmp (α := α) compare

/-- `TransOrd α` asserts that the `Ord` instance satisfies `TransCmp`. -/
@[deprecated Std.TransOrd (since := "2025-07-01")]
abbrev TransOrd (α) [Ord α] := TransCmp (α := α) compare

/-- `BEqOrd α` asserts that the `Ord` and `BEq` instances are coherent via `BEqCmp`. -/
@[deprecated Std.LawfulBEqOrd (since := "2025-07-01")]
abbrev BEqOrd (α) [BEq α] [Ord α] := BEqCmp (α := α) compare

/-- `LTOrd α` asserts that the `Ord` instance satisfies `LTCmp`. -/
@[deprecated Std.LawfulLTOrd (since := "2025-07-01")]
abbrev LTOrd (α) [LT α] [Ord α] := LTCmp (α := α) compare

/-- `LEOrd α` asserts that the `Ord` instance satisfies `LECmp`. -/
@[deprecated Std.LawfulLEOrd (since := "2025-07-01")]
abbrev LEOrd (α) [LE α] [Ord α] := LECmp (α := α) compare

/-- `LawfulOrd α` asserts that the `Ord` instance satisfies `LawfulCmp`. -/
@[deprecated Std.LawfulBOrd (since := "2025-07-01")]
abbrev LawfulOrd (α) [LE α] [LT α] [BEq α] [Ord α] := LawfulCmp (α := α) compare

@[deprecated Std.TransCmp.compareOfLessAndEq_of_irrefl_of_trans_of_antisymm
  (since := "2025-07-01")]
protected theorem TransCmp.compareOfLessAndEq
    [LT α] [DecidableRel (LT.lt (α := α))] [DecidableEq α]
    (lt_irrefl : ∀ x : α, ¬x < x)
    (lt_trans : ∀ {x y z : α}, x < y → y < z → x < z)
    (lt_antisymm : ∀ {x y : α}, ¬x < y → ¬y < x → x = y) :
    TransCmp (α := α) (compareOfLessAndEq · ·) := by
  have : OrientedCmp (α := α) (compareOfLessAndEq · ·) := by
    refine { symm := fun x y => ?_ }
    simp [compareOfLessAndEq]; split <;> [rename_i xy; split <;> [subst y; rename_i xy ne]]
    · rw [if_neg, if_neg]; rfl
      · rintro rfl; exact lt_irrefl _ xy
      · exact fun yx => lt_irrefl _ (lt_trans xy yx)
    · rw [if_neg ‹_›, if_pos rfl]; rfl
    · split <;> [rfl; rename_i yx]
      cases ne (lt_antisymm xy yx)
  refine { this with le_trans := fun {x y z} yx zy => ?_ }
  rw [Ne, this.cmp_eq_gt, compareOfLessAndEq_eq_lt] at yx zy ⊢
  intro zx
  if xy : x < y then exact zy (lt_trans zx xy)
  else exact zy (lt_antisymm yx xy ▸ zx)

@[deprecated Std.TransCmp.compareOfLessAndEq_of_irrefl_of_trans_of_not_lt_of_antisymm
  (since := "2025-07-01")]
theorem TransCmp.compareOfLessAndEq_of_le
    [LT α] [LE α] [DecidableRel (LT.lt (α := α))] [DecidableEq α]
    (lt_irrefl : ∀ x : α, ¬x < x)
    (lt_trans : ∀ {x y z : α}, x < y → y < z → x < z)
    (not_lt : ∀ {x y : α}, ¬x < y → y ≤ x)
    (le_antisymm : ∀ {x y : α}, x ≤ y → y ≤ x → x = y) :
    TransCmp (α := α) (compareOfLessAndEq · ·) :=
  .compareOfLessAndEq lt_irrefl lt_trans fun xy yx => le_antisymm (not_lt yx) (not_lt xy)

@[deprecated Std.LawfulBEqCmp.compareOfLessAndEq_of_lt_irrefl (since := "2025-07-01")]
protected theorem BEqCmp.compareOfLessAndEq
    [LT α] [DecidableRel (LT.lt (α := α))] [DecidableEq α] [BEq α] [LawfulBEq α]
    (lt_irrefl : ∀ x : α, ¬x < x) :
    BEqCmp (α := α) (compareOfLessAndEq · ·) where
  cmp_iff_beq {x y} := by
    simp [compareOfLessAndEq]
    split <;> [skip; split] <;> simp [*]
    rintro rfl; exact lt_irrefl _ ‹_›

@[deprecated Std.LawfulLTCmp.compareOfLessAndEq_of_irrefl_of_trans_of_antisymm
  (since := "2025-07-01")]
protected theorem LTCmp.compareOfLessAndEq
    [LT α] [DecidableRel (LT.lt (α := α))] [DecidableEq α]
    (lt_irrefl : ∀ x : α, ¬x < x)
    (lt_trans : ∀ {x y z : α}, x < y → y < z → x < z)
    (lt_antisymm : ∀ {x y : α}, ¬x < y → ¬y < x → x = y) :
    LTCmp (α := α) (compareOfLessAndEq · ·) :=
  { TransCmp.compareOfLessAndEq lt_irrefl lt_trans lt_antisymm with
    cmp_iff_lt := compareOfLessAndEq_eq_lt }

@[deprecated Std.LawfulLTCmp.compareOfLessAndEq_of_irrefl_of_trans_of_not_lt_of_antisymm
  (since := "2025-07-01")]
protected theorem LTCmp.compareOfLessAndEq_of_le
    [LT α] [DecidableRel (LT.lt (α := α))] [DecidableEq α] [LE α]
    (lt_irrefl : ∀ x : α, ¬x < x)
    (lt_trans : ∀ {x y z : α}, x < y → y < z → x < z)
    (not_lt : ∀ {x y : α}, ¬x < y → y ≤ x)
    (le_antisymm : ∀ {x y : α}, x ≤ y → y ≤ x → x = y) :
    LTCmp (α := α) (compareOfLessAndEq · ·) :=
  { TransCmp.compareOfLessAndEq_of_le lt_irrefl lt_trans not_lt le_antisymm with
    cmp_iff_lt := compareOfLessAndEq_eq_lt }

@[deprecated Std.LawfulLECmp.compareOfLessAndEq_of_irrefl_of_trans_of_not_lt_of_antisymm
  (since := "2025-07-01")]
protected theorem LECmp.compareOfLessAndEq
    [LT α] [DecidableRel (LT.lt (α := α))] [DecidableEq α] [LE α]
    (lt_irrefl : ∀ x : α, ¬x < x)
    (lt_trans : ∀ {x y z : α}, x < y → y < z → x < z)
    (not_lt : ∀ {x y : α}, ¬x < y ↔ y ≤ x)
    (le_antisymm : ∀ {x y : α}, x ≤ y → y ≤ x → x = y) :
    LECmp (α := α) (compareOfLessAndEq · ·) :=
  have := TransCmp.compareOfLessAndEq_of_le lt_irrefl lt_trans not_lt.1 le_antisymm
  { this with
    cmp_iff_le := (this.cmp_ne_gt).trans <| (not_congr compareOfLessAndEq_eq_lt).trans not_lt }

@[deprecated Std.LawfulCmp.compareOfLessAndEq_of_irrefl_of_trans_of_not_lt_of_antisymm
  (since := "2025-07-01")]
protected theorem LawfulCmp.compareOfLessAndEq
    [LT α] [DecidableRel (LT.lt (α := α))] [DecidableEq α] [BEq α] [LawfulBEq α] [LE α]
    (lt_irrefl : ∀ x : α, ¬x < x)
    (lt_trans : ∀ {x y z : α}, x < y → y < z → x < z)
    (not_lt : ∀ {x y : α}, ¬x < y ↔ y ≤ x)
    (le_antisymm : ∀ {x y : α}, x ≤ y → y ≤ x → x = y) :
    LawfulCmp (α := α) (compareOfLessAndEq · ·) :=
  { TransCmp.compareOfLessAndEq_of_le lt_irrefl lt_trans not_lt.1 le_antisymm,
    LTCmp.compareOfLessAndEq_of_le lt_irrefl lt_trans not_lt.1 le_antisymm,
    LECmp.compareOfLessAndEq lt_irrefl lt_trans not_lt le_antisymm,
    BEqCmp.compareOfLessAndEq lt_irrefl with }

@[deprecated Std.LawfulLTCmp.eq_compareOfLessAndEq (since := "2025-07-01")]
theorem LTCmp.eq_compareOfLessAndEq
    [LT α] [DecidableEq α] [BEq α] [LawfulBEq α] [BEqCmp cmp] [LTCmp cmp]
    (x y : α) [Decidable (x < y)] : cmp x y = compareOfLessAndEq x y := by
  simp [compareOfLessAndEq]
  split <;> rename_i h1 <;> [skip; split <;> rename_i h2]
  · exact LTCmp.cmp_iff_lt.2 h1
  · exact BEqCmp.cmp_iff_eq.2 h2
  · cases e : cmp x y
    · cases h1 (LTCmp.cmp_iff_lt.1 e)
    · cases h2 (BEqCmp.cmp_iff_eq.1 e)
    · rfl

instance [inst₁ : OrientedCmp cmp₁] [inst₂ : OrientedCmp cmp₂] :
    OrientedCmp (compareLex cmp₁ cmp₂) where
  symm _ _ := by simp [compareLex, Ordering.swap_then]; rw [inst₁.symm, inst₂.symm]

example [inst₁ : Std.OrientedCmp cmp₁] [inst₂ : Std.OrientedCmp cmp₂] :
    Std.OrientedCmp (compareLex cmp₁ cmp₂) := inferInstance

instance [inst₁ : TransCmp cmp₁] [inst₂ : TransCmp cmp₂] :
    TransCmp (compareLex cmp₁ cmp₂) where
  le_trans {a b c} h1 h2 := by
    simp only [compareLex, ne_eq, Ordering.then_eq_gt, not_or, not_and] at h1 h2 ⊢
    refine ⟨inst₁.le_trans h1.1 h2.1, fun e1 e2 => ?_⟩
    match ab : cmp₁ a b with
    | .gt => exact h1.1 ab
    | .eq => exact inst₂.le_trans (h1.2 ab) (h2.2 (inst₁.cmp_congr_left ab ▸ e1)) e2
    | .lt => exact h2.1 <| (inst₁.cmp_eq_gt).2 (inst₁.cmp_congr_left e1 ▸ ab)

example [inst₁ : Std.TransCmp cmp₁] [inst₂ : Std.TransCmp cmp₂] :
    Std.TransCmp (compareLex cmp₁ cmp₂) := inferInstance

instance [Ord β] [OrientedOrd β] (f : α → β) : OrientedCmp (compareOn f) where
  symm _ _ := OrientedCmp.symm (α := β) ..

example [Ord β] [Std.OrientedOrd β] (f : α → β) : Std.OrientedCmp (compareOn f) :=
  inferInstance

instance [Ord β] [TransOrd β] (f : α → β) : TransCmp (compareOn f) where
  le_trans := TransCmp.le_trans (α := β)

example [Ord β] [Std.TransOrd β] (f : α → β) : Std.TransCmp (compareOn f) :=
  inferInstance

section «non-canonical instances»
-- Note: the following instances seem to cause lean to fail, see:
-- https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Typeclass.20inference.20crashes/near/432836360

/-- Local instance for `OrientedOrd lexOrd`. -/
@[deprecated "instance exists" (since := "2025-07-01")]
theorem OrientedOrd.instLexOrd [Ord α] [Ord β]
    [OrientedOrd α] [OrientedOrd β] : @OrientedOrd (α × β) lexOrd := by
  rw [OrientedOrd, lexOrd_def]; infer_instance

/-- Local instance for `TransOrd lexOrd`. -/
@[deprecated "instance exists" (since := "2025-07-01")]
theorem TransOrd.instLexOrd [Ord α] [Ord β]
    [TransOrd α] [TransOrd β] : @TransOrd (α × β) lexOrd := by
  rw [TransOrd, lexOrd_def]; infer_instance

/-- Local instance for `OrientedOrd ord.opposite`. -/
@[deprecated Std.OrientedOrd.opposite (since := "2025-07-01")]
theorem OrientedOrd.instOpposite [ord : Ord α] [inst : OrientedOrd α] :
    @OrientedOrd _ ord.opposite where symm _ _ := inst.symm ..

/-- Local instance for `TransOrd ord.opposite`. -/
@[deprecated Std.TransOrd.opposite (since := "2025-07-01")]
theorem TransOrd.instOpposite [ord : Ord α] [inst : TransOrd α] : @TransOrd _ ord.opposite :=
  { OrientedOrd.instOpposite with le_trans := fun h1 h2 => inst.le_trans h2 h1 }

/-- Local instance for `OrientedOrd (ord.on f)`. -/
@[deprecated Std.OrientedOrd.instOn (since := "2025-07-01")]
theorem OrientedOrd.instOn [ord : Ord β] [OrientedOrd β] (f : α → β) : @OrientedOrd _ (ord.on f) :=
  inferInstanceAs (@OrientedCmp _ (compareOn f))

/-- Local instance for `TransOrd (ord.on f)`. -/
@[deprecated Std.TransOrd.instOn (since := "2025-07-01")]
theorem TransOrd.instOn [ord : Ord β] [TransOrd β] (f : α → β) : @TransOrd _ (ord.on f) :=
  inferInstanceAs (@TransCmp _ (compareOn f))

/-- Local instance for `OrientedOrd (oα.lex oβ)`. -/
@[deprecated "instance exists" (since := "2025-07-01")]
theorem OrientedOrd.instOrdLex [oα : Ord α] [oβ : Ord β] [OrientedOrd α] [OrientedOrd β] :
    @OrientedOrd _ (oα.lex oβ) := OrientedOrd.instLexOrd

/-- Local instance for `TransOrd (oα.lex oβ)`. -/
@[deprecated "instance exists" (since := "2025-07-01")]
theorem TransOrd.instOrdLex [oα : Ord α] [oβ : Ord β] [TransOrd α] [TransOrd β] :
    @TransOrd _ (oα.lex oβ) := TransOrd.instLexOrd

/-- Local instance for `OrientedOrd (oα.lex' oβ)`. -/
@[deprecated Std.OrientedOrd.instOrdLex' (since := "2025-07-01")]
theorem OrientedOrd.instOrdLex' (ord₁ ord₂ : Ord α) [@OrientedOrd _ ord₁] [@OrientedOrd _ ord₂] :
    @OrientedOrd _ (ord₁.lex' ord₂) :=
  inferInstanceAs (OrientedCmp (compareLex ord₁.compare ord₂.compare))

/-- Local instance for `TransOrd (oα.lex' oβ)`. -/
@[deprecated Std.TransOrd.instOrdLex' (since := "2025-07-01")]
theorem TransOrd.instOrdLex' (ord₁ ord₂ : Ord α) [@TransOrd _ ord₁] [@TransOrd _ ord₂] :
    @TransOrd _ (ord₁.lex' ord₂) :=
  inferInstanceAs (TransCmp (compareLex ord₁.compare ord₂.compare))

end «non-canonical instances»


================================================
FILE: Batteries/Classes/Order.lean
================================================
/-
Copyright (c) 2022 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
module

public import Batteries.Tactic.Basic
public import Batteries.Tactic.SeqFocus

@[expose] public section

theorem lexOrd_def [Ord α] [Ord β] :
    (lexOrd : Ord (α × β)).compare = compareLex (compareOn (·.1)) (compareOn (·.2)) := rfl

/-- Pull back a comparator by a function `f`, by applying the comparator to both arguments. -/
@[inline] def Ordering.byKey (f : α → β) (cmp : β → β → Ordering) (a b : α) : Ordering :=
  cmp (f a) (f b)

namespace Batteries

/-- `TotalBLE le` asserts that `le` has a total order, that is, `le a b ∨ le b a`. -/
class TotalBLE (le : α → α → Bool) : Prop where
  /-- `le` is total: either `le a b` or `le b a`. -/
  total : le a b ∨ le b a

theorem compareOfLessAndEq_eq_lt {x y : α} [LT α] [Decidable (x < y)] [DecidableEq α] :
    compareOfLessAndEq x y = .lt ↔ x < y := by
  simp [compareOfLessAndEq]
  split <;> simp

end Batteries

/-! Batteries features not in core Std -/

namespace Std
open Batteries (compareOfLessAndEq_eq_lt)

namespace OrientedCmp
variable {cmp : α → α → Ordering} [OrientedCmp cmp]

theorem le_iff_ge : cmp x y ≠ .gt ↔ cmp y x ≠ .lt :=
  not_congr OrientedCmp.gt_iff_lt

end OrientedCmp

namespace TransCmp
variable {cmp : α → α → Ordering} [TransCmp cmp]

theorem le_trans : cmp x y ≠ .gt → cmp y z ≠ .gt → cmp x z ≠ .gt := by
  simp only [ne_eq, ← Ordering.isLE_iff_ne_gt]; exact isLE_trans

theorem lt_of_lt_of_le : cmp x y = .lt → cmp y z ≠ .gt → cmp x z = .lt := by
  simp only [ne_eq, ← Ordering.isLE_iff_ne_gt]; exact lt_of_lt_of_isLE

theorem lt_of_le_of_lt : cmp x y ≠ .gt → cmp y z = .lt → cmp x z = .lt := by
  simp only [ne_eq, ← Ordering.isLE_iff_ne_gt]; exact lt_of_isLE_of_lt

theorem ge_trans : cmp x y ≠ .lt → cmp y z ≠ .lt → cmp x z ≠ .lt := by
  simp only [ne_eq, ← Ordering.isGE_iff_ne_lt]; exact isGE_trans

theorem gt_of_gt_of_ge : cmp x y = .gt → cmp y z ≠ .lt → cmp x z = .gt := by
  simp only [ne_eq, ← Ordering.isGE_iff_ne_lt]; exact gt_of_gt_of_isGE

theorem gt_of_ge_of_gt : cmp x y ≠ .lt → cmp y z = .gt → cmp x z = .gt := by
  simp only [ne_eq, ← Ordering.isGE_iff_ne_lt]; exact gt_of_isGE_of_gt

end TransCmp

/-- `LawfulLTCmp cmp` asserts that `cmp x y = .lt` and `x < y` coincide. -/
class LawfulLTCmp [LT α] (cmp : α → α → Ordering) : Prop extends OrientedCmp cmp where
  /-- `cmp x y = .lt` holds iff `x < y` is true. -/
  eq_lt_iff_lt : cmp x y = .lt ↔ x < y

theorem LawfulLTCmp.eq_gt_iff_gt [LT α] [LawfulLTCmp (α := α) cmp] :
    cmp x y = .gt ↔ y < x := by rw [OrientedCmp.gt_iff_lt, eq_lt_iff_lt]

/-- `LawfulLECmp cmp` asserts that `(cmp x y).isLE` and `x ≤ y` coincide. -/
class LawfulLECmp [LE α] (cmp : α → α → Ordering) : Prop extends OrientedCmp cmp where
  /-- `cmp x y ≠ .gt` holds iff `x ≤ y` is true. -/
  isLE_iff_le : (cmp x y).isLE ↔ x ≤ y

theorem LawfulLECmp.isGE_iff_ge [LE α] [LawfulLECmp (α := α) cmp] :
    (cmp x y).isGE ↔ y ≤ x := by rw [← Ordering.isLE_swap, ← OrientedCmp.eq_swap, isLE_iff_le]

theorem LawfulLECmp.ne_gt_iff_le [LE α] [LawfulLECmp (α := α) cmp] :
    cmp x y ≠ .gt ↔ x ≤ y := by rw [← isLE_iff_le (cmp := cmp), Ordering.isLE_iff_ne_gt]

theorem LawfulLECmp.ne_lt_iff_ge [LE α] [LawfulLECmp (α := α) cmp] :
    cmp x y ≠ .lt ↔ y ≤ x := by rw [← isGE_iff_ge (cmp := cmp), Ordering.isGE_iff_ne_lt]

/-- `LawfulBCmp cmp` asserts that the `LE`, `LT`, `BEq` are all coherent with each other
and with `cmp`, describing a strict weak order (a linear order except for antisymmetry). -/
class LawfulBCmp [LE α] [LT α] [BEq α] (cmp : α → α → Ordering) : Prop extends
  TransCmp cmp, LawfulBEqCmp cmp, LawfulLTCmp cmp, LawfulLECmp cmp

/-- `LawfulBCmp cmp` asserts that the `LE`, `LT`, `Eq` are all coherent with each other
and with `cmp`, describing a linear order. -/
class LawfulCmp [LE α] [LT α] (cmp : α → α → Ordering) : Prop extends
  TransCmp cmp, LawfulEqCmp cmp, LawfulLTCmp cmp, LawfulLECmp cmp

/-- Class for types where the ordering function is compatible with the `LT`. -/
abbrev LawfulLTOrd (α) [LT α] [Ord α] := LawfulLTCmp (α := α) compare

/-- Class for types where the ordering function is compatible with the `LE`. -/
abbrev LawfulLEOrd (α) [LE α] [Ord α] := LawfulLECmp (α := α) compare

/-- Class for types where the ordering function is compatible with the `LE`, `LT` and `BEq`. -/
abbrev LawfulBOrd (α) [LE α] [LT α] [BEq α] [Ord α] := LawfulBCmp (α := α) compare

/-- Class for types where the ordering function is compatible with the `LE`, `LT` and `Eq`. -/
abbrev LawfulOrd (α) [LE α] [LT α] [Ord α] := LawfulCmp (α := α) compare

instance [inst : Std.OrientedCmp cmp] : Std.OrientedCmp (flip cmp) where
  eq_swap := inst.eq_swap

instance [inst : Std.TransCmp cmp] : Std.TransCmp (flip cmp) where
  isLE_trans h1 h2 := inst.isLE_trans h2 h1

instance (f : α → β) (cmp : β → β → Ordering) [Std.OrientedCmp cmp] :
    Std.OrientedCmp (Ordering.byKey f cmp) where
  eq_swap {a b} := Std.OrientedCmp.eq_swap (a := f a) (b := f b)

instance (f : α → β) (cmp : β → β → Ordering) [Std.TransCmp cmp] :
    Std.TransCmp (Ordering.byKey f cmp) where
  isLE_trans h₁ h₂ := Std.TransCmp.isLE_trans (α := β) h₁ h₂

instance [inst₁ : OrientedCmp cmp₁] [inst₂ : OrientedCmp cmp₂] :
    OrientedCmp (compareLex cmp₁ cmp₂) := inferInstance

instance [inst₁ : TransCmp cmp₁] [inst₂ : TransCmp cmp₂] :
    TransCmp (compareLex cmp₁ cmp₂) := inferInstance

instance [Ord β] [OrientedOrd β] (f : α → β) : OrientedCmp (compareOn f) := inferInstance

instance [Ord β] [TransOrd β] (f : α → β) : TransCmp (compareOn f) := inferInstance

theorem OrientedOrd.instOn [ord : Ord β] [OrientedOrd β] (f : α → β) : @OrientedOrd _ (ord.on f) :=
  inferInstanceAs (@OrientedCmp _ (compareOn f))

theorem TransOrd.instOn [ord : Ord β] [TransOrd β] (f : α → β) : @TransOrd _ (ord.on f) :=
  inferInstanceAs (@TransCmp _ (compareOn f))

theorem OrientedOrd.instOrdLex' (ord₁ ord₂ : Ord α) [@OrientedOrd _ ord₁] [@OrientedOrd _ ord₂] :
    @OrientedOrd _ (ord₁.lex' ord₂) :=
  inferInstanceAs (OrientedCmp (compareLex ord₁.compare ord₂.compare))

theorem TransOrd.instOrdLex' (ord₁ ord₂ : Ord α) [@TransOrd _ ord₁] [@TransOrd _ ord₂] :
    @TransOrd _ (ord₁.lex' ord₂) :=
  inferInstanceAs (TransCmp (compareLex ord₁.compare ord₂.compare))

theorem LawfulLTCmp.eq_compareOfLessAndEq
    [LT α] [DecidableEq α] [LawfulEqCmp cmp] [LawfulLTCmp cmp]
    (x y : α) [Decidable (x < y)] : cmp x y = compareOfLessAndEq x y := by
  simp only [compareOfLessAndEq]
  split <;> rename_i h1 <;> [skip; split <;> rename_i h2]
  · exact LawfulLTCmp.eq_lt_iff_lt.2 h1
  · exact LawfulEqCmp.compare_eq_iff_eq.2 h2
  · cases e : cmp x y
    · cases h1 (LawfulLTCmp.eq_lt_iff_lt.1 e)
    · cases h2 (LawfulEqCmp.compare_eq_iff_eq.1 e)
    · rfl

theorem ReflCmp.compareOfLessAndEq_of_lt_irrefl [LT α] [DecidableLT α] [DecidableEq α]
    (lt_irrefl : ∀ x : α, ¬ x < x) :
    ReflCmp (α := α) (compareOfLessAndEq · ·) where
  compare_self {x} := by simp [compareOfLessAndEq, if_neg (lt_irrefl x)]

theorem LawfulBEqCmp.compareOfLessAndEq_of_lt_irrefl
    [LT α] [DecidableLT α] [DecidableEq α] [BEq α] [LawfulBEq α]
    (lt_irrefl : ∀ x : α, ¬x < x) :
    LawfulBEqCmp (α := α) (compareOfLessAndEq · ·) where
  compare_eq_iff_beq {x y} := by
    simp [compareOfLessAndEq]
    split <;> [skip; split] <;> simp [*]
    rintro rfl; exact lt_irrefl _ ‹_›

-- redundant? See `compareOfLessAndEq_of_lt_trans_of_lt_iff` in core
theorem TransCmp.compareOfLessAndEq_of_irrefl_of_trans_of_antisymm
    [LT α] [DecidableLT α] [DecidableEq α]
    (lt_irrefl : ∀ x : α, ¬x < x)
    (lt_trans : ∀ {x y z : α}, x < y → y < z → x < z)
    (lt_antisymm : ∀ {x y : α}, ¬x < y → ¬y < x → x = y) :
    TransCmp (α := α) (compareOfLessAndEq · ·) :=
  TransOrd.compareOfLessAndEq_of_lt_trans_of_lt_iff lt_trans <| by
    intros
    constructor
    · intro h₁
      constructor
      · intro h₂
        apply lt_irrefl
        exact lt_trans h₁ h₂
      · intro | rfl => exact lt_irrefl _ h₁
    · intro ⟨h₁, h₂⟩
      by_contra h₃
      apply h₂
      exact lt_antisymm h₃ h₁

-- redundant? See `compareOfLessAndEq_of_antisymm_of_trans_of_total_of_not_le` in core
theorem TransCmp.compareOfLessAndEq_of_irrefl_of_trans_of_not_lt_of_antisymm
    [LT α] [LE α] [DecidableLT α] [DecidableEq α]
    (lt_irrefl : ∀ x : α, ¬x < x)
    (lt_trans : ∀ {x y z : α}, x < y → y < z → x < z)
    (not_lt : ∀ {x y : α}, ¬x < y → y ≤ x)
    (le_antisymm : ∀ {x y : α}, x ≤ y → y ≤ x → x = y) :
    TransCmp (α := α) (compareOfLessAndEq · ·) :=
  .compareOfLessAndEq_of_irrefl_of_trans_of_antisymm
    lt_irrefl lt_trans fun xy yx => le_antisymm (not_lt yx) (not_lt xy)

-- make redundant?
theorem LawfulLTCmp.compareOfLessAndEq_of_irrefl_of_trans_of_antisymm
    [LT α] [DecidableLT α] [DecidableEq α]
    (lt_irrefl : ∀ x : α, ¬x < x)
    (lt_trans : ∀ {x y z : α}, x < y → y < z → x < z)
    (lt_antisymm : ∀ {x y : α}, ¬x < y → ¬y < x → x = y) :
    LawfulLTCmp (α := α) (compareOfLessAndEq · ·) :=
  { TransCmp.compareOfLessAndEq_of_irrefl_of_trans_of_antisymm
      lt_irrefl lt_trans lt_antisymm with
    eq_lt_iff_lt := Batteries.compareOfLessAndEq_eq_lt }

-- make redundant?
theorem LawfulLTCmp.compareOfLessAndEq_of_irrefl_of_trans_of_not_lt_of_antisymm
    [LT α] [DecidableLT α] [DecidableEq α] [LE α]
    (lt_irrefl : ∀ x : α, ¬x < x)
    (lt_trans : ∀ {x y z : α}, x < y → y < z → x < z)
    (not_lt : ∀ {x y : α}, ¬x < y → y ≤ x)
    (le_antisymm : ∀ {x y : α}, x ≤ y → y ≤ x → x = y) :
    LawfulLTCmp (α := α) (compareOfLessAndEq · ·) :=
  { TransCmp.compareOfLessAndEq_of_irrefl_of_trans_of_not_lt_of_antisymm
      lt_irrefl lt_trans not_lt le_antisymm with
    eq_lt_iff_lt := Batteries.compareOfLessAndEq_eq_lt }

-- make redundant?
theorem LawfulLECmp.compareOfLessAndEq_of_irrefl_of_trans_of_not_lt_of_antisymm
    [LT α] [DecidableLT α] [DecidableEq α] [LE α]
    (lt_irrefl : ∀ x : α, ¬x < x)
    (lt_trans : ∀ {x y z : α}, x < y → y < z → x < z)
    (not_lt : ∀ {x y : α}, ¬x < y ↔ y ≤ x)
    (le_antisymm : ∀ {x y : α}, x ≤ y → y ≤ x → x = y) :
    LawfulLECmp (α := α) (compareOfLessAndEq · ·) :=
  have := TransCmp.compareOfLessAndEq_of_irrefl_of_trans_of_not_lt_of_antisymm
      lt_irrefl lt_trans not_lt.1 le_antisymm
  { this with
    isLE_iff_le := by
      intro x y
      simp only [Ordering.isLE_iff_ne_gt, ← not_lt]
      apply not_congr
      rw [this.gt_iff_lt, Batteries.compareOfLessAndEq_eq_lt] }

theorem LawfulCmp.compareOfLessAndEq_of_irrefl_of_trans_of_not_lt_of_antisymm
    [LT α] [LE α] [DecidableLT α] [DecidableLE α] [DecidableEq α]
    (lt_irrefl : ∀ x : α, ¬x < x)
    (lt_trans : ∀ {x y z : α}, x < y → y < z → x < z)
    (not_lt : ∀ {x y : α}, ¬x < y ↔ y ≤ x)
    (le_antisymm : ∀ {x y : α}, x ≤ y → y ≤ x → x = y) :
    LawfulCmp (α := α) (compareOfLessAndEq · ·) :=
    have instT := TransCmp.compareOfLessAndEq_of_irrefl_of_trans_of_not_lt_of_antisymm
      lt_irrefl lt_trans not_lt.1 le_antisymm
    have instLT := LawfulLTCmp.compareOfLessAndEq_of_irrefl_of_trans_of_not_lt_of_antisymm
      lt_irrefl lt_trans not_lt.1 le_antisymm
    have instLE := LawfulLECmp.compareOfLessAndEq_of_irrefl_of_trans_of_not_lt_of_antisymm
      lt_irrefl lt_trans not_lt le_antisymm
    have le_refl (x : α) : x ≤ x := by rw [← not_lt]; exact lt_irrefl _
    have not_le {x y : α} : ¬x ≤ y ↔ y < x := by simp [← not_lt]
    { instT, instLT, instLE with
      eq_of_compare {_  _}:= by rw [compareOfLessAndEq_eq_eq le_refl not_le]; exact id
    }

instance : LawfulOrd Nat :=
  LawfulCmp.compareOfLessAndEq_of_irrefl_of_trans_of_not_lt_of_antisymm
    Nat.lt_irrefl Nat.lt_trans Nat.not_lt Nat.le_antisymm

instance : LawfulOrd Int :=
  LawfulCmp.compareOfLessAndEq_of_irrefl_of_trans_of_not_lt_of_antisymm
    Int.lt_irrefl Int.lt_trans Int.not_lt Int.le_antisymm

instance : LawfulOrd Bool := by
  apply LawfulCmp.mk <;> decide

instance : LawfulOrd (Fin n) where
  eq_swap := OrientedCmp.eq_swap (α := Nat) (cmp := compare) ..
  eq_lt_iff_lt := LawfulLTCmp.eq_lt_iff_lt (α := Nat) (cmp := compare)
  isLE_iff_le := LawfulLECmp.isLE_iff_le (α := Nat) (cmp := compare)
  isLE_trans := TransCmp.isLE_trans (α := Nat) (cmp := compare)

end Std


================================================
FILE: Batteries/Classes/RatCast.lean
================================================
/-
Copyright (c) 2014 Robert Lewis. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Robert Lewis, Leonardo de Moura, Johannes Hölzl, Mario Carneiro, Gabriel Ebner
-/
module

@[expose] public section

/-- Type class for the canonical homomorphism `Rat → K`. -/
class RatCast (K : Type u) where
  /-- The canonical homomorphism `Rat → K`. -/
  protected ratCast : Rat → K

instance : RatCast Rat where ratCast n := n

/-- Canonical homomorphism from `Rat` to a division ring `K`.
This is just the bare function in order to aid in creating instances of `DivisionRing`. -/
@[coe, reducible, match_pattern] protected def Rat.cast {K : Type u} [RatCast K] : Rat → K :=
  RatCast.ratCast

-- see note [coercion into rings]
instance [RatCast K] : CoeTail Rat K where coe := Rat.cast

-- see note [coercion into rings]
instance [RatCast K] : CoeHTCT Rat K where coe := Rat.cast


================================================
FILE: Batteries/Classes/SatisfiesM.lean
================================================
/-
Copyright (c) 2022 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Kim Morrison
-/
module

public import Batteries.Lean.EStateM
public import Batteries.Lean.Except

@[expose] public section

/-!
## SatisfiesM

The `SatisfiesM` predicate works over an arbitrary (lawful) monad / applicative / functor,
and enables Hoare-like reasoning over monadic expressions. For example, given a monadic
function `f : α → m β`, to say that the return value of `f` satisfies `Q` whenever
the input satisfies `P`, we write `∀ a, P a → SatisfiesM Q (f a)`.

For any monad equipped with `MonadSatisfying m`
one can lift `SatisfiesM` to a monadic value in `Subtype`,
using `satisfying x h : m {a // p a}`, where `x : m α` and `h : SatisfiesM p x`.
This includes `Option`, `ReaderT`, `StateT`, and `ExceptT`, and the Lean monad stack.
(Although it is not entirely clear one should treat the Lean monad stack as lawful,
even though Lean accepts this.)

## Notes

`SatisfiesM` is not yet a satisfactory solution for verifying the behaviour of large scale monadic
programs. Such a solution would allow ergonomic reasoning about large `do` blocks,
with convenient mechanisms for introducing invariants and loop conditions as needed.

It is possible that in the future `SatiesfiesM` will become part of such a solution,
presumably requiring more syntactic support (and smarter `do` blocks) from Lean.
Or it may be that such a solution will look different!
This is an open research program, and for now one should not be overly ambitious using `SatisfiesM`.

In particular lemmas about pure operations on data structures in `Batteries` except for `HashMap`
should avoid `SatisfiesM` for now, so that it is easy to migrate to other approaches in future.
-/

/--
`SatisfiesM p (x : m α)` lifts propositions over a monad. It asserts that `x` may as well
have the type `x : m {a // p a}`, because there exists some `m {a // p a}` whose image is `x`.
So `p` is the postcondition of the monadic value.
-/
def SatisfiesM {m : Type u → Type v} [Functor m] (p : α → Prop) (x : m α) : Prop :=
  ∃ x' : m {a // p a}, Subtype.val <$> x' = x

namespace SatisfiesM

/-- If `p` is always true, then every `x` satisfies it. -/
theorem of_true [Functor m] [LawfulFunctor m] {x : m α}
    (h : ∀ a, p a) : SatisfiesM p x :=
  ⟨(fun a => ⟨a, h a⟩) <$> x, by simp⟩

/--
If `p` is always true, then every `x` satisfies it.
(This is the strongest postcondition version of `of_true`.)
-/
protected theorem trivial [Functor m] [LawfulFunctor m] {x : m α} :
  SatisfiesM (fun _ => True) x := of_true fun _ => trivial

/-- The `SatisfiesM p x` predicate is monotonic in `p`. -/
theorem imp [Functor m] [LawfulFunctor m] {x : m α}
    (h : SatisfiesM p x) (H : ∀ {a}, p a → q a) : SatisfiesM q x :=
  let ⟨x, h⟩ := h; ⟨(fun ⟨_, h⟩ => ⟨_, H h⟩) <$> x, by rw [← h, ← comp_map]; rfl⟩

/-- `SatisfiesM` distributes over `<$>`, general version. -/
protected theorem map [Functor m] [LawfulFunctor m] {x : m α}
    (hx : SatisfiesM p x) (hf : ∀ {a}, p a → q (f a)) : SatisfiesM q (f <$> x) := by
  let ⟨x', hx⟩ := hx
  refine ⟨(fun ⟨a, h⟩ => ⟨f a, hf h⟩) <$> x', ?_⟩
  rw [← hx]; simp

/--
`SatisfiesM` distributes over `<$>`, strongest postcondition version.
(Use this for reasoning forward from assumptions.)
-/
theorem map_post [Functor m] [LawfulFunctor m] {x : m α}
    (hx : SatisfiesM p x) : SatisfiesM (fun b => ∃ a, p a ∧ b = f a) (f <$> x) :=
  hx.map fun h => ⟨_, h, rfl⟩

/--
`SatisfiesM` distributes over `<$>`, weakest precondition version.
(Use this for reasoning backward from the goal.)
-/
theorem map_pre [Functor m] [LawfulFunctor m] {x : m α}
    (hx : SatisfiesM (fun a => p (f a)) x) : SatisfiesM p (f <$> x) :=
  hx.map fun h => h

/-- `SatisfiesM` distributes over `mapConst`, general version. -/
protected theorem mapConst [Functor m] [LawfulFunctor m] {x : m α}
    (hx : SatisfiesM q x) (ha : ∀ {b}, q b → p a) : SatisfiesM p (Functor.mapConst a x) :=
  map_const (f := m) ▸ hx.map ha

/-- `SatisfiesM` distributes over `pure`, general version / weakest precondition version. -/
protected theorem pure [Applicative m] [LawfulApplicative m]
    (h : p a) : SatisfiesM (m := m) p (pure a) := ⟨pure ⟨_, h⟩, by simp⟩

/-- `SatisfiesM` distributes over `<*>`, general version. -/
protected theorem seq [Applicative m] [LawfulApplicative m] {x : m α}
    (hf : SatisfiesM p₁ f) (hx : SatisfiesM p₂ x)
    (H : ∀ {f a}, p₁ f → p₂ a → q (f a)) : SatisfiesM q (f <*> x) := by
  match f, x, hf, hx with | _, _, ⟨f, rfl⟩, ⟨x, rfl⟩ => ?_
  refine ⟨(fun ⟨a, h₁⟩ ⟨b, h₂⟩ => ⟨a b, H h₁ h₂⟩) <$> f <*> x, ?_⟩
  simp only [← pure_seq]; simp [seq_assoc]
  simp only [← pure_seq]; simp [seq_assoc, Function.comp_def]

/-- `SatisfiesM` distributes over `<*>`, strongest postcondition version. -/
protected theorem seq_post [Applicative m] [LawfulApplicative m] {x : m α}
    (hf : SatisfiesM p₁ f) (hx : SatisfiesM p₂ x) :
    SatisfiesM (fun c => ∃ f a, p₁ f ∧ p₂ a ∧ c = f a) (f <*> x) :=
  hf.seq hx fun  hf ha => ⟨_, _, hf, ha, rfl⟩

/--
`SatisfiesM` distributes over `<*>`, weakest precondition version 1.
(Use this when `x` and the goal are known and `f` is a subgoal.)
-/
protected theorem seq_pre [Applicative m] [LawfulApplicative m] {x : m α}
    (hf : SatisfiesM (fun f => ∀ {a}, p₂ a → q (f a)) f) (hx : SatisfiesM p₂ x) :
    SatisfiesM q (f <*> x) :=
  hf.seq hx fun hf ha => hf ha

/--
`SatisfiesM` distributes over `<*>`, weakest precondition version 2.
(Use this when `f` and the goal are known and `x` is a subgoal.)
-/
protected theorem seq_pre' [Applicative m] [LawfulApplicative m] {x : m α}
    (hf : SatisfiesM p₁ f) (hx : SatisfiesM (fun a => ∀ {f}, p₁ f → q (f a)) x) :
    SatisfiesM q (f <*> x) :=
  hf.seq hx fun hf ha => ha hf

/-- `SatisfiesM` distributes over `<*`, general version. -/
protected theorem seqLeft [Applicative m] [LawfulApplicative m] {x : m α}
    (hx : SatisfiesM p₁ x) (hy : SatisfiesM p₂ y)
    (H : ∀ {a b}, p₁ a → p₂ b → q a) : SatisfiesM q (x <* y) :=
  seqLeft_eq x y ▸ (hx.map fun h _ => H h).seq_pre hy

/-- `SatisfiesM` distributes over `*>`, general version. -/
protected theorem seqRight [Applicative m] [LawfulApplicative m] {x : m α}
    (hx : SatisfiesM p₁ x) (hy : SatisfiesM p₂ y)
    (H : ∀ {a b}, p₁ a → p₂ b → q b) : SatisfiesM q (x *> y) :=
  seqRight_eq x y ▸ (hx.map fun h _ => H h).seq_pre hy

/-- `SatisfiesM` distributes over `>>=`, general version. -/
protected theorem bind [Monad m] [LawfulMonad m] {f : α → m β}
    (hx : SatisfiesM p x) (hf : ∀ a, p a → SatisfiesM q (f a)) :
    SatisfiesM q (x >>= f) := by
  match x, hx with | _, ⟨x, rfl⟩ => ?_
  have g a ha := Classical.indefiniteDescription _ (hf a ha)
  refine ⟨x >>= fun ⟨a, h⟩ => g a h, ?_⟩
  simp [← bind_pure_comp]; congr; funext ⟨a, h⟩; simp [← (g a h).2, ← bind_pure_comp]

/-- `SatisfiesM` distributes over `>>=`, weakest precondition version. -/
protected theorem bind_pre [Monad m] [LawfulMonad m] {f : α → m β}
    (hx : SatisfiesM (fun a => SatisfiesM q (f a)) x) :
    SatisfiesM q (x >>= f) := hx.bind fun _ h => h

end SatisfiesM

@[simp] theorem SatisfiesM_Id_eq : SatisfiesM (m := Id) p x ↔ p x :=
  ⟨fun ⟨y, eq⟩ => eq ▸ y.2, fun h => ⟨⟨_, h⟩, rfl⟩⟩

@[simp] theorem SatisfiesM_Option_eq : SatisfiesM (m := Option) p x ↔ ∀ a, x = some a → p a :=
  ⟨by revert x; intro | some _, ⟨some ⟨_, h⟩, rfl⟩, _, rfl => exact h,
   fun h => match x with | some a => ⟨some ⟨a, h _ rfl⟩, rfl⟩ | none => ⟨none, rfl⟩⟩

@[simp] theorem SatisfiesM_Except_eq : SatisfiesM (m := Except ε) p x ↔ ∀ a, x = .ok a → p a :=
  ⟨by revert x; intro | .ok _, ⟨.ok ⟨_, h⟩, rfl⟩, _, rfl => exact h,
   fun h => match x with | .ok a => ⟨.ok ⟨a, h _ rfl⟩, rfl⟩ | .error e => ⟨.error e, rfl⟩⟩

theorem SatisfiesM_EStateM_eq :
    SatisfiesM (m := EStateM ε σ) p x ↔ ∀ s a s', x.run s = .ok a s' → p a := by
  constructor
  · rintro ⟨x, rfl⟩ s a s' h
    match w : x.run s with
    | .ok a s' => simp at h; exact h.1
    | .error e s' => simp [w] at h
  · intro w
    refine ⟨?_, ?_⟩
    · intro s
      match q : x.run s with
      | .ok a s' => exact .ok ⟨a, w s a s' q⟩ s'
      | .error e s' => exact .error e s'
    · ext s
      rw [EStateM.run_map, EStateM.run]
      split <;> simp_all

theorem SatisfiesM_ReaderT_eq [Monad m] :
    SatisfiesM (m := ReaderT ρ m) p x ↔ ∀ s, SatisfiesM p (x.run s) :=
  (exists_congr fun a => by exact ⟨fun eq _ => eq ▸ rfl, funext⟩).trans Classical.skolem.symm

theorem SatisfiesM_StateRefT_eq [Monad m] :
    SatisfiesM (m := StateRefT' ω σ m) p x ↔ ∀ s, SatisfiesM p (x s) :=
  SatisfiesM_ReaderT_eq

theorem SatisfiesM_StateT_eq [Monad m] [LawfulMonad m] :
    SatisfiesM (m := StateT ρ m) (α := α) p x ↔ ∀ s, SatisfiesM (m := m) (p ·.1) (x.run s) := by
  change SatisfiesM (m := StateT ρ m) (α := α) p x ↔ ∀ s, SatisfiesM (m := m) (p ·.1) (x s)
  refine .trans ⟨fun ⟨f, eq⟩ => eq ▸ ?_, fun ⟨f, h⟩ => ?_⟩ Classical.skolem.symm
  · refine ⟨fun s => (fun ⟨⟨a, h⟩, s'⟩ => ⟨⟨a, s'⟩, h⟩) <$> f s, fun s => ?_⟩
    rw [← comp_map, map_eq_pure_bind]; rfl
  · refine ⟨fun s => (fun ⟨⟨a, s'⟩, h⟩ => ⟨⟨a, h⟩, s'⟩) <$> f s, funext fun s => ?_⟩
    show _ >>= _ = _; simp [← h]

theorem SatisfiesM_ExceptT_eq [Monad m] [LawfulMonad m] :
    SatisfiesM (m := ExceptT ρ m) (α := α) p x ↔
      SatisfiesM (m := m) (∀ a, · = .ok a → p a) x.run := by
  change _ ↔ SatisfiesM (m := m) (∀ a, · = .ok a → p a) x
  refine ⟨fun ⟨f, eq⟩ => eq ▸ ?_, fun ⟨f, eq⟩ => eq ▸ ?_⟩
  · exists (fun | .ok ⟨a, h⟩ => ⟨.ok a, fun | _, rfl => h⟩ | .error e => ⟨.error e, nofun⟩) <$> f
    show _ = _ >>= _; rw [← comp_map, map_eq_pure_bind]; congr; funext a; cases a <;> rfl
  · exists ((fun | ⟨.ok a, h⟩ => .ok ⟨a, h _ rfl⟩ | ⟨.error e, _⟩ => .error e) <$> f : m _)
    show _ >>= _ = _; simp [← bind_pure_comp]; congr; funext ⟨a, h⟩; cases a <;> rfl

/--
If a monad has `MonadSatisfying m`, then we can lift a `h : SatisfiesM (m := m) p x` predicate
to monadic value `satisfying x p : m { x // p x }`.

Reader, state, and exception monads have `MonadSatisfying` instances if the base monad does.
-/
class MonadSatisfying (m : Type u → Type v) [Functor m] [LawfulFunctor m] where
  /-- Lift a `SatisfiesM` predicate to a monadic value. -/
  satisfying {p : α → Prop} {x : m α} (h : SatisfiesM (m := m) p x) : m {a // p a}
  /-- The value of the lifted monadic value is equal to the original monadic value. -/
  val_eq {p : α → Prop} {x : m α} (h : SatisfiesM (m := m) p x) : Subtype.val <$> satisfying h = x

export MonadSatisfying (satisfying)

namespace MonadSatisfying

instance : MonadSatisfying Id where
  satisfying {α p x} h := ⟨x, by obtain ⟨⟨_, h⟩, rfl⟩ := h; exact h⟩
  val_eq {α p x} h := rfl

instance : MonadSatisfying Option where
  satisfying {α p x?} h :=
    have h' := SatisfiesM_Option_eq.mp h
    match x? with
    | none => none
    | some x => some ⟨x, h' x rfl⟩
  val_eq {α p x?} h := by cases x? <;> simp

instance : MonadSatisfying (Except ε) where
  satisfying {α p x?} h :=
    have h' := SatisfiesM_Except_eq.mp h
    match x? with
    | .ok x => .ok ⟨x, h' x rfl⟩
    | .error e => .error e
  val_eq {α p x?} h := by cases x? <;> simp

instance [Monad m] [LawfulMonad m][MonadSatisfying m] : MonadSatisfying (ReaderT ρ m) where
  satisfying {α p x} h :=
    have h' := SatisfiesM_ReaderT_eq.mp h
    fun r => satisfying (h' r)
  val_eq {α p x} h := by
    have h' := SatisfiesM_ReaderT_eq.mp h
    ext r
    rw [ReaderT.run_map, ← MonadSatisfying.val_eq (h' r)]
    rfl

instance [Monad m] [LawfulMonad m] [MonadSatisfying m] : MonadSatisfying (StateRefT' ω σ m) :=
  inferInstanceAs <| MonadSatisfying (ReaderT (ST.Ref ω σ) m)

instance [Monad m] [LawfulMonad m] [MonadSatisfying m] : MonadSatisfying (StateT ρ m) where
  satisfying {α p x} h :=
    have h' := SatisfiesM_StateT_eq.mp h
    fun r => (fun ⟨⟨a, r'⟩, h⟩ => ⟨⟨a, h⟩, r'⟩) <$> satisfying (h' r)
  val_eq {α p x} h := by
    have h' := SatisfiesM_StateT_eq.mp h
    ext r
    rw [← MonadSatisfying.val_eq (h' r), StateT.run_map]
    simp [StateT.run]

instance [Monad m] [LawfulMonad m] [MonadSatisfying m] : MonadSatisfying (ExceptT ε m) where
  satisfying {α p x} h :=
    let x' := satisfying (SatisfiesM_ExceptT_eq.mp h)
    ExceptT.mk ((fun ⟨y, w⟩ => y.pmap fun a h => ⟨a, w _ h⟩) <$> x')
  val_eq {α p x} h := by
    ext
    refine Eq.trans ?_ (MonadSatisfying.val_eq (SatisfiesM_ExceptT_eq.mp h))
    simp

instance : MonadSatisfying (EStateM ε σ) where
  satisfying {α p x} h :=
    have h' := SatisfiesM_EStateM_eq.mp h
    fun s => match w : x.run s with
    | .ok a s' => .ok ⟨a, h' s a s' w⟩ s'
    | .error e s' => .error e s'
  val_eq {α p x} h := by
    ext s
    rw [EStateM.run_map, EStateM.run]
    split <;> simp_all

end MonadSatisfying


================================================
FILE: Batteries/CodeAction/Attr.lean
================================================
/-
Copyright (c) 2023 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
module

public import Lean.Server.CodeActions.Basic
public import Lean.Compiler.IR.CompilerM

@[expose] public section

/-!
# Initial setup for code action attributes

* `@[hole_code_action]` and `@[command_code_action]` now live in the Lean repository,
  and are builtin.

* Attribute `@[tactic_code_action]` collects code actions which will be called
  on each occurrence of a tactic.
-/
namespace Batteries.CodeAction

open Lean Elab Server Lsp RequestM Snapshots

/-- A tactic code action extension. -/
abbrev TacticCodeAction :=
  CodeActionParams → Snapshot →
  (ctx : ContextInfo) → (stack : Syntax.Stack) → (node : InfoTree) →
  RequestM (Array LazyCodeAction)

/-- A tactic code action extension. -/
abbrev TacticSeqCodeAction :=
  CodeActionParams → Snapshot →
  (ctx : ContextInfo) → (i : Nat) → (stack : Syntax.Stack) → (goals : List MVarId) →
  RequestM (Array LazyCodeAction)

/-- Read a tactic code action from a declaration of the right type. -/
def mkTacticCodeAction (n : Name) : ImportM TacticCodeAction := do
  let { env, opts, .. } ← read
  IO.ofExcept <| unsafe env.evalConstCheck TacticCodeAction opts ``TacticCodeAction n

/-- Read a tacticSeq code action from a declaration of the right type. -/
def mkTacticSeqCodeAction (n : Name) : ImportM TacticSeqCodeAction := do
  let { env, opts, .. } ← read
  IO.ofExcept <| unsafe env.evalConstCheck TacticSeqCodeAction opts ``TacticSeqCodeAction n

/-- An entry in the tactic code actions extension, containing the attribute arguments. -/
structure TacticCodeActionEntry where
  /-- The declaration to tag -/
  declName : Name
  /-- The tactic kinds that this extension supports. If empty it is called on all tactic kinds. -/
  tacticKinds : Array Name
  deriving Inhabited

/-- The state of the tactic code actions extension. -/
structure TacticCodeActions where
  /-- The list of tactic code actions to apply on any tactic. -/
  onAnyTactic : Array TacticCodeAction := {}
  /-- The list of tactic code actions to apply when a particular tactic kind is highlighted. -/
  onTactic : NameMap (Array TacticCodeAction) := {}
  deriving Inhabited

/-- Insert a tactic code action entry into the `TacticCodeActions` structure. -/
def TacticCodeActions.insert (self : TacticCodeActions)
    (tacticKinds : Array Name) (action : TacticCodeAction) : TacticCodeActions :=
  if tacticKinds.isEmpty then
    { self with onAnyTactic := self.onAnyTactic.push action }
  else
    { self with onTactic := tacticKinds.foldl (init := self.onTactic) fun m a =>
        m.insert a ((m.getD a #[]).push action) }

/-- An extension which collects all the tactic code actions. -/
initialize tacticSeqCodeActionExt :
    PersistentEnvExtension Name (Name × TacticSeqCodeAction)
      (Array Name × Array TacticSeqCodeAction) ←
  registerPersistentEnvExtension {
    mkInitial := pure (#[], #[])
    addImportedFn := fun as => return (#[], ← as.foldlM (init := #[]) fun m as =>
      as.foldlM (init := m) fun m a => return m.push (← mkTacticSeqCodeAction a))
    addEntryFn := fun (s₁, s₂) (n₁, n₂) => (s₁.push n₁, s₂.push n₂)
    exportEntriesFn := (·.1)
  }

/-- An extension which collects all the tactic code actions. -/
initialize tacticCodeActionExt :
    PersistentEnvExtension TacticCodeActionEntry (TacticCodeActionEntry × TacticCodeAction)
      (Array TacticCodeActionEntry × TacticCodeActions) ←
  registerPersistentEnvExtension {
    mkInitial := pure (#[], {})
    addImportedFn := fun as => return (#[], ← as.foldlM (init := {}) fun m as =>
      as.foldlM (init := m) fun m ⟨name, kinds⟩ =>
        return m.insert kinds (← mkTacticCodeAction name))
    addEntryFn := fun (s₁, s₂) (e, n₂) => (s₁.push e, s₂.insert e.tacticKinds n₂)
    exportEntriesFn := (·.1)
  }

/--
This attribute marks a code action, which is used to suggest new tactics or replace existing ones.

* `@[tactic_code_action]`: This is a code action which applies to the spaces between tactics,
  to suggest a new tactic to change the goal state.

* `@[tactic_code_action kind]`: This is a code action which applies to applications of the tactic
  `kind` (a tactic syntax kind), which can replace the tactic or insert things before or after it.

* `@[tactic_code_action kind₁ kind₂]`: shorthand for
  `@[tactic_code_action kind₁, tactic_code_action kind₂]`.

* `@[tactic_code_action *]`: This is a tactic code action that applies to all tactics.
  Use sparingly.
-/
syntax (name := tactic_code_action) "tactic_code_action" ("*" <|> (ppSpace ident)*) : attr

initialize
  registerBuiltinAttribute {
    name := `tactic_code_action
    descr := "Declare a new tactic code action, to appear in the code actions on tactics"
    applicationTime := .afterCompilation
    add := fun decl stx kind => do
      unless kind == AttributeKind.global do
        throwError "invalid attribute 'tactic_code_action', must be global"
      match stx with
      | `(attr| tactic_code_action *) =>
        if (IR.getSorryDep (← getEnv) decl).isSome then return -- ignore in progress definitions
        modifyEnv (tacticCodeActionExt.addEntry · (⟨decl, #[]⟩, ← mkTacticCodeAction decl))
      | `(attr| tactic_code_action $[$args]*) =>
        if args.isEmpty then
          if (IR.getSorryDep (← getEnv) decl).isSome then return -- ignore in progress definitions
          modifyEnv (tacticSeqCodeActionExt.addEntry · (decl, ← mkTacticSeqCodeAction decl))
        else
          let args ← args.mapM realizeGlobalConstNoOverloadWithInfo
          if (IR.getSorryDep (← getEnv) decl).isSome then return -- ignore in progress definitions
          modifyEnv (tacticCodeActionExt.addEntry · (⟨decl, args⟩, ← mkTacticCodeAction decl))
      | _ => pure ()
  }


================================================
FILE: Batteries/CodeAction/Basic.lean
================================================
/-
Copyright (c) 2023 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
module

public meta import Lean.Elab.BuiltinTerm
public meta import Lean.Elab.BuiltinNotation
public meta import Lean.Server.InfoUtils
public meta import Lean.Server.CodeActions.Provider
public meta import Batteries.CodeAction.Attr

public meta section

/-!
# Initial setup for code actions

This declares a code action provider that calls all `@[hole_code_action]` definitions
on each occurrence of a hole (`_`, `?_` or `sorry`).

(This is in a separate file from `Batteries.CodeAction.Hole.Attr` so that the server does not
attempt to use this code action provider when browsing the `Batteries.CodeAction.Hole.Attr` file
itself.)
-/
namespace Batteries.CodeAction

open Lean Elab Server RequestM CodeAction

/-- A code action which calls `@[tactic_code_action]` code actions. -/
@[code_action_provider] def tacticCodeActionProvider : CodeActionProvider := fun params snap => do
  let doc ← readDoc
  let startPos := doc.meta.text.lspPosToUtf8Pos params.range.start
  let endPos := doc.meta.text.lspPosToUtf8Pos params.range.end
  let pointerCol :=
    if params.range.start.line == params.range.end.line then
      max params.range.start.character params.range.end.character
    else 0
  let some result := findTactic?
    (fun pos => (doc.meta.text.utf8PosToLspPos pos).character ≤ pointerCol)
    ⟨startPos, endPos⟩ snap.stx | return #[]
  let tgtTac := match result with
    | .tactic (tac :: _)
    | .tacticSeq _ _ (_ :: tac :: _) => tac.1
    | _ => unreachable!
  let tgtRange := tgtTac.getRange?.get!
  have info := findInfoTree? tgtTac.getKind tgtRange none snap.infoTree (canonicalOnly := true)
    fun _ info => info matches .ofTacticInfo _
  let some (ctx, node@(.node (.ofTacticInfo info) _)) := info | return #[]
  let mut out := #[]
  match result with
  | .tactic stk@((tac, _) :: _) => do
    let ctx := { ctx with mctx := info.mctxBefore }
    let actions := (tacticCodeActionExt.getState snap.env).2
    if let some arr := actions.onTactic.find? tac.getKind then
      for act in arr do
        try out := out ++ (← act params snap ctx stk node) catch _ => pure ()
    for act in actions.onAnyTactic do
      try out := out ++ (← act params snap ctx stk node) catch _ => pure ()
  | .tacticSeq _ i stk@((seq, _) :: _) =>
    let (ctx, goals) ← if 2*i < seq.getNumArgs then
      let stx := seq[2*i]
      let some stxRange := stx.getRange? | return #[]
      let some (ctx, .node (.ofTacticInfo info') _) :=
          findInfoTree? stx.getKind stxRange ctx node fun _ info => (info matches .ofTacticInfo _)
        | return #[]
      pure ({ ctx with mctx := info'.mctxBefore }, info'.goalsBefore)
    else
      pure ({ ctx with mctx := info.mctxAfter }, info.goalsAfter)
    for act in (tacticSeqCodeActionExt.getState snap.env).2 do
      try out := out ++ (← act params snap ctx i stk goals) catch _ => pure ()
  | _ => unreachable!
  pure out


================================================
FILE: Batteries/CodeAction/Deprecated.lean
================================================
/-
Copyright (c) 2023 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
module

public meta import Lean.Server.CodeActions.Provider

public meta section

/-!
# Code action for @[deprecated] replacements

This is an opt-in mechanism for making machine-applicable `@[deprecated]` definitions. When enabled
(by setting the `machineApplicableDeprecated` tag attribute), a code action will be triggered
whenever the deprecation lint also fires, allowing the user to replace the usage of the deprecated
constant.
-/

namespace Batteries

open Lean Elab Server Lsp RequestM CodeAction

/-- An environment extension for identifying `@[deprecated]` definitions which can be auto-fixed -/
initialize machineApplicableDeprecated : TagDeclarationExtension ← mkTagDeclarationExtension

namespace CodeAction

/-- A code action which applies replacements for `@[deprecated]` definitions. -/
@[code_action_provider]
def deprecatedCodeActionProvider : CodeActionProvider := fun params snap => do
  let mut i := 0
  let doc ← readDoc
  let mut msgs := #[]
  for m in snap.msgLog.toList do
    if m.data.isDeprecationWarning then
      if h : _ then
        msgs := msgs.push (snap.cmdState.messages.toList[i]'h)
    i := i + 1
  if msgs.isEmpty then return #[]
  let start := doc.meta.text.lspPosToUtf8Pos params.range.start
  let stop := doc.meta.text.lspPosToUtf8Pos params.range.end
  for msg in msgs do
    let some endPos := msg.endPos | continue
    let pos := doc.meta.text.ofPosition msg.pos
    let endPos' := doc.meta.text.ofPosition endPos
    unless start ≤ endPos' && pos ≤ stop do continue
    let some (ctx, .node (.ofTermInfo info@{ expr := .const c .., ..}) _) :=
      findInfoTree? identKind ⟨pos, endPos'⟩ none snap.infoTree fun _ i =>
        (i matches .ofTermInfo { elaborator := .anonymous, expr := .const .., ..})
      | continue
    unless machineApplicableDeprecated.isTagged snap.cmdState.env c do continue
    let some c' := Linter.getDeprecatedNewName snap.cmdState.env c | continue
    let eager : CodeAction := {
      title := s!"Replace {c} with {c'}"
      kind? := "quickfix"
      isPreferred? := true
    }
    return #[{
      eager
      lazy? := some do
        let c' ← info.runMetaM ctx (unresolveNameGlobal c')
        let pos := doc.meta.text.leanPosToLspPos msg.pos
        let endPos' := doc.meta.text.leanPosToLspPos endPos
        pure { eager with
          edit? := some <| .ofTextEdit doc.versionedIdentifier {
            range := ⟨pos, endPos'⟩
            newText := toString c'
          }
        }
    }]
  return #[]


================================================
FILE: Batteries/CodeAction/Match.lean
================================================
/-
Copyright (c) 2026 Moritz Roos. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Moritz Roos
-/
module

public meta import Batteries.CodeAction.Misc
public meta import Batteries.Data.List.Basic

@[expose] public meta section

namespace Batteries.CodeAction

open Lean Meta Elab Server RequestM CodeAction


/-- Filter for the info-nodes to find the match-nodes. -/
def isMatchTerm : Info → Bool
  | .ofTermInfo i => i.stx.isOfKind ``Lean.Parser.Term.match
  | _ => false

/-- Returns the String.range that encompasses `match e (with)`. -/
def getMatchHeaderRange? (matchStx : Syntax) : Option Lean.Syntax.Range := do
  match matchStx with
  | `(term| match
    $[(generalizing := $generalizingVal)]?
    $[(motive := $motiveVal)]?
    $[$discrs:matchDiscr],*
    with $_) => --Here the $alts would go, if they were already typed. Else $_  will match "missing"

    -- Isolate the syntax of only the "match" atom to get the starting position:
    let mStx ← matchStx.getArgs.find? (fun s => s.isAtom && s.getAtomVal == "match")
    let startPos ← mStx.getPos? -- begin of 'match' keyword

    -- Depending on the existence of 'with', return the correct range:
    if let some withStx := (matchStx.getArgs.find? (fun s => s.isAtom && s.getAtomVal == "with"))
      then return ⟨startPos, ←withStx.getTailPos?⟩
    else
      let lastMatchDiscr ← discrs.back?
      return ⟨startPos, ←lastMatchDiscr.raw.getTailPos?⟩
  | _ => none

/-- Flattens an Infotree into an array of Info-nodes that fulfill p. -/
partial def findAllInfos (p : Info → Bool) (t : InfoTree) : Array Info :=
  loop t #[]
where
  /-- Inner loop for `findAllInfos`. -/
  loop (t : InfoTree) (acc : Array Info) : Array Info :=
    match t with
    | .context _ childTree => loop childTree acc
    | .node info children  =>
      let acc' := if p info then acc.push info else acc
      children.foldl (fun currentAcc child => loop child currentAcc) acc'
    | .hole _              => acc

/-- Computes for a constructor, if it makes sense to use `@constr` in a match, by determining
    if it has any non-parameter implicit arguments. -/
def hasImplicitNonparArg (ctor : Name) (env : Environment) : Bool := Id.run do
    let some (.ctorInfo ctorInfo) := env.find? ctor | panic! "bad inductive"
    let explicitArgs := getExplicitArgs ctorInfo.type #[]
    let allArgs := getAllArgs ctorInfo.type #[]
    let some (.inductInfo indInfo) := env.find? ctorInfo.induct | panic! "not an inductive"
    let numParams := indInfo.numParams
    return (allArgs.size - (explicitArgs.size + numParams) > 0)

/-- From a constructor-name e.g. `Option.some` construct the corresponding match pattern, e.g.
    `.some val`. We implement special cases for `Nat` and `List`, `Option` and `Bool` to e.g.
    produce `n + 1` instead of `Nat.succ n`. -/
def patternFromConstructor (ctor : Name) (env : Environment) (suffix : String)
    (explicitArgsOnly : Bool) (ctor_hasImplicitNonparArg : Bool): Option String := do
  let some (.ctorInfo ctorInfo) := env.find? ctor | panic! "bad inductive"
  let some (.inductInfo indInfo) := env.find? ctorInfo.induct | panic! "not an inductive"
  let numParams := indInfo.numParams
  let ctor_short := toString (ctor.updatePrefix .anonymous)
  let explicitCtorArgs := getExplicitArgs ctorInfo.type #[]
  let allCtorArgs := getAllArgs ctorInfo.type #[]

  /- Special cases with nicer Notation. None of these constructors has any implicit arguments
     that aren't parameters, i.e. that aren't already determined by the match discriminant.
     So it doesn't make sense to use them with `@`. That's why we *always* nicely print them
     regardless of the setting `explicitArgsOnly`. -/
  match ctor with
  | (.str (.str .anonymous "Nat") "zero") => "0"
  /- At the moment this evaluates to "n + 1": -/
  | (.str (.str .anonymous "Nat") "succ") => s!"{explicitCtorArgs[0]!}{suffix} + 1" --
  | (.str (.str .anonymous "List") "nil") => "[]"
  /- At the moment this evaluates to "head :: tail": -/
  | (.str (.str .anonymous "List") "cons") =>
    s!"{explicitCtorArgs[0]!}{suffix} :: {explicitCtorArgs[1]!}{suffix}"
  | (.str (.str .anonymous "Option") "some") => s!"some {explicitCtorArgs[0]!}{suffix}"
  | (.str (.str .anonymous "Option") "none") => "none"
  | (.str (.str .anonymous "Bool") "true") => "true"
  | (.str (.str .anonymous "Bool") "false") => "false"
  | _ =>
    /- This is the Default case. It fills the constructor arguments with the variable names `arg`
       which were used in the inductive type specification. When using this action with multiple
       (same-type) arguments these might clash, so we fix it by appending a suffix like `_2` -
       you will probably want to rename these suffixed names yourself.
       If the the user wants the match to contain the implicit arguments as well, we
       additionally put `_` for every `parameter` (a parameter is an argument to the inductive
       type that is fixed over constructors), since these should already be determined by the
       match discriminant. One could elaborate the type of this discriminant and fill the parameters
       from there, but we don't see any value in this. -/
    if explicitArgsOnly || Bool.not ctor_hasImplicitNonparArg then
      let mut str := s!".{ctor_short}"
      for arg in explicitCtorArgs do
        str := str ++ if arg.hasNum || arg.isInternal then " _" else s!" {arg}{suffix}"
      return str
    else
      let mut str := s!".{ctor_short}"
      /- This loop skips the first `numParams` many arguments, since these are the parameters
         and are already determined by the match discriminant and thus unlikely to be
         useful for the match. -/
      for i in [numParams:allCtorArgs.size] do
        let arg := allCtorArgs[i]!
        str := str ++
          if arg.hasNum || arg.isInternal then
            " _"
          else
            if arg ∈ explicitCtorArgs then
              s!" {arg}{suffix}"
            else
              s!" ({arg} := {arg}{suffix})"
      return str


/--
Invoking tactic code action `Generate a list of alternatives for this match.` in the
following:
```lean
def myfun2 (n : Nat) : Nat :=
  match n
```
produces:
```lean
def myfun2 (n : Nat) : Nat :=
  match n with
  | 0 => _
  | n + 1 => _
```
Also has support for multiple discriminants, e.g.
```
def myfun3 (o : Option Bool) (m : Nat) : Nat :=
  match o, m with
```
can be expanded into
```
def myfun3 (o : Option Bool) (m : Nat) : Nat :=
  match o, m with
  | none, 0 => _
  | none, n_2 + 1 => _
  | some val_1, 0 => _
  | some val_1, n_2 + 1 => _
```
If it makes sense to use at least one of the constructors with `@` (i.e. iff it has an
implicit non-parameter argument) then we also show a codeaction that expands every such constructor
with implicit arguments filled in with the syntax `implicitArg := implicitArg`.
E.g. invoking `Generate a list of equations with implicit arguments for this match.` in
the following
```lean
inductive TermWithImplicit (F : Nat → Type u) (α : Type w)
  | var (x : α) : TermWithImplicit F α
  | func {l : Nat} (f : F l) (ts : Fin l → TermWithImplicit F α) : TermWithImplicit F α

def myfun4 (t : TermWithImplicit F α) : Nat := by
  match t with
```
produces
```lean
def myfun4 (t : TermWithImplicit F α) : Nat := by
  match t with
  | .var x => _
  | .func (l := l) f ts => _
```
where the implicit argument `{l : Nat}` is now usable.
Note that the arguments `F` and `α` are not filled since they are `parameters`
(a parameter is an argument to an inductive type that is fixed over constructors), i.e.
they are already determined by the match discriminant `t`. This means they don't provide any
new information for you.
-/
@[command_code_action]
def matchExpand : CommandCodeAction := fun CodeActionParams snap ctx node => do
  /- Since `match` is a term (not a command) `@[command_code_action Parser.Term.match]` will
  not fire. So we filter `command_code_action` ourselves in Step 1 for now. -/

  /- 1. Find ALL ofTermInfo Info nodes that are of kind `Term.match` -/
  let allMatchInfos := findAllInfos isMatchTerm node

  /- 2. Filter these candidates within the `RequestM` monad based on the cursor being in the
  header lines of these matches. -/
  let doc ← readDoc
  let relevantMatchInfos ← allMatchInfos.filterM fun matchInfo => do
    let some headerRangeRaw := getMatchHeaderRange? matchInfo.stx | return false
    let headerRangeLsp := doc.meta.text.utf8RangeToLspRange headerRangeRaw

    let cursorRangeLsp := CodeActionParams.range
    -- check if the cursor range is contained in the header range
    return (cursorRangeLsp.start ≥ headerRangeLsp.start && cursorRangeLsp.end ≤ headerRangeLsp.end)

  /- 3. Pick the first (and mostly only) candidate. There might sometimes be more,
  since some things are just contained multiple times in 'node'. -/
  let some matchInfo := relevantMatchInfos[0]? | return #[]
  let some headerRangeRaw := getMatchHeaderRange? matchInfo.stx | return #[]

  /- Isolate the array of match-discriminants -/
  let discrs ← match matchInfo.stx with
  | `(term| match
    $[(generalizing := $generalizingVal)]?
    $[(motive := $motiveVal)]?
    $[$discrs:matchDiscr],*
    with $_) => pure discrs
  | _ => return #[]

  /- Reduce discrs to the array of match-discriminants-terms (i.e. "[n1, n2]" in "match n2,n2"). -/
  let some discrTerms := discrs.mapM (fun discr =>
    match discr with
    | `(matchDiscr| $t: term) => some t
    | `(matchDiscr| $_:ident : $t: term) => some t
    | _ => none
    ) | return #[]

  -- Get a Bool, that tells us if "with" is already typed in:
  let withPresent :=
    (matchInfo.stx.getArgs.find? (fun s => s.isAtom && s.getAtomVal == "with")).isSome

  /- Construct a list containing for each discriminant its list of constructor names paired with
     a Bool that determines if it makes sense to use the constructor with `@`.
     The list contains the first discriminant constructors last,
     since we are prepending in the loop. -/
  let mut constructors_rev : List (List (Name × Bool)) := []
  for discrTerm in discrTerms do
    let some (info, updatedCtx) := findTermInfoWithCtx? node discrTerm ctx | return #[]
    let ty ← info.runMetaM updatedCtx (Lean.Meta.inferType info.expr)
    let .const name _ := (← info.runMetaM updatedCtx (whnf ty)).getAppFn | return #[]
    -- Find the inductive constructors of e:
    let some (.inductInfo indInfo) := snap.env.find? name | return #[]
    let ctors := indInfo.ctors
    constructors_rev :=
      (ctors.map (fun ctor => (ctor, hasImplicitNonparArg ctor snap.env)))
        :: constructors_rev

  let mkAction (title : String) (explicitArgsOnly : Bool) : LazyCodeAction :=
      let eager : Lsp.CodeAction := {
      title := title
      kind? := "quickfix"
      }
    { --rest is lightly adapted from eqnStub:
    eager
    lazy? := some do
      let holePos := headerRangeRaw.stop --where we start inserting
      let (indent, _) := findIndentAndIsStart doc.meta.text.source headerRangeRaw.start
      let mut str := if withPresent then "" else " with"

      let indent := "\n".pushn ' ' (indent) --use the same indent as the 'match' line.
      let constructor_combinations := constructors_rev.sections.map List.reverse
      for l in constructor_combinations do
        str := str ++ indent ++ "| "
        for ctor_idx in [:l.length] do
          let (ctor, existsExplicitNonparArg) := l[ctor_idx]!
          let suffix := if constructors_rev.length ≥ 2 then s!"_{ctor_idx + 1}" else ""
          let some pat :=
            patternFromConstructor ctor snap.env suffix explicitArgsOnly existsExplicitNonparArg |
              panic! "bad inductive"
          str := str ++ pat
          if ctor_idx < l.length - 1 then
            str := str ++ ", "
        str := str ++ s!" => _"
      pure { eager with
        edit? := some <|.ofTextEdit doc.versionedIdentifier {
          range := doc.meta.text.utf8RangeToLspRange ⟨holePos, holePos⟩-- adapted to insert-only
          newText := str
        }
      }
  }
  /- Show the code action with implicit arguments if at least one constructor has an implicit
     non-parameter argument. -/
  let showExplicitCodeAction := constructors_rev.any (fun l =>
    l.any (fun (_, ctor_hasImplicitNonparArg) => ctor_hasImplicitNonparArg))

  if (showExplicitCodeAction) then
    return #[mkAction "Generate a list of equations for this match." True,
             mkAction "Generate a list of equations with implicit arguments for this match." False]
  else
    return #[mkAction "Generate a list of equations for this match." True]


================================================
FILE: Batteries/CodeAction/Misc.lean
================================================
/-
Copyright (c) 2023 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
module

public meta import Lean.Elab.Tactic.Induction
public meta import Batteries.Lean.Position
public meta import Batteries.CodeAction.Attr
public meta import Lean.Server.CodeActions.Provider

public meta section

/-!
# Miscellaneous code actions

This declares some basic tactic code actions, using the `@[tactic_code_action]` API.
-/
namespace Batteries.CodeAction

open Lean Meta Elab Server RequestM CodeAction

/-- Return the syntax stack leading to `target` from `root`, if one exists. -/
def findStack? (root target : Syntax) : Option Syntax.Stack := do
  let range ← target.getRange?
  root.findStack? (·.getRange?.any (·.includes range))
    (fun s => s.getKind == target.getKind && s.getRange? == range)

/-- Constructs a hole with a kind matching the provided hole elaborator.  -/
def holeKindToHoleString : (elaborator : Name) → (synthName : String) → String
  | ``Elab.Term.elabSyntheticHole, name => "?" ++ name
  | ``Elab.Term.elabSorry, _ => "sorry"
  | _, _ => "_"

/--
Hole code action used to fill in a structure's field when specifying an instance.

In the following:
```lean
instance : Monad Id := _
```

invoking the hole code action "Generate a (minimal) skeleton for the structure under construction."
produces:
```lean
instance : Monad Id where
  pure := _
  bind := _
```

and invoking "Generate a (maximal) skeleton for the structure under construction." produces:
```lean
instance : Monad Id where
  map := _
  mapConst := _
  pure := _
  seq := _
  seqLeft := _
  seqRight := _
  bind := _
```
-/
@[hole_code_action] partial def instanceStub : HoleCodeAction := fun _ snap ctx info => do
  let some ty := info.expectedType? | return #[]
  let .const name _ := (← info.runMetaM ctx (whnf ty)).getAppFn | return #[]
  unless isStructure snap.env name do return #[]
  let doc ← readDoc
  let fields := collectFields snap.env name #[] []
  let only := !fields.any fun (_, auto) => auto
  let mkAutofix minimal :=
    let eager := {
      title := s!"\
        Generate a {if only then "" else if minimal then "(minimal) " else "(maximal) "}\
        skeleton for the structure under construction."
      kind? := "quickfix"
      isPreferred? := minimal
    }
    let lazy? := some do
      let useWhere := do
        let _ :: (stx, _) :: _ ← findStack? snap.stx info.stx | none
        guard (stx.getKind == ``Parser.Command.declValSimple)
        stx[0].getPos?
      let holePos := useWhere.getD info.stx.getPos?.get!
      let (indent, isStart) := findIndentAndIsStart doc.meta.text.source holePos
      let indent := "\n".pushn ' ' indent
      let mut str := if useWhere.isSome then "where" else "{"
      let mut first := useWhere.isNone && isStart
      for (field, auto) in fields do
        if minimal && auto then continue
        if first then
          str := str ++ " "
          first := false
        else
          str := str ++ indent ++ "  "
        let field := toString field
        str := str ++ s!"{field} := {holeKindToHoleString info.elaborator field}"
      if useWhere.isNone then
        if isStart then
          str := str ++ " }"
        else
          str := str ++ indent ++ "}"
      pure { eager with
        edit? := some <| .ofTextEdit doc.versionedIdentifier {
          range := doc.meta.text.utf8RangeToLspRange ⟨holePos, info.stx.getTailPos?.get!⟩
          newText := str
        }
      }
    { eager, lazy? }
  pure <| if only then #[mkAutofix true] else #[mkAutofix true, mkAutofix false]
where
  /-- Returns true if this field is an autoParam or optParam, or if it is given an optional value
  in a child struct. -/
  isAutofillable (env : Environment) (fieldInfo : StructureFieldInfo) (stack : List Name) : Bool :=
    fieldInfo.autoParam?.isSome || env.contains (mkDefaultFnOfProjFn fieldInfo.projFn)
      || stack.any fun struct => env.contains (mkDefaultFnOfProjFn (struct ++ fieldInfo.fieldName))

  /-- Returns the fields of a structure, unfolding parent structures. -/
  collectFields (env : Environment) (structName : Name)
      (fields : Array (Name × Bool)) (stack : List Name) : Array (Name × Bool) :=
    (getStructureFields env structName).foldl (init := fields) fun fields field =>
      if let some fieldInfo := getFieldInfo? env structName field then
        if let some substructName := fieldInfo.subobject? then
          collectFields env substructName fields (structName :: stack)
        else
          fields.push (field, isAutofillable env fieldInfo stack)
      else fields

/-- Returns the explicit arguments given a type. The second argument of this
    function is an accumulator. -/
def getExplicitArgs : Expr → Array Name → Array Name
  | .forallE n _ body bi, args =>
    getExplicitArgs body <| if bi.isExplicit then args.push n else args
  | _, args => args

/-- Returns all of the arguments given a type. The second argument of this
    function is an accumulator. -/
def getAllArgs : Expr → Array Name → Array Name
  | .forallE n _ body _, args =>
    getAllArgs body <| args.push n
  | _, args => args

/--
Invoking hole code action "Generate a list of equations for a recursive definition" in the
following:
```lean
def foo : Expr → Unit := _
```

produces:

```lean
def foo : Expr → Unit := fun
  | .bvar deBruijnIndex => _
  | .fvar fvarId => _
  | .mvar mvarId => _
  | .sort u => _
  | .const declName us => _
  | .app fn arg => _
  | .lam binderName binderType body binderInfo => _
  | .forallE binderName binderType body binderInfo => _
  | .letE declName type value body nonDep => _
  | .lit _ => _
  | .mdata data expr => _
  | .proj typeName idx struct => _
```

-/
@[hole_code_action] def eqnStub : HoleCodeAction := fun _ snap ctx info => do
  let some ty := info.expectedType? | return #[]
  let .forallE _ dom .. ← info.runMetaM ctx (whnf ty) | return #[]
  let .const name _ := (← info.runMetaM ctx (whnf dom)).getAppFn | return #[]
  let some (.inductInfo val) := snap.env.find? name | return #[]
  let eager := {
    title := "Generate a list of equations for a recursive definition."
    kind? := "quickfix"
  }
  let doc ← readDoc
  pure #[{
    eager
    lazy? := some do
      let holePos := info.stx.getPos?.get!
      let (indent, isStart) := findIndentAndIsStart doc.meta.text.source holePos
      let mut str := "fun"
      let indent := "\n".pushn ' ' (if isStart then indent else indent + 2)
      for ctor in val.ctors do
        let some (.ctorInfo ci) := snap.env.find? ctor | panic! "bad inductive"
        let ctor := toString (ctor.updatePrefix .anonymous)
        str := str ++ indent ++ s!"| .{ctor}"
        for arg in getExplicitArgs ci.type #[] do
          str := str ++ if arg.hasNum || arg.isInternal then " _" else s!" {arg}"
        str := str ++ s!" => {holeKindToHoleString info.elaborator ctor}"
      pure { eager with
        edit? := some <|.ofTextEdit doc.versionedIdentifier {
          range := doc.meta.text.utf8RangeToLspRange ⟨holePos, info.stx.getTailPos?.get!⟩
          newText := str
        }
      }
  }]

/-- Invoking hole code action "Start a tactic proof" will fill in a hole with `by done`. -/
@[hole_code_action] def startTacticStub : HoleCodeAction := fun _ _ _ info => do
  let holePos := info.stx.getPos?.get!
  let doc ← readDoc
  let indent := (findIndentAndIsStart doc.meta.text.source holePos).1
  pure #[{
    eager.title := "Start a tactic proof."
    eager.kind? := "quickfix"
    eager.edit? := some <|.ofTextEdit doc.versionedIdentifier {
      range := doc.meta.text.utf8RangeToLspRange ⟨holePos, info.stx.getTailPos?.get!⟩
      newText := "by\n".pushn ' ' (indent + 2) ++ "done"
    }
  }]

/-- The "Remove tactics after 'no goals'" code action deletes any tactics following a completed
proof.
```
example : True := by
  trivial
  trivial -- <- remove this, proof is already done
  rfl
```
is transformed to
```
example : True := by
  trivial
```
-/
@[tactic_code_action*]
def removeAfterDoneAction : TacticCodeAction := fun _ _ _ stk node => do
  let .node (.ofTacticInfo info) _ := node | return #[]
  unless info.goalsBefore.isEmpty do return #[]
  let _ :: (seq, i) :: _ := stk | return #[]
  let some stop := seq.getTailPos? | return #[]
  let some prev := (seq.setArgs seq.getArgs[:i]).getTailPos? | return #[]
  let doc ← readDoc
  let eager := {
    title := "Remove tactics after 'no goals'"
    kind? := "quickfix"
    isPreferred? := true
    edit? := some <|.ofTextEdit doc.versionedIdentifier {
      range := doc.meta.text.utf8RangeToLspRange ⟨prev, stop⟩
      newText := ""
    }
  }
  pure #[{ eager }]

/--
Similar to `getElimExprInfo`, but returns the names of binders instead of just the numbers;
intended for code actions which need to name the binders.
-/
def getElimExprNames (elimType : Expr) : MetaM (Array (Name × Array Name)) := do
  -- let inductVal ← getConstInfoInduct inductName
  -- let decl ← getConstInfo declName
  forallTelescopeReducing elimType fun xs type => do
    let motive  := type.getAppFn
    let targets := type.getAppArgs
    let motiveType ← inferType motive
    let mut altsInfo := #[]
    for _h : i in [:xs.size] do
      let x := xs[i]
      if x != motive && !targets.contains x then
        let xDecl ← x.fvarId!.getDecl
        if xDecl.binderInfo.isExplicit then
          let args ← forallTelescopeReducing xDecl.type fun args _ => do
            let lctx ← getLCtx
            pure <| args.filterMap fun y =>
              let yDecl := (lctx.find? y.fvarId!).get!
              if yDecl.binderInfo.isExplicit then some yDecl.userName else none
          altsInfo := altsInfo.push (xDecl.userName, args)
    pure altsInfo

/-- Finds the `TermInfo` for an elaborated term `stx`. -/
def findTermInfo? (node : InfoTree) (stx : Term) : Option TermInfo :=
  match node.findInfo? fun
    | .ofTermInfo i => i.stx.getKind == stx.raw.getKind && i.stx.getRange? == stx.raw.getRange?
    | _ => false
  with
  | some (.ofTermInfo info) => pure info
  | _ => none

/-- `findTermInfoWithCtx?` finds the `TermInfo` for an elaborated term `stx`
    and also updates the inputted `ContextInfo` using all the
    `PartialContextInfo` on the path to the returned `TermInfo`. -/
partial def findTermInfoWithCtx? (t : InfoTree) (stx : Term) (ctx : ContextInfo)
    : Option (TermInfo × ContextInfo) :=
  match t with
  | .context partialCtx t' =>
    -- Merge partial context with outer, fall back to outer if merge fails
    let ctx' := partialCtx.mergeIntoOuter? ctx |>.getD ctx
    findTermInfoWithCtx? t' stx ctx'
  | .node info children =>
    let optResult : Option (TermInfo × ContextInfo) :=
      match info with
      | .ofTermInfo i =>
        if i.stx.getKind == stx.raw.getKind && i.stx.getRange? == stx.raw.getRange? then
          some (i, ctx)
        else none
      | _ => none
    if let some res := optResult then
      return res
    else
      children.findSome? (findTermInfoWithCtx? · stx ctx)
  | .hole _ => none

/--
Invoking tactic code action "Generate an explicit pattern match for 'induction'" in the
following:
```lean
example (x : Nat) : x = x := by
  induction x
```
produces:
```lean
example (x : Nat) : x = x := by
  induction x with
  | zero => sorry
  | succ n ih => sorry
```

It also works for `cases`.
-/
@[tactic_code_action Parser.Tactic.cases Parser.Tactic.induction]
def casesExpand : TacticCodeAction := fun _ snap ctx _ node => do
  let .node (.ofTacticInfo info) _ := node | return #[]
  let (targets, induction, using_, alts) ← match info.stx with
    | `(tactic| cases $[$[$_ :]? $targets],* $[using $u]? $(alts)?) =>
      pure (targets, false, u, alts)
    | `(tactic| induction $[$[$_ :]? $targets],* $[using $u]? $[generalizing $_*]? $(alts)?) =>
      pure (targets, true, u, alts)
    | _ => return #[]
  let some discrInfos := targets.mapM (findTermInfo? node) | return #[]
  let some discr₀ := discrInfos[0]? | return #[]
  let mut some ctors ← discr₀.runMetaM ctx do
      let targets := discrInfos.map (·.expr)
      match using_ with
      | none =>
        if tactic.customEliminators.get (← getOptions) then
          if let some elimName ← getCustomEliminator? targets induction then
            return some (← getElimExprNames (← getConstInfo elimName).type)
        matchConstInduct (← whnf (← inferType discr₀.expr)).getAppFn
            (fun _ => failure) fun val _ => do
          let elimName := if induction then mkRecName val.name else mkCasesOnName val.name
          return some (← getElimExprNames (← getConstInfo elimName).type)
      | some u =>
        let some info := findTermInfo? node u | return none
        return some (← getElimExprNames (← inferType info.expr))
    | return #[]
  let mut fallback := none
  if let some alts := alts then
    if let `(Parser.Tactic.inductionAlts| with $(_)? $alts*) := alts then
      for alt in alts do
        match alt with
        | `(Parser.Tactic.inductionAlt| | _ $_* => $fb) => fallback := fb.raw.getRange?
        | `(Parser.Tactic.inductionAlt| | $id:ident $_* => $_) =>
          ctors := ctors.filter (fun x => x.1 != id.getId)
        | _ => pure ()
  if ctors.isEmpty then return #[]
  let tacName := info.stx.getKind.updatePrefix .anonymous
  let eager := {
    title := s!"Generate an explicit pattern match for '{tacName}'."
    kind? := "quickfix"
  }
  let doc ← readDoc
  pure #[{
    eager
    lazy? := some do
      let tacPos := info.stx.getPos?.get!
      let endPos := doc.meta.text.utf8PosToLspPos info.stx.getTailPos?.get!
      let indent := "\n".pushn ' ' (findIndentAndIsStart doc.meta.text.source tacPos).1
      let (startPos, str') := if alts.isSome then
        let stx' := if fallback.isSome then
          info.stx.modifyArg (if induction then 4 else 3)
            (·.modifyArg 0 (·.modifyArg 2 (·.modifyArgs (·.filter fun s =>
              !(s matches `(Parser.Tactic.inductionAlt| | _ $_* => $_))))))
        else info.stx
        (doc.meta.text.utf8PosToLspPos stx'.getTailPos?.get!, "")
      else (endPos, " with")
      let fallback := if let some ⟨startPos, endPos⟩ := fallback then
        String.Pos.Raw.extract doc.meta.text.source startPos endPos
      else
        "sorry"
      let newText := Id.run do
        let mut str := str'
        for (name, args) in ctors do
          let mut ctor := toString name
          if let some _ := (Parser.getTokenTable snap.env).find? ctor then
            ctor := s!"{idBeginEscape}{ctor}{idEndEscape}"
          str := str ++ indent ++ s!"| {ctor}"
          -- replace n_ih with just ih if there is only one
          let args := if induction &&
            args.foldl (fun c n =>
              if n.eraseMacroScopes.getString!.endsWith "_ih" then c+1 else c) 0 == 1
          then
            args.map (fun n => if !n.hasMacroScopes && n.getString!.endsWith "_ih" then `ih else n)
          else args
          for arg in args do
            str := str ++ if arg.hasNum || arg.isInternal then " _" else s!" {arg}"
          str := str ++ s!" => " ++ fallback
        str
      pure { eager with
        edit? := some <|.ofTextEdit doc.versionedIdentifier {
          range := ⟨startPos, endPos⟩
          newText
        }
      }
  }]

/-- The "Add subgoals" code action puts `· done` subgoals for any goals remaining at the end of a
proof.
```
example : True ∧ True := by
  constructor
  -- <- here
```
is transformed to
```
example : True ∧ True := by
  constructor
  · done
  · done
```
-/
def addSubgoalsActionCore (params : Lsp.CodeActionParams)
  (i : Nat) (stk : Syntax.Stack) (goals : List MVarId) : RequestM (Array LazyCodeAction) := do
  -- If there are zero goals remaining, no need to do anything
  -- If there is one goal remaining, the user can just keep typing and subgoals are not helpful
  unless goals.length > 1 do return #[]
  let seq := stk.head!.1
  let nargs := (seq.getNumArgs + 1) / 2
  unless i == nargs do -- only trigger at the end of a block
    -- or if there is only a `done` or `sorry` terminator
    unless i + 1 == nargs && [
        ``Parser.Tactic.done, ``Parser.Tactic.tacticSorry, ``Parser.Tactic.tacticAdmit
      ].contains seq[2*i].getKind do
      return #[]
  let some startPos := seq[0].getPos? true | return #[]
  let doc ← readDoc
  let eager := { title := "Add subgoals", kind? := "quickfix" }
  pure #[{
    eager
    lazy? := some do
      let indent := "\n".pushn ' ' (doc.meta.text.toPosition startPos).column
      let mut (range, newText) := (default, "")
      if let some tac := seq.getArgs[2*i]? then
        let some range2 := tac.getRange? true | return eager
        range := range2
      else
        let trimmed := seq.modifyArgs (·[:2*i])
        let some tail := trimmed.getTailPos? true | return eager
        (range, newText) := (⟨tail, tail⟩, indent)
        let cursor := doc.meta.text.lspPosToUtf8Pos params.range.end
        if range.stop ≤ cursor && cursor.1 ≤ range.stop.1 + trimmed.getTrailingSize then
          range := { range with stop := cursor }
      newText := newText ++ "· done"
      for _ in goals.tail! do
        newText := newText ++ indent ++ "· done"
      pure { eager with
        edit? := some <|.ofTextEdit doc.versionedIdentifier {
          range := doc.meta.text.utf8RangeToLspRange range
          newText
        }
      }
  }]

@[inherit_doc addSubgoalsActionCore, tactic_code_action]
def addSubgoalsSeqAction : TacticSeqCodeAction := fun params _ _ => addSubgoalsActionCore params

-- This makes sure that the addSubgoals action also triggers
-- when the cursor is on the final `done` of a tactic block
@[inherit_doc addSubgoalsActionCore,
  tactic_code_action Parser.Tactic.done Parser.Tactic.tacticSorry Parser.Tactic.tacticAdmit]
def addSubgoalsAction : TacticCodeAction := fun params _ _ stk node => do
  let (_ :: (seq, i) :: stk@(_ :: t :: _), .node (.ofTacticInfo info) _) := (stk, node) | return #[]
  unless t.1.getKind == ``Parser.Tactic.tacticSeq do return #[]
  addSubgoalsActionCore params (i/2) ((seq, 0) :: stk) info.goalsBefore


================================================
FILE: Batteries/CodeAction.lean
================================================
module

public import Batteries.CodeAction.Attr
public import Batteries.CodeAction.Basic
public import Batteries.CodeAction.Misc
public import Batteries.CodeAction.Match


================================================
FILE: Batteries/Control/AlternativeMonad.lean
================================================
/-
Copyright (c) 2025 Devon Tuma. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Devon Tuma
-/
module

public import Batteries.Control.Lemmas
public import Batteries.Control.OptionT
import all Init.Control.Option
import all Init.Control.State
import all Init.Control.Reader
import all Init.Control.StateRef

@[expose] public section


/-!
# Laws for Monads with Failure

Definitions for monads that also have an `Alternative` instance while sharing the underlying
`Applicative` instance, and a class `LawfulAlternative` for types where the `failure` and `orElse`
operations behave in a natural way. More specifically they satisfy:

* `f <$> failure = failure`
* `failure <*> x = failure`
* `x <|> failure = x`
* `failure <|> y = y`
* `x <|> y <|> z = (x <|> y) <|> z`
* `f <$> (x <|> y) = (f <$> x <|> f <$> y)`

`Option`/`OptionT` are the most basic examples, but transformers like `StateT` also preserve
the lawfulness of this on the underlying monad.

The law `x *> failure = failure` is true for monads like `Option` and `List` that don't
have any "side effects" to execution, but not for something like `OptionT` on some monads,
so we don't include this condition.

We also define a class `LawfulAlternativeLift` similar to `LawfulMonadLift` that states that
a lifting between monads preserves `failure` and `orElse`.

## Tags

monad, alternative, failure
-/

/-- `AlternativeMonad m` means that `m` has both a `Monad` and `Alternative` instance,
which both share the same underlying `Applicative` instance.
The main example is `Option`, but many monad transformers also preserve or add this structure. -/
class AlternativeMonad (m : Type _ → Type _) extends Alternative m, Monad m

section LawfulAlternative

/-- `LawfulAlternative m` means that the `failure` operation on `m` behaves naturally
with respect to `map`, `seq`, and `orElse` operators. -/
class LawfulAlternative (m : Type _ → Type _) [Alternative m] : Prop
    extends LawfulApplicative m where
  /-- Mapping the result of a failure is still a failure -/
  map_failure (f : α → β) : f <$> (failure : m α) = failure
  /-- Sequencing a `failure` call results in failure -/
  failure_seq (x : m α) : (failure : m (α → β)) <*> x = failure
  /-- `failure` is a right identity for `orElse`. -/
  orElse_failure (x : m α) : (x <|> failure) = x
  /-- `failure` is a left identity for `orElse`. -/
  failure_orElse (y : m α) : (failure <|> y) = y
  /-- `orElse` is associative. -/
  orElse_assoc (x y z : m α) : (x <|> y <|> z) = ((x <|> y) <|> z)
  /-- `map` commutes with `orElse`. The stronger statement with `bind` generally isn't true -/
  map_orElse (x y : m α) (f : α → β) : f <$> (x <|> y) = (f <$> x <|> f <$> y)

export LawfulAlternative (map_failure failure_seq orElse_failure failure_orElse orElse_assoc
  map_orElse)
attribute [simp] map_failure failure_seq orElse_failure failure_orElse map_orElse

section Alternative

@[simp] theorem mapConst_failure [Alternative m] [LawfulAlternative m] (y : β) :
    Functor.mapConst y (failure : m α) = failure := by
  rw [LawfulFunctor.map_const, Function.comp_apply, map_failure]

@[simp] theorem mapConst_orElse [Alternative m] [LawfulAlternative m] (x x' : m α) (y : β) :
    Functor.mapConst y (x <|> x') = (Functor.mapConst y x <|> Functor.mapConst y x') := by
  simp only [map_const, Function.comp_apply, map_orElse]

@[simp] theorem failure_seqLeft [Alternative m] [LawfulAlternative m] (x : m α) :
    (failure : m β) <* x = failure := by
  simp only [seqLeft_eq, map_failure, failure_seq]

@[simp] theorem failure_seqRight [Alternative m] [LawfulAlternative m] (x : m α) :
    (failure : m β) *> x = failure := by
  simp only [seqRight_eq, map_failure, failure_seq]

end Alternative

section AlternativeMonad

@[simp] theorem failure_bind [AlternativeMonad m] [LawfulAlternative m] [LawfulMonad m]
    (x : α → m β) : failure >>= x = failure := by
  calc failure >>= x = (PEmpty.elim <$> failure) >>= x := by rw [map_failure]
    _ = failure >>= (x ∘ PEmpty.elim) := by rw [bind_map_left, Function.comp_def]
    _ = failure >>= (pure ∘ PEmpty.elim) := bind_congr fun a => a.elim
    _ = (PEmpty.elim <$> failure) >>= pure := by rw [bind_map_left, Function.comp_def]
    _ = failure := by rw [map_failure, bind_pure]

@[simp] theorem seq_failure [AlternativeMonad m] [LawfulAlternative m] [LawfulMonad m]
    (x : m (α → β)) : x <*> failure = x *> failure := by
  simp only [seq_eq_bind_map, map_failure, seqRight_eq, bind_map_left]

end AlternativeMonad

end LawfulAlternative

/-- Type-class for monad lifts that preserve the `Alternative` operations. -/
class LawfulAlternativeLift (m : semiOutParam (Type u → Type v)) (n : Type u → Type w)
    [Alternative m] [Alternative n] [MonadLift m n] : Prop where
  /-- Lifting preserves `failure`. -/
  monadLift_failure : monadLift (failure : m α) = (failure : n α)
  /-- Lifting preserves `orElse`. -/
  monadLift_orElse (x y : m α) : monadLift (x <|> y) = (monadLift x <|> monadLift y : n α)

export LawfulAlternativeLift (monadLift_failure monadLift_orElse)
attribute [simp] monadLift_failure monadLift_orElse

namespace Option

instance : AlternativeMonad Option.{u} where

instance : LawfulAlternative Option.{u} where
  map_failure _ := rfl
  failure_seq _ := rfl
  orElse_failure x := by cases x <;> rfl
  failure_orElse := by simp [failure]
  orElse_assoc | some _, _, _ => rfl | none, _, _ => rfl
  map_orElse | some _ => by simp | none => by simp

end Option

namespace OptionT

instance (m) [Monad m] : AlternativeMonad (OptionT m) where

instance (m) [Monad m] [LawfulMonad m] : LawfulAlternative (OptionT m) where
  map_failure _ := pure_bind _ _
  failure_seq _ := pure_bind _ _
  orElse_failure x := (bind_congr (fun | some _ => rfl | none => rfl)).trans (bind_pure x)
  failure_orElse _ := pure_bind _ _
  orElse_assoc _ _ _ := by
    simp only [OptionT.ext_iff, run_orElse, Option.elimM, bind_assoc]
    refine bind_congr fun | some _ => by simp | none => rfl
  map_orElse x y f := by
    simp only [OptionT.ext_iff, run_map, run_orElse, map_bind, bind_map_left, Option.elimM]
    refine bind_congr fun | some _ => by simp | none => rfl

end OptionT

namespace StateT

instance (m) [AlternativeMonad m] : AlternativeMonad (StateT σ m) where

instance (m) [AlternativeMonad m] [LawfulAlternative m] [LawfulMonad m] :
    LawfulAlternative (StateT σ m) where
  map_failure _ := StateT.ext fun _ => by simp only [run_map, run_failure, map_failure]
  failure_seq _ := StateT.ext fun _ => by simp only [run_seq, run_failure, failure_bind]
  orElse_failure _ := StateT.ext fun _ => orElse_failure _
  failure_orElse _ := StateT.ext fun _ => failure_orElse _
  orElse_assoc _ _ _ := StateT.ext fun _ => orElse_assoc _ _ _
  map_orElse _ _ _ := StateT.ext fun _ => by simp only [run_map, run_orElse, map_orElse]

instance (m) [AlternativeMonad m] [LawfulAlternative m] [LawfulMonad m] :
    LawfulAlternativeLift m (StateT σ m) where
  monadLift_failure {α} := StateT.ext fun s => by simp
  monadLift_orElse {α} x y := StateT.ext fun s => by simp

end StateT

namespace ReaderT

instance [AlternativeMonad m] : AlternativeMonad (ReaderT ρ m) where

instance [AlternativeMonad m] [LawfulAlternative m] : LawfulAlternative (ReaderT ρ m) where
  map_failure _ := ReaderT.ext fun _ => map_failure _
  failure_seq _ := ReaderT.ext fun _ => failure_seq _
  orElse_failure _ := ReaderT.ext fun _ => orElse_failure _
  failure_orElse _ := ReaderT.ext fun _ => failure_orElse _
  orElse_assoc _ _ _ := ReaderT.ext fun _ => orElse_assoc _ _ _
  map_orElse _ _ _ := ReaderT.ext fun _ => by simp only [run_map, run_orElse, map_orElse]

instance [AlternativeMonad m] : LawfulAlternativeLift m (ReaderT ρ m) where
  monadLift_failure {α} := ReaderT.ext fun s => by simp
  monadLift_orElse {α} x y := ReaderT.ext fun s => by simp

end ReaderT

namespace StateRefT'

instance [AlternativeMonad m] : AlternativeMonad (StateRefT' ω σ m) where

instance [AlternativeMonad m] [LawfulAlternative m] :
    LawfulAlternative (StateRefT' ω σ m) :=
  inferInstanceAs (LawfulAlternative (ReaderT (ST.Ref ω σ) m))

instance [AlternativeMonad m] : LawfulAlternativeLift m (StateRefT' ω σ m) :=
  inferInstanceAs (LawfulAlternativeLift m (ReaderT (ST.Ref ω σ) m))

end StateRefT'


================================================
FILE: Batteries/Control/ForInStep/Basic.lean
================================================
/-
Copyright (c) 2022 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
module

@[expose] public section

/-! # Additional definitions on `ForInStep` -/

/--
This is similar to a monadic `bind` operator, except that the two type parameters have to be
the same, which prevents putting a monad instance on `ForInStepT m α := m (ForInStep α)`.
-/
@[inline] protected def ForInStep.bind [Monad m]
    (a : ForInStep α) (f : α → m (ForInStep α)) : m (ForInStep α) :=
  match a with
  | .done a => return .done a
  | .yield a => f a

@[inherit_doc ForInStep.bind] protected abbrev ForInStep.bindM [Monad m]
    (a : m (ForInStep α)) (f : α → m (ForInStep α)) : m (ForInStep α) := a >>= (·.bind f)

/--
Get the value out of a `ForInStep`.
This is usually done at the end of a `forIn` loop to scope the early exit to the loop body.
-/
@[inline] def ForInStep.run : ForInStep α → α
  | .done a
  | .yield a => a

/-- Applies function `f` to each element of a list to accumulate a `ForInStep` value. -/
def ForInStep.bindList [Monad m]
      (f : α → β → m (ForInStep β)) : List α → ForInStep β → m (ForInStep β)
  | [], s => pure s
  | a::l, s => s.bind fun b => f a b >>= (·.bindList f l)


================================================
FILE: Batteries/Control/ForInStep/Lemmas.lean
================================================
/-
Copyright (c) 2022 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
module

public import Batteries.Control.ForInStep.Basic

@[expose] public section

/-! # Additional theorems on `ForInStep` -/

@[simp] theorem ForInStep.bind_done [Monad m] (a : α) (f : α → m (ForInStep α)) :
    (ForInStep.done a).bind (m := m) f = pure (.done a) := rfl
@[simp] theorem ForInStep.bind_yield [Monad m] (a : α) (f : α → m (ForInStep α)) :
    (ForInStep.yield a).bind (m := m) f = f a := rfl

attribute [simp] ForInStep.bindM

@[simp] theorem ForInStep.run_done : (ForInStep.done a).run = a := rfl
@[simp] theorem ForInStep.run_yield : (ForInStep.yield a).run = a := rfl

@[simp] theorem ForInStep.bindList_nil [Monad m] (f : α → β → m (ForInStep β))
    (s : ForInStep β) : s.bindList f [] = pure s := rfl

@[simp] theorem ForInStep.bindList_cons [Monad m]
    (f : α → β → m (ForInStep β)) (s : ForInStep β) (a l) :
    s.bindList f (a::l) = s.bind fun b => f a b >>= (·.bindList f l) := rfl

@[simp] theorem ForInStep.done_bindList [Monad m]
    (f : α → β → m (ForInStep β)) (a l) :
    (ForInStep.done a).bindList f l = pure (.done a) := by cases l <;> simp

@[simp] theorem ForInStep.bind_yield_bindList [Monad m]
    (f : α → β → m (ForInStep β)) (s : ForInStep β) (l) :
    (s.bind fun a => (yield a).bindList f l) = s.bindList f l := by cases s <;> simp

@[simp] theorem ForInStep.bind_bindList_assoc [Monad m] [LawfulMonad m]
    (f : β → m (ForInStep β)) (g : α → β → m (ForInStep β)) (s : ForInStep β) (l) :
    s.bind f >>= (·.bindList g l) = s.bind fun b => f b >>= (·.bindList g l)  := by
  cases s <;> simp

theorem ForInStep.bindList_cons' [Monad m] [LawfulMonad m]
    (f : α → β → m (ForInStep β)) (s : ForInStep β) (a l) :
    s.bindList f (a::l) = s.bind (f a) >>= (·.bindList f l) := by simp

@[simp] theorem ForInStep.bindList_append [Monad m] [LawfulMonad m]
    (f : α → β → m (ForInStep β)) (s : ForInStep β) (l₁ l₂) :
    s.bindList f (l₁ ++ l₂) = s.bindList f l₁ >>= (·.bindList f l₂) := by
  induction l₁ generalizing s <;> simp [*]


================================================
FILE: Batteries/Control/ForInStep.lean
================================================
module

public import Batteries.Control.ForInStep.Basic
public import Batteries.Control.ForInStep.Lemmas


================================================
FILE: Batteries/Control/LawfulMonadState.lean
================================================
/-
Copyright (c) 2025 Devon Tuma. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Devon Tuma, Quang Dao
-/
module
import all Init.Control.StateRef

@[expose] public section

/-!
# Laws for Monads with State

This file defines a typeclass for `MonadStateOf` with compatible `get` and `set` operations.

Note that we use `MonadStateOf` over `MonadState` as the first induces the second,
but we phrase things using `MonadStateOf.set` and `MonadState.get` as those are the
versions that are available at the top level namespace.
-/

/-- The namespaced `MonadStateOf.get` is equal to the `MonadState` provided `get`. -/
@[simp] theorem monadStateOf_get_eq_get [MonadStateOf σ m] :
    (MonadStateOf.get : m σ) = get := rfl

/-- The namespaced `MonadStateOf.modifyGet` is equal to the `MonadState` provided `modifyGet`. -/
@[simp] theorem monadStateOf_modifyGet_eq_modifyGet [MonadStateOf σ m]
    (f : σ → α × σ) : (MonadStateOf.modifyGet f : m α) = modifyGet f := rfl

@[simp] theorem liftM_get {m n}  [MonadStateOf σ m] [MonadLift m n] :
    (liftM (get (m := m)) : n _) = get := rfl

@[simp] theorem liftM_set {m n} [MonadStateOf σ m] [MonadLift m n]
    (s : σ) : (liftM (set (m := m) s) : n _) = set s := rfl

@[simp] theorem liftM_modify {m n} [MonadStateOf σ m] [MonadLift m n]
    (f : σ → σ) : (liftM (modify (m := m) f) : n _) = modify f := rfl

@[simp] theorem liftM_modifyGet {m n} [MonadStateOf σ m] [MonadLift m n]
    (f : σ → α × σ) : (liftM (modifyGet (m := m) f) : n _) = modifyGet f := rfl

@[simp] theorem liftM_getModify {m n} [MonadStateOf σ m] [MonadLift m n]
    (f : σ → σ) : (liftM (getModify (m := m) f) : n _) = getModify f := rfl

/-- Class for well behaved state monads, extending the base `MonadState` type.
Requires that `modifyGet` is equal to the same definition with only `get` and `set`,
that `get` is idempotent if the result isn't used, and that `get` after `set` returns
exactly the value that was previously `set`. -/
class LawfulMonadStateOf (σ : semiOutParam (Type _)) (m : Type _ → Type _)
    [Monad m] [MonadStateOf σ m] extends LawfulMonad m where
  /-- `modifyGet f` is equal to getting the state, modifying it, and returning a result. -/
  modifyGet_eq {α} (f : σ → α × σ) :
    modifyGet (m := m) f = do let z ← f <$> get; set z.2; return z.1
  /-- Discarding the result of `get` is the same as never getting the state. -/
  get_bind_const {α} (mx : m α) : (do let _ ← get; mx) = mx
  /-- Calling `get` twice is the same as just using the first retreived state value. -/
  get_bind_get_bind {α} (mx : σ → σ → m α) :
    (do let s ← get; let s' ← get; mx s s') = (do let s ← get; mx s s)
  /-- Setting the monad state to its current value has no effect. -/
  get_bind_set_bind {α} (mx : σ → PUnit → m α) :
    (do let s ← get; let u ← set s; mx s u) = (do let s ← get; mx s PUnit.unit)
  /-- Setting and then returning the monad state is the same as returning the set value. -/
  set_bind_get (s : σ) : (do set (m := m) s; get) = (do set s; return s)
  /-- Setting the monad twice is the same as just setting to the final state. -/
  set_bind_set (s s' : σ) : (do set (m := m) s; set s') = set s'

namespace LawfulMonadStateOf

variable {σ : Type _} {m : Type _ → Type _} [Monad m]
  [MonadStateOf σ m] [LawfulMonadStateOf σ m]

attribute [simp] get_bind_const get_bind_get_bind get_bind_set_bind set_bind_get set_bind_set

@[simp] theorem get_seqRight (mx : m α) : get *> mx = mx := by
  rw [seqRight_eq_bind, get_bind_const]

@[simp] theorem seqLeft_get (mx : m α) : mx <* get = mx := by
  simp only [seqLeft_eq_bind, get_bind_const, bind_pure]

@[simp] theorem get_map_const (x : α) :
    (fun _ => x) <$> get (m := m) = pure x := by
  rw [map_eq_pure_bind, get_bind_const]

theorem get_bind_get : (do let _ ← get (m := m); get) = get := get_bind_const get

@[simp] theorem get_bind_set :
    (do let s ← get (m := m); set s) = return PUnit.unit := by
  simpa only [bind_pure_comp, id_map', get_map_const] using
    get_bind_set_bind (σ := σ) (m := m) (fun _ _ => return PUnit.unit)

@[simp] theorem get_bind_map_set (f : σ → PUnit → α) :
    (do let s ← get (m := m); f s <$> set s) = (do return f (← get) PUnit.unit) := by
  simp [map_eq_pure_bind, -bind_pure_comp]

@[simp] theorem set_bind_get_bind (s : σ) (f : σ → m α) :
    (do set s; let s' ← get; f s') = (do set s; f s) := by
  rw [← bind_assoc, set_bind_get, bind_assoc, pure_bind]

@[simp] theorem set_bind_map_get (f : σ → α) (s : σ) :
    (do set (m := m) s; f <$> get) = (do set (m := m) s; pure (f s)) := by
  simp [map_eq_pure_bind, -bind_pure_comp]

@[simp] theorem set_bind_set_bind (s s' : σ) (mx : m α) :
    (do set s; set s'; mx) = (do set s'; mx) := by
  rw [← bind_assoc, set_bind_set]

@[simp] theorem set_bind_map_set (s s' : σ) (f : PUnit → α) :
    (do set (m := m) s; f <$> set s') = (do f <$> set s') := by
  simp [map_eq_pure_bind, ← bind_assoc, -bind_pure_comp]

section modify

theorem modifyGetThe_eq (f : σ → α × σ) :
    modifyGetThe σ (m := m) f = do let z ← f <$> get; set z.2; return z.1 := modifyGet_eq f

theorem modify_eq (f : σ → σ) :
    modify (m := m) f = (do set (f (← get))) := by simp [modify, modifyGet_eq]

theorem modifyThe_eq (f : σ → σ) :
    modifyThe σ (m := m) f = (do set (f (← get))) := modify_eq f

theorem getModify_eq (f : σ → σ) :
    getModify (m := m) f = do let s ← get; set (f s); return s := by
  rw [getModify, modifyGet_eq, bind_map_left]

/-- Version of `modifyGet_eq` that preserves an call to `modify`. -/
theorem modifyGet_eq' (f : σ → α × σ) :
    modifyGet (m := m) f = do let s ← get; modify (Prod.snd ∘ f); return (f s).fst := by
  simp [modify_eq, modifyGet_eq]

@[simp] theorem modify_id : modify (m := m) id = pure PUnit.unit := by
  simp [modify_eq]

@[simp] theorem getModify_id : getModify (m := m) id = get := by
  simp [getModify_eq]

@[simp] theorem set_bind_modify (s : σ) (f : σ → σ) :
    (do set (m := m) s; modify f) = set (f s) := by simp [modify_eq]

@[simp] theorem set_bind_modify_bind (s : σ) (f : σ → σ) (mx : PUnit → m α) :
    (do set s; let u ← modify f; mx u) = (do set (f s); mx PUnit.unit) := by
  simp [modify_eq, ← bind_assoc]

@[simp] theorem set_bind_modifyGet (s : σ) (f : σ → α × σ) :
    (do set (m := m) s; modifyGet f) = (do set (f s).2; return (f s).1) := by simp [modifyGet_eq]

@[simp] theorem set_bind_modifyGet_bind (s : σ) (f : σ → α × σ) (mx : α → m β) :
    (do set s; let x ← modifyGet f; mx x) = (do set (f s).2; mx (f s).1) := by simp [modifyGet_eq]

@[simp] theorem set_bind_getModify (s : σ) (f : σ → σ) :
    (do set (m := m) s; getModify f) = (do set (f s); return s) := by simp [getModify_eq]

@[simp] theorem set_bind_getModify_bind (s : σ) (f : σ → σ) (mx : σ → m α) :
    (do set s; let x ← getModify f; mx x) = (do set (f s); mx s) := by
  simp [getModify_eq, ← bind_assoc]

@[simp] theorem modify_bind_modify (f g : σ → σ) :
    (do modify (m := m) f; modify g) = modify (g ∘ f) := by simp [modify_eq]

@[simp] theorem modify_bind_modifyGet (f : σ → σ) (g : σ → α × σ) :
    (do modify (m := m) f; modifyGet g) = modifyGet (g ∘ f) := by
  simp [modify_eq, modifyGet_eq]

@[simp] theorem getModify_bind_modify (f : σ → σ) (g : σ → σ → σ) :
    (do let s ← getModify (m := m) f; modify (g s)) =
      (do let s ← get; modify (g s ∘ f)) := by
  simp [modify_eq, getModify_eq]

theorem modify_comm_of_comp_comm {f g : σ → σ} (h : f ∘ g = g ∘ f) :
    (do modify (m := m) f; modify g) = (do modify (m := m) g; modify f) := by
  simp [modify_bind_modify, h]

theorem modify_bind_get (f : σ → σ) :
    (do modify (m := m) f; get) = (do let s ← get; modify f; return (f s)) := by
  simp [modify_eq]

end modify

/-- `StateT` has lawful state operations if the underlying monad is lawful.
This is applied for `StateM` as well due to the reducibility of that definition. -/
instance {m σ} [Monad m] [LawfulMonad m] : LawfulMonadStateOf σ (StateT σ m) where
  modifyGet_eq f := StateT.ext fun s => by simp
  get_bind_const mx := StateT.ext fun s => by simp
  get_bind_get_bind mx := StateT.ext fun s => by simp
  get_bind_set_bind mx := StateT.ext fun s => by simp
  set_bind_get s := StateT.ext fun s => by simp
  set_bind_set s s' := StateT.ext fun s => by simp

/-- The continuation passing state monad variant `StateCpsT` always has lawful state instance. -/
instance {σ m} : LawfulMonadStateOf σ (StateCpsT σ m) where
  modifyGet_eq _ := rfl
  get_bind_const _ := rfl
  get_bind_get_bind _ := rfl
  get_bind_set_bind _ := rfl
  set_bind_get _ := rfl
  set_bind_set _ _ := rfl

/-- The `EStateM` monad always has a lawful state instance. -/
instance {σ ε} : LawfulMonadStateOf σ (EStateM ε σ) where
  modifyGet_eq _ := rfl
  get_bind_const _ := rfl
  get_bind_get_bind _ := rfl
  get_bind_set_bind _ := rfl
  set_bind_get _ := rfl
  set_bind_set _ _ := rfl

/-- If the underlying monad `m` has a lawful state instance, then the induced state instance on
`ReaderT ρ m` will also be lawful. -/
instance {m σ ρ} [Monad m] [LawfulMonad m] [MonadStateOf σ m] [LawfulMonadStateOf σ m] :
    LawfulMonadStateOf σ (ReaderT ρ m) where
  modifyGet_eq f := ReaderT.ext fun ctx => by
    simp [← liftM_modifyGet, LawfulMonadStateOf.modifyGet_eq, ← liftM_get]
  get_bind_const mx := ReaderT.ext fun ctx => by
    simp [← liftM_get]
  get_bind_get_bind mx := ReaderT.ext fun ctx => by
    simp [← liftM_get]
  get_bind_set_bind mx := ReaderT.ext fun ctx => by
    simp [← liftM_get, ← liftM_set]
  set_bind_get s := ReaderT.ext fun ctx => by
    simp [← liftM_get, ← liftM_set]
  set_bind_set s s' := ReaderT.ext fun ctx => by
    simp [← liftM_set]

instance {m ω σ} [Monad m] [LawfulMonad m] [MonadStateOf σ m] [LawfulMonadStateOf σ m] :
    LawfulMonadStateOf σ (StateRefT' ω σ m) :=
  inferInstanceAs (LawfulMonadStateOf σ (ReaderT (ST.Ref ω σ) m))


================================================
FILE: Batteries/Control/Lemmas.lean
================================================
/-
Copyright (c) 2023 François G. Dorais. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: François G. Dorais, Eric Wieser
-/
module
import all Init.Control.Reader
import all Init.Control.State

@[expose] public section

namespace ReaderT

attribute [ext] ReaderT.ext

@[simp] theorem run_failure [Monad m] [Alternative m] (ctx : ρ) :
    (failure : ReaderT ρ m α).run ctx = failure := (rfl)

@[simp] theorem run_orElse [Monad m] [Alternative m] (x y : ReaderT ρ m α) (ctx : ρ) :
    (x <|> y).run ctx = (x.run ctx <|> y.run ctx) := (rfl)

@[simp] theorem run_throw [MonadExceptOf ε m] (e : ε) (ctx : ρ) :
    (throw e : ReaderT ρ m α).run ctx = throw e := rfl

@[simp] theorem run_throwThe [MonadExceptOf ε m] (e : ε) (ctx : ρ) :
    (throwThe ε e : ReaderT ρ m α).run ctx = throwThe ε e := rfl

@[simp] theorem run_tryCatch [MonadExceptOf ε m]
    (body : ReaderT ρ m α) (handler : ε → ReaderT ρ m α) (ctx : ρ) :
    (tryCatch body handler).run ctx = tryCatch (body.run ctx) (handler · |>.run ctx) := rfl

@[simp] theorem run_tryCatchThe [MonadExceptOf ε m]
    (body : ReaderT ρ m α) (handler : ε → ReaderT ρ m α) (ctx : ρ) :
    (tryCatchThe ε body handler).run ctx = tryCatchThe ε (body.run ctx) (handler · |>.run ctx) :=
  rfl

end ReaderT

namespace StateT

attribute [ext] StateT.ext

@[simp] theorem run_failure {α σ} [Monad m] [Alternative m] (s : σ) :
    (failure : StateT σ m α).run s = failure := (rfl)

@[simp] theorem run_orElse {α σ} [Monad m] [Alternative m] (x y : StateT σ m α) (s : σ) :
    (x <|> y).run s = (x.run s <|> y.run s) := (rfl)

@[simp] theorem run_throw [Monad m] [MonadExceptOf ε m] (e : ε) (s : σ) :
    (throw e : StateT σ m α).run s = (do let a ← throw e; pure (a, s)) := rfl

@[simp] theorem run_throwThe [Monad m] [MonadExceptOf ε m] (e : ε) (s : σ) :
    (throwThe ε e : StateT σ m α).run s = (do let a ← throwThe ε e; pure (a, s)) := rfl

@[simp] theorem run_tryCatch [Monad m] [MonadExceptOf ε m]
    (body : StateT σ m α) (handler : ε → StateT σ m α) (s : σ) :
    (tryCatch body handler).run s = tryCatch (body.run s) (handler · |>.run s) := rfl

@[simp] theorem run_tryCatchThe [Monad m] [MonadExceptOf ε m]
    (body : StateT σ m α) (handler : ε → StateT σ m α) (s : σ) :
    (tryCatchThe ε body handler).run s = tryCatchThe ε (body.run s) (handler · |>.run s) := rfl

end StateT


================================================
FILE: Batteries/Control/Monad.lean
================================================
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module

public import Batteries.Tactic.Alias

@[expose] public section

@[deprecated (since := "2025-02-09")] alias LawfulFunctor.map_inj_right_of_nonempty :=
map_inj_right_of_nonempty
@[deprecated (since := "2025-02-09")] alias LawfulMonad.map_inj_right :=
map_inj_right


================================================
FILE: Batteries/Control/Nondet/Basic.lean
================================================
/-
Copyright (c) 2023 Kim Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module

public import Batteries.Tactic.Lint.Misc
public import Batteries.Data.MLList.Basic
import Lean.Util.MonadBacktrack

@[expose] public section

/-!
# A nondeterminism monad.

We represent nondeterministic values in a type `α` as a single field structure containing an
`MLList m (σ × α)`, i.e. as a monadic lazy list of possible values,
each equipped with the backtrackable state
required to run further computations in the ambient monad.

We provide an `Alternative` `Monad` instance, as well as functions `bind`, `mapM`, and `filterMapM`,
and functions `singletonM`, `ofListM`, `ofOptionM`, and `firstM`
for entering and leaving the nondeterministic world.

Operations on the nondeterministic value via `bind`, `mapM`, and `filterMapM`
run with the appropriate backtrackable state, and are responsible for updating the state themselves
(typically this doesn't need to be done explicitly,
but just happens as a side effect in the monad `m`).
-/

open Lean (MonadBacktrack)
open Lean.MonadBacktrack

/--
`Nondet m α` is variation on `MLList m α` suitable for use with backtrackable monads `m`.

We think of `Nondet m α` as a nondeterministic value in `α`,
with the possible alternatives stored in a monadic lazy list.

Along with each `a : α` we store the backtrackable state, and ensure that monadic operations
on alternatives run with the appropriate state.

Operations on the nondeterministic value via `bind`, `mapM`, and `filterMapM`
run with the appropriate backtrackable state, and are responsible for updating the state themselves
(typically this doesn't need to be done explicitly,
but just happens as a side effect in the monad `m`).
-/
@[nolint unusedArguments]
structure Nondet (m : Type → Type) [MonadBacktrack σ m] (α : Type) : Type where
  /--
  Convert a non-deterministic value into a lazy list, keeping the backtrackable state.
  Be careful that monadic operations on the `MLList` will not respect this state!
  -/
  toMLList : MLList m (α × σ)

namespace Nondet
variable {m : Type → Type}

section Monad
variable [Monad m] [MonadBacktrack σ m]

/-- The empty nondeterministic value. -/
def nil : Nondet m α := .mk .nil

instance : Inhabited (Nondet m α) := ⟨.nil⟩

/--
Squash a monadic nondeterministic value to a nondeterministic value.
-/
def squash (L : Unit → m (Nondet m α)) : Nondet m α :=
  .mk <| MLList.squash fun _ => return (← L ()).toMLList

/--
Bind a nondeterministic function over a nondeterministic value,
ensuring the function is run with the relevant backtrackable state at each value.
-/
partial def bind (L : Nondet m α) (f : α → Nondet m β) : Nondet m β := .squash fun _ => do
  match ← L.toMLList.uncons with
  | none => pure .nil
  | some (⟨x, s⟩, xs) => do
    let r := (Nondet.mk xs).bind f
    restoreState s
    match ← (f x).toMLList.uncons with
    | none => return r
    | some (y, ys) => return .mk <| .cons y (ys.append (fun _ => r.toMLList))

/-- Convert any value in the monad to the singleton nondeterministic value. -/
def singletonM (x : m α) : Nondet m α :=
  .mk <| .singletonM do
    let a ← x
    return (a, ← saveState)

/-- Convert a value to the singleton nondeterministic value. -/
def singleton (x : α) : Nondet m α := singletonM (pure x)

/-- `Nondet m` is an alternative monad. -/
instance : AlternativeMonad (Nondet m) where
  pure a := singletonM (pure a)
  bind := bind
  failure := .nil
  orElse x y := .mk <| x.toMLList.append fun _ => (y ()).toMLList

instance : MonadLift m (Nondet m) where
  monadLift := singletonM

/--
Lift a list of monadic values to a nondeterministic value.
We ensure that each monadic value is evaluated with the same backtrackable state.
-/
def ofListM (L : List (m α)) : Nondet m α :=
  .squash fun _ => do
    let s ← saveState
    return .mk <| MLList.ofListM <| L.map fun x => do
      restoreState s
      let a ← x
      pure (a, ← saveState)

/--
Lift a list of values to a nondeterministic value.
(The backtrackable state in each will be identical:
whatever the state was when we first read from the result.)
-/
def ofList (L : List α) : Nondet m α := ofListM (L.map pure)

/-- Apply a function which returns values in the monad to every alternative of a `Nondet m α`. -/
def mapM (f : α → m β) (L : Nondet m α) : Nondet m β :=
  L.bind fun a => singletonM (f a)

/-- Apply a function to each alternative in a `Nondet m α` . -/
def map (f : α → β) (L : Nondet m α) : Nondet m β :=
  L.mapM fun a => pure (f a)

/-- Convert a monadic optional value to a nondeterministic value. -/
def ofOptionM (x : m (Option α)) : Nondet m α := .squash fun _ => do
  match ← x with
  | none => return .nil
  | some a => return singleton a

/-- Convert an optional value to a nondeterministic value. -/
def ofOption (x : Option α) : Nondet m α := ofOptionM (pure x)

/-- Filter and map a nondeterministic value using a monadic function which may return `none`. -/
def filterMapM (f : α → m (Option β)) (L : Nondet m α) : Nondet m β :=
  L.bind fun a => ofOptionM (f a)

/-- Filter and map a nondeterministic value. -/
def filterMap (f : α → Option β) (L : Nondet m α) : Nondet m β :=
  L.filterMapM fun a => pure (f a)

/-- Filter a nondeterministic value using a monadic predicate. -/
def filterM (p : α → m (ULift Bool)) (L : Nondet m α) : Nondet m α :=
  L.filterMapM fun a => do
    if (← p a).down then
      pure (some a)
    else
      pure none

/-- Filter a nondeterministic value. -/
def filter (p : α → Bool) (L : Nondet m α) : Nondet m α :=
  L.filterM fun a => pure <| .up (p a)

/--
All iterations of a non-deterministic function on an initial value.

(That is, depth first search.)
-/
partial def iterate (f : α → Nondet m α) (a : α) : Nondet m α :=
  singleton a <|> (f a).bind (iterate f)

/--
Convert a non-deterministic value into a lazy list, by discarding the backtrackable state.
-/
def toMLList' (L : Nondet m α) : MLList m α := L.toMLList.map (·.1)

/--
Convert a non-deterministic value into a list in the monad, retaining the backtrackable state.
-/
def toList (L : Nondet m α) : m (List (α × σ)) := L.toMLList.force

/--
Convert a non-deterministic value into a list in the monad, by discarding the backtrackable state.
-/
def toList' (L : Nondet m α) : m (List α) := L.toMLList.map (·.1) |>.force

end Monad

section AlternativeMonad
variable [AlternativeMonad m] [MonadBacktrack σ m]

/--
Find the first alternative in a nondeterministic value, as a monadic value.
-/
def head (L : Nondet m α) : m α := do
  let (x, s) ← L.toMLList.head
  restoreState s
  return x

/--
Find the value of a monadic function on the first alternative in a nondeterministic value
where the function succeeds.
-/
def firstM (L : Nondet m α) (f : α → m (Option β)) : m β :=
  L.filterMapM f |>.head

end AlternativeMonad

end Nondet

/-- The `Id` monad is trivially backtrackable, with state `Unit`. -/
-- This is useful so we can use `Nondet Id α` instead of `List α`
-- as the basic non-determinism monad.
instance : MonadBacktrack Unit Id where
  saveState := pure ()
  restoreState _ := pure ()


================================================
FILE: Batteries/Control/OptionT.lean
================================================
/-
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sebastian Ullrich
-/
module

public import Batteries.Control.LawfulMonadState
import all Init.Control.Option

@[expose] public section

/-!
# Lemmas About Option Monad Transformer

This file contains lemmas about the behavior of `OptionT` and `OptionT.run`.
-/

namespace OptionT

@[simp] theorem run_monadLift [Monad m] [LawfulMonad m] [MonadLiftT n m] (x : n α) :
    (monadLift x : OptionT m α).run = some <$> (monadLift x : m α) := (map_eq_pure_bind _ _).symm

@[simp] theorem run_mapConst [Monad m] [LawfulMonad m] (x : OptionT m α) (y : β) :
    (Functor.mapConst y x).run = Option.map (Function.const α y) <$> x.run := run_map _ _

instance [Monad m] [LawfulMonad m] [MonadStateOf σ m] [LawfulMonadStateOf σ m] :
    LawfulMonadStateOf σ (OptionT m) where
  modifyGet_eq f := by simp [← liftM_modifyGet, ← liftM_get, LawfulMonadStateOf.modifyGet_eq]
  get_bind_const mx := OptionT.ext (by simp [← liftM_get])
  get_bind_get_bind mx := OptionT.ext (by simp [← liftM_get])
  get_bind_set_bind mx := OptionT.ext (by simp [← liftM_get, ← liftM_set])
  set_bind_get s := OptionT.ext (by simp [← liftM_get, ← liftM_set])
  set_bind_set s s' := OptionT.ext (by simp [← liftM_set])

end OptionT


================================================
FILE: Batteries/Data/Array/Basic.lean
================================================
/-
Copyright (c) 2021 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Arthur Paulino, Floris van Doorn, Jannis Limperg
-/
module
import Batteries.Tactic.Alias
import Batteries.Data.UInt

@[expose] public section

/-!
## Definitions on Arrays

This file contains various definitions on `Array`. It does not contain
proofs about these definitions, those are contained in other files in `Batteries.Data.Array`.
-/

namespace Array

/--
Check whether `xs` and `ys` are equal as sets, i.e. they contain the same
elements when disregarding order and duplicates. `O(n*m)`! If your element type
has an `Ord` instance, it is asymptotically more efficient to sort the two
arrays, remove duplicates and then compare them elementwise.
-/
def equalSet [BEq α] (xs ys : Array α) : Bool :=
  xs.all (ys.contains ·) && ys.all (xs.contains ·)

/--
Returns the first minimal element among `d` and elements of the array.
If `start` and `stop` are given, only the subarray `xs[start...stop]` is
considered (in addition to `d`).
-/
@[inline]
protected def rangeMinWith [ord : Ord α]
    (xs : Array α) (d : α) (start := 0) (stop := xs.size) : α :=
  xs.foldl (init := d) (start := start) (stop := stop) fun min x =>
    if compare x min |>.isLT then x else min

@[inherit_doc Array.rangeMinWith, deprecated Array.rangeMinWith (since := "2026-01-08")]
protected def minWith := @Array.rangeMinWith

/--
Find the first minimal element of an array. If the array is empty, `d` is
returned. If `start` and `stop` are given, only the subarray `xs[start...stop]` is
considered.
-/
@[inline]
protected def rangeMinD [ord : Ord α]
    (xs : Array α) (d : α) (start := 0) (stop := xs.size) : α :=
  if h: start < xs.size ∧ start < stop then
    xs.rangeMinWith xs[start] (start + 1) stop
  else
    d

@[inherit_doc Array.rangeMinD, deprecated Array.rangeMinD (since := "2026-01-08")]
protected def minD := @Array.rangeMinD

/--
Find the first minimal element of an array. If the array is empty, `none` is
returned. If `start` and `stop` are given, only the subarray `xs[start...stop]` is
considered.
-/
@[inline]
protected def rangeMin? [ord : Ord α]
    (xs : Array α) (start := 0) (stop := xs.size) : Option α :=
  if h : start < xs.size ∧ start < stop then
    some $ xs.rangeMinD xs[start] start stop
  else
    none

/--
Find the first minimal element of an array. If the array is empty, `default` is
returned. If `start` and `stop` are given, only the subarray `xs[start...stop]` is
considered.
-/
@[inline]
protected def rangeMinI [ord : Ord α] [Inhabited α]
    (xs : Array α) (start := 0) (stop := xs.size) : α :=
  xs.rangeMinD default start stop

@[inherit_doc Array.rangeMinI, deprecated Array.rangeMinI (since := "2026-01-08")]
protected def minI := @Array.rangeMinI

/--
Returns the first maximal element among `d` and elements of the array.
If `start` and `stop` are given, only the subarray `xs[start...stop]` is
considered (in addition to `d`).
-/
@[inline]
protected def rangeMaxWith [ord : Ord α]
    (xs : Array α) (d : α) (start := 0) (stop := xs.size) : α :=
  xs.rangeMinWith (ord := ord.opposite) d start stop

@[inherit_doc Array.rangeMaxWith, deprecated Array.rangeMaxWith (since := "2026-01-08")]
protected def maxWith := @Array.rangeMaxWith

/--
Find the first maximal element of an array. If the array is empty, `d` is
returned. If `start` and `stop` are given, only the subarray `xs[start...stop]` is
considered.
-/
@[inline]
protected def rangeMaxD [ord : Ord α]
    (xs : Array α) (d : α) (start := 0) (stop := xs.size) : α :=
  xs.rangeMinD (ord := ord.opposite) d start stop

@[inherit_doc Array.rangeMaxD, deprecated Array.rangeMaxD (since := "2026-01-08")]
protected def maxD := @Array.rangeMaxD

/--
Find the first maximal element of an array. If the array is empty, `none` is
returned. If `start` and `stop` are given, only the subarray `xs[start...stop]` is
considered.
-/
@[inline]
protected def rangeMax? [ord : Ord α]
    (xs : Array α) (start := 0) (stop := xs.size) : Option α :=
  xs.rangeMin? (ord := ord.opposite) start stop

/--
Find the first maximal element of an array. If the array is empty, `default` is
returned. If `start` and `stop` are given, only the subarray `xs[start...stop]` is
considered.
-/
@[inline]
protected def rangeMaxI [ord : Ord α] [Inhabited α]
    (xs : Array α) (start := 0) (stop := xs.size) : α :=
  xs.rangeMinI (ord := ord.opposite) start stop

@[inherit_doc Array.rangeMaxI, deprecated Array.rangeMaxI (since := "2026-01-08")]
protected def maxI := @Array.rangeMaxI

@[deprecated set (since := "2026-02-02")]
alias setN := set

/-
This is guaranteed by the Array docs but it is unprovable.
May be asserted to be true in an unsafe context via `Array.unsafe_sizeFitsUsize`
-/
private abbrev SizeFitsUSize (a : Array α) : Prop := a.size < USize.size

/-
This is guaranteed by the Array docs but it is unprovable.
Can be used in unsafe functions to write more efficient implementations
that avoid arbitrary precision integer arithmetic.
-/
private unsafe def unsafe_sizeFitsUSize (a : Array α) : SizeFitsUSize a :=
  lcProof

@[inline]
private def scanlMFast [Monad m] (f : β → α → m β) (init : β) (as : Array α)
    (start := 0) (stop := as.size) : m (Array β) :=
  let stop := min stop as.size
  let start := min start as.size
  loop f init as
    (start := USize.ofNat start) (stop := USize.ofNat stop)
    (h_stop := by grind only [USize.size_eq, USize.ofNat_eq_iff_mod_eq_toNat, = Nat.min_def])
    (acc := Array.mkEmpty <| stop - start + 1)
where
  @[specialize]
  loop (f : β → α → m β) (init: β) (as: Array α) (start stop : USize)
       (h_stop : stop.toNat ≤ as.size) (acc : Array β) : m (Array β) := do
    if h_lt: start < stop then
      let next ← f init (as.uget start <| Nat.lt_of_lt_of_le h_lt h_stop)
      loop f next as (start + 1) stop h_stop (acc.push init)
    else
      pure <| acc.push init
  termination_by stop.toNat - min start.toNat stop.toNat
  decreasing_by
      have : start < (start + 1) := by grind only [USize.size_eq]
      grind only [Nat.min_def, USize.lt_iff_toNat_lt]

/--
Folds a monadic function over an array from the left, accumulating the partial results starting with
`init`. The accumulated value is combined with the each element of the list in order, using `f`.

The optional parameters `start` and `stop` control the region of the array to be folded. Folding
proceeds from `start` (inclusive) to `stop` (exclusive), so no folding occurs unless `start < stop`.
By default, the entire array is folded.

Examples:
```lean example
example [Monad m] (f : α → β → m α) :
    Array.scanlM f x₀ #[a, b, c] = (do
      let x₁ ← f x₀ a
      let x₂ ← f x₁ b
      let x₃ ← f x₂ c
      pure #[x₀, x₁, x₂, x₃])
    := by simp [scanlM, scanlM.loop]
```

```lean example
example [Monad m] (f : α → β → m α) :
    Array.scanlM f x₀ #[a, b, c] (start := 1) (stop := 3) = (do
      let x₁ ← f x₀ b
      let x₂ ← f x₁ c
      pure #[x₀, x₁, x₂])
    := by simp [scanlM, scanlM.loop]
```
-/
@[implemented_by scanlMFast]
def scanlM [Monad m] (f : β → α → m β) (init : β) (as : Array α) (start := 0)
    (stop := as.size) : m (Array β) :=
  loop f init as (min start as.size) (min stop as.size) (Nat.min_le_right _ _) #[]
where
  /-- auxiliary tail-recursive function for scanlM -/
  loop (f : β → α → m β) (init : β ) (as : Array α) (start stop : Nat)
       (h_stop : stop ≤ as.size) (acc : Array β) : m (Array β) := do
    if h_lt : start < stop then
      loop f (← f init as[start]) as (start + 1) stop h_stop (acc.push init)
    else
      pure <| acc.push init

private theorem scanlM_loop_eq_scanlMFast_loop [Monad m]
    {f : β → α → m β} {init : β} {as : Array α} {h_size : as.SizeFitsUSize}
    {start stop : Nat} {h_start : start ≤ as.size}
    {h_stop : stop ≤ as.size} {acc : Array β} :
    scanlM.loop f init as start stop h_stop acc
      = scanlMFast.loop f init as (USize.ofNat start) (USize.ofNat stop)
      (by rw [USize.toNat_ofNat_of_le_of_lt h_size h_stop]; exact h_stop) acc := by
  generalize h_n : stop - start = n
  induction n using Nat.strongRecOn generalizing start acc init
  rename_i n ih
  rw [scanlM.loop, scanlMFast.loop]
  have h_stop_usize := USize.toNat_ofNat_of_le_of_lt h_size h_stop
  have h_start_usize := USize.toNat_ofNat_of_le_of_lt h_size h_start
  split
  case isTrue h_lt =>
    simp_all only [USize.toNat_ofNat', ↓reduceDIte, uget,
      show USize.ofNat start < USize.ofNat stop by simp_all [USize.lt_iff_toNat_lt]]
    apply bind_congr
    intro next
    have h_start_succ : USize.ofNat start + 1 = USize.ofNat (start + 1) := by
      simp_all only [← USize.toNat_inj, USize.toNat_add]
      grind only [USize.size_eq, USize.toNat_ofNat_of_le_of_lt]
    rw [h_start_succ]
    apply ih (stop - (start + 1)) <;> omega
  case isFalse h_nlt => grind [USize.lt_iff_toNat_lt]

-- this theorem establishes that given the (unprovable) assumption that as.size < USize.size,
-- the scanlMFast and scanlM are equivalent
-- TODO (cmlsharp): prova an analogous theorem for scanrM
private theorem scanlM_eq_scanlMFast [Monad m]
    {f : β → α → m β} {init : β} {as : Array α}
    {h_size : as.SizeFitsUSize} {start stop : Nat} :
    scanlM f init as start stop = scanlMFast f init as start stop := by
  unfold scanlM scanlMFast
  apply scanlM_loop_eq_scanlMFast_loop
  simp_all only [gt_iff_lt]
  apply Nat.min_le_right

@[inline]
private def scanrMFast [Monad m] (f : α → β → m β) (init : β) (as : Array α)
    (h_size : as.SizeFitsUSize) (start := as.size) (stop := 0) : m (Array β) :=
  let start := min start as.size
  let stop := min stop start
  loop f init as
    (start := USize.ofNat start) (stop := USize.ofNat stop)
    (h_start := by grind only [USize.size_eq, USize.ofNat_eq_iff_mod_eq_toNat, = Nat.min_def])
    (acc := Array.replicate (start - stop + 1) init)
    (by grind only [!Array.size_replicate, = Nat.min_def, USize.toNat_ofNat_of_le_of_lt])
where
  @[specialize]
  loop (f : α → β → m β) (init : β) (as : Array α)
       (start stop : USize)
       (h_start : start.toNat ≤ as.size)
       (acc : Array β)
       (h_bound : start.toNat - stop.toNat < acc.size) :
        m (Array β) := do
    if h_gt : stop < start then
      let startM1 := start - 1
      have : startM1 < start := by grind only [!USize.sub_add_cancel, USize.lt_iff_le_and_ne,
        USize.lt_add_one, USize.le_zero_iff]
      have : startM1.toNat < as.size := Nat.lt_of_lt_of_le ‹_› ‹_›
      have : (startM1 - stop) < (start - stop) := by grind only
        [!USize.sub_add_cancel, USize.sub_right_inj, USize.add_comm, USize.lt_add_one,
          USize.add_assoc, USize.add_right_inj]
      let next ← f (as.uget startM1 ‹_›) init
      loop f next as
        (start := startM1)
        (stop := stop)
        (h_start := Nat.le_of_succ_le_succ (Nat.le_succ_of_le ‹_›))
        (acc := acc.uset (startM1 - stop) next
          (by grind only [USize.toNat_sub_of_le, USize.le_of_lt, USize.lt_iff_toNat_lt]))
        (h_bound :=
          (by grind only [USize.toNat_sub_of_le, = uset_eq_set, = size_set, USize.size_eq]))
    else
      pure acc
  termination_by start.toNat - stop.toNat
  decreasing_by
    grind only [USize.lt_iff_toNat_lt, USize.toNat_sub,
      USize.toNat_sub_of_le, USize.le_iff_toNat_le]

@[inline]
private unsafe def scanrMUnsafe [Monad m] (f : α → β → m β) (init : β) (as : Array α)
    (start := as.size) (stop := 0) : m (Array β) :=
  scanrMFast (h_size := as.unsafe_sizeFitsUSize) f init as (start := start) (stop := stop)

/--
Folds a monadic function over an array from the right, accumulating the partial results starting
with `init`. The accumulated value is combined with the each element of the list in order using `f`.

The optional parameters `start` and `stop` control the region of the array to be folded. Folding
proceeds from `start` (exclusive) to `stop` (inclusive), so no folding occurs unless `start > stop`.
By default, the entire array is folded.

Examples:
```lean example
example [Monad m] (f : α → β → m β) :
    Array.scanrM f x₀ #[a, b, c] = (do
      let x₁ ← f c x₀
      let x₂ ← f b x₁
      let x₃ ← f a x₂
      pure #[x₃, x₂, x₁, x₀])
    := by simp [scanrM, scanrM.loop]
```

```lean example
example [Monad m] (f : α → β → m β) :
    Array.scanrM f x₀ #[a, b, c] (start := 3) (stop := 1) = (do
      let x₁ ← f c x₀
      let x₂ ← f b x₁
      pure #[x₂, x₁, x₀])
    := by simp [scanrM, scanrM.loop]
```
-/
@[implemented_by scanrMUnsafe]
def scanrM [Monad m]
    (f : α → β → m β) (init : β) (as : Array α) (start := as.size) (stop := 0) : m (Array β) :=
  let start := min start as.size
  loop f init as start stop (Nat.min_le_right _ _) #[]
where
  /-- auxiliary tail-recursive function for scanrM -/
  loop (f : α → β → m β) (init : β) (as : Array α)
       (start stop : Nat)
       (h_start : start ≤ as.size)
       (acc : Array β) :
       m (Array β) := do
    if h_gt : stop < start then
      let i := start - 1
      let next ← f as[i] init
      loop f next as i stop (by omega) (acc.push init)
    else
      pure <| acc.push init |>.reverse

/--
Fold a function `f` over the array from the left, returning the array of partial results.
```
scanl (· + ·) 0 #[1, 2, 3] = #[0, 1, 3, 6]
```
-/
@[inline]
def scanl (f : β → α → β) (init : β) (as : Array α) (start := 0) (stop := as.size) : Array β :=
  Id.run <| as.scanlM (pure <| f · ·) init start stop

/--
Fold a function `f` over the array from the right, returning the array of partial results.
```
scanr (· + ·) 0 #[1, 2, 3] = #[6, 5, 3, 0]
```
-/
@[inline]
def scanr (f : α → β → β) (init : β) (as : Array α) (start := as.size) (stop := 0) : Array β :=
  Id.run <| as.scanrM (pure <| f · ·) init start stop

end Array

namespace Subarray

/--
Fold a monadic function `f` over the subarray from the left, returning the list of partial results.
-/
@[inline]
def scanlM [Monad m] (f : β → α → m β) (init : β) (as : Subarray α) : m (Array β) :=
  as.array.scanlM f init (start := as.start) (stop := as.stop)

/--
Fold a monadic function `f` over the subarray from the right, returning the list of partial results.
-/
@[inline]
def scanrM [Monad m] (f : α → β → m β) (init : β) (as : Subarray α) : m (Array β) :=
  as.array.scanrM f init (start := as.start) (stop := as.stop)

/--
Fold a function `f` over the subarray from the left, returning the list of partial results.
-/
@[inline]
def scanl (f : β → α → β) (init : β) (as : Subarray α) : Array β :=
  as.array.scanl f init (start := as.start) (stop := as.stop)

/--
Fold a function `f` over the subarray from the right, returning the list of partial results.
-/
@[inline]
def scanr (f : α → β → β) (init : β) (as : Subarray α) : Array β :=
  as.array.scanr f init (start := as.start) (stop := as.stop)

/--
Check whether a subarray is empty.
-/
@[inline]
def isEmpty (as : Subarray α) : Bool :=
  as.start == as.stop

/--
Check whether a subarray contains a given element.
-/
@[inline]
def contains [BEq α] (as : Subarray α) (a : α) : Bool :=
  as.any (· == a)

/--
Remove the first element of a subarray. Returns the element and the remaining
subarray, or `none` if the subarray is empty.
-/
def popHead? (as : Subarray α) : Option (α × Subarray α) :=
  if h : as.start < as.stop
    then
      let head := as.array[as.start]'(Nat.lt_of_lt_of_le h as.stop_le_array_size)
      let tail :=
        ⟨{ as.internalRepresentation with
           start := as.start + 1
           start_le_stop := Nat.le_of_lt_succ $ Nat.succ_lt_succ h }⟩
      some (head, tail)
    else
      none

end Subarray


================================================
FILE: Batteries/Data/Array/Init/Lemmas.lean
================================================
/-
Copyright (c) 2024 Kim Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Authors: Kim Morrison
-/
module

@[expose] public section

/-!
While this file is currently empty, it is intended as a home for any lemmas which are required for
definitions in `Batteries.Data.Array.Basic`, but which are not provided by Lean.
-/


================================================
FILE: Batteries/Data/Array/Lemmas.lean
================================================
/-
Copyright (c) 2021 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Authors: Mario Carneiro, Gabriel Ebner
-/
module

public import Batteries.Data.List.Lemmas

@[expose] public section

namespace Array

@[deprecated forIn_toList (since := "2025-07-01")]
theorem forIn_eq_forIn_toList [Monad m]
    (as : Array α) (b : β) (f : α → β → m (ForInStep β)) :
    forIn as b f = forIn as.toList b f := by
  cases as
  simp

/-! ### idxOf? -/

@[grind =]
theorem idxOf?_toList [BEq α] {a : α} {l : Array α} :
    l.toList.idxOf? a = l.idxOf? a := by
  rcases l with ⟨l⟩
  simp

/-! ### erase -/

@[deprecated (since := "2025-02-06")] alias eraseP_toArray := List.eraseP_toArray
@[deprecated (since := "2025-02-06")] alias erase_toArray := List.erase_toArray

@[simp, grind =] theorem toList_erase [BEq α] (l : Array α) (a : α) :
    (l.erase a).toList = l.toList.erase a := by
  rcases l with ⟨l⟩
  simp

@[simp] theorem size_eraseIdxIfInBounds (a : Array α) (i : Nat) :
    (a.eraseIdxIfInBounds i).size = if i < a.size then a.size-1 else a.size := by
  grind

theorem toList_drop (as: Array α) (n : Nat) :
    (as.drop n).toList = as.toList.drop n := by
  simp only [drop, toList_extract, size_eq_length_toList, List.drop_eq_extract]

/-! ### set -/

theorem size_set! (a : Array α) (i v) : (a.set! i v).size = a.size := by simp

/-! ### map -/

/-! ### mem -/

/-! ### insertAt -/

@[deprecated (since := "2025-02-06")] alias getElem_insertIdx_lt := getElem_insertIdx_of_lt
@[deprecated (since := "2025-02-06")] alias getElem_insertIdx_eq := getElem_insertIdx_self
@[deprecated (since := "2025-02-06")] alias getElem_insertIdx_gt := getElem_insertIdx_of_gt

/-! ### extract -/

@[simp] theorem extract_empty_of_start_eq_stop {a : Array α} :
    a.extract i i = #[] := by grind

theorem extract_append_of_stop_le_size_left {a b : Array α} (h : j ≤ a.size) :
    (a ++ b).extract i j = a.extract i j := by grind

theorem extract_append_of_size_left_le_start {a b : Array α} (h : a.size ≤ i) :
    (a ++ b).extract i j = b.extract (i - a.size) (j - a.size) := by
  rw [extract_append]; grind

theorem extract_eq_of_size_le_stop {a : Array α} (h : a.size ≤ j) :
    a.extract i j = a.extract i := by grind


================================================
FILE: Batteries/Data/Array/Match.lean
================================================
/-
Copyright (c) 2023 F. G. Dorais. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: F. G. Dorais
-/
module

@[expose] public section

namespace Array

/-- Prefix table for the Knuth-Morris-Pratt matching algorithm

  This is an array of the form `t = [(x₀,n₀), (x₁,n₁), (x₂, n₂), ...]` where for each `i`, `nᵢ` is
  the length of the longest proper prefix of `xs = [x₀,x₁,...,xᵢ]` which is also a suffix of `xs`.
-/
structure PrefixTable (α : Type _) extends Array (α × Nat) where
  /-- Validity condition to help with termination proofs -/
  valid : (h : i < toArray.size) → toArray[i].2 ≤ i

instance : Inhabited (PrefixTable α) where
  default := ⟨#[], nofun⟩

/-- Returns the size of the prefix table -/
abbrev PrefixTable.size (t : PrefixTable α) := t.toArray.size

/-- Transition function for the KMP matcher

  Assuming we have an input `xs` with a suffix that matches the pattern prefix `t.pattern[:len]`
  where `len : Fin (t.size+1)`. Then `xs.push x` has a suffix that matches the pattern prefix
  `t.pattern[:t.step x len]`. If `len` is as large as possible then `t.step x len` will also be
  as large as possible.
-/
def PrefixTable.step [BEq α] (t : PrefixTable α) (x : α) : Fin (t.size+1) → Fin (t.size+1)
  | ⟨k, hk⟩ =>
    let cont := fun () =>
      match k with
      | 0 => ⟨0, Nat.zero_lt_succ _⟩
      | k + 1 =>
        have h2 : k < t.size := Nat.lt_of_succ_lt_succ hk
        let k' := t.toArray[k].2
        have hk' : k' < k + 1 := Nat.lt_succ_of_le (t.valid h2)
        step t x ⟨k', Nat.lt_trans hk' hk⟩
    if hsz : k < t.size then
      if x == t.toArray[k].1 then
        ⟨k+1, Nat.succ_lt_succ hsz⟩
      else cont ()
    else cont ()
termination_by k => k.val

/-- Extend a prefix table by one element

  If `t` is the prefix table for `xs` then `t.extend x` is the prefix table for `xs.push x`.
-/
def PrefixTable.extend [BEq α] (t : PrefixTable α) (x : α) : PrefixTable α where
  toArray := t.toArray.push (x, t.step x ⟨t.size, Nat.lt_succ_self _⟩)
  valid _ := by
    rw [Array.getElem_push]
    split
    · exact t.valid ..
    · next h => exact Nat.le_trans (Nat.lt_succ_iff.1 <| Fin.isLt ..) (Nat.not_lt.1 h)

/-- Make prefix table from a pattern array -/
def mkPrefixTable [BEq α] (xs : Array α) : PrefixTable α := xs.foldl (·.extend) default

/-- Make prefix table from a pattern stream -/
partial def mkPrefixTableOfStream [BEq α] [Std.Stream σ α] (stream : σ) : PrefixTable α :=
  loop default stream
where
  /-- Inner loop for `mkPrefixTableOfStream` -/
  loop (t : PrefixTable α) (stream : σ) :=
    match Stream.next? stream with
    | none => t
    | some (x, stream) => loop (t.extend x) stream

/-- KMP matcher structure -/
structure Matcher (α) where
  /-- Prefix table for the pattern -/
  table : PrefixTable α
  /-- Current longest matching prefix -/
  state : Fin (table.size + 1) := 0

/-- Make a KMP matcher for a given pattern array -/
def Matcher.ofArray [BEq α] (pat : Array α) : Matcher α where
  table := mkPrefixTable pat

/-- Make a KMP matcher for a given a pattern stream -/
def Matcher.ofStream [BEq α] [Std.Stream σ α] (pat : σ) : Matcher α where
  table := mkPrefixTableOfStream pat

/-- Find next match from a given stream

  Runs the stream until it reads a sequence that matches the sought pattern, then returns the stream
  state at that point and an updated matcher state.
-/
partial def Matcher.next? [BEq α] [Std.Stream σ α] (m : Matcher α) (stream : σ) :
    Option (σ × Matcher α) :=
  match Stream.next? stream with
  | none => none
  | some (x, stream) =>
    let state := m.table.step x m.state
    if state = m.table.size then
      some (stream, { m with state })
    else
      next? { m with state } stream

namespace Matcher
open Std Std.Iterators

/-- Iterator transformer for KMP matcher. -/
protected structure Iterator (σ n α) [BEq α] (m : Matcher α) [Iterator σ n α] where
  /-- Inner iterator. -/
  inner : IterM (α := σ) n α
  /-- Matcher state. -/
  state : Fin (m.table.size + 1) := 0

/-- Implementation datail for `Matcher.Iterator`. -/
def modifyStep [BEq α] (m : Matcher α) [Iterator σ n α]
    (it : IterM (α := m.Iterator σ n α) n σ) :
    it.internalState.inner.Step (α := σ) → IterStep (IterM (α := m.Iterator σ n α) n σ) σ
  | .done _ => .done
  | .skip it' _ => .skip ⟨{it.internalState with inner := it'}⟩
  | .yield it' x _ =>
    let state := m.table.step x m.state
    if state = m.table.size then
      .yield ⟨{inner := it', state := state}⟩ it'.internalState
    else
      .skip ⟨{inner := it', state := state}⟩

instance [Monad n] [BEq α] (m : Matcher α) [Iterator σ n α] :
    Iterator (m.Iterator σ n α) n σ where
  IsPlausibleStep it step := ∃ step', m.modifyStep it step' = step
  step it := it.internalState.inner.step >>=
    fun step => pure (.deflate ⟨m.modifyStep _ _, step.inflate, rfl⟩)

private def finitenessRelation [Monad n] [BEq α] (m : Matcher α) [Iterator σ n α] [Finite σ n] :
    FinitenessRelation (m.Iterator σ n α) n where
  Rel := InvImage IterM.IsPlausibleSuccessorOf fun it => it.internalState.inner
  wf := InvImage.wf _ Finite.wf
  subrelation {it it'} h := by
    obtain ⟨_, hsucc, step, rfl⟩ := h
    simp only [IterM.Step] at step
    cases step with simp only [IterStep.successor, modifyStep, reduceCtorEq] at hsucc
    | skip =>
      cases hsucc
      apply IterM.isPlausibleSuccessorOf_of_skip
      assumption
    | yield =>
      split at hsucc
      · next heq =>
        cases hsucc
        split at heq
        · cases heq
          apply IterM.isPlausibleSuccessorOf_of_yield
          assumption
        · contradiction
      · next heq =>
        cases hsucc
        split at heq
        · contradiction
        · cases heq
          apply IterM.isPlausibleSuccessorOf_of_yield
          assumption
      · contradiction

instance [Monad n] [BEq α] (m : Matcher α) [Iterator σ n α] [inst : Finite σ n] :
    Finite (m.Iterator σ n α) n (β := σ) := .of_finitenessRelation m.finitenessRelation

end Matcher

end Array


================================================
FILE: Batteries/Data/Array/Merge.lean
================================================
/-
Copyright (c) 2022 Jannis Limperg. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jannis Limperg
-/
module

@[expose] public section

namespace Array

/--
`O(|xs| + |ys|)`. Merge arrays `xs` and `ys`. If the arrays are sorted according to `lt`, then the
result is sorted as well. If two (or more) elements are equal according to `lt`, they are preserved.
-/
def merge (lt : α → α → Bool) (xs ys : Array α) : Array α :=
  go (Array.mkEmpty (xs.size + ys.size)) 0 0
where
  /-- Auxiliary definition for `merge`. -/
  go (acc : Array α) (i j : Nat) : Array α :=
    if hi : i ≥ xs.size then
      acc ++ ys[j:]
    else if hj : j ≥ ys.size then
      acc ++ xs[i:]
    else
      let x := xs[i]
      let y := ys[j]
      if lt x y then go (acc.push x) (i + 1) j else go (acc.push y) i (j + 1)
  termination_by xs.size + ys.size - (i + j)

-- We name `ord` so it can be provided as a named argument.
set_option linter.unusedVariables.funArgs false in
/--
`O(|xs| + |ys|)`. Merge arrays `xs` and `ys`, which must be sorted according to `compare` and must
not contain duplicates. Equal elements are merged using `merge`. If `merge` respects the order
(i.e. for all `x`, `y`, `y'`, `z`, if `x < y < z` and `x < y' < z` then `x < merge y y' < z`)
then the resulting array is again sorted.
-/
def mergeDedupWith [ord : Ord α] (xs ys : Array α) (merge : α → α → α) : Array α :=
  go (Array.mkEmpty (xs.size + ys.size)) 0 0
where
  /-- Auxiliary definition for `mergeDedupWith`. -/
  go (acc : Array α) (i j : Nat) : Array α :=
    if hi : i ≥ xs.size then
      acc ++ ys[j:]
    else if hj : j ≥ ys.size then
      acc ++ xs[i:]
    else
      let x := xs[i]
      let y := ys[j]
      match compare x y with
      | .lt => go (acc.push x) (i + 1) j
      | .gt => go (acc.push y) i (j + 1)
      | .eq => go (acc.push (merge x y)) (i + 1) (j + 1)
  termination_by xs.size + ys.size - (i + j)

/--
`O(|xs| + |ys|)`. Merge arrays `xs` and `ys`, which must be sorted according to `compare` and must
not contain duplicates. If an element appears in both `xs` and `ys`, only one copy is kept.
-/
@[inline] def mergeDedup [ord : Ord α] (xs ys : Array α) : Array α :=
  mergeDedupWith (ord := ord) xs ys fun x _ => x

set_option linter.unusedVariables false in
/--
`O(|xs| * |ys|)`. Merge `xs` and `ys`, which do not need to be sorted. Elements which occur in
both `xs` and `ys` are only added once. If `xs` and `ys` do not contain duplicates, then neither
does the result.
-/
def mergeUnsortedDedup 
Download .txt
gitextract_6ram4obd/

├── .docker/
│   └── gitpod/
│       └── Dockerfile
├── .github/
│   └── workflows/
│       ├── build.yml
│       ├── docs-deploy.yml
│       ├── docs-release.yml
│       ├── labels-from-comments.yml
│       ├── labels-from-status.yml
│       ├── merge_conflicts.yml
│       ├── nightly_bump_and_merge.yml
│       ├── nightly_detect_failure.yml
│       ├── nightly_merge_master.yml
│       └── test_mathlib.yml
├── .gitignore
├── .gitpod.yml
├── .vscode/
│   ├── copyright.code-snippets
│   └── settings.json
├── Batteries/
│   ├── Classes/
│   │   ├── Cast.lean
│   │   ├── Deprecated.lean
│   │   ├── Order.lean
│   │   ├── RatCast.lean
│   │   └── SatisfiesM.lean
│   ├── CodeAction/
│   │   ├── Attr.lean
│   │   ├── Basic.lean
│   │   ├── Deprecated.lean
│   │   ├── Match.lean
│   │   └── Misc.lean
│   ├── CodeAction.lean
│   ├── Control/
│   │   ├── AlternativeMonad.lean
│   │   ├── ForInStep/
│   │   │   ├── Basic.lean
│   │   │   └── Lemmas.lean
│   │   ├── ForInStep.lean
│   │   ├── LawfulMonadState.lean
│   │   ├── Lemmas.lean
│   │   ├── Monad.lean
│   │   ├── Nondet/
│   │   │   └── Basic.lean
│   │   └── OptionT.lean
│   ├── Data/
│   │   ├── Array/
│   │   │   ├── Basic.lean
│   │   │   ├── Init/
│   │   │   │   └── Lemmas.lean
│   │   │   ├── Lemmas.lean
│   │   │   ├── Match.lean
│   │   │   ├── Merge.lean
│   │   │   ├── Monadic.lean
│   │   │   ├── Pairwise.lean
│   │   │   └── Scan.lean
│   │   ├── Array.lean
│   │   ├── AssocList.lean
│   │   ├── BinaryHeap/
│   │   │   └── Basic.lean
│   │   ├── BinaryHeap.lean
│   │   ├── BinomialHeap/
│   │   │   ├── Basic.lean
│   │   │   └── Lemmas.lean
│   │   ├── BinomialHeap.lean
│   │   ├── BitVec/
│   │   │   ├── Basic.lean
│   │   │   └── Lemmas.lean
│   │   ├── BitVec.lean
│   │   ├── Bool.lean
│   │   ├── ByteArray.lean
│   │   ├── ByteSlice.lean
│   │   ├── Char/
│   │   │   ├── AsciiCasing.lean
│   │   │   └── Basic.lean
│   │   ├── Char.lean
│   │   ├── DList/
│   │   │   ├── Basic.lean
│   │   │   └── Lemmas.lean
│   │   ├── DList.lean
│   │   ├── Fin/
│   │   │   ├── Basic.lean
│   │   │   ├── Fold.lean
│   │   │   ├── Lemmas.lean
│   │   │   └── OfBits.lean
│   │   ├── Fin.lean
│   │   ├── FloatArray.lean
│   │   ├── HashMap/
│   │   │   └── Basic.lean
│   │   ├── HashMap.lean
│   │   ├── Int.lean
│   │   ├── List/
│   │   │   ├── ArrayMap.lean
│   │   │   ├── Basic.lean
│   │   │   ├── Count.lean
│   │   │   ├── Init/
│   │   │   │   └── Lemmas.lean
│   │   │   ├── Lemmas.lean
│   │   │   ├── Matcher.lean
│   │   │   ├── Monadic.lean
│   │   │   ├── Pairwise.lean
│   │   │   ├── Perm.lean
│   │   │   └── Scan.lean
│   │   ├── List.lean
│   │   ├── MLList/
│   │   │   ├── Basic.lean
│   │   │   ├── Heartbeats.lean
│   │   │   └── IO.lean
│   │   ├── MLList.lean
│   │   ├── NameSet.lean
│   │   ├── Nat/
│   │   │   ├── Basic.lean
│   │   │   ├── Bisect.lean
│   │   │   ├── Bitwise/
│   │   │   │   └── Lemmas.lean
│   │   │   ├── Bitwise.lean
│   │   │   ├── Gcd.lean
│   │   │   └── Lemmas.lean
│   │   ├── Nat.lean
│   │   ├── PairingHeap.lean
│   │   ├── RBMap/
│   │   │   ├── Alter.lean
│   │   │   ├── Basic.lean
│   │   │   ├── Depth.lean
│   │   │   ├── Lemmas.lean
│   │   │   └── WF.lean
│   │   ├── RBMap.lean
│   │   ├── Random/
│   │   │   └── MersenneTwister.lean
│   │   ├── Random.lean
│   │   ├── Range/
│   │   │   └── Lemmas.lean
│   │   ├── Range.lean
│   │   ├── Rat/
│   │   │   └── Float.lean
│   │   ├── Rat.lean
│   │   ├── RunningStats.lean
│   │   ├── Stream.lean
│   │   ├── String/
│   │   │   ├── AsciiCasing.lean
│   │   │   ├── Basic.lean
│   │   │   ├── Legacy.lean
│   │   │   ├── Lemmas.lean
│   │   │   └── Matcher.lean
│   │   ├── String.lean
│   │   ├── UInt.lean
│   │   ├── UnionFind/
│   │   │   ├── Basic.lean
│   │   │   └── Lemmas.lean
│   │   ├── UnionFind.lean
│   │   ├── Vector/
│   │   │   ├── Basic.lean
│   │   │   ├── Lemmas.lean
│   │   │   └── Monadic.lean
│   │   └── Vector.lean
│   ├── Lean/
│   │   ├── AttributeExtra.lean
│   │   ├── EStateM.lean
│   │   ├── Except.lean
│   │   ├── Expr.lean
│   │   ├── Float.lean
│   │   ├── HashMap.lean
│   │   ├── HashSet.lean
│   │   ├── IO/
│   │   │   └── Process.lean
│   │   ├── Json.lean
│   │   ├── LawfulMonad.lean
│   │   ├── LawfulMonadLift.lean
│   │   ├── Meta/
│   │   │   ├── Basic.lean
│   │   │   ├── DiscrTree.lean
│   │   │   ├── Expr.lean
│   │   │   ├── Inaccessible.lean
│   │   │   ├── InstantiateMVars.lean
│   │   │   ├── SavedState.lean
│   │   │   ├── Simp.lean
│   │   │   └── UnusedNames.lean
│   │   ├── MonadBacktrack.lean
│   │   ├── NameMapAttribute.lean
│   │   ├── PersistentHashMap.lean
│   │   ├── PersistentHashSet.lean
│   │   ├── Position.lean
│   │   ├── SatisfiesM.lean
│   │   ├── Syntax.lean
│   │   ├── System/
│   │   │   └── IO.lean
│   │   ├── TagAttribute.lean
│   │   └── Util/
│   │       └── EnvSearch.lean
│   ├── Linter/
│   │   ├── UnnecessarySeqFocus.lean
│   │   └── UnreachableTactic.lean
│   ├── Linter.lean
│   ├── Logic.lean
│   ├── Tactic/
│   │   ├── Alias.lean
│   │   ├── Basic.lean
│   │   ├── Case.lean
│   │   ├── Congr.lean
│   │   ├── Exact.lean
│   │   ├── GeneralizeProofs.lean
│   │   ├── HelpCmd.lean
│   │   ├── Init.lean
│   │   ├── Instances.lean
│   │   ├── Lemma.lean
│   │   ├── Lint/
│   │   │   ├── Basic.lean
│   │   │   ├── Frontend.lean
│   │   │   ├── Misc.lean
│   │   │   ├── Simp.lean
│   │   │   └── TypeClass.lean
│   │   ├── Lint.lean
│   │   ├── NoMatch.lean
│   │   ├── OpenPrivate.lean
│   │   ├── PermuteGoals.lean
│   │   ├── PrintDependents.lean
│   │   ├── PrintOpaques.lean
│   │   ├── PrintPrefix.lean
│   │   ├── SeqFocus.lean
│   │   ├── ShowUnused.lean
│   │   ├── SqueezeScope.lean
│   │   ├── Trans.lean
│   │   └── Unreachable.lean
│   └── Util/
│       ├── Cache.lean
│       ├── ExtendedBinder.lean
│       ├── LibraryNote.lean
│       ├── Panic.lean
│       ├── Pickle.lean
│       └── ProofWanted.lean
├── Batteries.lean
├── BatteriesTest/
│   ├── ArrayMap.lean
│   ├── Char.lean
│   ├── GeneralizeProofs.lean
│   ├── Internal/
│   │   ├── DummyLabelAttr.lean
│   │   ├── DummyLibraryNote.lean
│   │   └── DummyLibraryNote2.lean
│   ├── MLList.lean
│   ├── OpenPrivateDefs.lean
│   ├── String.lean
│   ├── absurd.lean
│   ├── alias.lean
│   ├── array.lean
│   ├── array_scan.lean
│   ├── by_contra.lean
│   ├── case.lean
│   ├── congr.lean
│   ├── conv_equals.lean
│   ├── except.lean
│   ├── exfalso.lean
│   ├── float.lean
│   ├── help_cmd.lean
│   ├── import_lean.lean
│   ├── instances.lean
│   ├── isIndependentOf.lean
│   ├── kmp_matcher.lean
│   ├── lemma_cmd.lean
│   ├── library_note.lean
│   ├── lintTC.lean
│   ├── lintTrace.lean
│   ├── lint_coinductive.lean
│   ├── lint_docBlame.lean
│   ├── lint_docBlameThm.lean
│   ├── lint_dupNamespace.lean
│   ├── lint_lean.lean
│   ├── lint_simpNF.lean
│   ├── lint_simpNF_respectTransparency.lean
│   ├── lint_unreachableTactic.lean
│   ├── linterVisibility.lean
│   ├── lintsimp.lean
│   ├── lintunused.lean
│   ├── list_enumeration.lean
│   ├── list_sublists.lean
│   ├── mersenne_twister.lean
│   ├── nondet.lean
│   ├── norm_cast.lean
│   ├── omega/
│   │   └── benchmark.lean
│   ├── on_goal.lean
│   ├── openPrivate.lean
│   ├── print_opaques.lean
│   ├── print_prefix.lean
│   ├── proof_wanted.lean
│   ├── register_label_attr.lean
│   ├── rfl.lean
│   ├── satisfying.lean
│   ├── seq_focus.lean
│   ├── show_term.lean
│   ├── show_unused.lean
│   ├── simp_trace.lean
│   ├── simpa.lean
│   ├── solve_by_elim.lean
│   ├── trans.lean
│   ├── tryThis.lean
│   ├── vector.lean
│   └── where.lean
├── LICENSE
├── README.md
├── Shake/
│   └── Main.lean
├── bors.toml
├── docs/
│   └── lakefile.toml
├── lake-manifest.json
├── lakefile.toml
├── lean-toolchain
└── scripts/
    ├── check_imports.lean
    ├── create-adaptation-pr.sh
    ├── lintWhitespace.sh
    ├── merge-lean-testing-pr.sh
    ├── nolints.json
    ├── noshake.json
    ├── runLinter.lean
    └── updateBatteries.sh
Condensed preview — 270 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,392K chars).
[
  {
    "path": ".docker/gitpod/Dockerfile",
    "chars": 1887,
    "preview": "# This is the Dockerfile for leanprover-community/batteries\n# This file is mostly copied from [mathlib4](https://github."
  },
  {
    "path": ".github/workflows/build.yml",
    "chars": 1601,
    "preview": "on:\n  push:\n    branches-ignore:\n      # ignore tmp branches used by bors\n      - 'staging.tmp*'\n      - 'trying.tmp*'\n "
  },
  {
    "path": ".github/workflows/docs-deploy.yml",
    "chars": 947,
    "preview": "name: Deploy Docs\n\non:\n  workflow_dispatch:\n  schedule:\n    - cron: '0 10 * * *' # daily (UTC 10:00)\n\npermissions:\n  con"
  },
  {
    "path": ".github/workflows/docs-release.yml",
    "chars": 1199,
    "preview": "name: Release Docs\n\non:\n  push:\n    tags:\n      - \"v[0-9]+.[0-9]+.[0-9]+\"\n      - \"v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+\"\n\nperm"
  },
  {
    "path": ".github/workflows/labels-from-comments.yml",
    "chars": 2246,
    "preview": "# This workflow allows any user to add one of the `awaiting-review`, `awaiting-author`, or `WIP` labels,\n# by commenting"
  },
  {
    "path": ".github/workflows/labels-from-status.yml",
    "chars": 2068,
    "preview": "# This workflow assigns `awaiting-review` or `WIP` labels to new PRs, and it removes\n# `awaiting-review`, `awaiting-auth"
  },
  {
    "path": ".github/workflows/merge_conflicts.yml",
    "chars": 865,
    "preview": "name: Merge conflicts\n\non:\n  schedule:\n    - cron: '*/60 * * * *' # run every 60 minutes\n\njobs:\n  main:\n    if: github.r"
  },
  {
    "path": ".github/workflows/nightly_bump_and_merge.yml",
    "chars": 13777,
    "preview": "name: Bump toolchain and merge pr-testing branches\n\n# This workflow combines the former `nightly_bump_toolchain.yml` and"
  },
  {
    "path": ".github/workflows/nightly_detect_failure.yml",
    "chars": 16561,
    "preview": "name: Post to zulip if the nightly-testing branch is failing.\n\non:\n  workflow_run:\n    workflows: [\"ci\"]\n    types:\n    "
  },
  {
    "path": ".github/workflows/nightly_merge_master.yml",
    "chars": 1442,
    "preview": "# This job merges every commit to `main` into `nightly-testing`, resolving merge conflicts in favor of `nightly-testing`"
  },
  {
    "path": ".github/workflows/test_mathlib.yml",
    "chars": 4779,
    "preview": "# Test Mathlib against a Batteries PR\n\nname: Test Mathlib\n\non:\n  workflow_run:\n    workflows: [ci]\n    types: [completed"
  },
  {
    "path": ".gitignore",
    "chars": 246,
    "preview": "# Prior to v4.3.0-rc2 lake stored files in these locations.\n# We'll leave them in the `.gitignore` for a while for users"
  },
  {
    "path": ".gitpod.yml",
    "chars": 147,
    "preview": "image:\n  file: .docker/gitpod/Dockerfile\n\nvscode:\n  extensions:\n    - leanprover.lean4\n\ntasks:\n  - init: |\n      elan se"
  },
  {
    "path": ".vscode/copyright.code-snippets",
    "chars": 275,
    "preview": "{\n\t\"Copyright header for batteries\": {\n\t\t\"scope\": \"lean4\",\n\t\t\"prefix\": \"copyright\",\n\t\t\"body\": [\n\t\t\t\"/-\",\n\t\t\t\"Copyright ("
  },
  {
    "path": ".vscode/settings.json",
    "chars": 271,
    "preview": "{\n  \"editor.insertSpaces\": true,\n  \"editor.tabSize\": 2,\n  \"editor.rulers\" : [100],\n  \"files.encoding\": \"utf8\",\n  \"files."
  },
  {
    "path": "Batteries/Classes/Cast.lean",
    "chars": 823,
    "preview": "/-\nCopyright (c) 2014 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Classes/Deprecated.lean",
    "chars": 17548,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Classes/Order.lean",
    "chars": 12212,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Classes/RatCast.lean",
    "chars": 919,
    "preview": "/-\nCopyright (c) 2014 Robert Lewis. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICE"
  },
  {
    "path": "Batteries/Classes/SatisfiesM.lean",
    "chars": 12705,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/CodeAction/Attr.lean",
    "chars": 5823,
    "preview": "/-\nCopyright (c) 2023 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/CodeAction/Basic.lean",
    "chars": 3023,
    "preview": "/-\nCopyright (c) 2023 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/CodeAction/Deprecated.lean",
    "chars": 2641,
    "preview": "/-\nCopyright (c) 2023 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/CodeAction/Match.lean",
    "chars": 12605,
    "preview": "/-\nCopyright (c) 2026 Moritz Roos. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICEN"
  },
  {
    "path": "Batteries/CodeAction/Misc.lean",
    "chars": 18040,
    "preview": "/-\nCopyright (c) 2023 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/CodeAction.lean",
    "chars": 170,
    "preview": "module\n\npublic import Batteries.CodeAction.Attr\npublic import Batteries.CodeAction.Basic\npublic import Batteries.CodeAct"
  },
  {
    "path": "Batteries/Control/AlternativeMonad.lean",
    "chars": 8281,
    "preview": "/-\nCopyright (c) 2025 Devon Tuma. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICENS"
  },
  {
    "path": "Batteries/Control/ForInStep/Basic.lean",
    "chars": 1263,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Control/ForInStep/Lemmas.lean",
    "chars": 2133,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Control/ForInStep.lean",
    "chars": 105,
    "preview": "module\n\npublic import Batteries.Control.ForInStep.Basic\npublic import Batteries.Control.ForInStep.Lemmas\n"
  },
  {
    "path": "Batteries/Control/LawfulMonadState.lean",
    "chars": 9813,
    "preview": "/-\nCopyright (c) 2025 Devon Tuma. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICENS"
  },
  {
    "path": "Batteries/Control/Lemmas.lean",
    "chars": 2376,
    "preview": "/-\nCopyright (c) 2023 François G. Dorais. All rights reserved.\nReleased under Apache 2.0 license as described in the fil"
  },
  {
    "path": "Batteries/Control/Monad.lean",
    "chars": 423,
    "preview": "/-\nCopyright (c) 2025 Lean FRO, LLC. All rights reserved.\nReleased under Apache 2.0 license as described in the file LIC"
  },
  {
    "path": "Batteries/Control/Nondet/Basic.lean",
    "chars": 7151,
    "preview": "/-\nCopyright (c) 2023 Kim Morrison. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICE"
  },
  {
    "path": "Batteries/Control/OptionT.lean",
    "chars": 1337,
    "preview": "/-\nCopyright (c) 2017 Microsoft Corporation. All rights reserved.\nReleased under Apache 2.0 license as described in the "
  },
  {
    "path": "Batteries/Data/Array/Basic.lean",
    "chars": 15548,
    "preview": "/-\nCopyright (c) 2021 Floris van Doorn. All rights reserved.\nReleased under Apache 2.0 license as described in the file "
  },
  {
    "path": "Batteries/Data/Array/Init/Lemmas.lean",
    "chars": 372,
    "preview": "/-\nCopyright (c) 2024 Kim Morrison. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICE"
  },
  {
    "path": "Batteries/Data/Array/Lemmas.lean",
    "chars": 2257,
    "preview": "/-\nCopyright (c) 2021 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Data/Array/Match.lean",
    "chars": 6023,
    "preview": "/-\nCopyright (c) 2023 F. G. Dorais. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICE"
  },
  {
    "path": "Batteries/Data/Array/Merge.lean",
    "chars": 4124,
    "preview": "/-\nCopyright (c) 2022 Jannis Limperg. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Data/Array/Monadic.lean",
    "chars": 8367,
    "preview": "/-\nCopyright (c) 2021 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Data/Array/Pairwise.lean",
    "chars": 2422,
    "preview": "/-\nCopyright (c) 2024 François G. Dorais. All rights reserved.\nReleased under Apache 2.0 license as described in the fil"
  },
  {
    "path": "Batteries/Data/Array/Scan.lean",
    "chars": 13335,
    "preview": "/-\nCopyright (c) 2026 Chad Sharp. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICENS"
  },
  {
    "path": "Batteries/Data/Array.lean",
    "chars": 347,
    "preview": "module\n\npublic import Batteries.Data.Array.Basic\npublic import Batteries.Data.Array.Init.Lemmas\npublic import Batteries."
  },
  {
    "path": "Batteries/Data/AssocList.lean",
    "chars": 12149,
    "preview": "/-\nCopyright (c) 2019 Microsoft Corporation. All rights reserved.\nReleased under Apache 2.0 license as described in the "
  },
  {
    "path": "Batteries/Data/BinaryHeap/Basic.lean",
    "chars": 6697,
    "preview": "/-\nCopyright (c) 2021 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Data/BinaryHeap.lean",
    "chars": 53,
    "preview": "module\npublic import Batteries.Data.BinaryHeap.Basic\n"
  },
  {
    "path": "Batteries/Data/BinomialHeap/Basic.lean",
    "chars": 24040,
    "preview": "/-\nCopyright (c) 2019 Microsoft Corporation. All rights reserved.\nReleased under Apache 2.0 license as described in the "
  },
  {
    "path": "Batteries/Data/BinomialHeap/Lemmas.lean",
    "chars": 1147,
    "preview": "/-\nCopyright (c) 2023 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Data/BinomialHeap.lean",
    "chars": 105,
    "preview": "module\n\npublic import Batteries.Data.BinomialHeap.Basic\npublic import Batteries.Data.BinomialHeap.Lemmas\n"
  },
  {
    "path": "Batteries/Data/BitVec/Basic.lean",
    "chars": 763,
    "preview": "/-\nCopyright (c) 2024 François G. Dorais. All rights reserved.\nReleased under Apache 2.0 license as described in the fil"
  },
  {
    "path": "Batteries/Data/BitVec/Lemmas.lean",
    "chars": 4485,
    "preview": "/-\nCopyright (c) 2024 François G. Dorais. All rights reserved.\nReleased under Apache 2.0 license as described in the fil"
  },
  {
    "path": "Batteries/Data/BitVec.lean",
    "chars": 93,
    "preview": "module\n\npublic import Batteries.Data.BitVec.Basic\npublic import Batteries.Data.BitVec.Lemmas\n"
  },
  {
    "path": "Batteries/Data/Bool.lean",
    "chars": 929,
    "preview": "/-\nCopyright (c) 2026 Chad Sharp. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICENS"
  },
  {
    "path": "Batteries/Data/ByteArray.lean",
    "chars": 4906,
    "preview": "/-\nCopyright (c) 2023 François G. Dorais. All rights reserved.\nReleased under Apache 2.0 license as described in the fil"
  },
  {
    "path": "Batteries/Data/ByteSlice.lean",
    "chars": 6963,
    "preview": "\n/-\nCopyright (c) 2021 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file L"
  },
  {
    "path": "Batteries/Data/Char/AsciiCasing.lean",
    "chars": 7333,
    "preview": "/-\nCopyright (c) 2025 François G. Dorais. All rights reserved.\nReleased under Apache 2.0 license as described in the fil"
  },
  {
    "path": "Batteries/Data/Char/Basic.lean",
    "chars": 6204,
    "preview": "/-\nCopyright (c) 2022 Jannis Limperg. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Data/Char.lean",
    "chars": 94,
    "preview": "module\n\npublic import Batteries.Data.Char.AsciiCasing\npublic import Batteries.Data.Char.Basic\n"
  },
  {
    "path": "Batteries/Data/DList/Basic.lean",
    "chars": 2769,
    "preview": "/-\nCopyright (c) 2018 Microsoft Corporation. All rights reserved.\nReleased under Apache 2.0 license as described in the "
  },
  {
    "path": "Batteries/Data/DList/Lemmas.lean",
    "chars": 1804,
    "preview": "/-\nCopyright (c) 2017 Microsoft Corporation. All rights reserved.\nReleased under Apache 2.0 license as described in the "
  },
  {
    "path": "Batteries/Data/DList.lean",
    "chars": 91,
    "preview": "module\n\npublic import Batteries.Data.DList.Basic\npublic import Batteries.Data.DList.Lemmas\n"
  },
  {
    "path": "Batteries/Data/Fin/Basic.lean",
    "chars": 5043,
    "preview": "/-\nCopyright (c) 2017 Robert Y. Lewis. All rights reserved.\nReleased under Apache 2.0 license as described in the file L"
  },
  {
    "path": "Batteries/Data/Fin/Fold.lean",
    "chars": 6346,
    "preview": "/-\nCopyright (c) 2024 François G. Dorais. All rights reserved.\nReleased under Apache 2.0 license as described in the fil"
  },
  {
    "path": "Batteries/Data/Fin/Lemmas.lean",
    "chars": 17237,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Data/Fin/OfBits.lean",
    "chars": 513,
    "preview": "/-\nCopyright (c) 2025 François G. Dorais. All rights reserved.\nReleased under Apache 2. license as described in the file"
  },
  {
    "path": "Batteries/Data/Fin.lean",
    "chars": 165,
    "preview": "module\n\npublic import Batteries.Data.Fin.Basic\npublic import Batteries.Data.Fin.Fold\npublic import Batteries.Data.Fin.Le"
  },
  {
    "path": "Batteries/Data/FloatArray.lean",
    "chars": 1223,
    "preview": "/-\nCopyright (c) 2024 François G. Dorais. All rights reserved.\nReleased under Apache 2. license as described in the file"
  },
  {
    "path": "Batteries/Data/HashMap/Basic.lean",
    "chars": 9921,
    "preview": "/-\nCopyright (c) 2018 Microsoft Corporation. All rights reserved.\nReleased under Apache 2.0 license as described in the "
  },
  {
    "path": "Batteries/Data/HashMap.lean",
    "chars": 51,
    "preview": "module\n\npublic import Batteries.Data.HashMap.Basic\n"
  },
  {
    "path": "Batteries/Data/Int.lean",
    "chars": 1970,
    "preview": "/-\nCopyright (c) 2025 François G. Dorais. All rights reserved.\nReleased under Apache 2.0 license as described in the fil"
  },
  {
    "path": "Batteries/Data/List/ArrayMap.lean",
    "chars": 1496,
    "preview": "/-\nCopyright (c) 2024 Michael Rothgang. All rights reserved.\nReleased under Apache 2.0 license as described in the file "
  },
  {
    "path": "Batteries/Data/List/Basic.lean",
    "chars": 42379,
    "preview": "/-\nCopyright (c) 2016 Microsoft Corporation. All rights reserved.\nReleased under Apache 2.0 license as described in the "
  },
  {
    "path": "Batteries/Data/List/Count.lean",
    "chars": 3945,
    "preview": "/-\nCopyright (c) 2014 Parikshit Khanna. All rights reserved.\nReleased under Apache 2.0 license as described in the file "
  },
  {
    "path": "Batteries/Data/List/Init/Lemmas.lean",
    "chars": 371,
    "preview": "/-\nCopyright (c) 2024 Kim Morrison. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICE"
  },
  {
    "path": "Batteries/Data/List/Lemmas.lean",
    "chars": 53170,
    "preview": "/-\nCopyright (c) 2014 Parikshit Khanna. All rights reserved.\nReleased under Apache 2.0 license as described in the file "
  },
  {
    "path": "Batteries/Data/List/Matcher.lean",
    "chars": 3044,
    "preview": "/-\nCopyright (c) 2024 François G. Dorais. All rights reserved.\nReleased under Apache 2.0 license as described in the fil"
  },
  {
    "path": "Batteries/Data/List/Monadic.lean",
    "chars": 1383,
    "preview": "/-\nCopyright (c) 2024 Lean FRO, LLC. All rights reserved.\nReleased under Apache 2.0 license as described in the file LIC"
  },
  {
    "path": "Batteries/Data/List/Pairwise.lean",
    "chars": 4406,
    "preview": "/-\nCopyright (c) 2018 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Data/List/Perm.lean",
    "chars": 16498,
    "preview": "/-\nCopyright (c) 2015 Microsoft Corporation. All rights reserved.\nReleased under Apache 2.0 license as described in the "
  },
  {
    "path": "Batteries/Data/List/Scan.lean",
    "chars": 8849,
    "preview": "/-\nCopyright (c) 2014 Parikshit Khanna. All rights reserved.\nReleased under Apache 2.0 license as described in the file "
  },
  {
    "path": "Batteries/Data/List.lean",
    "chars": 423,
    "preview": "module\n\npublic import Batteries.Data.List.ArrayMap\npublic import Batteries.Data.List.Basic\npublic import Batteries.Data."
  },
  {
    "path": "Batteries/Data/MLList/Basic.lean",
    "chars": 18986,
    "preview": "/-\nCopyright (c) 2018 Kim Morrison. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICE"
  },
  {
    "path": "Batteries/Data/MLList/Heartbeats.lean",
    "chars": 1271,
    "preview": "/-\nCopyright (c) 2023 Kim Morrison. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICE"
  },
  {
    "path": "Batteries/Data/MLList/IO.lean",
    "chars": 624,
    "preview": "/-\nCopyright (c) 2023 Kim Morrison. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICE"
  },
  {
    "path": "Batteries/Data/MLList.lean",
    "chars": 136,
    "preview": "module\n\npublic import Batteries.Data.MLList.Basic\npublic import Batteries.Data.MLList.Heartbeats\npublic import Batteries"
  },
  {
    "path": "Batteries/Data/NameSet.lean",
    "chars": 645,
    "preview": "/-\nCopyright (c) 2023 Kim Morrison. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICE"
  },
  {
    "path": "Batteries/Data/Nat/Basic.lean",
    "chars": 3489,
    "preview": "/-\nCopyright (c) 2016 Microsoft Corporation. All rights reserved.\nReleased under Apache 2.0 license as described in the "
  },
  {
    "path": "Batteries/Data/Nat/Bisect.lean",
    "chars": 4603,
    "preview": "/-\nCopyright (c) 2024 François G. Dorais. All rights reserved.\nReleased under Apache 2.0 license as described in the fil"
  },
  {
    "path": "Batteries/Data/Nat/Bitwise/Lemmas.lean",
    "chars": 3782,
    "preview": "/-\nCopyright (c) 2025 François G. Dorais. All rights reserved.\nReleased under Apache 2.0 license as described in the fil"
  },
  {
    "path": "Batteries/Data/Nat/Bitwise.lean",
    "chars": 56,
    "preview": "module\n\npublic import Batteries.Data.Nat.Bitwise.Lemmas\n"
  },
  {
    "path": "Batteries/Data/Nat/Gcd.lean",
    "chars": 400,
    "preview": "/-\nCopyright (c) 2014 Jeremy Avigad. All rights reserved.\nReleased under Apache 2.0 license as described in the file LIC"
  },
  {
    "path": "Batteries/Data/Nat/Lemmas.lean",
    "chars": 10264,
    "preview": "/-\nCopyright (c) 2016 Microsoft Corporation. All rights reserved.\nReleased under Apache 2.0 license as described in the "
  },
  {
    "path": "Batteries/Data/Nat.lean",
    "chars": 253,
    "preview": "module\n\npublic import Batteries.Data.Nat.Basic\npublic import Batteries.Data.Nat.Bisect\npublic import Batteries.Data.Nat."
  },
  {
    "path": "Batteries/Data/PairingHeap.lean",
    "chars": 14595,
    "preview": "/-\nCopyright (c) 2022 Yuyang Zhao. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICEN"
  },
  {
    "path": "Batteries/Data/RBMap/Alter.lean",
    "chars": 16184,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Data/RBMap/Basic.lean",
    "chars": 48278,
    "preview": "/-\nCopyright (c) 2017 Microsoft Corporation. All rights reserved.\nReleased under Apache 2.0 license as described in the "
  },
  {
    "path": "Batteries/Data/RBMap/Depth.lean",
    "chars": 3087,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Data/RBMap/Lemmas.lean",
    "chars": 56641,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Data/RBMap/WF.lean",
    "chars": 26351,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Data/RBMap.lean",
    "chars": 211,
    "preview": "module\n\npublic import Batteries.Data.RBMap.Alter\npublic import Batteries.Data.RBMap.Basic\npublic import Batteries.Data.R"
  },
  {
    "path": "Batteries/Data/Random/MersenneTwister.lean",
    "chars": 5812,
    "preview": "/-\nCopyright (c) 2024 François G. Dorais. All rights reserved.\nReleased under Apache 2.0 license as described in the fil"
  },
  {
    "path": "Batteries/Data/Random.lean",
    "chars": 60,
    "preview": "module\n\npublic import Batteries.Data.Random.MersenneTwister\n"
  },
  {
    "path": "Batteries/Data/Range/Lemmas.lean",
    "chars": 536,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Data/Range.lean",
    "chars": 50,
    "preview": "module\n\npublic import Batteries.Data.Range.Lemmas\n"
  },
  {
    "path": "Batteries/Data/Rat/Float.lean",
    "chars": 864,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Data/Rat.lean",
    "chars": 47,
    "preview": "module\n\npublic import Batteries.Data.Rat.Float\n"
  },
  {
    "path": "Batteries/Data/RunningStats.lean",
    "chars": 2055,
    "preview": "/-\nCopyright (c) 2025 François G. Dorais. All rights reserved.\nReleased under Apache 2.0 license as described in the fil"
  },
  {
    "path": "Batteries/Data/Stream.lean",
    "chars": 2873,
    "preview": "/-\nCopyright (c) 2024 François G. Dorais. All rights reserved.\nReleased under Apache 2. license as described in the file"
  },
  {
    "path": "Batteries/Data/String/AsciiCasing.lean",
    "chars": 3172,
    "preview": "/-\nCopyright (c) 2025 Christopher Bailey. All rights reserved.\nReleased under Apache 2.0 license as described in the fil"
  },
  {
    "path": "Batteries/Data/String/Basic.lean",
    "chars": 1320,
    "preview": "/-\nCopyright (c) 2022 Jannis Limperg. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Data/String/Legacy.lean",
    "chars": 18894,
    "preview": "/-\nCopyright (c) 2016 Microsoft Corporation. All rights reserved.\nReleased under Apache 2.0 license as described in the "
  },
  {
    "path": "Batteries/Data/String/Lemmas.lean",
    "chars": 53857,
    "preview": "/-\nCopyright (c) 2023 Bulhwi Cha. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICENS"
  },
  {
    "path": "Batteries/Data/String/Matcher.lean",
    "chars": 4314,
    "preview": "/-\nCopyright (c) 2023 F. G. Dorais. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICE"
  },
  {
    "path": "Batteries/Data/String.lean",
    "chars": 228,
    "preview": "module\n\npublic import Batteries.Data.String.AsciiCasing\npublic import Batteries.Data.String.Basic\npublic import Batterie"
  },
  {
    "path": "Batteries/Data/UInt.lean",
    "chars": 7966,
    "preview": "/-\nCopyright (c) 2023 François G. Dorais. All rights reserved.\nReleased under Apache 2.0 license as described in the fil"
  },
  {
    "path": "Batteries/Data/UnionFind/Basic.lean",
    "chars": 23893,
    "preview": "/-\nCopyright (c) 2021 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Data/UnionFind/Lemmas.lean",
    "chars": 6303,
    "preview": "/-\nCopyright (c) 2021 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Data/UnionFind.lean",
    "chars": 99,
    "preview": "module\n\npublic import Batteries.Data.UnionFind.Basic\npublic import Batteries.Data.UnionFind.Lemmas\n"
  },
  {
    "path": "Batteries/Data/Vector/Basic.lean",
    "chars": 509,
    "preview": "/-\nCopyright (c) 2024 Shreyas Srinivas. All rights reserved.\nReleased under Apache 2.0 license as described in the file "
  },
  {
    "path": "Batteries/Data/Vector/Lemmas.lean",
    "chars": 3904,
    "preview": "/-\nCopyright (c) 2024 Shreyas Srinivas. All rights reserved.\nReleased under Apache 2.0 license as described in the file "
  },
  {
    "path": "Batteries/Data/Vector/Monadic.lean",
    "chars": 1456,
    "preview": "/-\nCopyright (c) 2025 Lean FRO, LLC. All rights reserved.\nReleased under Apache 2.0 license as described in the file LIC"
  },
  {
    "path": "Batteries/Data/Vector.lean",
    "chars": 137,
    "preview": "module\n\npublic import Batteries.Data.Vector.Basic\npublic import Batteries.Data.Vector.Lemmas\npublic import Batteries.Dat"
  },
  {
    "path": "Batteries/Lean/AttributeExtra.lean",
    "chars": 4535,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Lean/EStateM.lean",
    "chars": 6798,
    "preview": "/-\nCopyright (c) 2024 Lean FRO, LLC. All rights reserved.\nReleased under Apache 2.0 license as described in the file LIC"
  },
  {
    "path": "Batteries/Lean/Except.lean",
    "chars": 1982,
    "preview": "/-\nCopyright (c) 2023 Lean FRO, LLC. All rights reserved.\nReleased under Apache 2.0 license as described in the file LIC"
  },
  {
    "path": "Batteries/Lean/Expr.lean",
    "chars": 3665,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Lean/Float.lean",
    "chars": 3720,
    "preview": "/-\n Copyright (c) 2023 Mario Carneiro. All rights reserved.\n Released under Apache 2.0 license as described in the file "
  },
  {
    "path": "Batteries/Lean/HashMap.lean",
    "chars": 1185,
    "preview": "/-\nCopyright (c) 2022 Jannis Limperg. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Lean/HashSet.lean",
    "chars": 833,
    "preview": "/-\nCopyright (c) 2022 Jannis Limperg. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Lean/IO/Process.lean",
    "chars": 1284,
    "preview": "/-\nCopyright (c) 2023 Kim Morrison. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICE"
  },
  {
    "path": "Batteries/Lean/Json.lean",
    "chars": 930,
    "preview": "/-\n Copyright (c) 2022 E.W.Ayers. All rights reserved.\n Released under Apache 2.0 license as described in the file LICEN"
  },
  {
    "path": "Batteries/Lean/LawfulMonad.lean",
    "chars": 1499,
    "preview": "/-\nCopyright (c) 2024 Lean FRO, LLC. All rights reserved.\nReleased under Apache 2.0 license as described in the file LIC"
  },
  {
    "path": "Batteries/Lean/LawfulMonadLift.lean",
    "chars": 2761,
    "preview": "/-\nCopyright (c) 2025 Quang Dao. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICENSE"
  },
  {
    "path": "Batteries/Lean/Meta/Basic.lean",
    "chars": 5728,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Lean/Meta/DiscrTree.lean",
    "chars": 1749,
    "preview": "/-\nCopyright (c) 2022 Jannis Limperg. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Lean/Meta/Expr.lean",
    "chars": 428,
    "preview": "/-\nCopyright (c) 2022 Jannis Limperg. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Lean/Meta/Inaccessible.lean",
    "chars": 2276,
    "preview": "/-\nCopyright (c) 2022 Jannis Limperg. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Lean/Meta/InstantiateMVars.lean",
    "chars": 2245,
    "preview": "/-\nCopyright (c) 2022 Jannis Limperg. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Lean/Meta/SavedState.lean",
    "chars": 1622,
    "preview": "/-\nCopyright (c) 2022 Jannis Limperg. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Lean/Meta/Simp.lean",
    "chars": 2290,
    "preview": "/-\nCopyright (c) 2022 Kim Morrison. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICE"
  },
  {
    "path": "Batteries/Lean/Meta/UnusedNames.lean",
    "chars": 4066,
    "preview": "/-\nCopyright (c) 2022 Jannis Limperg. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Lean/MonadBacktrack.lean",
    "chars": 568,
    "preview": "/-\nCopyright (c) 2022 Jannis Limperg. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Lean/NameMapAttribute.lean",
    "chars": 2530,
    "preview": "/-\nCopyright (c) 2022 E.W.Ayers. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICENSE"
  },
  {
    "path": "Batteries/Lean/PersistentHashMap.lean",
    "chars": 2350,
    "preview": "/-\nCopyright (c) 2022 Jannis Limperg. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Lean/PersistentHashSet.lean",
    "chars": 2197,
    "preview": "/-\nCopyright (c) 2022 Jannis Limperg. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Lean/Position.lean",
    "chars": 4821,
    "preview": "/-\nCopyright (c) 2023 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Lean/SatisfiesM.lean",
    "chars": 1517,
    "preview": "/-\nCopyright (c) 2024 Lean FRO, LLC. All rights reserved.\nReleased under Apache 2.0 license as described in the file LIC"
  },
  {
    "path": "Batteries/Lean/Syntax.lean",
    "chars": 582,
    "preview": "/-\nCopyright (c) 2022 Microsoft Corporation. All rights reserved.\nReleased under Apache 2.0 license as described in the "
  },
  {
    "path": "Batteries/Lean/System/IO.lean",
    "chars": 913,
    "preview": "/-\nCopyright (c) 2023 Kim Morrison. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICE"
  },
  {
    "path": "Batteries/Lean/TagAttribute.lean",
    "chars": 677,
    "preview": "/-\nCopyright (c) 2022 Gabriel Ebner. All rights reserved.\nReleased under Apache 2.0 license as described in the file LIC"
  },
  {
    "path": "Batteries/Lean/Util/EnvSearch.lean",
    "chars": 884,
    "preview": "/-\nCopyright (c) 2021 Shing Tak Lam. All rights reserved.\nReleased under Apache 2.0 license as described in the file LIC"
  },
  {
    "path": "Batteries/Linter/UnnecessarySeqFocus.lean",
    "chars": 6635,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Linter/UnreachableTactic.lean",
    "chars": 4887,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Linter.lean",
    "chars": 118,
    "preview": "module\n\npublic meta import Batteries.Linter.UnreachableTactic\npublic meta import Batteries.Linter.UnnecessarySeqFocus\n"
  },
  {
    "path": "Batteries/Logic.lean",
    "chars": 4182,
    "preview": "/-\nCopyright (c) 2014 Microsoft Corporation. All rights reserved.\nReleased under Apache 2.0 license as described in the "
  },
  {
    "path": "Batteries/Tactic/Alias.lean",
    "chars": 8185,
    "preview": "/-\nCopyright (c) 2017 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Tactic/Basic.lean",
    "chars": 298,
    "preview": "module\n\npublic meta import Lean.Elab.Tactic.ElabTerm\npublic meta import Batteries.Linter\npublic meta import Batteries.Ta"
  },
  {
    "path": "Batteries/Tactic/Case.lean",
    "chars": 8578,
    "preview": "/-\nCopyright (c) 2023 Kyle Miller. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICEN"
  },
  {
    "path": "Batteries/Tactic/Congr.lean",
    "chars": 4153,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Tactic/Exact.lean",
    "chars": 798,
    "preview": "/-\nCopyright (c) 2023 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Tactic/GeneralizeProofs.lean",
    "chars": 23219,
    "preview": "/-\nCopyright (c) 2022 Alex J. Best. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICE"
  },
  {
    "path": "Batteries/Tactic/HelpCmd.lean",
    "chars": 14598,
    "preview": "/-\nCopyright (c) 2024 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Tactic/Init.lean",
    "chars": 4474,
    "preview": "/-\nCopyright (c) 2021 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Tactic/Instances.lean",
    "chars": 2780,
    "preview": "/-\nCopyright (c) 2023 Kyle Miller. All rights reserved.\nReleased under Apache 2.0 license as described in the file LICEN"
  },
  {
    "path": "Batteries/Tactic/Lemma.lean",
    "chars": 2054,
    "preview": "/-\nCopyright (c) 2024 Johan Commelin. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Tactic/Lint/Basic.lean",
    "chars": 7069,
    "preview": "/-\nCopyright (c) 2020 Floris van Doorn. All rights reserved.\nReleased under Apache 2.0 license as described in the file "
  },
  {
    "path": "Batteries/Tactic/Lint/Frontend.lean",
    "chars": 14761,
    "preview": "/-\nCopyright (c) 2020 Floris van Doorn. All rights reserved.\nReleased under Apache 2.0 license as described in the file "
  },
  {
    "path": "Batteries/Tactic/Lint/Misc.lean",
    "chars": 13726,
    "preview": "/-\nCopyright (c) 2020 Floris van Doorn. All rights reserved.\nReleased under Apache 2.0 license as described in the file "
  },
  {
    "path": "Batteries/Tactic/Lint/Simp.lean",
    "chars": 14515,
    "preview": "/-\nCopyright (c) 2020 Gabriel Ebner. All rights reserved.\nReleased under Apache 2.0 license as described in the file LIC"
  },
  {
    "path": "Batteries/Tactic/Lint/TypeClass.lean",
    "chars": 1882,
    "preview": "/-\nCopyright (c) 2022 Microsoft Corporation. All rights reserved.\nReleased under Apache 2.0 license as described in the "
  },
  {
    "path": "Batteries/Tactic/Lint.lean",
    "chars": 223,
    "preview": "module\n\npublic import Batteries.Tactic.Lint.Basic\npublic import Batteries.Tactic.Lint.Misc\npublic import Batteries.Tacti"
  },
  {
    "path": "Batteries/Tactic/NoMatch.lean",
    "chars": 1616,
    "preview": "/-\nCopyright (c) 2021 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Tactic/OpenPrivate.lean",
    "chars": 6941,
    "preview": "/-\nCopyright (c) 2021 Microsoft Corporation. All rights reserved.\nReleased under Apache 2.0 license as described in the "
  },
  {
    "path": "Batteries/Tactic/PermuteGoals.lean",
    "chars": 2593,
    "preview": "/-\nCopyright (c) 2022 Arthur Paulino. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Tactic/PrintDependents.lean",
    "chars": 5012,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Tactic/PrintOpaques.lean",
    "chars": 3234,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Tactic/PrintPrefix.lean",
    "chars": 4548,
    "preview": "/-\nCopyright (c) 2021 Shing Tak Lam. All rights reserved.\nReleased under Apache 2.0 license as described in the file LIC"
  },
  {
    "path": "Batteries/Tactic/SeqFocus.lean",
    "chars": 1415,
    "preview": "/-\nCopyright (c) 2022 Jeremy Avigad. All rights reserved.\nReleased under Apache 2.0 license as described in the file LIC"
  },
  {
    "path": "Batteries/Tactic/ShowUnused.lean",
    "chars": 3138,
    "preview": "/-\nCopyright (c) 2024 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Tactic/SqueezeScope.lean",
    "chars": 7498,
    "preview": "/-\nCopyright (c) 2022 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Tactic/Trans.lean",
    "chars": 8322,
    "preview": "/-\nCopyright (c) 2022 Siddhartha Gadgil. All rights reserved.\nReleased under Apache 2.0 license as described in the file"
  },
  {
    "path": "Batteries/Tactic/Unreachable.lean",
    "chars": 1137,
    "preview": "/-\nCopyright (c) 2021 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Util/Cache.lean",
    "chars": 6847,
    "preview": "/-\nCopyright (c) 2021 Gabriel Ebner. All rights reserved.\nReleased under Apache 2.0 license as described in the file LIC"
  },
  {
    "path": "Batteries/Util/ExtendedBinder.lean",
    "chars": 2184,
    "preview": "/-\nCopyright (c) 2021 Microsoft Corporation. All rights reserved.\nReleased under Apache 2.0 license as described in the "
  },
  {
    "path": "Batteries/Util/LibraryNote.lean",
    "chars": 4026,
    "preview": "/-\nCopyright (c) 2022 Gabriel Ebner. All rights reserved.\nReleased under Apache 2.0 license as described in the file LIC"
  },
  {
    "path": "Batteries/Util/Panic.lean",
    "chars": 399,
    "preview": "/-\nCopyright (c) 2024 François G. Dorais. All rights reserved.\nReleased under Apache 2.0 license as described in the fil"
  },
  {
    "path": "Batteries/Util/Pickle.lean",
    "chars": 1597,
    "preview": "/-\nCopyright (c) 2023 Mario Carneiro. All rights reserved.\nReleased under Apache 2.0 license as described in the file LI"
  },
  {
    "path": "Batteries/Util/ProofWanted.lean",
    "chars": 1593,
    "preview": "/-\nCopyright (c) 2023 Lean FRO, LLC. All rights reserved.\nReleased under Apache 2.0 license as described in the file LIC"
  },
  {
    "path": "Batteries.lean",
    "chars": 4599,
    "preview": "module\n\npublic import Batteries.Classes.Cast\npublic import Batteries.Classes.Deprecated\npublic import Batteries.Classes."
  },
  {
    "path": "BatteriesTest/ArrayMap.lean",
    "chars": 242,
    "preview": "import Batteries.Data.List.ArrayMap\n\nopen List\n\n/-- info: #[3, 4, 5, 6] -/\n#guard_msgs in\n#eval List.toArrayMap [0, 1, 2"
  },
  {
    "path": "BatteriesTest/Char.lean",
    "chars": 940,
    "preview": "import Batteries.Data.Char\n\n/- Failing on nightly-2025-12-18\n#guard Char.caseFoldAsciiOnly 'A' == 'a'\n#guard Char.caseFo"
  },
  {
    "path": "BatteriesTest/GeneralizeProofs.lean",
    "chars": 6407,
    "preview": "import Batteries.Tactic.GeneralizeProofs\n\nprivate axiom test_sorry : ∀ {α}, α\nset_option autoImplicit true\nnoncomputable"
  },
  {
    "path": "BatteriesTest/Internal/DummyLabelAttr.lean",
    "chars": 497,
    "preview": "/-\nCopyright (c) 2023 Lean FRO, LLC. All rights reserved.\nReleased under Apache 2.0 license as described in the file LIC"
  },
  {
    "path": "BatteriesTest/Internal/DummyLibraryNote.lean",
    "chars": 443,
    "preview": "import Batteries.Util.LibraryNote\n\nlibrary_note «test1» /--\n1: This is a testnote for testing the library note feature o"
  },
  {
    "path": "BatteriesTest/Internal/DummyLibraryNote2.lean",
    "chars": 435,
    "preview": "import BatteriesTest.Internal.DummyLibraryNote\n\nlibrary_note «test3» /--\n3: this is a note in a different file importing"
  },
  {
    "path": "BatteriesTest/MLList.lean",
    "chars": 1063,
    "preview": "import Lean.Meta.Basic\nimport Batteries.Data.MLList.IO\nimport Batteries.Data.List.Basic\n\nset_option linter.missingDocs f"
  },
  {
    "path": "BatteriesTest/OpenPrivateDefs.lean",
    "chars": 120,
    "preview": "/-!\nThis file contains a private declaration. It's tested in `openPrivate.lean`.\n-/\nprivate def secretNumber : Nat := 2\n"
  },
  {
    "path": "BatteriesTest/String.lean",
    "chars": 1467,
    "preview": "/- Failing on nightly-2025-12-18\nimport Batteries.Data.String.AsciiCasing\n\n#guard \"ABC\".caseFoldAsciiOnly == \"abc\"\n#guar"
  },
  {
    "path": "BatteriesTest/absurd.lean",
    "chars": 1043,
    "preview": "import Batteries.Tactic.Basic\n\n/-! Tests for `absurd` tactic -/\n\n-- Basic example\n/--\nerror: unsolved goals\np : Prop\nh :"
  }
]

// ... and 70 more files (download for full content)

About this extraction

This page contains the full source code of the leanprover/std4 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 270 files (1.2 MB), approximately 437.3k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!