function _degree_centrality(g::SimpleGraph, gtype::Integer; normalize=true)
   n_v = nv(g)
   c = zeros(n_v)
   for v in 1:n_v
       if gtype == 0    # count both in and out degree if appropriate
           deg = outdegree(g, v) + (typeof(g) == DiGraph? indegree(g, v) : 0.0)
       elseif gtype == 1    # count only in degree
           deg = indegree(g, v)
       else                 # count only out degree
           deg = outdegree(g, v)
       end
       s = normalize? (1.0 / (n_v - 1.0)) : 1.0
       c[v] = deg*s
   end
   return c
end

# TODO avoid repetition of this docstring
"""Calculates the [degree centrality](https://en.wikipedia.org/wiki/Centrality#Degree_centrality)
of the graph `g`, with optional (default) normalization."""
degree_centrality(g::SimpleGraph; all...) = _degree_centrality(g, 0; all...)
"""Calculates the [degree centrality](https://en.wikipedia.org/wiki/Centrality#Degree_centrality)
of the graph `g`, with optional (default) normalization."""
indegree_centrality(g::SimpleGraph; all...) = _degree_centrality(g, 1; all...)
"""Calculates the [degree centrality](https://en.wikipedia.org/wiki/Centrality#Degree_centrality)
of the graph `g`, with optional (default) normalization."""
outdegree_centrality(g::SimpleGraph; all...) = _degree_centrality(g, 2; all...)


using FFTW
using DifferentialEquations
using Plots

# Define spatial domain
c = 2 # Wave speed
L = 20 # Length of domain
N = 1000 # Number of discretization points
dx = L/N
x = -L/2:dx:L/2-dx # Define x domain

# Define discrete wavenumbers
kappa = (2pi/L)*(-N/2:N/2-1)
kappa = fftshift(kappa) # Re-order fft wavenumbers

# Initial condition
u0 = sech.(x)
uhat0 = fft(u0)

# Simulate in Fourier frequency domain
f(u, p, t) = -c*im*kappa.*u
prob = ODEProblem(f, uhat0, (0.0,2.5))
uhat = solve(prob)

# Inverse FFT to bring back to spatial domain
u = zeros(size(uhat))
for k = 1:size(u)[2]
    u[:,k] = real(ifft(uhat[:,k]))
end

# Alternatively, simulate in spatial domain
f2(u, p, t) = -c*real.(ifft(im*kappa.*fft(u)))
prob2 = ODEProblem(f2, u0, (0.0,2.5))
u2 = solve(prob2)

# Plot solution in time
p1 = surface(u)
p2 = surface(hcat(u2.u...))


function orthogonalize(x, bases)
    F = 1.0
    x_new = copy(x)
    @inbounds for j = 1:length(bases)
        cji = bases[j] ⋅ x
        F -= cji ^ 2
        x_new -= cji * bases[j]
    end
    x_new /= √F
    x_new
end

@doc raw"""All Constraints. Fixed lengths, potential energy, etc.

```math
P = 1 - \sum_{i}^{N_C}|C_i'\rangle\langle C_i'|
```
"""
function projection_matrix(∇Cs)
    N_C = length(∇Cs)
    ∇C_unit_new = normalize.(∇Cs)
    P = I
    @inbounds for i = 1:N_C
        ∇C_unit_new[i] = orthogonalize(∇C_unit_new[i], ∇C_unit_new[1:i-1])
        tmp = flatten(∇C_unit_new[i])
        P -= tmp * tmp'
    end
    P
end