Skip to content

[pull] master from TheAlgorithms:master #20

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "The Algorithms C#",
"image": "mcr.microsoft.com/devcontainers/dotnet:1-8.0-bookworm",
"customizations": {
"vscode": {
"extensions": [
"ms-dotnettools.csharp",
"ms-dotnettools.csdevkit",
"nunit.nunit-adapter",
"fluentassertions.fluentassertions"
]
}
},
"postCreateCommand": "sudo chown -R $(whoami) /workspaces"
}
1 change: 1 addition & 0 deletions Algorithms.Tests/Algorithms.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
</PackageReference>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="nunit" Version="4.0.1" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
</ItemGroup>
Expand Down
23 changes: 23 additions & 0 deletions Algorithms.Tests/Numeric/AdditionWithoutArithmeticsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Numerics;
using Algorithms.Numeric;
using NUnit.Framework;

namespace Algorithms.Tests.Numeric;

public static class AdditionWithoutArithmeticTests
{
[TestCase(3, 5, 8)]
[TestCase(13, 5, 18)]
[TestCase(-7, 2, -5)]
[TestCase(0, -7, -7)]
[TestCase(-321, 0, -321)]
public static void CalculateAdditionWithoutArithmetic_Test(int first, int second, int expectedResult)
{
// Act
var result = AdditionWithoutArithmetic.CalculateAdditionWithoutArithmetic(first, second);

// Assert
Assert.That(result, Is.EqualTo(expectedResult));
}
}
59 changes: 59 additions & 0 deletions Algorithms.Tests/Other/GeohashTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using Algorithms.Other;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Algorithms.Tests.Other
{
[TestFixture]
public class GeohashTests
{
[Test]
public void Encode_ShouldReturnCorrectGeohash_ForHoChiMinhCity()
{
double latitude = 10.8231;
double longitude = 106.6297;
string result = Geohash.Encode(latitude, longitude);
Assert.That(result, Is.EqualTo("w3gvd6m3hh54"));
}

[Test]
public void Encode_ShouldReturnCorrectGeohash_ForHanoi()
{
double latitude = 21.0285;
double longitude = 105.8542;
string result = Geohash.Encode(latitude, longitude);
Assert.That(result, Is.EqualTo("w7er8u0evss2"));
}

[Test]
public void Encode_ShouldReturnCorrectGeohash_ForDaNang()
{
double latitude = 16.0544;
double longitude = 108.2022;
string result = Geohash.Encode(latitude, longitude);
Assert.That(result, Is.EqualTo("w6ugq4w7wj04"));
}

[Test]
public void Encode_ShouldReturnCorrectGeohash_ForNhaTrang()
{
double latitude = 12.2388;
double longitude = 109.1967;
string result = Geohash.Encode(latitude, longitude);
Assert.That(result, Is.EqualTo("w6jtsu485t8v"));
}

[Test]
public void Encode_ShouldReturnCorrectGeohash_ForVungTau()
{
double latitude = 10.3460;
double longitude = 107.0843;
string result = Geohash.Encode(latitude, longitude);
Assert.That(result, Is.EqualTo("w3u4ug2mv41m"));
}
}
}
95 changes: 95 additions & 0 deletions Algorithms.Tests/RecommenderSystem/CollaborativeFilteringTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using Algorithms.RecommenderSystem;
using Moq;
using NUnit.Framework;
using System.Collections.Generic;

namespace Algorithms.Tests.RecommenderSystem
{
[TestFixture]
public class CollaborativeFilteringTests
{
private Mock<ISimilarityCalculator>? mockSimilarityCalculator;
private CollaborativeFiltering? recommender;
private Dictionary<string, Dictionary<string, double>> testRatings = null!;

[SetUp]
public void Setup()
{
mockSimilarityCalculator = new Mock<ISimilarityCalculator>();
recommender = new CollaborativeFiltering(mockSimilarityCalculator.Object);

testRatings = new Dictionary<string, Dictionary<string, double>>
{
["user1"] = new()
{
["item1"] = 5.0,
["item2"] = 3.0,
["item3"] = 4.0
},
["user2"] = new()
{
["item1"] = 4.0,
["item2"] = 2.0,
["item3"] = 5.0
},
["user3"] = new()
{
["item1"] = 3.0,
["item2"] = 4.0,
["item4"] = 3.0
}
};
}

[Test]
[TestCase("item1", 4.0, 5.0)]
[TestCase("item2", 2.0, 4.0)]
public void CalculateSimilarity_WithValidInputs_ReturnsExpectedResults(
string commonItem,
double rating1,
double rating2)
{
var user1Ratings = new Dictionary<string, double> { [commonItem] = rating1 };
var user2Ratings = new Dictionary<string, double> { [commonItem] = rating2 };

var similarity = recommender?.CalculateSimilarity(user1Ratings, user2Ratings);

Assert.That(similarity, Is.InRange(-1.0, 1.0));
}

[Test]
public void CalculateSimilarity_WithNoCommonItems_ReturnsZero()
{
var user1Ratings = new Dictionary<string, double> { ["item1"] = 5.0 };
var user2Ratings = new Dictionary<string, double> { ["item2"] = 4.0 };

var similarity = recommender?.CalculateSimilarity(user1Ratings, user2Ratings);

Assert.That(similarity, Is.EqualTo(0));
}

[Test]
public void PredictRating_WithNonexistentItem_ReturnsZero()
{
var predictedRating = recommender?.PredictRating("nonexistentItem", "user1", testRatings);

Assert.That(predictedRating, Is.EqualTo(0));
}

[Test]
public void PredictRating_WithOtherUserHavingRatedTargetItem_ShouldCalculateSimilarityAndWeightedSum()
{
var targetItem = "item1";
var targetUser = "user1";

mockSimilarityCalculator?
.Setup(s => s.CalculateSimilarity(It.IsAny<Dictionary<string, double>>(), It.IsAny<Dictionary<string, double>>()))
.Returns(0.8);

var predictedRating = recommender?.PredictRating(targetItem, targetUser, testRatings);

Assert.That(predictedRating, Is.Not.EqualTo(0.0d));
Assert.That(predictedRating, Is.EqualTo(3.5d).Within(0.01));
}
}
}
28 changes: 28 additions & 0 deletions Algorithms/Numeric/AdditionWithoutArithmetic.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Numerics;

namespace Algorithms.Numeric;

/// <summary>
/// Add the integers without arithmetic operation.
/// </summary>
public static class AdditionWithoutArithmetic
{
/// <summary>
/// Returns the sum of two integers.
/// </summary>
/// <param name="first">First number to add.</param>
/// <param name="second">Second number to add.</param>
/// <returns>Sum of the two numbers.</returns>
public static int CalculateAdditionWithoutArithmetic(int first, int second)
{
while (second != 0)
{
int c = first & second; // Carry
first ^= second; // Sum without carry
second = c << 1; // Carry shifted left
}

return first;
}
}
84 changes: 84 additions & 0 deletions Algorithms/Other/Geohash.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Algorithms.Other
{
public static class Geohash
{
private const string Base32Characters = "0123456789bcdefghjkmnpqrstuvwxyz"; // Convert latitude and longitude coordinates into a concise string
private const int GeohashLength = 12; // ± 1.86 cm

/// <summary>
/// Encodes the provided latitude and longitude coordinates into a Geohash string.
/// Geohashing is a method to encode geographic coordinates (latitude, longitude).
/// into a short string of letters and digits. Each character in the resulting Geohash .
/// string adds more precision to the location. The longer the Geohash, the smaller the area.
/// </summary>
/// <param name="latitude">The latitude of the location to encode. It must be a value between -90 and 90.</param>
/// <param name="longitude">The longitude of the location to encode. It must be a value between -180 and 180.</param>
/// <returns>
/// A Geohash string of length 12 representing the location with high precision.
/// A longer Geohash provides higher precision in terms of geographic area.
/// and a 12-character Geohash can be accurate down to around 1.86 cm.
/// </returns>
public static string Encode(double latitude, double longitude)
{
double[] latitudeRange = new[] { -90.0, 90.0 };
double[] longitudeRange = new[] { -180.0, 180.0 };
bool isEncodingLongitude = true;
int currentBit = 0;
int base32Index = 0;
StringBuilder geohashResult = new StringBuilder();

while (geohashResult.Length < GeohashLength)
{
double midpoint;

if (isEncodingLongitude)
{
midpoint = (longitudeRange[0] + longitudeRange[1]) / 2;
if (longitude > midpoint)
{
base32Index |= 1 << (4 - currentBit);
longitudeRange[0] = midpoint;
}
else
{
longitudeRange[1] = midpoint;
}
}
else
{
midpoint = (latitudeRange[0] + latitudeRange[1]) / 2;
if (latitude > midpoint)
{
base32Index |= 1 << (4 - currentBit);
latitudeRange[0] = midpoint;
}
else
{
latitudeRange[1] = midpoint;
}
}

isEncodingLongitude = !isEncodingLongitude;

if (currentBit < 4)
{
currentBit++;
}
else
{
geohashResult.Append(Base32Characters[base32Index]);
currentBit = 0;
base32Index = 0;
}
}

return geohashResult.ToString();
}
}
}
Loading