// For more information see https://aka.ms/fsharp-console-apps
module PipelinesAndComposition =

  let square x = x*x
  let addOne x = x+1
  let isOdd x = x%2 <> 0
  let numbers = [1;2;3;4;5;6;7;8;9;10;11]
  let squareOddValuesAndAddOne values =
    let odds = List.filter isOdd values
    let squares = List.map square odds
    let result = List.map addOne squares
    result

  printfn $"processing {numbers} through 'squareOddValuesAndAddOne' produces: {squareOddValuesAndAddOne numbers}!"

  let squareOddValuesAndAddOneShorterPipeline values =
    values
    |> List.filter isOdd
    |> List.map(fun x -> x |> square |> addOne)

  printfn $"processing {numbers} through 'squareOddValuesAndAddOneShorterPipeline' produces: {squareOddValuesAndAddOneShorterPipeline numbers}!"

  let squareOddValuesAndAddOneNested values = 
    List.map addOne (List.map square (List.filter isOdd values))

  printfn $"processing {numbers} through 'squareOddValuesAndAddOneNested' produces: {squareOddValuesAndAddOneNested numbers}"

  let squareOddValuesAndAddOnePipeline values =
    values
    |> List.filter isOdd
    |> List.map square
    |> List.map addOne

  printfn $"processing {numbers} through 'squareOddValuesAndAddOnePipeline' produces: {squareOddValuesAndAddOnePipeline numbers}"

module Lists =

    /// Lists are defined using [ ... ].  This is an empty list.
    let list1 = [ ]  

    /// This is a list with 3 elements.  ';' is used to separate elements on the same line.
    let list2 = [ 1; 2; 3 ]

    /// You can also separate elements by placing them on their own lines.
    let list3 = [
        1
        2
        3
    ]

    /// This is a list of integers from 1 to 1000
    let numberList = [ 1 .. 1000 ]  

    /// Lists can also be generated by computations. This is a list containing 
    /// all the days of the year.
    ///
    /// 'yield' is used for on-demand evaluation. More on this later in Sequences.
    let daysList = 
        [ for month in 1 .. 12 do
              for day in 1 .. System.DateTime.DaysInMonth(2022, month) do 
                  yield System.DateTime(2022, month, day) ]

    // Print the first 5 elements of 'daysList' using 'List.take'.
    printfn $"The first 5 days of 2022 are: {daysList |> List.take 5}"

    /// Computations can include conditionals.  This is a list containing the tuples
    /// which are the coordinates of the black squares on a chess board.
    let blackSquares = 
        [ for i in 0 .. 7 do
              for j in 0 .. 7 do 
                  if (i+j) % 2 = 1 then 
                      yield (i, j) ]

    /// Lists can be transformed using 'List.map' and other functional programming combinators.
    /// This definition produces a new list by squaring the numbers in numberList, using the pipeline 
    /// operator to pass an argument to List.map.
    let squares = 
        numberList 
        |> List.map (fun x -> x*x) 

    /// There are many other list combinations. The following computes the sum of the squares of the 
    /// numbers divisible by 3.
    let modulo = 7
    let sumOfSquares = 
        numberList
        |> List.filter (fun x -> x % modulo = 0)
        |> List.sumBy (fun x -> x * x)

    printfn $"The sum of the squares of numbers up to 1000 that are divisible by %d{modulo} is: %d{sumOfSquares}"
