Skip to content

Commit

Permalink
Checkpoint declarative sorting ordering rules
Browse files Browse the repository at this point in the history
  • Loading branch information
kg committed Aug 9, 2015
1 parent d36c4f3 commit 352f4f8
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 11 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -3,6 +3,8 @@
*.suo
*.user
*.pidb
*.mdf
*.ldf
_ReSharper.*
/Squared/360/TaskLib360/bin
/Squared/360/TaskLib360/obj
Expand Down
143 changes: 138 additions & 5 deletions Squared/Util/DeclarativeSort.cs
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
Expand All @@ -11,6 +12,10 @@ public interface ITags {
bool Contains (Tag tag);
int Count { get; }
Tag this [ int index ] { get; }

/// <summary>
/// For internal use only.
/// </summary>
Dictionary<Tag, ITags> TransitionCache { get; }
}

Expand All @@ -27,7 +32,8 @@ public class EqualityComparer : IEqualityComparer<Tag> {
}
}

public class Comparer : IComparer<Tag> {
// Only for sorting within tag arrays to make equality comparisons of tag arrays valid
internal class Comparer : IComparer<Tag> {
public static readonly Comparer Instance = new Comparer();

public int Compare (Tag x, Tag y) {
Expand All @@ -53,7 +59,7 @@ public class Comparer : IComparer<Tag> {
if (index == 0)
return this;
else
throw new ArgumentOutOfRangeException("index");
throw new ArgumentOutOfRangeException(nameof(index));
}
}

Expand Down Expand Up @@ -91,6 +97,24 @@ public class Comparer : IComparer<Tag> {
return TagSet.Transition(lhs, rhs);
}

/// <returns>Whether lhs contains rhs.</returns>
public static bool operator & (ITags lhs, Tag rhs) {
return lhs.Contains(rhs);
}

/// <returns>Whether lhs does not contain rhs.</returns>
public static bool operator ^ (ITags lhs, Tag rhs) {
return !lhs.Contains(rhs);
}

public static TagOrdering operator < (Tag lhs, Tag rhs) {
return new TagOrdering(lhs, rhs);
}

public static TagOrdering operator > (Tag lhs, Tag rhs) {
return new TagOrdering(rhs, lhs);
}

public static Tag New (string name) {
Tag result;
if (!TagCache.TryGetValue(name, out result))
Expand All @@ -100,6 +124,49 @@ public class Comparer : IComparer<Tag> {
}
}

public struct TagOrdering {
public readonly Tag Lower, Higher;

public TagOrdering (Tag lower, Tag higher) {
if (lower == null)
throw new ArgumentNullException(nameof(lower));
else if (higher == null)
throw new ArgumentNullException(nameof(higher));

Lower = lower;
Higher = higher;
}

public int Compare (ITags lhs, ITags rhs) {
if (lhs.Contains(Lower) && rhs.Contains(Higher))
return -1;

if (lhs.Contains(Higher) && rhs.Contains(Lower))
return 1;

return 0;
}

public override int GetHashCode () {
return Lower.Id ^ (Higher.Id << 8);
}

public bool Equals (TagOrdering rhs) {
return (Lower == rhs.Lower) && (Higher == rhs.Higher);
}

public override bool Equals (object rhs) {
if (rhs is TagOrdering)
return Equals((TagOrdering)rhs);
else
return false;
}

public override string ToString () {
return string.Format("{0} < {1}", Lower, Higher);
}
}

public partial class TagSet : ITags {
private static int NextId = 1;

Expand Down Expand Up @@ -234,9 +301,75 @@ public class Group {
public readonly string Name;
}

public class Sorter {
public void AddRules (RuleSet ruleSet) {
throw new NotImplementedException();
public class ContradictoryOrderingException : Exception {
public readonly TagOrdering A, B;

public ContradictoryOrderingException (TagOrdering a, TagOrdering b)
: base(
string.Format("Orderings {0} and {1} are contradictory", a, b)
) {
A = a;
B = b;
}
}

public class TagOrderingCollection : HashSet<TagOrdering> {
public int? Compare (ITags lhs, ITags rhs, out Exception error) {
int result = 0;
var lastOrdering = default(TagOrdering);

foreach (var ordering in this) {
var subResult = ordering.Compare(lhs, rhs);

if (subResult == 0)
continue;
else if (
(result != 0) &&
(Math.Sign(subResult) != Math.Sign(result))
) {
error = new ContradictoryOrderingException(
lastOrdering, ordering
);
return null;
} else {
result = subResult;
lastOrdering = ordering;
}
}

error = null;
return result;
}

public int Compare (ITags lhs, ITags rhs) {
Exception error;
var result = Compare(lhs, rhs, out error);

if (result.HasValue)
return result.Value;
else
throw error;
}
}

public class Sorter : IEnumerable<TagOrdering> {
public readonly TagOrderingCollection Orderings = new TagOrderingCollection();

public void Add (TagOrdering ordering) {
Orderings.Add(ordering);
}

public void Add (params TagOrdering[] orderings) {
foreach (var o in orderings)
Orderings.Add(o);
}

IEnumerator IEnumerable.GetEnumerator () {
return Orderings.GetEnumerator();
}

IEnumerator<TagOrdering> IEnumerable<TagOrdering>.GetEnumerator () {
return Orderings.GetEnumerator();
}
}
}
70 changes: 64 additions & 6 deletions Squared/Util/UtilTests/Tests/DeclarativeSortTests.cs
Expand Up @@ -7,12 +7,6 @@
using Squared.Util.DeclarativeSort;

namespace Squared.Util {
[TestFixture]
public class DeclarativeSortTests {
public DeclarativeSortTests () {
}
}

[TestFixture]
public class TaggingTests {
private readonly Tag A, B, C;
Expand Down Expand Up @@ -52,10 +46,74 @@ public class TaggingTests {

[Test]
public void Contains () {
var a = (ITags)A;
Assert.IsTrue(a.Contains(A));
Assert.IsFalse(a.Contains(B));

var ab = A + B;
Assert.IsTrue(ab.Contains(A));
Assert.IsTrue(ab.Contains(B));
Assert.IsFalse(ab.Contains(C));

Assert.IsTrue (ab & A);
Assert.IsFalse(ab & C);

Assert.IsFalse(ab ^ A);
Assert.IsTrue (ab ^ C);
}
}

[TestFixture]
public class OrderingRuleTests {
private readonly Tag A, B, C;

public OrderingRuleTests () {
A = Tag.New("a");
B = Tag.New("b");
C = Tag.New("c");
}

[Test]
public void RulesAreUnique () {
var sorter = new Sorter {
A < B,
B < C
};

Assert.AreEqual(2, sorter.Orderings.Count);

sorter.Add(A < B);

Assert.AreEqual(2, sorter.Orderings.Count);
}

[Test]
public void CanCompareUsingOrderings () {
var rs = new TagOrderingCollection {
A < B,
B < C
};

Assert.AreEqual(0, rs.Compare(A, A));
Assert.AreEqual(0, rs.Compare(A, C));
Assert.AreEqual(0, rs.Compare(C, A));
Assert.AreEqual(-1, rs.Compare(A, B));
Assert.AreEqual(-1, rs.Compare(B, C));
Assert.AreEqual(1, rs.Compare(B, A));
Assert.AreEqual(1, rs.Compare(C, B));
}

[Test]
public void CompareAbortsOnContradiction () {
var rs = new TagOrderingCollection {
A < B,
A > C
};

Exception error;
var result = rs.Compare(A + B + C, A, out error);
Assert.IsTrue(error is ContradictoryOrderingException);
Assert.IsFalse(result.HasValue);
}
}
}

0 comments on commit 352f4f8

Please sign in to comment.