diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 0000000..9ca65c1 --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,18 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "fantomas": { + "version": "6.3.15", + "commands": [ + "fantomas" + ] + }, + "dotnet-fsharplint": { + "version": "0.24.2", + "commands": [ + "dotnet-fsharplint" + ] + } + } +} \ No newline at end of file diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 217f7cb..2527e17 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -5,9 +5,7 @@ name: .NET on: push: - branches: [ "main" ] pull_request: - branches: [ "main" ] jobs: build: diff --git a/Lectures.sln b/Lectures.sln index 1b8fc08..983bd57 100644 --- a/Lectures.sln +++ b/Lectures.sln @@ -11,6 +11,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{1C9B3502-1 EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Tests", "test\Tests\Tests.fsproj", "{DB1BB7B2-4207-4F77-A40E-CE98C7C13A51}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Benchmarks", "benchmarks\Benchmarks.fsproj", "{FF096F73-CAD8-4733-A1B9-C306629C637A}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "ImageProcessing", "src\ImageProcessing\ImageProcessing.fsproj", "{DA2F7A21-E6CF-43FA-BFB3-922A8B57B76B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -28,9 +32,18 @@ Global {DB1BB7B2-4207-4F77-A40E-CE98C7C13A51}.Debug|Any CPU.Build.0 = Debug|Any CPU {DB1BB7B2-4207-4F77-A40E-CE98C7C13A51}.Release|Any CPU.ActiveCfg = Release|Any CPU {DB1BB7B2-4207-4F77-A40E-CE98C7C13A51}.Release|Any CPU.Build.0 = Release|Any CPU + {FF096F73-CAD8-4733-A1B9-C306629C637A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FF096F73-CAD8-4733-A1B9-C306629C637A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FF096F73-CAD8-4733-A1B9-C306629C637A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FF096F73-CAD8-4733-A1B9-C306629C637A}.Release|Any CPU.Build.0 = Release|Any CPU + {DA2F7A21-E6CF-43FA-BFB3-922A8B57B76B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DA2F7A21-E6CF-43FA-BFB3-922A8B57B76B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DA2F7A21-E6CF-43FA-BFB3-922A8B57B76B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DA2F7A21-E6CF-43FA-BFB3-922A8B57B76B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {DCC7AC6B-CA90-4890-B8AB-45BF39157AB2} = {0C857B10-C523-4833-B3FF-6D3A9A7A2099} {DB1BB7B2-4207-4F77-A40E-CE98C7C13A51} = {1C9B3502-1FB1-452A-B5E7-4951A0D52C86} + {DA2F7A21-E6CF-43FA-BFB3-922A8B57B76B} = {0C857B10-C523-4833-B3FF-6D3A9A7A2099} EndGlobalSection EndGlobal diff --git a/benchmarks/Benchmarks.fsproj b/benchmarks/Benchmarks.fsproj new file mode 100644 index 0000000..19e23ad --- /dev/null +++ b/benchmarks/Benchmarks.fsproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + + + + + + + + + + + diff --git a/benchmarks/Program.fs b/benchmarks/Program.fs new file mode 100644 index 0000000..78eaaea --- /dev/null +++ b/benchmarks/Program.fs @@ -0,0 +1,32 @@ +namespace Benchmarks + +open BenchmarkDotNet.Running +open BenchmarkDotNet.Attributes + +type SortsBenchmark() = + + static member ArrayLengths = [|10000..10000..100000|] + //static member ArrayLengths = [|0..100..1000|] + + member this.Random = System.Random() + + [] + member val ArrayLength = 0 with get, set + + member val ArrayToSort = [|0.0|] with get, set + + [] + member this.GetArrayToSort () = + this.ArrayToSort <- Array.init this.ArrayLength (fun _ -> this.Random.NextDouble()) + + [] + member this.Benchmark () = Array.sort this.ArrayToSort + +module main = + [] + let main argv = + let benchmarks = + BenchmarkSwitcher [| typeof |] + + benchmarks.Run argv |> ignore + 0 \ No newline at end of file diff --git a/src/Exercises/Exercises.fsproj b/src/Exercises/Exercises.fsproj index f81f7f5..6b65264 100644 --- a/src/Exercises/Exercises.fsproj +++ b/src/Exercises/Exercises.fsproj @@ -1,12 +1,9 @@ - - + net8.0 true - - - + \ No newline at end of file diff --git a/src/Exercises/Library.fs b/src/Exercises/Library.fs index 486d287..d0adbc8 100644 --- a/src/Exercises/Library.fs +++ b/src/Exercises/Library.fs @@ -1,6 +1,11 @@ namespace Exercises module Say = + + type Compare = Le | Eq | Ge + + type T = A of int | B of int + let hello name = printfn "Hello %s" name @@ -22,13 +27,9 @@ module Say = let err_arr = [|"1", "2", "3"|] let int_lst = [1;2;3] + + let fn x b = (if x then 1 else b) + 7 - let fn x b = - (if x - then - 1 - else b) - + 7 let fn2 x = for i in 1..2..10 do printfn $"{i}" @@ -42,6 +43,22 @@ module Say = let count = count + 1 printfn $"{count}" + + (*let sort (arr: 'a[]) (compare: 'a -> 'a -> Compare) = + match compare arr.[0] arr.[1] with + | Eq -> .. + | Le -> .. + | Ge -> .. +*) + let compareT x y = + match x,y with + | A i, A j -> if i < j then Le elif i > j then Ge else Eq + | B i, B j -> if i < j then Le elif i > j then Ge else Eq + | A _, B _ -> Ge + | B _, A _ -> Le + + // let r = sort [|A 10; B 1|] compareT + let rec g x = h x and h y= g y @@ -57,5 +74,32 @@ module Say = then n else fib (n - 1) + fib (n - 2) +module MyList = + + type MyList<'elem> = + | Empty + | Cons of 'elem * MyList<'elem> + + let rec len lst = + match lst with + | Empty -> 0 + | Cons (_,tl) -> 1 + len tl + + let rec fold f state lst = + match lst with + | Empty -> state + | Cons (hd, tl) -> fold f (f state hd) tl + //| Cons (hd, tl) -> f (fold f state tl) hd + let rec map f lst = + let f state elem = + Cons (f elem, state) + let state = Empty + let lst = lst + fold f state lst +module main = + [] + let main argv = + printfn "!!!!" + 0 \ No newline at end of file diff --git a/src/ImageProcessing/ImageProcessing.fs b/src/ImageProcessing/ImageProcessing.fs new file mode 100644 index 0000000..b873c8b --- /dev/null +++ b/src/ImageProcessing/ImageProcessing.fs @@ -0,0 +1,102 @@ +module ImageProcessing + +open System +open SixLabors.ImageSharp +open SixLabors.ImageSharp.PixelFormats + +[] +type Image = + val Data: array + val Width: int + val Height: int + val Name: string + + new(data, width, height, name) = + { + Data = data + Width = width + Height = height + Name = name + } + +let loadAs2DArray (file: string) = + let img = Image.Load file + let res = Array2D.zeroCreate img.Height img.Width + + for i in 0 .. img.Width - 1 do + for j in 0 .. img.Height - 1 do + res.[j, i] <- img.Item(i, j).PackedValue + + printfn $"H=%A{img.Height} W=%A{img.Width}" + res + +let loadAsImage (file: string) = + let img = Image.Load file + + let buf = Array.zeroCreate (img.Width * img.Height) + + img.CopyPixelDataTo(Span buf) + Image(buf, img.Width, img.Height, System.IO.Path.GetFileName file) + +let save2DByteArrayAsImage (imageData: byte[,]) file = + let h = imageData.GetLength 0 + let w = imageData.GetLength 1 + printfn $"H=%A{h} W=%A{w}" + + let flat2Darray array2D = + [| + for x in [0 .. (Array2D.length1 array2D) - 1] do + for y in [0 .. (Array2D.length2 array2D) - 1] do + yield array2D.[x, y] + |] + + let img = Image.LoadPixelData(flat2Darray imageData, w, h) + img.Save file + +let saveImage (image: Image) file = + let img = Image.LoadPixelData(image.Data, image.Width, image.Height) + img.Save file + +let gaussianBlurKernel = + [| + [| 1; 4; 6; 4; 1 |] + [| 4; 16; 24; 16; 4 |] + [| 6; 24; 36; 24; 6 |] + [| 4; 16; 24; 16; 4 |] + [| 1; 4; 6; 4; 1 |] + |] + |> Array.map (Array.map (fun x -> (float32 x) / 256.0f)) + +let edgesKernel = + [| + [| 0; 0; -1; 0; 0 |] + [| 0; 0; -1; 0; 0 |] + [| 0; 0; 2; 0; 0 |] + [| 0; 0; 0; 0; 0 |] + [| 0; 0; 0; 0; 0 |] + |] + |> Array.map (Array.map float32) + +let applyFilter (filter: float32[][]) (img: byte[,]) = + let imgH = img.GetLength 0 + let imgW = img.GetLength 1 + + let filterD = (Array.length filter) / 2 + + let filter = Array.concat filter + + let processPixel px py = + let dataToHandle = [| + for i in px - filterD .. px + filterD do + for j in py - filterD .. py + filterD do + if i < 0 + || i >= imgH + || j < 0 + || j >= imgW + then float32 img.[px, py] + else float32 img.[i, j] + |] + + Array.fold2 (fun s x y -> s + x * y) 0.0f filter dataToHandle + + Array2D.mapi (fun x y _ -> byte (processPixel x y)) img \ No newline at end of file diff --git a/src/ImageProcessing/ImageProcessing.fsproj b/src/ImageProcessing/ImageProcessing.fsproj new file mode 100644 index 0000000..7dd1ceb --- /dev/null +++ b/src/ImageProcessing/ImageProcessing.fsproj @@ -0,0 +1,14 @@ + + + Exe + net8.0 + + + + + + + + + + \ No newline at end of file diff --git a/src/ImageProcessing/Program.fs b/src/ImageProcessing/Program.fs new file mode 100644 index 0000000..2e1960e --- /dev/null +++ b/src/ImageProcessing/Program.fs @@ -0,0 +1,35 @@ +open ImageProcessing +open Argu + +let pathToExamples = "/home/gsv/Projects/TestProj2020/src/ImgProcessing/Examples" +let inputFolder = System.IO.Path.Combine(pathToExamples, "input") + +let demoFile = + System.IO.Path.Combine(inputFolder, "armin-djuhic-ohc29QXbS-s-unsplash.jpg") + +type CmdArgs = + | [] Input_File of string + | [] Out_File of string + interface IArgParserTemplate with + member this.Usage = + match this with + | Input_File _ -> "File to process." + | Out_File _ -> "Where to save result." + +[] +let main argv = + printfn "%A" argv + let parser = ArgumentParser.Create(programName = "ImageProcessing") + let usage = parser.PrintUsage() + let results = parser.Parse argv + let inFile = results.GetResult Input_File + let outFile = results.GetResult Out_File + let inImage = loadAs2DArray inFile + let resultImage = + applyFilter gaussianBlurKernel inImage + //|> applyFilter gaussianBlurKernel + //|> applyFilter gaussianBlurKernel + //|> applyFilter gaussianBlurKernel + //|> applyFilter edgesKernel + save2DByteArrayAsImage resultImage outFile + 0 \ No newline at end of file diff --git a/test/Tests/PropertyTetst.fs b/test/Tests/PropertyTetst.fs new file mode 100644 index 0000000..b5d45f6 --- /dev/null +++ b/test/Tests/PropertyTetst.fs @@ -0,0 +1,32 @@ +namespace Tests + +open FsCheck.Xunit + + +(* +module test = + let f x = 2 +*) + + +[] +type PlusPropertyTest () = + + [] + member _.``Plus is commutative`` (a: int, b: int) = + //printfn $"{a}, {b}" + a + b = b + a + + [] + member _.``Div is commutative`` (a: int, b: int) = + //printfn $"{a}, {b}" + if b <> 0 && a <> 0 then a / b = b / a else true + + [] + member _.``Plus is associative`` (a: int, b: int, c: int) = a + b + c = a + (b + c) + + + [] + member _.``Double rev is id`` (lst: List) = + //printfn $"{a}, {b}" + List.rev (List.rev lst) = lst \ No newline at end of file diff --git a/test/Tests/Tests.fs b/test/Tests/Tests.fs index 7fe8a08..452a9dc 100644 --- a/test/Tests/Tests.fs +++ b/test/Tests/Tests.fs @@ -1,11 +1,11 @@ namespace Tests -open System -open Microsoft.VisualStudio.TestTools.UnitTesting +open Xunit -[] type TestClass () = - [] + [] member this.TestMethodPassing () = - Assert.IsTrue(true); + Assert.True(true); + + diff --git a/test/Tests/Tests.fsproj b/test/Tests/Tests.fsproj index b0f54db..8cd8b9b 100644 --- a/test/Tests/Tests.fsproj +++ b/test/Tests/Tests.fsproj @@ -1,27 +1,25 @@ - - - - net8.0 - - false - false - true - - - - - - - - - - - - - - + + + net8.0 + false + false + true + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + - - - + + \ No newline at end of file