module MultidimensionalOptimizationMethods.Program

open System.Diagnostics
open Plotly.NET
open System
open DiffSharp

let targetErrors = [ 1e-1; 1e-2; 1e-3; 1e-4; 1e-8; 1e-10; 1e-11 ]

let header = [ "Title" ] @ List.map (fun x -> $"ε = {x}") targetErrors

let emptyRow length =
    Seq.init length (fun _ -> String.Empty) |> Seq.toList

let func (x: Tensor) = x[0] ** 2.0 + x[1] ** 2.0 + x[2] ** 2.0
let initial_x: Tensor = dsharp.tensor ([ 10.0; 10.0; 10.0 ])
let alpha = 0.2
let betta = 0.3

let gradientDescentStepsRow title =
    let stepsCountList =
        Seq.init (targetErrors |> List.length) (fun i ->
            targetErrors[i] |> GradientDescentMethod.run alpha func initial_x)
        |> Seq.map (fun iterationResult -> $"{iterationResult.StepsCount}")
        |> Seq.toList

    title :: stepsCountList

let Polak'sStepsRow title =
    let stepsCountList =
        Seq.init (targetErrors |> List.length) (fun i ->
            targetErrors[i] |> Polak_sMethod.run alpha betta func initial_x)
        |> Seq.map (fun iterationResult -> $"{iterationResult.StepsCount}")
        |> Seq.toList

    title :: stepsCountList

let Nesterov'sStepsRow title =
    let stepsCountList =
        Seq.init (targetErrors |> List.length) (fun i ->
            targetErrors[i] |> Nesterov_sMethod.run alpha betta func initial_x)
        |> Seq.map (fun iterationResult -> $"{iterationResult.StepsCount}")
        |> Seq.toList

    title :: stepsCountList

let Newton'sStepsRow title =
    let stepsCountList =
        Seq.init (targetErrors |> List.length) (fun i -> targetErrors[i] |> Newton_sMethod.run func initial_x)
        |> Seq.map (fun iterationResult -> $"{iterationResult.StepsCount}")
        |> Seq.toList

    title :: stepsCountList

let gradientDescentTimeRow title =
    let stepsCountList =
        Seq.init (targetErrors |> List.length) (fun i ->
            let timer = Stopwatch.StartNew()
            targetErrors[i] |> GradientDescentMethod.run alpha func initial_x |> ignore
            timer.Stop |> ignore
            timer.ElapsedMilliseconds)
        |> Seq.map (fun elapsedMilliseconds -> $"{elapsedMilliseconds}")
        |> Seq.toList

    title :: stepsCountList

let Polak'sTimeRow title =
    let stepsCountList =
        Seq.init (targetErrors |> List.length) (fun i ->
            let timer = Stopwatch.StartNew()
            targetErrors[i] |> Polak_sMethod.run alpha betta func initial_x |> ignore
            timer.Stop |> ignore
            timer.ElapsedMilliseconds)
        |> Seq.map (fun elapsedMilliseconds -> $"{elapsedMilliseconds}")
        |> Seq.toList

    title :: stepsCountList

let Nesterov'sTimeRow title =
    let stepsCountList =
        Seq.init (targetErrors |> List.length) (fun i ->
            let timer = Stopwatch.StartNew()
            targetErrors[i] |> Nesterov_sMethod.run alpha betta func initial_x |> ignore
            timer.Stop |> ignore
            timer.ElapsedMilliseconds)
        |> Seq.map (fun elapsedMilliseconds -> $"{elapsedMilliseconds}")
        |> Seq.toList

    title :: stepsCountList

let Newton'sTimeRow title =
    let stepsCountList =
        Seq.init (targetErrors |> List.length) (fun i ->
            let timer = Stopwatch.StartNew()
            targetErrors[i] |> Newton_sMethod.run func initial_x |> ignore
            timer.Stop |> ignore
            timer.ElapsedMilliseconds)
        |> Seq.map (fun elapsedMilliseconds -> $"{elapsedMilliseconds}")
        |> Seq.toList

    title :: stepsCountList

let gradientDescentErrorsRow title =
    let stepsCountList =
        Seq.init (targetErrors |> List.length) (fun i ->
            targetErrors[i] |> GradientDescentMethod.run alpha func initial_x)
        |> Seq.map (fun iterationResult -> $"{Math.Round(iterationResult.Error, 12)}")
        |> Seq.toList

    title :: stepsCountList

let Polak'sErrorsRow title =
    let stepsCountList =
        Seq.init (targetErrors |> List.length) (fun i ->
            targetErrors[i] |> Polak_sMethod.run alpha betta func initial_x)
        |> Seq.map (fun iterationResult -> $"{Math.Round(iterationResult.Error, 12)}")
        |> Seq.toList

    title :: stepsCountList

let Nesterov'sErrorsRow title =
    let stepsCountList =
        Seq.init (targetErrors |> List.length) (fun i ->
            targetErrors[i] |> Nesterov_sMethod.run alpha betta func initial_x)
        |> Seq.map (fun iterationResult -> $"{Math.Round(iterationResult.Error, 12)}")
        |> Seq.toList

    title :: stepsCountList

let Newton'sErrorsRow title =
    let stepsCountList =
        Seq.init (targetErrors |> List.length) (fun i -> targetErrors[i] |> Newton_sMethod.run func initial_x)
        |> Seq.map (fun iterationResult -> $"{Math.Round(iterationResult.Error, 12)}")
        |> Seq.toList

    title :: stepsCountList

let gradientDescentSolutionsRow title =
    let stepsCountList =
        Seq.init (targetErrors |> List.length) (fun i ->
            targetErrors[i] |> GradientDescentMethod.run alpha func initial_x)
        |> Seq.map (fun iterationResult -> $"{iterationResult.ResultTensor}")
        |> Seq.toList

    title :: stepsCountList

let Polak'sSolutionsRow title =
    let stepsCountList =
        Seq.init (targetErrors |> List.length) (fun i ->
            targetErrors[i] |> Polak_sMethod.run alpha betta func initial_x)
        |> Seq.map (fun iterationResult -> $"{iterationResult.ResultTensor}")
        |> Seq.toList

    title :: stepsCountList

let Nesterov'sSolutionsRow title =
    let stepsCountList =
        Seq.init (targetErrors |> List.length) (fun i ->
            targetErrors[i] |> Nesterov_sMethod.run alpha betta func initial_x)
        |> Seq.map (fun iterationResult -> $"{iterationResult.ResultTensor}")
        |> Seq.toList

    title :: stepsCountList

let Newton'sSolutionsRow title =
    let stepsCountList =
        Seq.init (targetErrors |> List.length) (fun i -> targetErrors[i] |> Newton_sMethod.run func initial_x)
        |> Seq.map (fun iterationResult -> $"{iterationResult.ResultTensor}")
        |> Seq.toList

    title :: stepsCountList

let rows =
    [ gradientDescentStepsRow "Шаги метода градиентного спуска"
      Polak'sStepsRow "Шаги метода Поляка"
      Nesterov'sStepsRow "Шаги метода Нестерова"
      Newton'sStepsRow "Шаги метода Ньютона"
      emptyRow header.Length
      gradientDescentTimeRow "Время метода Градиентного спуска (миллисекунды)"
      Polak'sTimeRow "Время метода Поляка (миллисекунды)"
      Nesterov'sTimeRow "Время метода Нестерова (миллисекунды)"
      Newton'sTimeRow "Время метода Ньютона (миллисекунды)"
      emptyRow header.Length
      gradientDescentErrorsRow "Погрешность метода Градиентного спуска"
      Polak'sErrorsRow "Погрешность метода Поляка"
      Nesterov'sErrorsRow "Погрешность метода Нестерова"
      Newton'sErrorsRow "Погрешность метода Ньютона"
      emptyRow header.Length
      gradientDescentSolutionsRow "Решение метода Градиентного спуска"
      Polak'sSolutionsRow "Решение метода Поляка"
      Nesterov'sSolutionsRow "Решение метода Нестерова"
      Newton'sSolutionsRow "Решение метода Ньютона" ]

Chart.Table(header, rows) |> Chart.withSize (1000, 1500) |> Chart.show
