## Lecture 13
using LinearAlgebra
""" Compute a rotation matrix such that
  G = Givens(v, i, j, sigma=1)
  returns an orthogonal matrix G
  where
  (G*v)[j] = 0
  and
  (G*v)[i] has sign sigma.
  Also, the vector is modified so that we return G*v
"""
function Givens!(v::AbstractVector, i::Int, j::Int, sigma::Int=1)
  @assert i != j "the indices must be disjoint"
  v1 = v[i]
  v2 = v[j]
  d = sigma*sqrt(v1^2 + v2^2)
  c = v1/d
  s = v2/d
  G = Matrix(1.0I, length(v), length(v))
  G[i,i] = c
  G[j,j] = c
  G[i,j] = s
  G[j,i] = -s
  v[i] = d
  v[j] = 0
  return G, v
end
v = randn(10)
G, Gv = Givens!(copy(v), 9, 10)
@show norm(G*v - Gv)
##
""" Compute a QR factorization via a sequence of Givens
rotations. This is a pedagogical routine. It can be
greatly improved!
"""
function givens_qr!(A)
  m,n = size(A)
  Qt = Matrix(1.0I, m, m)
  for j=1:n
    v = @view A[:,j]
    Z = @view A[:,j+1:end]
    for i=m:-1:(1+j)
      # rotate i to i-1
      G = Givens!(v, i-1, i)[1]
      Qt = G*Qt
      Z .= G*Z
    end
  end
  return copy(Qt'), A
end
A = randn(10,4)
Q, R = givens_qr!(copy(A))
## Givens least squares
function least_squares_givens(A,b)
  m,n = size(A)
  Q,R = givens_qr!(copy(A))
  bt = Q'*b
  x = R[1:n,1:n]\bt[1:n]
  return x
end
A = randn(10,4)
b = randn(10)
xtrue = A\b
norm(xtrue - least_squares_givens(A,b))
##