diff --git a/.gitignore b/.gitignore index c64fcdf..e69de29 100644 --- a/.gitignore +++ b/.gitignore @@ -1,422 +0,0 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore - -# User-specific files -*.rsuser -*.suo -*.user -*.userosscache -*.sln.docstates -*.env - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Mono auto generated files -mono_crash.* - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -[Ww][Ii][Nn]32/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -[Aa][Rr][Mm]64[Ee][Cc]/ -bld/ -[Oo]bj/ -[Oo]ut/ -[Ll]og/ -[Ll]ogs/ - -# Build results on 'Bin' directories -**/[Bb]in/* -# Uncomment if you have tasks that rely on *.refresh files to move binaries -# (https://github.com/github/gitignore/pull/3736) -#!**/[Bb]in/*.refresh - -# Visual Studio 2015/2017 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# Visual Studio 2017 auto generated files -Generated\ Files/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* -*.trx - -# NUnit -*.VisualState.xml -TestResult.xml -nunit-*.xml - -# Approval Tests result files -*.received.* - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ - -# ASP.NET Scaffolding -ScaffoldingReadMe.txt - -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio -*_i.c -*_p.c -*_h.h -*.ilk -*.meta -*.obj -*.idb -*.iobj -*.pch -*.pdb -*.ipdb -*.pgc -*.pgd -*.rsp -# but not Directory.Build.rsp, as it configures directory-level build defaults -!Directory.Build.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*_wpftmp.csproj -*.log -*.tlog -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# Visual Studio Trace Files -*.e2e - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Coverlet is a free, cross platform Code Coverage Tool -coverage*.json -coverage*.xml -coverage*.info - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# NuGet Symbol Packages -*.snupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx -*.appxbundle -*.appxupload - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!?*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser -*- [Bb]ackup.rdl -*- [Bb]ackup ([0-9]).rdl -*- [Bb]ackup ([0-9][0-9]).rdl - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio 6 auto-generated project file (contains which files were open etc.) -*.vbp - -# Visual Studio 6 workspace and project file (working project files containing files to include in project) -*.dsw -*.dsp - -# Visual Studio 6 technical files -*.ncb -*.aps - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -**/.paket/paket.exe -paket-files/ - -# FAKE - F# Make -**/.fake/ - -# CodeRush personal settings -**/.cr/personal - -# Python Tools for Visual Studio (PTVS) -**/__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -#tools/** -#!tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ - -# MSBuild Binary and Structured Log -*.binlog -MSBuild_Logs/ - -# AWS SAM Build and Temporary Artifacts folder -.aws-sam - -# NVidia Nsight GPU debugger configuration file -*.nvuser - -# MFractors (Xamarin productivity tool) working folder -**/.mfractor/ - -# Local History for Visual Studio -**/.localhistory/ - -# Visual Studio History (VSHistory) files -.vshistory/ - -# BeatPulse healthcheck temp database -healthchecksdb - -# Backup folder for Package Reference Convert tool in Visual Studio 2017 -MigrationBackup/ - -# Ionide (cross platform F# VS Code tools) working folder -**/.ionide/ - -# Fody - auto-generated XML schema -FodyWeavers.xsd - -# VS Code files for those working on multiple tools -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -!.vscode/*.code-snippets - -# Local History for Visual Studio Code -.history/ - -# Built Visual Studio Code Extensions -*.vsix - -# Windows Installer files from build outputs -*.cab -*.msi -*.msix -*.msm -*.msp - -# Rider -.idea -.git \ No newline at end of file diff --git a/HW2/HW2.sln b/HW2/HW2.sln new file mode 100644 index 0000000..4045d39 --- /dev/null +++ b/HW2/HW2.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LazyEvaluation", "LazyEvaluation\LazyEvaluation.csproj", "{D2A3863F-D69C-419E-9580-826C8718A7E5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LazyEvaluation.Test", "LazyEvaluation.Test\LazyEvaluation.Test.csproj", "{C9B07A5A-EAD9-461C-B638-858E14BD4E7E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D2A3863F-D69C-419E-9580-826C8718A7E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D2A3863F-D69C-419E-9580-826C8718A7E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D2A3863F-D69C-419E-9580-826C8718A7E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D2A3863F-D69C-419E-9580-826C8718A7E5}.Release|Any CPU.Build.0 = Release|Any CPU + {C9B07A5A-EAD9-461C-B638-858E14BD4E7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C9B07A5A-EAD9-461C-B638-858E14BD4E7E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C9B07A5A-EAD9-461C-B638-858E14BD4E7E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C9B07A5A-EAD9-461C-B638-858E14BD4E7E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/HW2/LazyEvaluation.Test/LazyEvaluation.Test.csproj b/HW2/LazyEvaluation.Test/LazyEvaluation.Test.csproj new file mode 100644 index 0000000..43a291d --- /dev/null +++ b/HW2/LazyEvaluation.Test/LazyEvaluation.Test.csproj @@ -0,0 +1,40 @@ + + + + net9.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + diff --git a/HW2/LazyEvaluation.Test/LazyEvaluationTest.cs b/HW2/LazyEvaluation.Test/LazyEvaluationTest.cs new file mode 100644 index 0000000..304e3f9 --- /dev/null +++ b/HW2/LazyEvaluation.Test/LazyEvaluationTest.cs @@ -0,0 +1,97 @@ +// +// Copyright (c) khusainovilas. All rights reserved. +// + +namespace LazyEvaluation.Test; + +/// +/// Unit tests for lazy evaluation. +/// +public abstract class LazyEvaluationTest +{ + /// + /// Verifies that the supplier delegate is called exactly one time. + /// + [Test] + public void Lazy_Get_NonNullSupplier_SupplierCalledOnce() + { + var callCount = 0; + + var supplier = new Func(() => + { + callCount++; + return "test"; + }); + + var lazy = this.CreateLazy(supplier); + + var result1 = lazy.Get(); + var result2 = lazy.Get(); + + Assert.Multiple(() => + { + Assert.That(result1, Is.EqualTo("test")); + Assert.That(result2, Is.EqualTo("test")); + Assert.That(callCount, Is.EqualTo(1)); + }); + } + + /// + /// Verifies that multiple calls to Get() return the same result. + /// + [Test] + public void Lazy_Get_SimpleString_NonNullSupplier_SameResult() + { + var supplier = new Func(() => "test"); + var lazy = this.CreateLazy(supplier); + + var result1 = lazy.Get(); + var result2 = lazy.Get(); + + Assert.That(result2, Is.EqualTo(result1)); + } + + /// + /// Verifies that constructing with a null supplier throws ArgumentNullException. + /// + [Test] + public void Lazy_Constructor_NullSupplier_ThrowsException() + { + Assert.Throws(() => + { + Func supplier = null!; + this.CreateLazy(supplier); + }); + } + + /// + /// Verifies that Get() correctly handles a null result from the supplier. + /// + [Test] + public void Lazy_Get_NullResultSupplier_ReturnsNull() + { + var callCount = 0; + var supplier = new Func(() => + { + callCount++; + return null; + }); + + var lazy = this.CreateLazy(supplier); + + Assert.Multiple(() => + { + Assert.That(lazy.Get(), Is.Null); + Assert.That(lazy.Get(), Is.Null); + Assert.That(callCount, Is.EqualTo(1)); + }); + } + + /// + /// abstract method for creating an ILazy instance. + /// + /// function responsible for computing the value. + /// the type of the computed value. + /// a new ILazy instance. + protected abstract ILazy CreateLazy(Func supplier); +} \ No newline at end of file diff --git a/HW2/LazyEvaluation.Test/LazyMultiThreadedTest.cs b/HW2/LazyEvaluation.Test/LazyMultiThreadedTest.cs new file mode 100644 index 0000000..267a895 --- /dev/null +++ b/HW2/LazyEvaluation.Test/LazyMultiThreadedTest.cs @@ -0,0 +1,64 @@ +// +// Copyright (c) khusainovilas. All rights reserved. +// + +namespace LazyEvaluation.Test; + +/// +/// Unit tests for LazyMultiThreaded implementation. +/// +public class LazyMultiThreadedTest : LazyEvaluationTest +{ + /// + /// Verifies that the supplier is called exactly once during concurrent Get() calls in LazyMultiThreaded. + /// + [Test] + public void LazyMultiThreaded_Get_String_SingleSupplierCall() + { + var callCount = 0; + var lazy = this.CreateLazy(() => + { + Interlocked.Increment(ref callCount); + return "test"; + }); + + const int threadsCount = 100; + var threads = new Thread[threadsCount]; + var results = new string[threadsCount]; + + var startEvent = new ManualResetEvent(false); + + for (var i = 0; i < threadsCount; i++) + { + var index = i; + threads[i] = new Thread(() => + { + results[index] = lazy.Get(); + }); + } + + startEvent.Set(); + + foreach (var thread in threads) + { + thread.Start(); + } + + foreach (var thread in threads) + { + thread.Join(); + } + + Assert.Multiple(() => + { + Assert.That(results, Has.All.EqualTo("test")); + Assert.That(callCount, Is.EqualTo(1)); + }); + } + + /// + protected override ILazy CreateLazy(Func supplier) + { + return new LazyMultiThreaded(supplier); + } +} \ No newline at end of file diff --git a/HW2/LazyEvaluation.Test/LazySingleThreadedTest.cs b/HW2/LazyEvaluation.Test/LazySingleThreadedTest.cs new file mode 100644 index 0000000..7c0d4e5 --- /dev/null +++ b/HW2/LazyEvaluation.Test/LazySingleThreadedTest.cs @@ -0,0 +1,51 @@ +// +// Copyright (c) khusainovilas. All rights reserved. +// + +namespace LazyEvaluation.Test; + +/// +/// Unit tests for LazySingleThreaded implementation. +/// +public class LazySingleThreadedTest : LazyEvaluationTest +{ + /// + /// Test for correct work Get() with double. + /// + [Test] + public void LazySingleThreaded_Get_Double_ReturnsCorrectValue() + { + var callCount = 0; + var lazy = this.CreateLazy(() => + { + callCount++; + return 1.5; + }); + + var result1 = lazy.Get(); + var result2 = lazy.Get(); + + Assert.Multiple(() => + { + Assert.That(result1, Is.EqualTo(1.5).Within(1e-9)); + Assert.That(result2, Is.EqualTo(1.5).Within(1e-9)); + Assert.That(callCount, Is.EqualTo(1)); + }); + } + + /// + /// Test for handling exception from supplier in Get(). + /// + [Test] + public void LazySingleThreaded_Get_Double_ThrowsException() + { + var lazy = this.CreateLazy(() => throw new InvalidOperationException("Supplier failed")); + Assert.Throws(() => lazy.Get()); + } + + /// + protected override ILazy CreateLazy(Func supplier) + { + return new LazySingleThreaded(supplier); + } +} \ No newline at end of file diff --git a/HW2/LazyEvaluation.Test/stylecop.json b/HW2/LazyEvaluation.Test/stylecop.json new file mode 100644 index 0000000..76c8e76 --- /dev/null +++ b/HW2/LazyEvaluation.Test/stylecop.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "companyName": "khusainovilas", + "copyrightText": "Copyright (c) {companyName}. All rights reserved." + } + } +} \ No newline at end of file diff --git a/HW2/LazyEvaluation/ILazy.cs b/HW2/LazyEvaluation/ILazy.cs new file mode 100644 index 0000000..d9059e1 --- /dev/null +++ b/HW2/LazyEvaluation/ILazy.cs @@ -0,0 +1,18 @@ +// +// Copyright (c) khusainovilas. All rights reserved. +// + +namespace LazyEvaluation; + +/// +/// Defines a contract for lazy evaluation, where a value of type T is computed only on the first call and cached for subsequent access. +/// +/// The type of the value to be lazily computed. +public interface ILazy +{ + /// + /// Gets the lazily computed value. The value is computed on the first call and cached for subsequent calls. + /// + /// The computed or cached value. + T Get(); +} \ No newline at end of file diff --git a/HW2/LazyEvaluation/LazyEvaluation.csproj b/HW2/LazyEvaluation/LazyEvaluation.csproj new file mode 100644 index 0000000..b158c23 --- /dev/null +++ b/HW2/LazyEvaluation/LazyEvaluation.csproj @@ -0,0 +1,21 @@ + + + + Library + net9.0 + enable + enable + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/HW2/LazyEvaluation/LazyMultiThreaded.cs b/HW2/LazyEvaluation/LazyMultiThreaded.cs new file mode 100644 index 0000000..62b5cd1 --- /dev/null +++ b/HW2/LazyEvaluation/LazyMultiThreaded.cs @@ -0,0 +1,49 @@ +// +// Copyright (c) khusainovilas. All rights reserved. +// + +namespace LazyEvaluation; + +/// +/// A multithreaded implementation of the ILazy interface for lazy evaluation. +/// +/// The type of the value produced by the lazy computation. +public class LazyMultiThreaded : ILazy +{ + private readonly object lockObject = new(); + private Func? supplier; + private T? value; + private volatile bool isComputed; + + /// + /// Initializes a new instance of the class. + /// + /// The delegate that produces the value when needed. + public LazyMultiThreaded(Func supplier) + { + this.supplier = supplier ?? throw new ArgumentNullException(nameof(supplier)); + } + + /// + public T Get() + { + if (this.isComputed) + { + return this.value!; + } + + lock (this.lockObject) + { + if (this.isComputed) + { + return this.value!; + } + + this.value = this.supplier!(); + this.isComputed = true; + this.supplier = null!; + } + + return this.value!; + } +} \ No newline at end of file diff --git a/HW2/LazyEvaluation/LazySingleThreaded.cs b/HW2/LazyEvaluation/LazySingleThreaded.cs new file mode 100644 index 0000000..b6490a1 --- /dev/null +++ b/HW2/LazyEvaluation/LazySingleThreaded.cs @@ -0,0 +1,40 @@ +// +// Copyright (c) khusainovilas. All rights reserved. +// + +namespace LazyEvaluation; + +/// +/// A single-threaded implementation of the ILazy interface for lazy evaluation. +/// +/// The type of the value produced by the lazy computation. +public class LazySingleThreaded : ILazy +{ + private Func? supplier; + private T? value; + private bool isComputed; + + /// + /// Initializes a new instance of the class. + /// + /// The delegate that produces the value when needed. + public LazySingleThreaded(Func supplier) + { + this.supplier = supplier ?? throw new ArgumentNullException(nameof(supplier)); + } + + /// + public T Get() + { + if (this.isComputed) + { + return this.value!; + } + + this.value = this.supplier!(); + this.isComputed = true; + this.supplier = null!; + + return this.value!; + } +} \ No newline at end of file diff --git a/HW2/LazyEvaluation/stylecop.json b/HW2/LazyEvaluation/stylecop.json new file mode 100644 index 0000000..76c8e76 --- /dev/null +++ b/HW2/LazyEvaluation/stylecop.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "companyName": "khusainovilas", + "copyrightText": "Copyright (c) {companyName}. All rights reserved." + } + } +} \ No newline at end of file