Skip to content

Commit

Permalink
To hell with it - breaking changes to DiscriminationTree to bring it …
Browse files Browse the repository at this point in the history
…up to speed.
  • Loading branch information
sdcondon committed Aug 12, 2023
1 parent f422bd5 commit 46cb9a9
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 260 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
using System;
using System.Collections.Generic;
using static SCFirstOrderLogic.SentenceCreation.OperableSentenceFactory;
using IElementInfo = SCFirstOrderLogic.TermIndexing.DiscriminationTree<SCFirstOrderLogic.Term>.IElementInfo;
using ConstantInfo = SCFirstOrderLogic.TermIndexing.DiscriminationTree<SCFirstOrderLogic.Term>.ConstantInfo;
using FunctionInfo = SCFirstOrderLogic.TermIndexing.DiscriminationTree<SCFirstOrderLogic.Term>.FunctionInfo;
using VariableInfo = SCFirstOrderLogic.TermIndexing.DiscriminationTree<SCFirstOrderLogic.Term>.VariableInfo;
using ConstantInfo = SCFirstOrderLogic.TermIndexing.DiscriminationTreeConstantInfo;
using FunctionInfo = SCFirstOrderLogic.TermIndexing.DiscriminationTreeFunctionInfo;
using IElementInfo = SCFirstOrderLogic.TermIndexing.IDiscriminationTreeElementInfo;
using VariableInfo = SCFirstOrderLogic.TermIndexing.DiscriminationTreeVariableInfo;

namespace SCFirstOrderLogic.TermIndexing
{
Expand Down Expand Up @@ -117,9 +117,10 @@ public static class DiscriminationTreeTests
})
.When(tc =>
{
var tree = new DiscriminationTree(tc.CurrentTerms);
var root = new DiscriminationTreeDictionaryNode<Term>();
var tree = new DiscriminationTree(root, tc.CurrentTerms);
tree.Add(tc.NewTerm);
return tree.Root.Children;
return root.Children;
})
.ThenReturns((tc, rv) => rv.Should().BeEquivalentTo(tc.ExpectedRootChildren));

Expand Down
31 changes: 17 additions & 14 deletions src/SCFirstOrderLogic/TermIndexing/DiscriminationTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,44 @@
namespace SCFirstOrderLogic.TermIndexing
{
/// <summary>
/// An implementation of an in-memory discrimination tree for <see cref="Term"/>s - specifically, one for which the attached values are the terms themselves.
/// An implementation of a discrimination tree for <see cref="Term"/>s - specifically, one for which the attached values are the terms themselves.
/// </summary>
/// <seealso href="https://www.google.com/search?q=discrimination+tree"/>
public class DiscriminationTree
{
private readonly DiscriminationTree<Term> actualTree;

/// <summary>
/// Initializes a new instance of the <see cref="DiscriminationTree"/> class that is empty to begin with.
/// Initializes a new instance of the <see cref="DiscriminationTree"/> class.
/// </summary>
public DiscriminationTree()
: this(new DiscriminationTreeDictionaryNode<Term>(), Enumerable.Empty<Term>())
{
}

/// <summary>
/// Initializes a new instance of the <see cref="DiscriminationTree"/> class with a specified root node.
/// </summary>
public DiscriminationTree(IDiscriminationTreeNode<Term> root)
: this(root, Enumerable.Empty<Term>())
{
actualTree = new();
}

/// <summary>
/// Initializes a new instance of the <see cref="DiscriminationTree"/> class with some initial content.
/// </summary>
public DiscriminationTree(IEnumerable<Term> content)
: this(new DiscriminationTreeDictionaryNode<Term>(), content)
{
actualTree = new(content.Select(t => KeyValuePair.Create(t, t)));
}

/// <summary>
/// <para>
/// Gets the root node of the tree.
/// </para>
/// <para>
/// NB: In "normal" usage consumers shouldn't need to access this property.
/// However, discrimination trees are a specific, well-known data structure, and
/// learning and experimentation is a key priority for this library. As such,
/// interrogability of the internal structure is considered a useful feature.
/// </para>
/// Initializes a new instance of the <see cref="DiscriminationTree"/> class with a specified root node and some initial content.
/// </summary>
public DiscriminationTree<Term>.InternalNode Root => actualTree.Root;
public DiscriminationTree(IDiscriminationTreeNode<Term> root, IEnumerable<Term> content)
{
actualTree = new(root, content.Select(t => KeyValuePair.Create(t, t)));
}

/// <summary>
/// Adds a <see cref="Term"/> to the tree.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) 2021-2023 Simon Condon.
// You may use this file in accordance with the terms of the MIT license.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace SCFirstOrderLogic.TermIndexing
{
/// <summary>
/// <para>
/// An implementation of <see cref="IDiscriminationTreeNode{TValue}"/> that stores its content using a dictionary.
/// </para>
/// <para>
/// NB: If you are using this type, you might as well be using <see cref="DiscriminationTree{TValue}"/> to avoid the overhead of asynchronicity.
/// <see cref="AsyncDiscriminationTree{TValue}"/> is intended to facilitate indices that use secondary storage - this type is just an example
/// node implementation to base real (secondary storage utilising) node implmentations on.
/// </para>
/// </summary>
/// <typeparam name="TValue">The type of value attached for each term.</typeparam>
public class DiscriminationTreeDictionaryNode<TValue> : IDiscriminationTreeNode<TValue>
{
private readonly Dictionary<IDiscriminationTreeElementInfo, IDiscriminationTreeNode<TValue>> children = new();

/// <inheritdoc/>
// NB: we don't bother wrapping children in a ReadOnlyDict to stop unscrupulous
// users from casting. Would be more mem for a real edge case..
public IReadOnlyDictionary<IDiscriminationTreeElementInfo, IDiscriminationTreeNode<TValue>> Children => children;

/// <inheritdoc/>
public TValue Value => throw new NotSupportedException("Internal node - has no value");

/// <inheritdoc/>
public IDiscriminationTreeNode<TValue> GetOrAddInternalChild(IDiscriminationTreeElementInfo elementInfo)
{
if (!Children.TryGetValue(elementInfo, out var node))
{
node = new DiscriminationTreeDictionaryNode<TValue>();
children.Add(elementInfo, node);
}

return node;
}

/// <inheritdoc/>
public void AddLeafChild(IDiscriminationTreeElementInfo elementInfo, TValue value)
{
if (!children.TryAdd(elementInfo, new LeafNode(value)))
{
throw new ArgumentException("Key already present", nameof(elementInfo));
}
}

/// <summary>
/// Representation of a leaf node (that is, a node with an attached value) of a discrimination tree.
/// </summary>
private sealed class LeafNode : IDiscriminationTreeNode<TValue>
{
private static readonly ReadOnlyDictionary<IDiscriminationTreeElementInfo, IDiscriminationTreeNode<TValue>> emptyChildren = new(new Dictionary<IDiscriminationTreeElementInfo, IDiscriminationTreeNode<TValue>>());

internal LeafNode(TValue value) => Value = value;

/// <inheritdoc/>
public IReadOnlyDictionary<IDiscriminationTreeElementInfo, IDiscriminationTreeNode<TValue>> Children => emptyChildren;

/// <inheritdoc/>
public TValue Value { get; }

/// <inheritdoc/>
public IDiscriminationTreeNode<TValue> GetOrAddInternalChild(IDiscriminationTreeElementInfo elementInfo)
{
throw new NotSupportedException("Leaf node - cannot have children");
}

/// <inheritdoc/>
public void AddLeafChild(IDiscriminationTreeElementInfo elementInfo, TValue value)
{
throw new NotSupportedException("Leaf node - cannot have children");
}
}
}
}
Loading

0 comments on commit 46cb9a9

Please sign in to comment.