diff --git a/src/SIL.Machine/FeatureModel/BitArraySymbolicFeatureValueFlags.cs b/src/SIL.Machine/FeatureModel/BitArraySymbolicFeatureValueFlags.cs new file mode 100644 index 000000000..451bae29b --- /dev/null +++ b/src/SIL.Machine/FeatureModel/BitArraySymbolicFeatureValueFlags.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace SIL.Machine.FeatureModel +{ + internal class BitArraySymbolicFeatureValueFlags : ISymbolicFeatureValueFlags + { + private readonly SymbolicFeature _feature; + private readonly BitArray _flags; + + public BitArraySymbolicFeatureValueFlags(SymbolicFeature feature) + { + _feature = feature; + _flags = new BitArray(feature.PossibleSymbols.Count, false); + } + + private BitArraySymbolicFeatureValueFlags(SymbolicFeature feature, BitArray flags) + { + _feature = feature; + _flags = flags; + } + + public bool HasAnySet() + { + return HasAnySet(_flags); + } + + public bool HasAllSet() + { + return HasAllSet(_flags); + } + + public bool Get(FeatureSymbol symbol) + { + return _flags.Get(symbol.Index); + } + + public FeatureSymbol GetFirst() + { + return _feature.PossibleSymbols.FirstOrDefault(Get); + } + + public void Set(IEnumerable symbols) + { + foreach (FeatureSymbol symbol in symbols) + _flags.Set(symbol.Index, true); + } + + public bool IsSupersetOf(bool not, ISymbolicFeatureValueFlags other, bool notOther) + { + var otherBitArray = (BitArraySymbolicFeatureValueFlags)other; + if (!not && !notOther) + { + return AreEqual(Copy(_flags).And(otherBitArray._flags), otherBitArray._flags); + } + else if (!not) + { + BitArray notOtherFlags = Copy(otherBitArray._flags).Not(); + return AreEqual(Copy(_flags).And(notOtherFlags), notOtherFlags); + } + else if (!notOther) + { + return AreEqual(Copy(_flags).Not().And(otherBitArray._flags), otherBitArray._flags); + } + else + { + BitArray notOtherFlags = Copy(otherBitArray._flags).Not(); + return AreEqual(Copy(_flags).Not().And(notOtherFlags), notOtherFlags); + } + } + + public bool Overlaps(bool not, ISymbolicFeatureValueFlags other, bool notOther) + { + var otherBitArray = (BitArraySymbolicFeatureValueFlags)other; + if (!not && !notOther) + { + return HasAnySet(Copy(_flags).And(otherBitArray._flags)); + } + else if (!not) + { + BitArray notOtherFlags = Copy(otherBitArray._flags).Not(); + return AreEqual(Copy(_flags).And(notOtherFlags), notOtherFlags); + } + else if (!notOther) + { + return AreEqual(Copy(_flags).Not().And(otherBitArray._flags), otherBitArray._flags); + } + else + { + BitArray notOtherFlags = Copy(otherBitArray._flags).Not(); + return AreEqual(Copy(_flags).Not().And(notOtherFlags), notOtherFlags); + } + } + + public void IntersectWith(bool not, ISymbolicFeatureValueFlags other, bool notOther) + { + var otherBitArray = (BitArraySymbolicFeatureValueFlags)other; + if (!not && !notOther) + { + _flags.And(otherBitArray._flags); + } + else if (!not) + { + _flags.And(Copy(otherBitArray._flags).Not()); + } + else if (!notOther) + { + _flags.Not().And(otherBitArray._flags); + } + else + { + _flags.Not().And(Copy(otherBitArray._flags).Not()); + } + } + + public void UnionWith(bool not, ISymbolicFeatureValueFlags other, bool notOther) + { + var otherBitArray = (BitArraySymbolicFeatureValueFlags)other; + if (!not && !notOther) + { + _flags.Or(otherBitArray._flags); + } + else if (!not) + { + _flags.Or(Copy(otherBitArray._flags).Not()); + } + else if (!notOther) + { + _flags.Not().Or(otherBitArray._flags); + } + else + { + _flags.Not().Or(Copy(otherBitArray._flags).Not()); + } + } + + public void ExceptWith(bool not, ISymbolicFeatureValueFlags other, bool notOther) + { + var otherBitArray = (BitArraySymbolicFeatureValueFlags)other; + if (!not && !notOther) + { + _flags.And(Copy(otherBitArray._flags).Not()); + } + else if (!not) + { + _flags.And(otherBitArray._flags); + } + else if (!notOther) + { + _flags.Not().And(Copy(otherBitArray._flags).Not()); + } + else + { + _flags.Not().And(otherBitArray._flags); + } + } + + public ISymbolicFeatureValueFlags Not() + { + return new BitArraySymbolicFeatureValueFlags(_feature, Copy(_flags).Not()); + } + + public bool ValueEquals(ISymbolicFeatureValueFlags other) + { + var otherBitArray = (BitArraySymbolicFeatureValueFlags)other; + return AreEqual(_flags, otherBitArray._flags); + } + + public int GetValuesHashCode() + { + int hash = 0; + foreach (bool value in _flags) + { + hash ^= (value ? 2 : 1); + } + return hash; + } + + public ISymbolicFeatureValueFlags Clone() + { + return new BitArraySymbolicFeatureValueFlags(_feature, Copy(_flags)); + } + + private static bool AreEqual(BitArray array1, BitArray array2) + { + return array1.Cast().SequenceEqual(array2.Cast()); + } + + private static bool HasAnySet(BitArray flags) + { + foreach (bool flag in flags) + { + if (flag) + return true; + } + return false; + } + + private static bool HasAllSet(BitArray flags) + { + foreach (bool flag in flags) + { + if (!flag) + return false; + } + return true; + } + + private static BitArray Copy(BitArray flags) + { + return new BitArray(flags); + } + } +} diff --git a/src/SIL.Machine/FeatureModel/ISymbolicFeatureValueFlags.cs b/src/SIL.Machine/FeatureModel/ISymbolicFeatureValueFlags.cs new file mode 100644 index 000000000..7011774d7 --- /dev/null +++ b/src/SIL.Machine/FeatureModel/ISymbolicFeatureValueFlags.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using SIL.ObjectModel; + +namespace SIL.Machine.FeatureModel +{ + internal interface ISymbolicFeatureValueFlags : ICloneable + { + bool HasAnySet(); + bool HasAllSet(); + + bool Get(FeatureSymbol symbol); + FeatureSymbol GetFirst(); + void Set(IEnumerable symbols); + + bool IsSupersetOf(bool not, ISymbolicFeatureValueFlags other, bool notOther); + bool Overlaps(bool not, ISymbolicFeatureValueFlags other, bool notOther); + + void IntersectWith(bool not, ISymbolicFeatureValueFlags other, bool notOther); + void UnionWith(bool not, ISymbolicFeatureValueFlags other, bool notOther); + void ExceptWith(bool not, ISymbolicFeatureValueFlags other, bool notOther); + + ISymbolicFeatureValueFlags Not(); + + bool ValueEquals(ISymbolicFeatureValueFlags other); + int GetValuesHashCode(); + } +} diff --git a/src/SIL.Machine/FeatureModel/SymbolicFeature.cs b/src/SIL.Machine/FeatureModel/SymbolicFeature.cs index 56361aded..4fad07741 100644 --- a/src/SIL.Machine/FeatureModel/SymbolicFeature.cs +++ b/src/SIL.Machine/FeatureModel/SymbolicFeature.cs @@ -6,7 +6,6 @@ namespace SIL.Machine.FeatureModel public class SymbolicFeature : Feature { private readonly PossibleSymbolCollection _possibleSymbols; - private readonly ulong _mask; public SymbolicFeature(string id, params FeatureSymbol[] possibleSymbols) : this(id, (IEnumerable)possibleSymbols) { } @@ -21,7 +20,6 @@ public SymbolicFeature(string id, IEnumerable possibleSymbols) symbol.Feature = this; symbol.Index = i++; } - _mask = (1UL << _possibleSymbols.Count) - 1UL; } /// @@ -37,10 +35,5 @@ public string DefaultSymbolID { set { DefaultValue = new SymbolicFeatureValue(_possibleSymbols[value]); } } - - internal ulong Mask - { - get { return _mask; } - } } } diff --git a/src/SIL.Machine/FeatureModel/SymbolicFeatureValue.cs b/src/SIL.Machine/FeatureModel/SymbolicFeatureValue.cs index 4f982c5d5..724911e0a 100644 --- a/src/SIL.Machine/FeatureModel/SymbolicFeatureValue.cs +++ b/src/SIL.Machine/FeatureModel/SymbolicFeatureValue.cs @@ -19,13 +19,33 @@ public static explicit operator FeatureSymbol(SymbolicFeatureValue sfv) return sfv._first; } + private static ISymbolicFeatureValueFlags CreateFlags(SymbolicFeature feature) + { + if (feature.PossibleSymbols.Count <= MaxUlongSize) + return new UlongSymbolicFeatureValueFlags(feature); + return new BitArraySymbolicFeatureValueFlags(feature); + } + + internal static void ForceBitArrayFlagsImplementation() + { + MaxUlongSize = 0; + } + + internal static void ResetFlagsImplementation() + { + MaxUlongSize = sizeof(ulong) * 8; + } + + private static int MaxUlongSize { get; set; } = sizeof(ulong) * 8; + private readonly SymbolicFeature _feature; - private ulong _flags; + private readonly ISymbolicFeatureValueFlags _flags; private FeatureSymbol _first; public SymbolicFeatureValue(SymbolicFeature feature) { _feature = feature; + _flags = CreateFlags(_feature); } public SymbolicFeatureValue(IEnumerable values) @@ -34,50 +54,44 @@ public SymbolicFeatureValue(IEnumerable values) if (symbols.Length == 0) throw new ArgumentException("values cannot be empty", "values"); _feature = symbols[0].Feature; + _flags = CreateFlags(_feature); + _flags.Set(symbols); _first = symbols[0]; - Set(symbols); } public SymbolicFeatureValue(FeatureSymbol value) { _feature = value.Feature; + _flags = CreateFlags(_feature); + _flags.Set(value.ToEnumerable()); _first = value; - Set(value.ToEnumerable()); } public SymbolicFeatureValue(SymbolicFeature feature, string varName, bool agree) : base(varName, agree) { _feature = feature; + _flags = CreateFlags(_feature); } private SymbolicFeatureValue(SymbolicFeatureValue sfv) : base(sfv) { _feature = sfv._feature; + _flags = sfv._flags.Clone(); _first = sfv._first; - _flags = sfv._flags; } - private SymbolicFeatureValue(SymbolicFeature feature, ulong flags) + private SymbolicFeatureValue(SymbolicFeature feature, ISymbolicFeatureValueFlags flags) { _feature = feature; _flags = flags; SetFirst(); } - private void Set(IEnumerable symbols) - { - foreach (FeatureSymbol symbol in symbols) - { - ulong mask = 1UL << symbol.Index; - _flags |= mask; - } - } - public IEnumerable Values { - get { return _feature.PossibleSymbols.Where(Get); } + get { return _feature.PossibleSymbols.Where(_flags.Get); } } public bool IsSupersetOf(SymbolicFeatureValue other, bool notOther = false) @@ -92,12 +106,7 @@ public bool Overlaps(SymbolicFeatureValue other, bool notOther = false) private void SetFirst() { - _first = _flags == 0 ? null : _feature.PossibleSymbols.First(Get); - } - - private bool Get(FeatureSymbol symbol) - { - return (_flags & (1UL << symbol.Index)) != 0; + _first = _flags.GetFirst(); } public SymbolicFeature Feature @@ -107,12 +116,12 @@ public SymbolicFeature Feature protected override bool IsSatisfiable { - get { return base.IsSatisfiable || _flags != 0; } + get { return base.IsSatisfiable || _flags.HasAnySet(); } } protected override bool IsUninstantiated { - get { return base.IsUninstantiated && _flags == _feature.Mask; } + get { return base.IsUninstantiated && _flags.HasAllSet(); } } protected override bool IsSupersetOf(bool not, SimpleFeatureValue other, bool notOther) @@ -120,14 +129,7 @@ protected override bool IsSupersetOf(bool not, SimpleFeatureValue other, bool no if (!(other is SymbolicFeatureValue otherSfv)) return false; - if (!not && !notOther) - return (_flags & otherSfv._flags) == otherSfv._flags; - if (!not) - return (_flags & (~otherSfv._flags & _feature.Mask)) == (~otherSfv._flags & _feature.Mask); - if (!notOther) - return ((~_flags & _feature.Mask) & otherSfv._flags) == otherSfv._flags; - return ((~_flags & _feature.Mask) & (~otherSfv._flags & _feature.Mask)) - == (~otherSfv._flags & _feature.Mask); + return _flags.IsSupersetOf(not, otherSfv._flags, notOther); } protected override bool Overlaps(bool not, SimpleFeatureValue other, bool notOther) @@ -135,13 +137,7 @@ protected override bool Overlaps(bool not, SimpleFeatureValue other, bool notOth if (!(other is SymbolicFeatureValue otherSfv)) return false; - if (!not && !notOther) - return (_flags & otherSfv._flags) != 0; - if (!not) - return (_flags & (~otherSfv._flags & _feature.Mask)) != 0; - if (!notOther) - return ((~_flags & _feature.Mask) & otherSfv._flags) != 0; - return ((~_flags & _feature.Mask) & (~otherSfv._flags & _feature.Mask)) != 0; + return _flags.Overlaps(not, otherSfv._flags, notOther); } protected override void IntersectWith(bool not, SimpleFeatureValue other, bool notOther) @@ -149,14 +145,7 @@ protected override void IntersectWith(bool not, SimpleFeatureValue other, bool n if (!(other is SymbolicFeatureValue otherSfv)) return; - if (!not && !notOther) - _flags = _flags & otherSfv._flags; - else if (!not) - _flags = _flags & (~otherSfv._flags & _feature.Mask); - else if (!notOther) - _flags = (~_flags & _feature.Mask) & otherSfv._flags; - else - _flags = (~_flags & _feature.Mask) & (~otherSfv._flags & _feature.Mask); + _flags.IntersectWith(not, otherSfv._flags, notOther); SetFirst(); } @@ -165,14 +154,7 @@ protected override void UnionWith(bool not, SimpleFeatureValue other, bool notOt if (!(other is SymbolicFeatureValue otherSfv)) return; - if (!not && !notOther) - _flags = _flags | otherSfv._flags; - else if (!not) - _flags = _flags | (~otherSfv._flags & _feature.Mask); - else if (!notOther) - _flags = (~_flags & _feature.Mask) | otherSfv._flags; - else - _flags = (~_flags & _feature.Mask) | (~otherSfv._flags & _feature.Mask); + _flags.UnionWith(not, otherSfv._flags, notOther); SetFirst(); } @@ -181,14 +163,7 @@ protected override void ExceptWith(bool not, SimpleFeatureValue other, bool notO if (!(other is SymbolicFeatureValue otherSfv)) return; - if (!not && !notOther) - _flags = _flags & (~otherSfv._flags & _feature.Mask); - else if (!not) - _flags = _flags & otherSfv._flags; - else if (!notOther) - _flags = (~_flags & _feature.Mask) & (~otherSfv._flags & _feature.Mask); - else - _flags = (~_flags & _feature.Mask) & otherSfv._flags; + _flags.ExceptWith(not, otherSfv._flags, notOther); } protected override SimpleFeatureValue CloneImpl() @@ -200,7 +175,7 @@ public override SimpleFeatureValue Negation() { return IsVariable ? new SymbolicFeatureValue(_feature, VariableName, !Agree) - : new SymbolicFeatureValue(_feature, (~_flags & _feature.Mask)); + : new SymbolicFeatureValue(_feature, _flags.Not()); } public override bool ValueEquals(SimpleFeatureValue other) @@ -213,13 +188,13 @@ public bool ValueEquals(SymbolicFeatureValue other) if (other == null) return false; - return base.ValueEquals(other) && _flags == other._flags; + return base.ValueEquals(other) && _flags.ValueEquals(other._flags); } protected override int GetValuesHashCode() { int code = base.GetValuesHashCode(); - return code * 31 + _flags.GetHashCode(); + return code * 31 + _flags.GetValuesHashCode(); } public new SymbolicFeatureValue Clone() diff --git a/src/SIL.Machine/FeatureModel/UlongSymbolicFeatureValueFlags.cs b/src/SIL.Machine/FeatureModel/UlongSymbolicFeatureValueFlags.cs new file mode 100644 index 000000000..bdb1596c4 --- /dev/null +++ b/src/SIL.Machine/FeatureModel/UlongSymbolicFeatureValueFlags.cs @@ -0,0 +1,138 @@ +using System.Collections.Generic; +using System.Linq; + +namespace SIL.Machine.FeatureModel +{ + internal class UlongSymbolicFeatureValueFlags : ISymbolicFeatureValueFlags + { + private readonly SymbolicFeature _feature; + private readonly ulong _mask; + private ulong _flags = 0; + + public UlongSymbolicFeatureValueFlags(SymbolicFeature feature) + { + _feature = feature; + _mask = (1UL << feature.PossibleSymbols.Count) - 1UL; + } + + private UlongSymbolicFeatureValueFlags(SymbolicFeature feature, ulong mask, ulong flags) + { + _feature = feature; + _mask = mask; + _flags = flags; + } + + public bool HasAnySet() + { + return _flags != 0; + } + + public bool HasAllSet() + { + return _flags == _mask; + } + + public bool Get(FeatureSymbol symbol) + { + return (_flags & (1UL << symbol.Index)) != 0; + } + + public FeatureSymbol GetFirst() + { + return _flags == 0 ? null : _feature.PossibleSymbols.First(Get); + } + + public void Set(IEnumerable symbols) + { + foreach (FeatureSymbol symbol in symbols) + { + ulong mask = 1UL << symbol.Index; + _flags |= mask; + } + } + + public bool IsSupersetOf(bool not, ISymbolicFeatureValueFlags other, bool notOther) + { + var otherUlong = (UlongSymbolicFeatureValueFlags)other; + if (!not && !notOther) + return (_flags & otherUlong._flags) == otherUlong._flags; + if (!not) + return (_flags & (~otherUlong._flags & _mask)) == (~otherUlong._flags & _mask); + if (!notOther) + return ((~_flags & _mask) & otherUlong._flags) == otherUlong._flags; + return ((~_flags & _mask) & (~otherUlong._flags & _mask)) == (~otherUlong._flags & _mask); + } + + public bool Overlaps(bool not, ISymbolicFeatureValueFlags other, bool notOther) + { + var otherUlong = (UlongSymbolicFeatureValueFlags)other; + if (!not && !notOther) + return (_flags & otherUlong._flags) != 0; + if (!not) + return (_flags & (~otherUlong._flags & _mask)) != 0; + if (!notOther) + return ((~_flags & _mask) & otherUlong._flags) != 0; + return ((~_flags & _mask) & (~otherUlong._flags & _mask)) != 0; + } + + public void IntersectWith(bool not, ISymbolicFeatureValueFlags other, bool notOther) + { + var otherUlong = (UlongSymbolicFeatureValueFlags)other; + if (!not && !notOther) + _flags = _flags & otherUlong._flags; + else if (!not) + _flags = _flags & (~otherUlong._flags & _mask); + else if (!notOther) + _flags = (~_flags & _mask) & otherUlong._flags; + else + _flags = (~_flags & _mask) & (~otherUlong._flags & _mask); + } + + public void UnionWith(bool not, ISymbolicFeatureValueFlags other, bool notOther) + { + var otherUlong = (UlongSymbolicFeatureValueFlags)other; + if (!not && !notOther) + _flags = _flags | otherUlong._flags; + else if (!not) + _flags = _flags | (~otherUlong._flags & _mask); + else if (!notOther) + _flags = (~_flags & _mask) | otherUlong._flags; + else + _flags = (~_flags & _mask) | (~otherUlong._flags & _mask); + } + + public void ExceptWith(bool not, ISymbolicFeatureValueFlags other, bool notOther) + { + var otherUlong = (UlongSymbolicFeatureValueFlags)other; + if (!not && !notOther) + _flags = _flags & (~otherUlong._flags & _mask); + else if (!not) + _flags = _flags & otherUlong._flags; + else if (!notOther) + _flags = (~_flags & _mask) & (~otherUlong._flags & _mask); + else + _flags = (~_flags & _mask) & otherUlong._flags; + } + + public ISymbolicFeatureValueFlags Not() + { + return new UlongSymbolicFeatureValueFlags(_feature, _mask, ~_flags & _mask); + } + + public bool ValueEquals(ISymbolicFeatureValueFlags other) + { + var otherUlong = (UlongSymbolicFeatureValueFlags)other; + return _flags == otherUlong._flags; + } + + public int GetValuesHashCode() + { + return _flags.GetHashCode(); + } + + public ISymbolicFeatureValueFlags Clone() + { + return new UlongSymbolicFeatureValueFlags(_feature, _mask, _flags); + } + } +} diff --git a/src/SIL.Machine/Properties/AssemblyInfo.cs b/src/SIL.Machine/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..a9bd9a03f --- /dev/null +++ b/src/SIL.Machine/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("SIL.Machine.Tests")] diff --git a/tests/SIL.Machine.Tests/FeatureModel/FeatureStructTests.cs b/tests/SIL.Machine.Tests/FeatureModel/FeatureStructTests.cs index 16c0bd513..3d6ce691b 100644 --- a/tests/SIL.Machine.Tests/FeatureModel/FeatureStructTests.cs +++ b/tests/SIL.Machine.Tests/FeatureModel/FeatureStructTests.cs @@ -21,7 +21,7 @@ public void Unify() { return fs1.Unify(fs2, varBindings, out FeatureStruct res) ? res : null; }; - TestBinaryOperation( + TestBinaryOperationUlongAndBitArray( FreezableEqualityComparer.Default, resultsSelector, varResultsSelector, @@ -205,7 +205,7 @@ public void IsUnifiable() Func resultsSelector = (fs1, fs2) => fs1.IsUnifiable(fs2); Func varResultsSelector = (fs1, fs2, varBindings) => fs1.IsUnifiable(fs2, varBindings); - TestBinaryOperation( + TestBinaryOperationUlongAndBitArray( EqualityComparer.Default, resultsSelector, varResultsSelector, @@ -256,7 +256,7 @@ public void PriorityUnion() fs1.PriorityUnion(fs2, varBindings); return fs1; }; - TestBinaryOperation( + TestBinaryOperationUlongAndBitArray( FreezableEqualityComparer.Default, resultsSelector, varResultsSelector, @@ -412,7 +412,7 @@ public void Union() return fs1; }; - TestBinaryOperation( + TestBinaryOperationUlongAndBitArray( FreezableEqualityComparer.Default, resultsSelector, varResultsSelector, @@ -490,7 +490,7 @@ public void Subtract() return fs1; }; - TestBinaryOperation( + TestBinaryOperationUlongAndBitArray( FreezableEqualityComparer.Default, resultsSelector, varResultsSelector, @@ -589,6 +589,21 @@ public void Subtract() ); } + private static void TestBinaryOperationUlongAndBitArray( + IEqualityComparer comparer, + Func resultsSelector, + Func varResultsSelector, + params Func[] expectedSelectors + ) + { + // Use default ulong for values + TestBinaryOperation(comparer, resultsSelector, varResultsSelector, expectedSelectors); + // Use BitArray for values + SymbolicFeatureValue.ForceBitArrayFlagsImplementation(); + TestBinaryOperation(comparer, resultsSelector, varResultsSelector, expectedSelectors); + SymbolicFeatureValue.ResetFlagsImplementation(); + } + private static void TestBinaryOperation( IEqualityComparer comparer, Func resultsSelector, @@ -897,4 +912,124 @@ params Func[] expectedSelectors Is.EqualTo(expectedSelectors[21](featSys)).Using(comparer) ); } + + [Test] + public void BitArray() + { + // Parts of Speech for Ket has more than 64 values. + // Using ulong alone in SymbolicFeatureValue can result in extra values being matched. + var featSys = new FeatureSystem + { + new SymbolicFeature( + "POS", + new FeatureSymbol("adv"), + new FeatureSymbol("adv.inc"), + new FeatureSymbol("n"), + new FeatureSymbol("nprop"), + new FeatureSymbol("n.inc"), + new FeatureSymbol("pro"), + new FeatureSymbol("pers"), + new FeatureSymbol("dempro"), + new FeatureSymbol("posspro"), + new FeatureSymbol("reflpro"), + new FeatureSymbol("que"), + new FeatureSymbol("adj"), + new FeatureSymbol("adj.inc"), + new FeatureSymbol("actn"), + new FeatureSymbol("v"), + new FeatureSymbol("v.inc"), + new FeatureSymbol("vi"), + new FeatureSymbol("v1"), + new FeatureSymbol("v1.aoln"), + new FeatureSymbol("v1.ao"), + new FeatureSymbol("v1.sln"), + new FeatureSymbol("v1.ln"), + new FeatureSymbol("v2"), + new FeatureSymbol("v2.aoln"), + new FeatureSymbol("v2.ao"), + new FeatureSymbol("v2.sln"), + new FeatureSymbol("v2.ln"), + new FeatureSymbol("v3"), + new FeatureSymbol("v3.aoln"), + new FeatureSymbol("v3.ao"), + new FeatureSymbol("v3.sln"), + new FeatureSymbol("v3.ln"), + new FeatureSymbol("v4"), + new FeatureSymbol("v4.aoln"), + new FeatureSymbol("v4.ao"), + new FeatureSymbol("v4.sln"), + new FeatureSymbol("v4.ln"), + new FeatureSymbol("v5"), + new FeatureSymbol("v5.aoln"), + new FeatureSymbol("v5.ao"), + new FeatureSymbol("v5.sln"), + new FeatureSymbol("v5.ln"), + new FeatureSymbol("v2.appl"), + new FeatureSymbol("v2.appl.ao"), + new FeatureSymbol("v2.appl.aoln"), + new FeatureSymbol("vt"), + new FeatureSymbol("vt1"), + new FeatureSymbol("vt1.aoln"), + new FeatureSymbol("vt1.ao"), + new FeatureSymbol("vt1.sln"), + new FeatureSymbol("vt1.ln"), + new FeatureSymbol("vt2"), + new FeatureSymbol("vt2.aoln"), + new FeatureSymbol("vt2.ao"), + new FeatureSymbol("vt2.sln"), + new FeatureSymbol("vt2.ln"), + new FeatureSymbol("vt3"), + new FeatureSymbol("vt3.aoln"), + new FeatureSymbol("vt3.ao"), + new FeatureSymbol("vt3.sln"), + new FeatureSymbol("vt3.ln"), + new FeatureSymbol("vt4"), + new FeatureSymbol("vt4.aoln"), + new FeatureSymbol("vt4.ao"), + new FeatureSymbol("vt4.sln"), + new FeatureSymbol("vt4.ln"), + new FeatureSymbol("vt2/appl"), + new FeatureSymbol("vt2.appl.aoln"), + new FeatureSymbol("vt2.appl.ln"), + new FeatureSymbol("vt1.appl"), + new FeatureSymbol("vt1.appl.ln"), + new FeatureSymbol("v.irr"), + new FeatureSymbol("ptcl"), + new FeatureSymbol("sub"), + new FeatureSymbol("pp"), + new FeatureSymbol("vpred"), + new FeatureSymbol("conj"), + new FeatureSymbol("interj"), + new FeatureSymbol("num"), + new FeatureSymbol("ncp") + ) + }; + featSys.Freeze(); + // 80 values + CheckFirstAndLastValues(featSys, "adv"); + + SymbolicFeature featPos = (SymbolicFeature)featSys.GetFeature("POS"); + // 65 values + SkipAndCheck(featPos, 15, "v.inc"); + // 64 values ( = sizeof(ulong) ) + SkipAndCheck(featPos, 16, "vi"); + // 63 values + SkipAndCheck(featPos, 17, "v1"); + } + + private static void SkipAndCheck(SymbolicFeature featPos, int iSkip, string sFirst) + { + var symbols = featPos.PossibleSymbols.Skip(iSkip); + Assert.That(symbols.Count, Is.EqualTo(80 - iSkip)); + FeatureSystem featSys = new FeatureSystem { new SymbolicFeature("POS", symbols) }; + CheckFirstAndLastValues(featSys, sFirst); + } + + private static void CheckFirstAndLastValues(FeatureSystem featSys, string sFirst) + { + FeatureStruct fs1 = FeatureStruct.NewMutable(featSys).Symbol(sFirst).Value; + Assert.That(fs1.ToString(), Is.EqualTo("[POS:" + sFirst + "]")); + FeatureStruct fs2 = FeatureStruct.NewMutable(featSys).Symbol("ncp").Value; + Assert.That(fs2.ToString(), Is.EqualTo("[POS:ncp]")); + } }