// Copyright (C) 2026 Kiyotsugu Arai
// SPDX-License-Identifier: LGPL-3.0-or-later

// example_vector_demo.cpp
// Vector demo: showcasing Vector's capabilities in 3 scenarios
//
// Build:
//   cl /std:c++latest /EHsc /O2 /MD /utf-8 /I<sangi>/include examples\example_vector_demo.cpp /MACHINE:X64

#include <math/core/vector.hpp>
#include <iostream>
#include <iomanip>
#include <cmath>
#include <string>
#include <vector>

using namespace sangi;

// ============================================================================
// Demo 1: PageRank -- Ranking Web Pages with the Power Method
//   -- Google's original algorithm, computed with vector iteration
// ============================================================================
static void demo_pagerank() {
    std::cout << "============================================================\n";
    std::cout << " Demo 1: PageRank -- Ranking Web Pages\n";
    std::cout << "============================================================\n\n";

    // A small web graph (4 pages):
    //   Page 0 -> Page 1, Page 2
    //   Page 1 -> Page 2
    //   Page 2 -> Page 0
    //   Page 3 -> Page 0, Page 1, Page 2

    const int N = 4;
    const double damping = 0.85;
    const std::string names[] = {"Home", "About", "Blog", "External"};

    // Build column-stochastic transition matrix (stored as rows for simplicity)
    // M[i][j] = probability of going from page j to page i
    double M[N][N] = {};
    // Page 0 links to 1, 2 (each gets 1/2)
    M[1][0] = 0.5; M[2][0] = 0.5;
    // Page 1 links to 2
    M[2][1] = 1.0;
    // Page 2 links to 0
    M[0][2] = 1.0;
    // Page 3 links to 0, 1, 2 (each gets 1/3)
    M[0][3] = 1.0/3; M[1][3] = 1.0/3; M[2][3] = 1.0/3;

    // Power iteration: r = d * M * r + (1-d)/N
    Vector<double> r(N, 1.0 / N);  // uniform initial
    const double teleport = (1.0 - damping) / N;

    std::cout << "  Iterating with damping factor = " << damping << " ...\n\n";

    for (int iter = 0; iter < 50; ++iter) {
        Vector<double> next(N, teleport);
        for (int i = 0; i < N; ++i)
            for (int j = 0; j < N; ++j)
                next[i] += damping * M[i][j] * r[j];

        double diff = norm(next - r);
        r = std::move(next);

        if (diff < 1e-10) {
            std::cout << "  Converged after " << (iter + 1) << " iterations.\n\n";
            break;
        }
    }

    // Display rankings
    std::cout << std::fixed << std::setprecision(4);
    std::cout << "  Page Rankings:\n";
    for (int i = 0; i < N; ++i) {
        int bar_len = static_cast<int>(r[i] * 100);
        std::cout << "    " << std::setw(10) << names[i] << ": " << r[i]
                  << "  " << std::string(bar_len, '#') << "\n";
    }
    std::cout << "  Sum = " << (r[0] + r[1] + r[2] + r[3]) << "\n\n";
}

// ============================================================================
// Demo 2: Ray Reflection in 3D -- Light Bouncing Off Mirrors
//   -- Using dot product and cross product for geometric computation
// ============================================================================
static void demo_ray_reflection() {
    std::cout << "============================================================\n";
    std::cout << " Demo 2: Ray Reflection in 3D\n";
    std::cout << "============================================================\n\n";

    // Incident ray direction (pointing downward at 45 degrees)
    StaticVector<double, 3> ray{1.0, -1.0, 0.0};
    ray = ray.normalized();

    // Mirror surface normals for 3 bounces
    StaticVector<double, 3> normals[] = {
        StaticVector<double, 3>{0.0, 1.0, 0.0},                               // horizontal floor
        StaticVector<double, 3>{-1.0, 1.0, 0.0}.normalized(),                 // tilted wall
        StaticVector<double, 3>{0.0, 0.0, 1.0},                               // vertical wall (z)
    };

    auto print_v3 = [](const char* label, const StaticVector<double, 3>& v) {
        std::cout << "    " << std::setw(16) << label << ": ("
                  << std::setw(7) << v[0] << ", "
                  << std::setw(7) << v[1] << ", "
                  << std::setw(7) << v[2] << ")\n";
    };

    std::cout << std::fixed << std::setprecision(4);
    print_v3("Initial ray", ray);

    for (int bounce = 0; bounce < 3; ++bounce) {
        const auto& n = normals[bounce];

        // Reflection formula: r' = r - 2(r . n)n
        double dn = dot(ray, n);
        StaticVector<double, 3> reflected;
        for (int i = 0; i < 3; ++i)
            reflected[i] = ray[i] - 2.0 * dn * n[i];

        // Angle of incidence
        double angle = std::acos(-dn) * 180.0 / 3.14159265358979;

        std::cout << "\n  Bounce " << (bounce + 1) << ":\n";
        print_v3("Surface normal", n);
        std::cout << "    Angle of incidence: " << angle << " degrees\n";
        print_v3("Reflected ray", reflected);

        ray = reflected;
    }
    std::cout << std::endl;
}

// ============================================================================
// Demo 3: Cosine Similarity -- How Similar Are Two Vectors?
//   -- A fundamental operation in information retrieval and machine learning
// ============================================================================
static void demo_cosine_similarity() {
    std::cout << "============================================================\n";
    std::cout << " Demo 3: Cosine Similarity -- Document Comparison\n";
    std::cout << "============================================================\n\n";

    // Term frequency vectors for 5 documents
    // Dimensions: [math, science, art, music, sports]
    struct Doc {
        const char* name;
        Vector<double> tf;
    };

    Doc docs[] = {
        {"Algebra Textbook",    Vector<double>{9.0, 3.0, 0.0, 0.0, 0.0}},
        {"Physics Paper",       Vector<double>{5.0, 8.0, 0.0, 0.0, 1.0}},
        {"Art History",         Vector<double>{0.0, 0.0, 9.0, 2.0, 0.0}},
        {"Music Theory",        Vector<double>{2.0, 0.0, 3.0, 8.0, 0.0}},
        {"Sports Analytics",    Vector<double>{4.0, 2.0, 0.0, 0.0, 9.0}},
    };
    const int nd = 5;

    std::cout << "  Documents (term frequencies: [math, science, art, music, sports]):\n";
    for (auto& d : docs) {
        std::cout << "    " << std::setw(20) << d.name << ": [";
        for (size_t i = 0; i < d.tf.size(); ++i)
            std::cout << (i ? ", " : "") << static_cast<int>(d.tf[i]);
        std::cout << "]\n";
    }

    // Cosine similarity matrix
    const char* abbr[] = {"Algebra", "Physics", "Art", "Music", "Sports"};
    std::cout << "\n  Cosine Similarity Matrix:\n\n";
    std::cout << std::setw(22) << "";
    for (int i = 0; i < nd; ++i) std::cout << std::setw(10) << abbr[i];
    std::cout << "\n";

    std::cout << std::fixed << std::setprecision(3);
    for (int i = 0; i < nd; ++i) {
        std::cout << "    " << std::setw(18) << docs[i].name;
        for (int j = 0; j < nd; ++j) {
            double sim = dot(docs[i].tf, docs[j].tf) / (norm(docs[i].tf) * norm(docs[j].tf));
            std::cout << std::setw(10) << sim;
        }
        std::cout << "\n";
    }

    std::cout << "\n  Most similar pair: Algebra Textbook <-> Physics Paper\n";
    std::cout << "  Most different:    Art History <-> Sports Analytics\n";
    std::cout << std::endl;
}

// ============================================================================
// main
// ============================================================================
int main() {
    std::cout << "###########################################################\n";
    std::cout << "#  sangi Vector Demo                                       #\n";
    std::cout << "###########################################################\n\n";

    demo_pagerank();
    demo_ray_reflection();
    demo_cosine_similarity();

    std::cout << "###########################################################\n";
    std::cout << "#  All demos completed                                    #\n";
    std::cout << "###########################################################\n";

    return 0;
}
