### Every hero needs a nemesis

While `perm`

has been a crucial ingredient in our previous victory against malevolent `length_sum_perm_of_B`

, it is still but a young padawan and must be trained properly to reach its full potential. And the best way to do that, of course, is introducing a formidable enemy:

```
theorem perm_training_ground
(Bacb : B a c b)
(Bcbe : B c b e)
(Bbed : B b e d)
(h1 : length b a = length d b)
(h2 : length e b + length a c = length d e + length c b)
(h3 : length a c + length d e = length e c) :
length a c = length b c ∧
length b e = length c b ∧
length e b = length e d := by sorry
```

Yikes. Even the point order is unsettling. Let's not get intimidated and try to analyze the situation. The goal only talks about lengths, but we need to get the information about betweenness into play. Invoking `length_sum_of_B`

seems like a promising start:

```
have accb := length_sum_of_B Bacb
have beed := length_sum_of_B Bbed
have cbbe := length_sum_of_B Bcbe
```

### Growing `perm`

's boundaries

Now it would be nice to substitute these new equations into the original hypotheses, but unfortunately all of them have swapped arguments. Sounds like a time for `perm`

to shine! But…it can only handle permutations in the goal, not in the hypotheses. Luckily, `conv`

can also target a hypothesis, so let's fix that:

```
namespace Lean.Elab.Tactic
syntax "perm" ("at " ident)? : tactic
macro_rules
| `(tactic| perm) =>`(tactic|
(try conv in (occs := *) length _ _ => all_goals length_nf))
| `(tactic| perm at $name) =>`(tactic|
(try conv at $name in (occs := *) length _ _ => all_goals length_nf))
end Lean.Elab.Tactic
```

This was relatively straightforward — we just replaced `macro`

with `syntax`

and `macro_rules`

, extending the syntax with pattern matching (`ident`

will be matched to a hypothesis name and `?`

means that `"at " ident`

is optional). Calling `perm; perm at h1; perm at h2; perm at h3`

now puts all the arguments in the right order. But it seems a little awkward. Is this the best you can do, `perm`

?

```
syntax "perm" ("at " ident)? ("at *")? : tactic
macro_rules
| `(tactic| perm) =>`(tactic|
(try conv in (occs := *) length _ _ => all_goals length_nf))
| `(tactic| perm at $name) =>`(tactic|
(try conv at $name in (occs := *) length _ _ => all_goals length_nf))
elab_rules : tactic
| `(tactic| perm at *) => withMainContext do
evalTactic (← `(tactic| perm))
for ldecl in ← getLCtx do
let name := mkIdent ldecl.userName
if !ldecl.isImplementationDetail then
evalTactic (← `(tactic| perm at $name))
```

Now we're talking! `elab_rules`

allows us to go deeper, invoking `perm`

first by itself and that at every relevant hypothesis. (You might object this is a little wasteful, but in practice it's not an issue). Note that we also had to extend the syntax.

Let's continue the proof (`←`

refers to the symmetric version, so `rw [← abbc] at h1`

replaces the right hand side of `abbc`

at `h1`

by the left hand side):

```
perm at *
rw [← accb] at h1
rw [← beed] at h1
rw [← cbbe] at h3
```

The infoview is now cluttered with obsolete assumptions, so let's `clear`

it a bit: `clear Bacb Bcbe Bbed accb beed cbbe`

. Now it reads:

```
i : incidence_geometry
a b c d e: point
h1 : length a b + length b c = length c d + length d e
h2 : length c d + length a b = length d e + length b c
h3 : length a b + length d e = length b c + length c d
⊢ length a b = length b c ∧ length c d = length b c ∧ length c d = length d e
```

What is not so `clear`

, however, is how to proceed. Sure, we can call our pal `splitAll`

to get three subgoals, but what's next?

### The holy grail of linear algebra

This is an ideal place for getting stuck. We could try various things — subtracting terms from both sides of the equation and them substituting them elsewhere (perhaps in calc mode), using commutativity,…or we can just use `linarith`

. Isn't that cheating? Maybe a little, but this a powerful finishing tactic makes our live much easier. It closes the goal if it follows from linear (in)equalities in the context (or else fails), which is exactly case. Let's see it in action:

```
theorem perm_training_ground
(Bacb : B a c b)
(Bcbe : B c b e)
(Bbed : B b e d)
(h1 : length b a = length d b)
(h2 : length e b + length a c = length d e + length c b)
(h3 : length a c + length d e = length e c) :
length a c = length b c ∧
length b e = length c b ∧
length e b = length e d := by
have accb := length_sum_of_B Bacb
have beed := length_sum_of_B Bbed
have cbbe := length_sum_of_B Bcbe
perm at *
rw [← accb] at h1
rw [← beed] at h1
rw [← cbbe] at h3
clear Bacb Bcbe Bbed accb beed cbbe
splitAll
repeat linarith
```

Tada! The big bad boss has been conquered. And yet…it still feels like the proof could be more efficient. `linarith`

has a nice feature — `linarith[H1, H2,...]`

basically acts as `have := H1; have := H2; linarith`

without polluting the infoview. So maybe we could write `linarith[length_sum_of_B Bacb, length_sum_of_B Bbed, length_sum_of_B Bcbe]`

and get rid of the `have`

's and `rw`

's? Normally this would work well, but in this case, the hypotheses added inside `linarith`

would have the wrong order, and there is no opportunity to call `perm`

on them, as everything happens at once. What to do?

### A match made in heaven

Sometimes, a hero must make sacrifices and become a part of a greater whole. And what's better than both `perm`

and `linarith`

? You guessed it — their fusion, `linperm`

! We just need to augment `perm at *`

with `linarith`

's `[]`

capabilities and finish with `linarith`

. First, we need to mimic the `have`

behavior:

```
open Lean Meta in
def haveExpr (n:Name) (h:Expr) :=
withMainContext do
let t ← inferType h
liftMetaTactic fun mvarId => do
let mvarIdNew ← Lean.MVarId.assert mvarId n t h
let (_, mvarIdNew) ← Lean.MVarId.intro1P mvarIdNew
return [mvarIdNew]
```

Now we can use `haveExpr`

inside an auxilliary `havePerms`

tactic, which parses its arguments in brackets, adds them to the context as `this`

, and runs `perm`

on them:

```
open Parser Tactic Syntax
syntax "havePerms" (" [" term,* "]")? : tactic
elab_rules : tactic
| `(tactic| havePerms $[[$args,*]]?) => withMainContext do
let hyps := (← ((args.map (TSepArray.getElems)).getD {}).mapM
(elabTerm ·.raw none)).toList
for h in hyps do
haveExpr "this" h
evalTactic (← `(tactic| perm at $(mkIdent "this")))
```

It remains to extend `perm`

's syntax again by replacing

```
syntax "perm"("at " ident)? ("at *")? : tactic
```

with

```
syntax "perm" (" [" term,* "]")? ("at " ident)? ("at *")? : tactic
```

and let it use `havePerms`

internally:

```
macro_rules
| `(tactic| perm [$args,*] ) => `(tactic| havePerms [$args,*])
```

Finally, let's define `linperm`

by bundling it all together:

```
syntax "linperm " ("[" term,* "]")? : tactic
macro_rules
| `(tactic| linperm) => `(tactic| perm at *; linarith)
| `(tactic| linperm [$args,*] ) =>
`(tactic| perm at *; havePerms [$args,*]; linarith)
```

Phew. After all this hard work, it's time the harvest the fruit and watch `linperm`

in its moment of glory. Oh, how far has it come!

```
theorem perm_training_ground'
(Bacb : B a c b)
(Bcbe : B c b e)
(Bbed : B b e d)
(h1 : length b a = length d b)
(h2 : length e b + length a c = length d e + length c b)
(h3 : length a c + length d e = length e c) :
length a c = length b c ∧
length b e = length c b ∧
length e b = length e d := by
splitAll
repeat linperm [length_sum_of_B Bacb, length_sum_of_B Bcbe, length_sum_of_B Bbed]
```

There are still many other ways `(lin)perm`

could keep growing, but unlike lines in Euclidean geometry, stories must come to an end. So get out your handkerchiefs and say your goodbyes to our friend `(lin)perm`

, at least for now. I hope its journey through the tactic-writing world inspired you and you'll be able to level up your skills in the same way it did.

*If you have any suggestions for improving this blog post, please let me know.*