{-# LANGUAGE Safe #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}

--------------------------------------------------------------------------------------------
-- |
-- Copyright   :  (C) 2018 Nathan Waivio
-- License     :  BSD3
-- Maintainer  :  Nathan Waivio <nathan.waivio@gmail.com>
-- Stability   :  Stable
-- Portability :  unportable
--
-- Library implementing standard functions for the Jones Calculus in the Cl3 Library.
-- This implementation of the Jones Calculus is based on the convensions of SPIE's Field Guide to Polarization (ϕ = ω t − k z).
-- 
-- * E. Collett, Field Guide to Polarization, SPIE Field Guides vol. FG05, SPIE (2005). ISBN 0-8194-5868-6.
-- 
--  
-- = Jones Vectors
-- 
-- Within the system of the Bloch Sphere, the Jones Vectors in Cl3 are calculated
-- by generating the left ideal of the rotation of a unit vector to the 'e3' basis.
-- Standard form for for a versor is 'rot = exp $ (-i/2) * theta * u' for angle 'theta'
-- and the rotational axis unit vector 'u'.
--
--          Bloch Sphere Coordinates:
-- 
-- @
--                 e3
--                 |
--                 |____e2
--                / 
--               /
--              e1
-- @
--
--------------------------------------------

module Algebra.Geometric.Cl3.JonesCalculus
(
 -- * Jones Vectors
 hpv, vpv,
 dpv, apv,
 rpv, lpv,
 jv,
 -- * Jones Matrices
 hpm, vpm,
 dpm, apm,
 rpm, lpm,
 jm,
 hpmRot,
 -- * Wave Plates
 qwp, hwp,
 qwpRot, hwpRot,
 wp,
 wpRot,
 -- * Reflection
 refl,
 -- * Random Jones Vectors
 randJonesVec,
 randOrthogonalJonesVec,
 -- * Normalization Factorization
 factorize
) where


import safe Algebra.Geometric.Cl3 (Cl3(..), dag, bar, toR, toV3, toC, project, randUnitV3)
import System.Random (RandomGen)


e0 = R 1
e1 = V3 1 0 0
e2 = V3 0 1 0
e3 = V3 0 0 1

i = I 1

p1 = 0.5 * (e0 + e1)
p2 = 0.5 * (e0 + e2)
p3 = 0.5 * (e0 + e3)




-- | 'hpv' horizontally polarized Jones vector
hpv = signum $ e0 * p3  -- e0 == exp $ (-i/2) * 0 * e2, any vector orthoganl to e3 could have been selected as the rotational axis because the angle is zero

-- | 'vpv' vertically polarized Jones vector
vpv = signum $ exp ((-i/2) * pi * e2) * p3  -- e2 is selected to obtain the standard form, e1 or any vector orthoganl to e3 could have been selected

-- | 'dpv' diagonally polarized Jones vector
dpv = signum $ exp ((-i/2) * (pi/2) * e2) * p3  -- rotate -e1 to e3 around rotational axis e2, an angle of pi/2

-- | 'apv' anti-diagonally polarized Jones vector
apv = signum $ exp ((-i/2) * (pi/2) * (-e2)) * p3  -- rotate e1 to e3 around rotational axis -e2, an angle of pi/2

-- | 'rpv' right hand circularly polarized Jones vector
rpv = signum $ exp ((-i/2) * (pi/2) * (-e1)) * p3  -- rotate -e2 to e3 around rotational axis -e1, and angle of pi/2

-- | 'lpv' left hand circularly polarized Jones vector
lpv = signum $ exp ((-i/2) * (pi/2) * e1) * p3  -- rotate e2 to e3 around rotational axis e1, an angle of pi/2

-- | 'jv' function that returns Jones vector from input vector unit vector
-- currently converts the input to a unit vector
jv (signum.toV3 -> v) | v == e3 = hpv
                      | v == -e3 = vpv
                      | otherwise = signum $ sqrt (e3 * v) * p3


-- | 'hpm' Horizontal Polarizer Jones Matrix
hpm = p3

-- | 'vpm' Vertical Polarizer Jones Matrix
vpm = bar p3

-- | 'dpm' Diagonal Polarizer Jones Matrix
dpm = p1

-- | 'apm' Anti-diagonal Polarizer Jones Matrix
apm = bar p1

-- | 'rpm' Right Hand Circular Polarizer Jones Matrix
rpm = p2

-- | 'lpm' Left Hand Circular Polarizer Jones Matrix
lpm = bar p2


-- | 'jm' funciton that returns a Jones Matrix from an input Bloch Vector
-- currently converts the input to a unit vector
jm (signum.toV3 -> v) = project v

-- | 'rot' will produce a versor that rotates a vector by an angle about
-- a unit vector axis.
rot (toR -> theta) (signum.toV3 -> axis) = exp $ (-i/2) * theta * axis

-- | 'rotIsh' will produce a versor that rotates by double the input angle
-- for rotating polarizers and wave plates the axis is e2.
rotIsh (toR -> theta) = rot (2*theta) e2

-- | 'hpmRot' Jones matrix for a rotated ideal Linear Horizontal Polarizer.
-- Input value should be a scalar angle in Radians.
hpmRot (toR -> theta) = 
  let roted = rotIsh theta
  in roted * hpm * dag roted

-- | 'qwp' Quarter Wave Plate Jones Matrix
qwp = p3 - i * bar p3

-- | 'qwpRot' Rotated Quarter Wave Plate Jones Matrix.
-- Input value should be a scalar angle in Radians.
qwpRot (toR -> theta) = 
  let roted = rotIsh theta
  in roted * qwp * dag roted

-- | 'hwp' Half Wave Plate Jones Matrix
hwp = e3

-- | 'hwpRot' Rotated Half Wave Plate Jones Matrix.
-- Input value should be a scalar angle in Radians.
hwpRot (toR -> theta) = 
  let roted = rotIsh theta
  in roted * hwp * dag roted

-- | 'wp' a Wave Plate with phase shift of phi Jones Matrix.
-- Input value should be a scalar angle in Radians.
wp (toR -> phi) = exp $ (i/2) * phi * e3

-- | 'wpRot' a Rotated Wave Plate with phase shift of phi and rotation theta Jones Matrix.
-- The first input value is phi the phase shift as a scalar value in Radians. The second
-- input value is theta the rotation a scalar angle in Radians.
wpRot (toR -> phi) (toR -> theta) = 
  let roted = rotIsh theta
  in roted * wp phi * dag roted

-- | 'refl' a Refelection Jones Matrix
refl = e3


-- | 'factorize' is a function that takes an Jones Vector after transformation by an 
-- optical chain, and returns the amplitude (amp), phase (phi), and normalized Jones 
-- Vector (vec), by the factorization of the input such that: @__amp * exp (i*phi/2) * vec__@
factorize :: Cl3 -> (Cl3,Cl3,Cl3)
factorize jonesVec = 
  let c = toC jonesVec
      jonesVec' = recip c * jonesVec
      ampC = abs c
      ampJonesVec' = abs jonesVec'
      normJonesVec = recip ampJonesVec' * jonesVec'
      amp = ampC * ampJonesVec'
      normC = recip ampC * c
      phi = 2 * (-i) * log normC
  in (amp, phi, normJonesVec)

-------------------------------------------------------------------
--
--  Random Jones Vectors
--
-------------------------------------------------------------------

-- | 'randJonesVec' a Random Jones Vector.
randJonesVec :: RandomGen g => g -> (Cl3, g)
randJonesVec g =
  let (v3, g') = randUnitV3 g
  in (jv v3,g')

-- | 'randOrthogonalJonesVec' a Random Orthogonal Complementary pair of Jones
-- Vectors.
randOrthogonalJonesVec :: RandomGen g => g -> ((Cl3, Cl3), g)
randOrthogonalJonesVec g = 
  let (v3, g') = randUnitV3 g
  in ((jv v3, jv (bar v3)),g')