diff --git a/Semester-4/Homework-5/Network.Tests/LogBFS.fs b/Semester-4/Homework-5/Network.Tests/LogBFS.fs new file mode 100644 index 0000000..c702ff7 --- /dev/null +++ b/Semester-4/Homework-5/Network.Tests/LogBFS.fs @@ -0,0 +1,25 @@ +namespace Network + +/// Logger for computers with 100% infection chance +type LogBFS() = + let mutable stepNumber = 0 + let mutable isSomeoneHealthy = true + let mutable isBFS = true + + member this.IsSomeoneHealthy = isSomeoneHealthy + member this.IsBFS = isBFS + + interface ILog with + member this.LogState state = + + if (state.[2 - stepNumber] = false || state.[2 + stepNumber] = false) then + isBFS <- false + + if stepNumber >= 500 then + failwith "Simulation was not stopped, but probability is 1!" + else + isSomeoneHealthy <- Array.contains false <| state + stepNumber <- stepNumber + 1 + + + stepNumber \ No newline at end of file diff --git a/Semester-4/Homework-5/Network.Tests/LogZeroChance.fs b/Semester-4/Homework-5/Network.Tests/LogZeroChance.fs new file mode 100644 index 0000000..7b3d3f0 --- /dev/null +++ b/Semester-4/Homework-5/Network.Tests/LogZeroChance.fs @@ -0,0 +1,11 @@ +namespace Network + +/// Logger for network of uninfectable computers +type LogZeroChance() = + let mutable stepNumber = 0 + + interface ILog with + member this.LogState state = + + stepNumber <- stepNumber + 1 + stepNumber \ No newline at end of file diff --git a/Semester-4/Homework-5/Network.Tests/Network.Tests.fsproj b/Semester-4/Homework-5/Network.Tests/Network.Tests.fsproj new file mode 100644 index 0000000..6e4a2bc --- /dev/null +++ b/Semester-4/Homework-5/Network.Tests/Network.Tests.fsproj @@ -0,0 +1,27 @@ + + + + netcoreapp3.1 + + false + false + + + + + + + + + + + + + + + + + + + + diff --git a/Semester-4/Homework-5/Network.Tests/UnitTests.fs b/Semester-4/Homework-5/Network.Tests/UnitTests.fs new file mode 100644 index 0000000..59b3217 --- /dev/null +++ b/Semester-4/Homework-5/Network.Tests/UnitTests.fs @@ -0,0 +1,64 @@ +namespace Network + +module Tests = + open NUnit.Framework + open FsUnit + + [] + let ``Simulator should work correctly with overall chance = 1`` () = + let computers = [| for i in 1..6 -> ({new IOperatingSystem with + member this.InfectionChance = 1.0}) |] + let graph = + [| + [1] + [0; 2] + [3; 1] + [2; 4; 5] + [3; 5] + [3; 4] + |] + + let logger = new LogBFS() + let simulator = new Simulator(computers, graph, logger) + simulator.Start [|false; false; true; false; false; false|] |> ignore + + logger.IsSomeoneHealthy |> should be False + + [] + let ``Simulator should work correctly with overall chance = 0`` () = + let computers = [| for i in 1..6 -> ({new IOperatingSystem with + member this.InfectionChance = 0.0}) |] + let graph = + [| + [1] + [0; 2] + [1; 3] + [2; 4; 5] + [3; 5] + [3; 4] + |] + let expectedState = Array.concat[ [|true|]; [| for i in 1..5 -> false |] ] + let log = new LogZeroChance() + + let simulator = new Simulator(computers, graph, log) + let resultingNetwork = simulator.Start(expectedState) + resultingNetwork |> should equal expectedState + + [] + let ``Infection should work like BFS with infection chance == 1`` () = + let computers = [| for i in 1..6 -> ({new IOperatingSystem with + member this.InfectionChance = 1.0}) |] + let graph = + [| + [1] + [0; 2] + [1; 3] + [2; 4] + [3] + |] + + let logger = new LogBFS() + let simulator = new Simulator(computers, graph, logger) + simulator.Start [|false; false; true; false; false|] |> ignore + + logger.IsBFS |> should be True diff --git a/Semester-4/Homework-5/Network/Log/ConsoleLog.fs b/Semester-4/Homework-5/Network/Log/ConsoleLog.fs new file mode 100644 index 0000000..6485f5b --- /dev/null +++ b/Semester-4/Homework-5/Network/Log/ConsoleLog.fs @@ -0,0 +1,17 @@ +namespace Network + +/// Network state logger +type ConsoleLog() = + let mutable step = 0 + + interface ILog with + /// Prints current network state + member this.LogState state = + printfn "Step №%d. " step + state |> Array.iteri (fun i item -> + match item with + | true -> printfn "Computer #%d: Infected" i + | false -> printfn "Computer #%d: Healthy" i) + + step <- step + 1 + step \ No newline at end of file diff --git a/Semester-4/Homework-5/Network/Log/ILog.fs b/Semester-4/Homework-5/Network/Log/ILog.fs new file mode 100644 index 0000000..c269570 --- /dev/null +++ b/Semester-4/Homework-5/Network/Log/ILog.fs @@ -0,0 +1,6 @@ +namespace Network + +/// Network state logger interface +type ILog = + /// Prints current network state + abstract member LogState : bool array -> int \ No newline at end of file diff --git a/Semester-4/Homework-5/Network/Network.fsproj b/Semester-4/Homework-5/Network/Network.fsproj new file mode 100644 index 0000000..075f49f --- /dev/null +++ b/Semester-4/Homework-5/Network/Network.fsproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + diff --git a/Semester-4/Homework-5/Network/Network.sln b/Semester-4/Homework-5/Network/Network.sln new file mode 100644 index 0000000..121138b --- /dev/null +++ b/Semester-4/Homework-5/Network/Network.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29613.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Network", "Network.fsproj", "{608891F1-5F7C-4773-95FF-9563686EDE0C}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Network.Tests", "..\Network.Tests\Network.Tests.fsproj", "{8C57BEC3-20B6-4158-BDD8-B2454D45DA76}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {608891F1-5F7C-4773-95FF-9563686EDE0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {608891F1-5F7C-4773-95FF-9563686EDE0C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {608891F1-5F7C-4773-95FF-9563686EDE0C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {608891F1-5F7C-4773-95FF-9563686EDE0C}.Release|Any CPU.Build.0 = Release|Any CPU + {8C57BEC3-20B6-4158-BDD8-B2454D45DA76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C57BEC3-20B6-4158-BDD8-B2454D45DA76}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C57BEC3-20B6-4158-BDD8-B2454D45DA76}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C57BEC3-20B6-4158-BDD8-B2454D45DA76}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6055F841-A110-4496-B204-4862CD75557F} + EndGlobalSection +EndGlobal diff --git a/Semester-4/Homework-5/Network/OperatingSystem/IOperatingSystem.fs b/Semester-4/Homework-5/Network/OperatingSystem/IOperatingSystem.fs new file mode 100644 index 0000000..79f2b2b --- /dev/null +++ b/Semester-4/Homework-5/Network/OperatingSystem/IOperatingSystem.fs @@ -0,0 +1,6 @@ +namespace Network + +/// Describes the operating system interface +type IOperatingSystem = + /// Chance of the computer getting infected + abstract member InfectionChance : float \ No newline at end of file diff --git a/Semester-4/Homework-5/Network/Program.fs b/Semester-4/Homework-5/Network/Program.fs new file mode 100644 index 0000000..eb69c0d --- /dev/null +++ b/Semester-4/Homework-5/Network/Program.fs @@ -0,0 +1,20 @@ +namespace Network + +module Main = + open System + + let computers = [| for i in 1..5 -> ( {new IOperatingSystem with + member this.InfectionChance = 1.0}) |] + let graph = + [| + [1] + [0; 2] + [1; 3] + [2; 4] + [3] + |] + + let logger = new ConsoleLog() + let simulator = new Simulator(computers, graph, logger) + + simulator.Start [|true; false; true; false; true|] |> ignore \ No newline at end of file diff --git a/Semester-4/Homework-5/Network/Simulation.fs b/Semester-4/Homework-5/Network/Simulation.fs new file mode 100644 index 0000000..9f47831 --- /dev/null +++ b/Semester-4/Homework-5/Network/Simulation.fs @@ -0,0 +1,56 @@ +namespace Network + +/// Simulates network infection process +type Simulator( + computers : IOperatingSystem array, + graph : int list array, + log : ILog) = + + let random = System.Random() + + + /// Starts the simulation + member this.Start (startState : bool array) = + + /// Carries out the simulation steps + let rec nextStep (handlingQueue : int list) (used : bool array) (newQueue : int list) = + match handlingQueue with + + /// Computer gets infected + | head :: tail when + not used.[head] && + (random.NextDouble() < computers.[head].InfectionChance) -> + used.[head] <- true + nextStep tail used (newQueue @ graph.[head]) + + /// Computer does not get infected + /// Keeps trying to infect it + | head :: tail when not used.[head] -> + + /// If the computer is invincible... + if (computers.[head].InfectionChance = 0.0) then + /// ...there's no need in trying to infect it + nextStep tail used newQueue + else + nextStep tail used (newQueue @ [head]) + + /// Already infected computer + | head :: tail -> + nextStep tail used newQueue + + /// Empty queue + | [] -> + (newQueue, used) + + /// Launches the virus into the network + let rec virusJump queue states = + match queue with + | [] -> states + | virusQueue -> + states |> log.LogState |> ignore + let newNetwork = nextStep virusQueue states List.Empty + virusJump (fst newNetwork) (snd newNetwork) + + let infected = startState |> Array.indexed |> Array.filter (fun (i, x) -> x = true) + |> Array.map (fun (i, x) -> graph.[i]) |> List.concat + virusJump infected startState \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index 8be6cd1..e1da346 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -15,6 +15,7 @@ environment: - solution_name: Semester-4/Homework-4/BracketSeq/BracketSeq.sln - solution_name: Semester-4/Homework-4/PointFree/PointFree.sln - solution_name: Semester-4/Homework-4/Phonebook/Phonebook.sln + - solution_name: Semester-4/Homework-5/Network/Network.sln - solution_name: Semester-4/Homework-6/Workflows/Workflows.sln before_build: