/-
Copyright (c) 2025 Henrique Borges. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Henrique Borges
-/
import Mathlib.Analysis.InnerProductSpace.Basic
import Mathlib.Data.Matrix.Auto
import Mathlib.Data.Matrix.Notation
import Mathlib.Analysis.Normed.Group.Basic
import Mathlib.LinearAlgebra.AffineSpace.AffineMap
import Mathlib.Data.Matrix.Basic

/-!
# Linear Algebra Foundations for Vizagrams

This module establishes the mathematical foundation for 2D geometric operations in Vizagrams.
It provides vector spaces, affine transformations, and geometric operations that form the
basis for the categorical framework described in "Data Visualization from a Category Theory Perspective".

## Geometric Framework

Following Felix Klein's Erlanger Program, a geometry is determined by its symmetry group.
For Vizagrams, we work in the 2D Euclidean plane with the group of affine transformations,
which includes:
- Translations
- Rotations
- Uniform scaling
- Reflections

## Key Components

- **Vec2**: 2D vectors over Float (the geometric space)
- **Mat2**: 2×2 matrices for linear transformations
- **Mat2Vec2**: Affine transformations (linear + translation)
- **apply/compose**: Operations for categorical transformation structure

## Categorical Perspective

In the categorical framework:
- Vec2 forms the object space for our geometry
- Mat2Vec2 provides the morphisms (transformations)
- `apply` implements morphism evaluation
- `compose` follows categorical laws (associativity, identity)
- This enables the geometric primitive system to be mathematically rigorous

-/

namespace LinearAlgebra

open Matrix Fin

/-!
### Constants and Basic Definitions

Mathematical constants and fundamental vector definitions.
-/

/-- Mathematical constant π with sufficient precision for geometric operations. -/
def π : Float := 3.141592653589793

/-- 2D vector type: functions from Fin 2 to Float. -/
abbrev Vec2 := Fin 2 → Float

/-- Standard basis vector e₁ = (1, 0). -/
def e₁ : Vec2 := ![1, 0]

/-- Standard basis vector e₂ = (0, 1). -/
def e₂ : Vec2 := ![0, 1]

/-- Zero vector (0, 0). -/
def null_vec2 : Vec2 := ![0.0, 0.0]

/-!
### Inner Product and Norms

Inner product structure making Vec2 into a Euclidean space.
This provides the geometric foundation for distances, angles, and orthogonality.
-/

/--
Inner product on Vec2 giving it Euclidean structure.
Essential for geometric operations like distance and angle calculations.
-/
instance : Inner Float Vec2 where
  inner v₁ v₂ := v₁ 0 * v₂ 0 + v₁ 1 * v₂ 1

/-- Convenient notation for inner products. -/
notation "⟪" x "," y "⟫" => inner Float x y

/-- Squared Euclidean norm (avoids square root for efficiency). -/
def norm_square (v : Vec2) : Float := ⟪v, v⟫

/-- Notation for squared norm. -/
notation:max "‖" v:max "‖²" => norm_square v

/-- Euclidean norm (length of vector). -/
def vec_norm (v : Vec2) : Float := Float.sqrt ⟪v, v⟫

/-- Standard notation for norm. -/
notation:max "‖" v:max "‖" => vec_norm v

/-!
### Vector Operations

Fundamental geometric operations on vectors.
-/

/--
Normalize a vector to unit length.
Returns the original vector if it's the zero vector to avoid division by zero.
-/
def normalize (v : Vec2) : Vec2 :=
  let n := vec_norm v
  if n == 0 then v else fun i => v i / n

/-- Euclidean distance between two points. -/
def distance (v₁ v₂ : Vec2) : Float := vec_norm (v₁ - v₂)

/-- Convenient notation for distance. -/
notation "d(" x "," y ")" => distance x y

/-- Angle between two vectors (in radians). -/
def angle_between (v₁ v₂ : Vec2) : Float :=
  Float.acos (⟪v₁, v₂⟫ / (vec_norm v₁ * vec_norm v₂))

/-- Orthogonal projection of v₁ onto v₂. -/
def projection (v₁ v₂ : Vec2) : Vec2 :=
  (⟪v₁, v₂⟫ / norm_square v₂) • v₂

/-- 90-degree counterclockwise rotation: (x, y) ↦ (-y, x). -/
def perpendicular (v : Vec2) : Vec2 := ![-v 1, v 0]

/-!
### Matrix Types and Operations

2×2 matrices for linear transformations and their operations.
-/

/-- 2×2 matrix type for linear transformations. -/
abbrev Mat2 := Matrix (Fin 2) (Fin 2) Float

/-- Identity matrix. -/
instance : One Mat2 where
  one := !![1, 0; 0, 1]

/--
Matrix multiplication.
Uses custom notation to distinguish from other operations.
-/
def mat_mul (A B : Mat2) : Mat2 :=
  fun i j => A i 0 * B 0 j + A i 1 * B 1 j

/-- Custom infix notation for matrix multiplication. -/
infixl:70 "∘ₘ" => mat_mul

/-- Matrix-vector multiplication. -/
def mul_vec (A : Mat2) (v : Vec2) : Vec2 :=
  fun i => (A i 0) * v 0 + (A i 1) * v 1

/-- Infix notation for matrix-vector multiplication. -/
infixl:65 "@" => mul_vec

/-!
### Affine Transformations

Affine transformations combine linear transformations with translations.
They form the core geometric transformation system for Vizagrams.
-/

/--
Affine transformation represented as matrix A and translation vector b.
Transforms point x to Ax + b.

This is the fundamental transformation type in Vizagrams, enabling
all geometric manipulations while preserving affine properties.
-/
structure Mat2Vec2 where
  /-- Linear transformation component -/
  A : Mat2
  /-- Translation component -/
  b : Vec2
deriving Repr

/-!
### Coercion System

Smooth conversions between transformation types using the direct operations.
-/

/-- Treat translation vector as affine transformation. -/
instance : Coe Vec2 Mat2Vec2 where
  coe v := { A := 1, b := v }

/-- Treat linear transformation as affine transformation (no translation). -/
instance : Coe Mat2 Mat2Vec2 where
  coe M := { A := M, b := null_vec2 }

/-!
### Affine Map Operations

Direct operations for Mat2Vec2 transformations, providing the categorical structure.
-/

/-- Identity transformation (categorical identity morphism). -/
def identity : Mat2Vec2 := { A := 1, b := null_vec2 }

/-- Apply affine transformation to a point. -/
def apply (f : Mat2Vec2) (x : Vec2) : Vec2 := mul_vec f.A x + f.b

/-- Compose two affine transformations following categorical law: (f ∘ g)(x) = f(g(x)).
    This satisfies:
    - Associativity: (f ∘ₜ g) ∘ₜ h = f ∘ₜ (g ∘ₜ h)
    - Identity: identity ∘ₜ f = f and f ∘ₜ identity = f
-/
def compose (f g : Mat2Vec2) : Mat2Vec2 :=
  { A := f.A ∘ₘ g.A, b := (f.A @ g.b) + f.b }

/-- Infix notation for transformation application. -/
infixr:75 " ▷ " => apply

/-- Infix notation for transformation composition. -/
infixl:80 " ∘ₜ " => compose

/-- Alternative composition via multiplication operator. -/
instance : HMul Mat2Vec2 Mat2Vec2 Mat2Vec2 where
  hMul f g := compose f g

/-!
### Standard Geometric Transformations

Common transformations used throughout Vizagrams.
These provide the building blocks for all geometric manipulations.
-/

/-- Translation by vector t. -/
def translate (t : Vec2) : Mat2Vec2 :=
  { A := 1, b := t }

/-- Uniform scaling by factor s. -/
def scale (s : Float) : Mat2Vec2 :=
  { A := !![s, 0.0; 0.0, s], b := null_vec2 }

/-- Rotation by angle θ (in radians, counterclockwise). -/
def rotate (θ : Float) : Mat2Vec2 :=
  let c := Float.cos θ
  let s := Float.sin θ
  { A := !![c, -s; s, c], b := null_vec2 }

/-- Reflection across line making angle θ with x-axis. -/
def reflect (θ : Float) : Mat2Vec2 :=
  let c := Float.cos (2 * θ)
  let s := Float.sin (2 * θ)
  { A := !![c, s; s, -c], b := null_vec2 }

/-!
### Convenience Functions

Higher-level operations built on the transformation primitives.
-/

/-- Apply rotation transformation to a vector. -/
def rotate_vec2 (v : Vec2) (θ : Float) : Vec2 :=
  apply (rotate θ) v

/-- Point on ellipse with given angle and radii. -/
def point_on_ellipse (θ rx ry : Float) : Vec2 :=
  ![rx * Float.cos θ, ry * Float.sin θ]

/-- Angle of vector from positive x-axis (atan2 function). -/
def atan2_pi (v : Vec2) : Float :=
  Float.atan2 (v 1) (v 0)

/-- Create transformation matrix for general 2D rotation around origin. -/
def rotation_matrix (θ : Float) : Mat2 :=
  let c := Float.cos θ
  let s := Float.sin θ
  !![c, -s; s, c]

/-- Create transformation matrix for scaling. -/
def scaling_matrix (sx sy : Float) : Mat2 :=
  !![sx, 0; 0, sy]

/-!
### Coordinate System Utilities

Functions for working with different coordinate systems and transformations.
-/

/-- Convert from polar coordinates (r, θ) to Cartesian. -/
def from_polar (r θ : Float) : Vec2 :=
  ![r * Float.cos θ, r * Float.sin θ]

/-- Convert from Cartesian to polar coordinates. -/
def to_polar (v : Vec2) : Float × Float :=
  (vec_norm v, atan2_pi v)

/-- Transform that maps unit square [0,1]² to given rectangle. -/
def rect_transform (corner : Vec2) (width height : Float) : Mat2Vec2 :=
  let scaling := { A := !![width, 0; 0, height], b := null_vec2 }
  let translation := translate corner
  translation ∘ₜ scaling

/-!
### Geometric Predicates and Tests

Functions for geometric reasoning and validation.
-/

/-- Check if two vectors are approximately equal (within floating point tolerance). -/
def approx_equal (v₁ v₂ : Vec2) (ε : Float := 1e-10) : Bool :=
  vec_norm (v₁ - v₂) < ε

/-- Check if vector is approximately zero. -/
def is_zero (v : Vec2) (ε : Float := 1e-10) : Bool :=
  vec_norm v < ε

/-- Check if two vectors are approximately orthogonal. -/
def is_orthogonal (v₁ v₂ : Vec2) (ε : Float := 1e-10) : Bool :=
  Float.abs ⟪v₁, v₂⟫ < ε

/-- Check if transformation preserves orientation (determinant > 0). -/
def preserves_orientation (t : Mat2Vec2) : Bool :=
  let det := t.A 0 0 * t.A 1 1 - t.A 0 1 * t.A 1 0
  det > 0

end LinearAlgebra
