Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complex numbers as a field #21

Merged
merged 7 commits into from
Jan 7, 2018
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
281 changes: 281 additions & 0 deletions analysis/complex.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
/-
Copyright (c) 2017 Kevin Buzzard. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Kevin Buzzard

The complex numbers, modelled as R^2 in the obvious way.

Natural next step: one could prove that complexes are a topological ring.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Obviously, the commentary should be removed. We can continue the discussion here on github.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to tidy up / remove comments.

-/
import analysis.real
noncomputable theory
-- because reals are noncomputable

local attribute [instance] classical.decidable_inhabited classical.prop_decidable
-- because I don't know how to do inverses sensibly otherwise;
-- e.g. I needed to know that if z was non-zero then either its real part
-- was non-zero or its imaginary part was non-zero.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine, we're in classical land here, although you probably don't need it since the reals already have a decidable_eq instance. Easy way to find out: remove it and see what breaks.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed it and stuff broke so I put it back.


structure complex : Type :=
(re : ℝ) (im : ℝ)

notation `ℂ` := complex

-- definition goes outside namespace, then everything else in it?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. You don't want the definition being called complex.complex.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK got it.


namespace complex

-- handy checks for equality etc

theorem eta (z : complex) : complex.mk z.re z.im = z :=
cases_on z (λ _ _, rfl)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no indentation after theorem name : ... :=. The cases_on should start in the first column

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK!


-- very useful

theorem eq_of_re_eq_and_im_eq (z w : complex) : z.re=w.re ∧ z.im=w.im → z=w :=
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use the form _ ∧ _ → _ always use the curried form _ → _ → _. In this case z.re=w.re → z.im=w.im → z=w.
Hint: use the equation compiler:

example : ∀ (z w : complex), z.re = w.re → z.im = w.im → z = w
| ⟨zr, zi⟩ ⟨wr, wi⟩ rfl rfl := rfl

begin
intro H,rw [←eta z,←eta w,H.left,H.right],
end

-- simp version

theorem eq_iff_re_eq_and_im_eq (z w : complex) : z=w ↔ z.re=w.re ∧ z.im=w.im :=
begin
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you should use a tactic proof here. I suggest to use a term mode proof here.
Take more care of indentation. Indent 2 spaces between begin and end.
Also: if you want to mark a sequence of tactics to solve one subgoal use { ... }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does anyone care whether I prove a lemma with a tactic proof or a term mode proof? I am serious -- I thought that by proof irrelevance nobody cares what my proofs look like. As long as they compile, we're done. Why am I wrong?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is for maintenance reasons. Tactic proofs tend to involve magic (like simp), and magic is fragile. If the list of simp lemma changes (say associativity comes back...) then proofs using simp can break. A term style proof tends to explicitly call out used lemmas. If such a lemma changes, it will probably much easier to understand what went wrong.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My tactic proof was three lines long and used split, intro, rw, trivial and exact. My term proof uses pretty much the same thing :-)

Copy link
Member

@digama0 digama0 Dec 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As always, you will get the best results with a combination of tactics and terms. Here's how this is proven in prod (which is basically identical to what you want here):

@[simp] theorem mk.inj_iff {a₁ a₂ : α} {b₁ b₂ : β} : (a₁, b₁) = (a₂, b₂) ↔ (a₁ = a₂ ∧ b₁ = b₂) :=
⟨prod.mk.inj, by cc⟩

lemma eq_iff_fst_eq_snd_eq : ∀{p q : α × β}, p = q ↔ (p.1 = q.1 ∧ p.2 = q.2)
| ⟨p₁, p₂⟩ ⟨q₁, q₂⟩ := by simp

It is nice but usually surprising to see a successful proof by cc. The first thing I would have tried is λ ⟨h₁, h₂⟩, by congr; assumption instead of by cc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I must admit I didn't look at the proof before writing my comment. But it gives me the opportunity to ask what is this cc tactic. The documentation is not very illuminating to me.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc means "congruence closure" but it's one of those tactics that I (and my students) tend to just try occasionally to see if it finishes a (typically proof-by-basic-logic-arguments-only) proof.

split,
intro H,rw [H],split;trivial,
exact eq_of_re_eq_and_im_eq _ _,
end

lemma proj_re (r0 i0 : real) : (complex.mk r0 i0).re = r0 := rfl
lemma proj_im (r0 i0 : real) : (complex.mk r0 i0).im = i0 := rfl

local attribute [simp] eq_iff_re_eq_and_im_eq proj_re proj_im

-- Am I right in
-- thinking that the end user should not need to
-- have to use this function?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, they (and you) should use the coercion wherever possible.


def of_real : ℝ → ℂ := λ x, { re := x, im := 0 }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be def of_real (r : ℝ) : ℂ := { re := x, im := 0 }
This is not only an aesthetic reason (why use the unnecessary lambda?), but a practical one:
With of_real : ℝ → ℂ the generated simp rule for of_real matches all occurrences of of_real, while the later only matches of_real r.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This I really do not understand -- I mean, I understand that you're asking me to change something, and I understand how to change it, but I do not understand why changing something to something which looks to me to be definitionally equal should be a sensible idea. Of course I do not doubt that it is! But I would really like to understand better where these comments are coming from. What is "the generated simp rule for of_real"?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should try #print prefix of_real with both versions. The generated *.equations lemma for of_real will have an x in Johannes's version on the LHS, meaning that if you simp [of_real] it will not unfold unless it's already been applied to an argument.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My slow understanding of these comments is currently at the stage where I understand that even though the two approaches give definitionally equal functions, the associated equation lemmas are not the same, however I am unclear about why one prefers Johannes' version, because I am not clear about what is unfolding or whether we want it to unfold. This, as is sometimes the case with me, seems to come back to the fact that I do not actually understand what simp is doing or how it does it. I guess that's my point here -- the two equation lemmas are "different ways of saying exactly the same thing" to me, but for someone who knows better what simp is doing I guess this is not the case.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me try the following explanation:
First, in this case unfold just means, that simp will rewrite occurrences of_real using the equation provided by the definition.

Second, it is hard to say why I prefer the of_real r variant. At first glance the of_real : R -> C variant would match more often. But by experience I had quiet some proofs where I only want to replace f x instead of every occurrence of f.


-- does one name these instances or not? I've named a random selection

instance coe_real_complex : has_coe ℝ ℂ := ⟨of_real⟩
instance : has_zero complex := ⟨of_real 0⟩
instance : has_one complex := ⟨of_real 1⟩
instance inhabited_complex : inhabited complex := ⟨0⟩

-- dangerously short variable name so I protected it.
-- It's never used in the library (other than in the projection
-- commands) but I think end users will use it.

protected def i : complex := {re := 0, im := 1}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I would want to avoid direct reference to complex.mk, replacing { re := x, im := y } with x + i*y, which is the standard notation (except possibly in the definitions of add etc.)

A possibility for i is to use I (which is rarely used because of the capitals) or a unicode i-like character. You shouldn't protect i in any case, users will want to open complex specifically so they can use i as is, and there is little danger of overwriting a variable name since local variables take precedence.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've switched to I.


def conjugate (z : complex) : complex := {re := z.re, im := -(z.im)}

-- Are these supposed to be protected too?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conventionally, yes we protect add and other things that have definitions in the root namespace (although maybe that convention should change now that _root_.add is gone). This is not usually a problem because these are accessed through their has_add instances anyway.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've protected add, neg, mul, inv


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need explicit constants for complex.add, complex.neg, complex.mul, this all should be in the corresponding definition of has_add etc. Especially as you do not need the equation compiler or proof anything special for them before you set up the instances of has_add etc.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see! Thanks.

def add : complex → complex → complex :=
λ z w, { re :=z.re+w.re, im:=z.im+w.im}

def neg : complex → complex :=
λ z, { re := -z.re, im := -z.im}

def mul : complex → complex → complex :=
λ z w, { re := z.re*w.re - z.im*w.im,
im := z.re*w.im + z.im*w.re}

def norm_squared : complex → real :=
λ z, z.re*z.re+z.im*z.im

def inv : complex → complex :=
λ z, { re := z.re / norm_squared z,
im := -z.im / norm_squared z }

instance : has_add complex := ⟨complex.add⟩
instance : has_neg complex := ⟨complex.neg⟩
instance : has_sub complex := ⟨λx y, x + - y⟩
instance : has_mul complex := ⟨complex.mul⟩
instance : has_inv complex := ⟨complex.inv⟩
instance : has_div complex := ⟨λx y, x * y⁻¹⟩
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw that gitter conversation with Kenny, he diagnosed your problem well. Don't define has_sub and has_div instances, as this will cause an undesirable shadowing situation and may cause the usual theorems to not apply correctly (like div_mul_cancel in your example). This does mean that you won't be able to use these notations until you have proven it's a field (you can also split up the proof so that you prove it's an additive group first, then you get subtraction and can use it in the rest of the proof that it's a field).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the has_sub and has_div instances and split the proof up but it caused problems -- when proving the complexes formed a field, I seemed to end up with a situation where complexes would sometimes forget they had an addition. I ended up proving all the additive group axioms a second time when proving the complexes were a field and I am concerned that my code is now somehow worse as I seem to be defining addition on the complexes more than once.


-- I was initially astounded to find that at some point there was a typo in has_div but
-- this didn't cause any problems at all. I have since understood what is
-- going on: "/" is never used in the field axioms, only ^{-1} .

-- These are very useful for proofs in the library so I make them local simp lemmas.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No harm in having these as global simp lemmas.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am slightly reluctant to overload simp, especially as I don't really understand how it works.


lemma proj_zero_re : (0:complex).re=0 := rfl
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the special rules for complex.add etc. Instead of has_add.add use + etc.
Drop the proj_ prefix and the ' suffix.
These are all good simp rules, set them up globally, i.e. @[simp] everywhere instead of just local attribute.

lemma proj_zero_im : (0:complex).im=0 := rfl
lemma proj_one_re : (1:complex).re=1 := rfl
lemma proj_one_im : (1:complex).im=0 := rfl
lemma proj_i_re : complex.i.re=0 := rfl
lemma proj_i_im : complex.i.im=1 := rfl
lemma proj_conj_re (z : complex) : (conjugate z).re = z.re := rfl
lemma proj_conj_im (z : complex) : (conjugate z).im = -z.im := rfl
lemma proj_add_re (z w: complex) : (z+w).re=z.re+w.re := rfl
lemma proj_add_im (z w: complex) : (z+w).im=z.im+w.im := rfl
lemma proj_neg_re (z: complex) : (-z).re=-z.re := rfl
lemma proj_neg_im (z: complex) : (-z).im=-z.im := rfl
lemma proj_neg_re' (z: complex) : (neg z).re=-z.re := rfl
lemma proj_neg_im' (z: complex) : (neg z).im=-z.im := rfl
lemma proj_sub_re (z w : complex) : (z-w).re=z.re-w.re := rfl
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as before. Also move the of_real_im an of_real_re rules upwards, also see @digama0's hint to use coercion instead of of_real.

lemma proj_sub_im (z w : complex) : (z-w).im=z.im-w.im := rfl
lemma proj_mul_re (z w: complex) : (z*w).re=z.re*w.re-z.im*w.im := rfl
lemma proj_mul_im (z w: complex) : (z*w).im=z.re*w.im+z.im*w.re := rfl
lemma proj_of_real_re (r:real) : (of_real r).re = r := rfl
lemma proj_of_real_im (r:real) : (of_real r).im = 0 := rfl
local attribute [simp] proj_zero_re proj_zero_im proj_one_re proj_one_im
local attribute [simp] proj_i_re proj_i_im proj_conj_re proj_conj_im
local attribute [simp] proj_add_re proj_add_im proj_neg_re proj_neg_im
local attribute [simp] proj_neg_re' proj_neg_im' proj_sub_re proj_sub_im
local attribute [simp] proj_mul_re proj_mul_im proj_of_real_re proj_of_real_im

lemma norm_squared_pos_of_nonzero (z : complex) (H : z ≠ 0) : norm_squared z > 0 :=
begin -- far more painful than it should be but I need it for inverses
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right that this is overcomplicated. I'll clean it up after merge

suffices : z.re ≠ 0 ∨ z.im ≠ 0,
apply lt_of_le_of_ne,
exact add_nonneg (mul_self_nonneg _) (mul_self_nonneg _),
intro H2,
cases this with Hre Him,
exact Hre (eq_zero_of_mul_self_add_mul_self_eq_zero (eq.symm H2)),
unfold norm_squared at H2,rw [add_comm] at H2,
exact Him (eq_zero_of_mul_self_add_mul_self_eq_zero (eq.symm H2)),
have : ¬ (z.re = 0 ∧ z.im = 0),
intro H2,
exact H (eq_of_re_eq_and_im_eq z 0 H2),
cases classical.em (z.re = 0) with Hre_eq Hre_ne,
right,
intro H2,
apply this,
exact ⟨Hre_eq,H2⟩,
left,assumption,
end

-- I don't know how to set up
-- real.cast_zero etc
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's true that you can define a real.cast, but it has complicated topological side conditions and I'd rather not define it until I need it. Using of_real as the designated coercion is fine, although we will want theorems saying that the casts commute with of_real, i.e. of_real ((n : nat) : real) = ((n : nat) : complex) and similarly for rat and int.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wrote these for nat and int; I almost wrote one for rat but I needed that the complexes had characteristic zero. I proved this but then for some reason I could not put it all together -- I was getting timeouts. I've left my proof in but commented it out.


lemma of_real_injective : function.injective of_real :=
begin
intros x₁ x₂ H,
exact congr_arg complex.re H,
end

lemma of_real_zero : (0:complex) = of_real 0 := rfl
lemma of_real_one : (1:complex) = of_real 1 := rfl
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be a good simp lemma if pointed the other direction.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand why but I switched them round.


-- amateurish definition of killer tactic but it works!
meta def crunch : tactic unit := do
`[intros],
`[rw [eq_iff_re_eq_and_im_eq]],
`[split;simp[add_mul,mul_add]]

lemma of_real_neg (r : real) : -of_real r = of_real (-r) := by crunch

lemma of_real_add (r s: real) : of_real r + of_real s = of_real (r+s) := by crunch

lemma of_real_sub (r s:real) : of_real r - of_real s = of_real(r-s) := by crunch

lemma of_real_mul (r s:real) : of_real r * of_real s = of_real (r*s) := by crunch

lemma of_real_inv (r:real) : (of_real r)⁻¹ = of_real (r⁻¹) :=
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use coercions here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I changed these to use coercions

begin
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

of_real_add, of_real_sub, of_real_mul and of_real_inv are a rfl lemmas (i.e. they can be proved by just rfl).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also: indentation after begin

rw [eq_iff_re_eq_and_im_eq],
split,
suffices : r/(r*r+0*0) = r⁻¹,
exact this,
cases classical.em (r=0) with Heq Hne,
-- this is taking longer than it should be.
rw [Heq],
simp [inv_zero,div_zero],
rw [mul_zero,add_zero,div_mul_left r Hne,inv_eq_one_div],
suffices : -0/(r*r+0*0) = 0,
exact this,
rw [neg_zero,zero_div],
end

lemma of_real_abs_squared (r:real) : norm_squared (of_real r) = (abs r)*(abs r) :=
begin
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an interesting proof:
by simp [abs_mul_abs_self, norm_squared, of_real_eq_coe]

But it only works if norm_squared has as equational lemma norm_squared z = z.re*z.re+z.im*z.im!
Why: When normed_squared is rewritten using the lambda-term the simplifier gets to the term
(fun z, z.re*z.re+z.im*z.im) (of_real r), then it tries to simplify inside the lambda-term.But z.re and z.im cannot be simplified. Later it beta-reduces to (of_real r).re*(of_real r).re+(of_real r).im*(of_real r).im, somehow it does not try to rewrite after beta-reduction, so (of_real r).im and (of_real r).re isn't further simplified. Maybe this should be changed, but it is one occurrence where the normed_square c = equation form is preferred over the normed_square = fun c, ... form.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aah! So this is a very good way of explaining in concrete terms the issue that came up earlier. Thanks!

rw [abs_mul_abs_self],
suffices : r*r+0*0=r*r,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This suffices : ..., exact this can be replaces by show ....

exact this,
simp,
end

lemma add_comm : ∀ (a b : ℂ), a + b = b + a := by crunch

-- I don't think I ever use these actually.
local attribute [simp] of_real_zero of_real_one of_real_neg of_real_add
local attribute [simp] of_real_sub of_real_mul of_real_inv

instance : discrete_field complex :=
{ discrete_field .
zero := 0,
add := (+),
neg := complex.neg,
zero_add := by crunch,
add_zero := by crunch,
add_comm := by crunch,
add_assoc := by crunch,
add_left_neg := by crunch,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a fairly common pattern. Rather than reproving the new properties the second time around, just omit them entirely, and fill them in with { discrete_field . <new stuff> , ..complex.add_group }, where the dot dot notation says "copy the fields from here". See rat.discrete_linear_ordered_field for an example.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried this but then got into a real mess with type class inference. Will try again tomorrow.

one := 1,
mul := (*),
inv := has_inv.inv,
mul_one := by crunch,
one_mul := by crunch,
mul_comm := by crunch,
mul_assoc := by crunch,
left_distrib := by crunch,
right_distrib := by crunch,
zero_ne_one := begin
intro H,
suffices : (0:complex).re = (1:complex).re,
revert this,
apply zero_ne_one,
rw [←H],
end,
mul_inv_cancel := begin
intros z H,
have H2 : norm_squared z ≠ 0 := ne_of_gt (norm_squared_pos_of_nonzero z H),
apply eq_of_re_eq_and_im_eq,
unfold has_inv.inv inv,
rw [proj_mul_re,proj_mul_im],
split,
suffices : z.re*(z.re/norm_squared z) + -z.im*(-z.im/norm_squared z) = 1,
by simpa,
rw [←mul_div_assoc,←mul_div_assoc,neg_mul_neg,div_add_div_same],
unfold norm_squared at *,
exact div_self H2,
suffices : z.im * (z.re / norm_squared z) + z.re * (-z.im / norm_squared z) = 0,
by simpa,
rw [←mul_div_assoc,←mul_div_assoc,div_add_div_same],
simp [zero_div],
end,
inv_mul_cancel := begin -- let's try cut and pasting mul_inv_cancel proof
intros z H,
have H2 : norm_squared z ≠ 0 := ne_of_gt (norm_squared_pos_of_nonzero z H),
apply eq_of_re_eq_and_im_eq,
unfold has_inv.inv inv,
rw [proj_mul_re,proj_mul_im],
split,
suffices : z.re*(z.re/norm_squared z) + -z.im*(-z.im/norm_squared z) = 1,
by simpa,
rw [←mul_div_assoc,←mul_div_assoc,neg_mul_neg,div_add_div_same],
unfold norm_squared at *,
exact div_self H2,
suffices : z.im * (z.re / norm_squared z) + z.re * (-z.im / norm_squared z) = 0,
by simpa,
rw [←mul_div_assoc,←mul_div_assoc,div_add_div_same],
simp [zero_div],
end, -- it worked without modification!
-- Presumably I could just have proved mul_comm outside the verification that C is a field
-- and then used that too?
inv_zero := begin
unfold has_inv.inv inv add_comm_group.zero,
apply eq_of_re_eq_and_im_eq,
split;simp [zero_div],
end,
has_decidable_eq := by apply_instance }

-- instance : topological_ring complex := missing

end complex