public void TestComparableDvInt4() { const int count = 100; var rand = RandomUtils.Create(42); var values = new DvInt4[2 * count]; for (int i = 0; i < count; i++) { var v = values[i] = rand.Next(); values[values.Length - i - 1] = v; } // Assign two NA's at random. int iv1 = rand.Next(values.Length); int iv2 = rand.Next(values.Length - 1); if (iv2 >= iv1) { iv2++; } values[iv1] = DvInt4.NA; values[iv2] = DvInt4.NA; Array.Sort(values); Assert.True(values[0].IsNA); Assert.True(values[1].IsNA); Assert.True(!values[2].IsNA); Assert.True((values[0] == values[1]).IsNA); Assert.True((values[0] != values[1]).IsNA); Assert.True((values[0] <= values[1]).IsNA); Assert.True(values[0].Equals(values[1])); Assert.True(values[0].CompareTo(values[1]) == 0); Assert.True((values[1] == values[2]).IsNA); Assert.True((values[1] != values[2]).IsNA); Assert.True((values[1] <= values[2]).IsNA); Assert.True(!values[1].Equals(values[2])); Assert.True(values[1].CompareTo(values[2]) < 0); for (int i = 3; i < values.Length; i++) { DvBool eq = values[i - 1] == values[i]; DvBool ne = values[i - 1] != values[i]; DvBool le = values[i - 1] <= values[i]; bool feq = values[i - 1].Equals(values[i]); int cmp = values[i - 1].CompareTo(values[i]); Assert.True(!eq.IsNA); Assert.True(!ne.IsNA); Assert.True(eq.IsTrue == ne.IsFalse); Assert.True(le.IsTrue); Assert.True(feq == eq.IsTrue); Assert.True(cmp <= 0); Assert.True(feq == (cmp == 0)); } }
private static Action <TRow> CreateDvBoolToBoolSetter(IRow input, int col, Delegate poke) { var getter = input.GetGetter <DvBool>(col); var typedPoke = poke as Poke <TRow, bool>; Contracts.AssertValue(typedPoke); DvBool value = default(DvBool); return(row => { getter(ref value); typedPoke(row, Convert.ToBoolean(value.RawValue)); }); }
private void ValidateMetadata(IDataView result) { Assert.True(result.Schema.TryGetColumnIndex("CatA", out int colA)); Assert.True(result.Schema.TryGetColumnIndex("CatB", out int colB)); Assert.True(result.Schema.TryGetColumnIndex("CatC", out int colC)); Assert.True(result.Schema.TryGetColumnIndex("CatD", out int colD)); var types = result.Schema.GetMetadataTypes(colA); Assert.Equal(types.Select(x => x.Key), new string[1] { MetadataUtils.Kinds.SlotNames }); VBuffer <DvText> slots = default; DvBool normalized = default; result.Schema.GetMetadata(MetadataUtils.Kinds.SlotNames, colA, ref slots); Assert.True(slots.Length == 6); Assert.Equal(slots.Items().Select(x => x.Value.ToString()), new string[6] { "[0].Bit2", "[0].Bit1", "[0].Bit0", "[1].Bit2", "[1].Bit1", "[1].Bit0" }); types = result.Schema.GetMetadataTypes(colB); Assert.Equal(types.Select(x => x.Key), new string[2] { MetadataUtils.Kinds.SlotNames, MetadataUtils.Kinds.IsNormalized }); result.Schema.GetMetadata(MetadataUtils.Kinds.SlotNames, colB, ref slots); Assert.True(slots.Length == 2); Assert.Equal(slots.Items().Select(x => x.Value.ToString()), new string[2] { "Bit1", "Bit0" }); result.Schema.GetMetadata(MetadataUtils.Kinds.IsNormalized, colB, ref normalized); Assert.True(normalized.IsTrue); types = result.Schema.GetMetadataTypes(colC); Assert.Equal(types.Select(x => x.Key), new string[0]); types = result.Schema.GetMetadataTypes(colD); Assert.Equal(types.Select(x => x.Key), new string[1] { MetadataUtils.Kinds.IsNormalized }); result.Schema.GetMetadata(MetadataUtils.Kinds.IsNormalized, colD, ref normalized); Assert.True(normalized.IsTrue); }
public static NumericColumn Operation(NumericColumn c1, DvBool value) { switch (c1.Kind) { case DataKind.BL: { DvBool[] a; DataColumn <DvBool> res; Operation(c1, out a, out res); for (int i = 0; i < res.Length; ++i) { res.Set(i, a[i] == value); } return(new NumericColumn(res)); } default: throw new DataTypeError(string.Format("{0} not implemented for column {1}.", OperationName, c1.Kind)); } }
/// <summary> /// Returns a .NET type corresponding to the static pipelines that would tend to represent this column. /// Generally this will return <c>null</c> if it simply does not recognize the type but might throw if /// there is something seriously wrong with it. /// </summary> /// <param name="col">The column</param> /// <returns>The .NET type for the static pipelines that should be used to reflect this type, given /// both the characteristics of the <see cref="ColumnType"/> as well as one or two crucial pieces of metadata</returns> private static Type GetTypeOrNull(IColumn col) { Contracts.AssertValue(col); var t = col.Type; Type vecType = null; if (t is VectorType vt) { vecType = vt.VectorSize > 0 ? typeof(Vector <>) : typeof(VarVector <>); // Check normalized subtype of vectors. if (vt.VectorSize > 0) { // Check to see if the column is normalized. // Once we shift to metadata being a row globally we can also make this a bit more efficient: var meta = col.Metadata; if (meta.Schema.TryGetColumnIndex(MetadataUtils.Kinds.IsNormalized, out int normcol)) { var normtype = meta.Schema.GetColumnType(normcol); if (normtype == BoolType.Instance) { DvBool val = default; meta.GetGetter <DvBool>(normcol)(ref val); if (val.IsTrue) { vecType = typeof(NormVector <>); } } } } t = t.ItemType; // Fall through to the non-vector case to handle subtypes. } Contracts.Assert(!t.IsVector); if (t is KeyType kt) { Type physType = StaticKind(kt.RawKind); Contracts.Assert(physType == typeof(byte) || physType == typeof(ushort) || physType == typeof(uint) || physType == typeof(ulong)); var keyType = kt.Count > 0 ? typeof(Key <>) : typeof(VarKey <>); keyType = keyType.MakeGenericType(physType); if (kt.Count > 0) { // Check to see if we have key value metadata of the appropriate type, size, and whatnot. var meta = col.Metadata; if (meta.Schema.TryGetColumnIndex(MetadataUtils.Kinds.KeyValues, out int kvcol)) { var kvType = meta.Schema.GetColumnType(kvcol); if (kvType.VectorSize == kt.Count) { Contracts.Assert(kt.Count > 0); var subtype = GetTypeOrNull(RowColumnUtils.GetColumn(meta, kvcol)); if (subtype != null && subtype.IsGenericType) { var sgtype = subtype.GetGenericTypeDefinition(); if (sgtype == typeof(NormVector <>) || sgtype == typeof(Vector <>)) { var args = subtype.GetGenericArguments(); Contracts.Assert(args.Length == 1); keyType = typeof(Key <,>).MakeGenericType(physType, args[0]); } } } } } return(vecType?.MakeGenericType(keyType) ?? keyType); } if (t is PrimitiveType pt) { Type physType = StaticKind(pt.RawKind); // Though I am unaware of any existing instances, it is theoretically possible for a // primitive type to exist, have the same data kind as one of the existing types, and yet // not be one of the built in types. (E.g., an outside analogy to the key types.) For this // reason, we must be certain that when we return here we are covering one fo the builtin types. if (physType != null && ( pt == NumberType.I1 || pt == NumberType.I2 || pt == NumberType.I4 || pt == NumberType.I8 || pt == NumberType.U1 || pt == NumberType.U2 || pt == NumberType.U4 || pt == NumberType.U8 || pt == NumberType.R4 || pt == NumberType.R8 || pt == NumberType.UG || pt == BoolType.Instance || pt == DateTimeType.Instance || pt == DateTimeZoneType.Instance || pt == TimeSpanType.Instance || pt == TextType.Instance)) { return((vecType ?? typeof(Scalar <>)).MakeGenericType(physType)); } } return(null); }
public void TestCrossValidationMacro() { var dataPath = GetDataPath(TestDatasets.winequalitymacro.trainFilename); using (var env = new TlcEnvironment(42)) { var subGraph = env.CreateExperiment(); var nop = new Legacy.Transforms.NoOperation(); var nopOutput = subGraph.Add(nop); var generate = new Legacy.Transforms.RandomNumberGenerator(); generate.Column = new[] { new Legacy.Transforms.GenerateNumberTransformColumn() { Name = "Weight1" } }; generate.Data = nopOutput.OutputData; var generateOutput = subGraph.Add(generate); var learnerInput = new Legacy.Trainers.PoissonRegressor { TrainingData = generateOutput.OutputData, NumThreads = 1, WeightColumn = "Weight1" }; var learnerOutput = subGraph.Add(learnerInput); var modelCombine = new Legacy.Transforms.ManyHeterogeneousModelCombiner { TransformModels = new ArrayVar <ITransformModel>(nopOutput.Model, generateOutput.Model), PredictorModel = learnerOutput.PredictorModel }; var modelCombineOutput = subGraph.Add(modelCombine); var experiment = env.CreateExperiment(); var importInput = new Legacy.Data.TextLoader(dataPath) { Arguments = new Legacy.Data.TextLoaderArguments { Separator = new[] { ';' }, HasHeader = true, Column = new[] { new TextLoaderColumn() { Name = "Label", Source = new [] { new TextLoaderRange(11) }, Type = Legacy.Data.DataKind.Num }, new TextLoaderColumn() { Name = "Features", Source = new [] { new TextLoaderRange(0, 10) }, Type = Legacy.Data.DataKind.Num } } } }; var importOutput = experiment.Add(importInput); var crossValidate = new Legacy.Models.CrossValidator { Data = importOutput.Data, Nodes = subGraph, Kind = Legacy.Models.MacroUtilsTrainerKinds.SignatureRegressorTrainer, TransformModel = null, WeightColumn = "Weight1" }; crossValidate.Inputs.Data = nop.Data; crossValidate.Outputs.PredictorModel = modelCombineOutput.PredictorModel; var crossValidateOutput = experiment.Add(crossValidate); experiment.Compile(); importInput.SetInput(env, experiment); experiment.Run(); var data = experiment.GetOutput(crossValidateOutput.OverallMetrics); var schema = data.Schema; var b = schema.TryGetColumnIndex("L1(avg)", out int metricCol); Assert.True(b); b = schema.TryGetColumnIndex("Fold Index", out int foldCol); Assert.True(b); b = schema.TryGetColumnIndex("IsWeighted", out int isWeightedCol); using (var cursor = data.GetRowCursor(col => col == metricCol || col == foldCol || col == isWeightedCol)) { var getter = cursor.GetGetter <double>(metricCol); var foldGetter = cursor.GetGetter <DvText>(foldCol); var isWeightedGetter = cursor.GetGetter <DvBool>(isWeightedCol); DvText fold = default; DvBool isWeighted = default; double avg = 0; double weightedAvg = 0; for (int w = 0; w < 2; w++) { // Get the average. b = cursor.MoveNext(); Assert.True(b); if (w == 1) { getter(ref weightedAvg); } else { getter(ref avg); } foldGetter(ref fold); Assert.True(fold.EqualsStr("Average")); isWeightedGetter(ref isWeighted); Assert.True(isWeighted.IsTrue == (w == 1)); // Get the standard deviation. b = cursor.MoveNext(); Assert.True(b); double stdev = 0; getter(ref stdev); foldGetter(ref fold); Assert.True(fold.EqualsStr("Standard Deviation")); if (w == 1) { Assert.Equal(0.004557, stdev, 6); } else { Assert.Equal(0.000393, stdev, 6); } isWeightedGetter(ref isWeighted); Assert.True(isWeighted.IsTrue == (w == 1)); } double sum = 0; double weightedSum = 0; for (int f = 0; f < 2; f++) { for (int w = 0; w < 2; w++) { b = cursor.MoveNext(); Assert.True(b); double val = 0; getter(ref val); foldGetter(ref fold); if (w == 1) { weightedSum += val; } else { sum += val; } Assert.True(fold.EqualsStr("Fold " + f)); isWeightedGetter(ref isWeighted); Assert.True(isWeighted.IsTrue == (w == 1)); } } Assert.Equal(weightedAvg, weightedSum / 2); Assert.Equal(avg, sum / 2); b = cursor.MoveNext(); Assert.False(b); } } }
public void SimpleTextLoaderCopyColumnsTest() { var env = new TlcEnvironment(new SysRandom(0), verbose: true); const string data = "0 hello 3.14159 -0 2\n" + "1 1 2 4 15"; var dataSource = new BytesStreamSource(data); var text = TextLoader.CreateReader(env, ctx => ( label: ctx.LoadBool(0), text: ctx.LoadText(1), numericFeatures: ctx.LoadFloat(2, null)), // If fit correctly, this ought to be equivalent to max of 4, that is, length of 3. dataSource, separator: ' '); // While we have a type-safe wrapper for `IDataView` it is utterly useless except as an input to the `Fit` functions // of the other statically typed wrappers. We perhaps ought to make it useful in its own right, but perhaps not now. // For now, just operate over the actual `IDataView`. var textData = text.Read(dataSource).AsDynamic; var schema = textData.Schema; // First verify that the columns are there. There ought to be at least one column corresponding to the identifiers in the tuple. CheckSchemaHasColumn(schema, "label", out int labelIdx); CheckSchemaHasColumn(schema, "text", out int textIdx); CheckSchemaHasColumn(schema, "numericFeatures", out int numericFeaturesIdx); // Next verify they have the expected types. Assert.Equal(BoolType.Instance, schema.GetColumnType(labelIdx)); Assert.Equal(TextType.Instance, schema.GetColumnType(textIdx)); Assert.Equal(new VectorType(NumberType.R4, 3), schema.GetColumnType(numericFeaturesIdx)); // Next actually inspect the data. using (var cursor = textData.GetRowCursor(c => true)) { var labelGetter = cursor.GetGetter <DvBool>(labelIdx); var textGetter = cursor.GetGetter <DvText>(textIdx); var numericFeaturesGetter = cursor.GetGetter <VBuffer <float> >(numericFeaturesIdx); DvBool labelVal = default; DvText textVal = default; VBuffer <float> numVal = default; void CheckValuesSame(bool bl, string tx, float v0, float v1, float v2) { labelGetter(ref labelVal); textGetter(ref textVal); numericFeaturesGetter(ref numVal); Assert.Equal((DvBool)bl, labelVal); Assert.Equal(new DvText(tx), textVal); Assert.Equal(3, numVal.Length); Assert.Equal(v0, numVal.GetItemOrDefault(0)); Assert.Equal(v1, numVal.GetItemOrDefault(1)); Assert.Equal(v2, numVal.GetItemOrDefault(2)); } Assert.True(cursor.MoveNext(), "Could not move even to first row"); CheckValuesSame(false, "hello", 3.14159f, -0f, 2f); Assert.True(cursor.MoveNext(), "Could not move to second row"); CheckValuesSame(true, "1", 2f, 4f, 15f); Assert.False(cursor.MoveNext(), "Moved to third row, but there should have been only two"); } // The next step where we shuffle the names around a little bit is one where we are // testing out the implicit usage of copy columns. var est = text.MakeNewEstimator().Append(r => (text: r.label, label: r.numericFeatures)); var newText = text.Append(est); var newTextData = newText.Fit(dataSource).Read(dataSource); schema = newTextData.AsDynamic.Schema; // First verify that the columns are there. There ought to be at least one column corresponding to the identifiers in the tuple. CheckSchemaHasColumn(schema, "label", out labelIdx); CheckSchemaHasColumn(schema, "text", out textIdx); // Next verify they have the expected types. Assert.Equal(BoolType.Instance, schema.GetColumnType(textIdx)); Assert.Equal(new VectorType(NumberType.R4, 3), schema.GetColumnType(labelIdx)); }
public static Func <DvBool[], DvBool> GetAggFunction(AggregatedFunction func, DvBool defaultValue) { switch (func) { case AggregatedFunction.Mean: case AggregatedFunction.Count: return((DvBool[] arr) => { return DvBool.NA; }); case AggregatedFunction.Max: case AggregatedFunction.Sum: return((DvBool[] arr) => { return arr.Aggregate((a, b) => a | b); }); case AggregatedFunction.Min: return((DvBool[] arr) => { return arr.Aggregate((a, b) => a & b); }); default: throw new NotImplementedException($"Unkown aggregated function ${func}."); } }
private void ValidateMetadata(IDataView result) { Assert.True(result.Schema.TryGetColumnIndex("CatA", out int colA)); Assert.True(result.Schema.TryGetColumnIndex("CatB", out int colB)); Assert.True(result.Schema.TryGetColumnIndex("CatC", out int colC)); Assert.True(result.Schema.TryGetColumnIndex("CatD", out int colD)); Assert.True(result.Schema.TryGetColumnIndex("CatE", out int colE)); Assert.True(result.Schema.TryGetColumnIndex("CatF", out int colF)); Assert.True(result.Schema.TryGetColumnIndex("CatE", out int colG)); Assert.True(result.Schema.TryGetColumnIndex("CatF", out int colH)); var types = result.Schema.GetMetadataTypes(colA); Assert.Equal(types.Select(x => x.Key), new string[1] { MetadataUtils.Kinds.SlotNames }); VBuffer <DvText> slots = default; VBuffer <DvInt4> slotRanges = default; DvBool normalized = default; result.Schema.GetMetadata(MetadataUtils.Kinds.SlotNames, colA, ref slots); Assert.True(slots.Length == 2); Assert.Equal(slots.Values.Select(x => x.ToString()), new string[2] { "A", "B" }); types = result.Schema.GetMetadataTypes(colB); Assert.Equal(types.Select(x => x.Key), new string[3] { MetadataUtils.Kinds.SlotNames, MetadataUtils.Kinds.CategoricalSlotRanges, MetadataUtils.Kinds.IsNormalized }); result.Schema.GetMetadata(MetadataUtils.Kinds.SlotNames, colB, ref slots); Assert.True(slots.Length == 1); Assert.Equal(slots.Items().Select(x => x.Value.ToString()), new string[1] { "C" }); result.Schema.GetMetadata(MetadataUtils.Kinds.CategoricalSlotRanges, colB, ref slotRanges); Assert.True(slotRanges.Length == 2); Assert.Equal(slotRanges.Items().Select(x => x.Value.RawValue), new int[2] { 0, 0 }); result.Schema.GetMetadata(MetadataUtils.Kinds.IsNormalized, colB, ref normalized); Assert.True(normalized.IsTrue); types = result.Schema.GetMetadataTypes(colC); Assert.Equal(types.Select(x => x.Key), new string[3] { MetadataUtils.Kinds.SlotNames, MetadataUtils.Kinds.CategoricalSlotRanges, MetadataUtils.Kinds.IsNormalized }); result.Schema.GetMetadata(MetadataUtils.Kinds.SlotNames, colC, ref slots); Assert.True(slots.Length == 4); Assert.Equal(slots.Items().Select(x => x.Value.ToString()), new string[4] { "[0].3", "[0].5", "[1].3", "[1].5" }); result.Schema.GetMetadata(MetadataUtils.Kinds.CategoricalSlotRanges, colC, ref slotRanges); Assert.True(slotRanges.Length == 4); Assert.Equal(slotRanges.Items().Select(x => x.Value.RawValue), new int[4] { 0, 1, 2, 3 }); result.Schema.GetMetadata(MetadataUtils.Kinds.IsNormalized, colC, ref normalized); Assert.True(normalized.IsTrue); types = result.Schema.GetMetadataTypes(colD); Assert.Equal(types.Select(x => x.Key), new string[2] { MetadataUtils.Kinds.SlotNames, MetadataUtils.Kinds.IsNormalized }); result.Schema.GetMetadata(MetadataUtils.Kinds.SlotNames, colD, ref slots); Assert.True(slots.Length == 2); Assert.Equal(slots.Items().Select(x => x.Value.ToString()), new string[2] { "6", "1" }); result.Schema.GetMetadata(MetadataUtils.Kinds.IsNormalized, colD, ref normalized); Assert.True(normalized.IsTrue); types = result.Schema.GetMetadataTypes(colE); Assert.Equal(types.Select(x => x.Key), new string[2] { MetadataUtils.Kinds.CategoricalSlotRanges, MetadataUtils.Kinds.IsNormalized }); result.Schema.GetMetadata(MetadataUtils.Kinds.CategoricalSlotRanges, colE, ref slotRanges); Assert.True(slotRanges.Length == 4); Assert.Equal(slotRanges.Items().Select(x => x.Value.RawValue), new int[4] { 0, 5, 6, 11 }); result.Schema.GetMetadata(MetadataUtils.Kinds.IsNormalized, colE, ref normalized); Assert.True(normalized.IsTrue); types = result.Schema.GetMetadataTypes(colF); Assert.Equal(types.Select(x => x.Key), new string[1] { MetadataUtils.Kinds.IsNormalized }); result.Schema.GetMetadata(MetadataUtils.Kinds.IsNormalized, colF, ref normalized); Assert.True(normalized.IsTrue); types = result.Schema.GetMetadataTypes(colG); Assert.Equal(types.Select(x => x.Key), new string[2] { MetadataUtils.Kinds.CategoricalSlotRanges, MetadataUtils.Kinds.IsNormalized }); result.Schema.GetMetadata(MetadataUtils.Kinds.CategoricalSlotRanges, colG, ref slotRanges); Assert.True(slotRanges.Length == 4); Assert.Equal(slotRanges.Items().Select(x => x.Value.RawValue), new int[4] { 0, 5, 6, 11 }); result.Schema.GetMetadata(MetadataUtils.Kinds.IsNormalized, colF, ref normalized); Assert.True(normalized.IsTrue); types = result.Schema.GetMetadataTypes(colH); Assert.Equal(types.Select(x => x.Key), new string[1] { MetadataUtils.Kinds.IsNormalized }); result.Schema.GetMetadata(MetadataUtils.Kinds.IsNormalized, colF, ref normalized); Assert.True(normalized.IsTrue); }
public static NumericColumn Operation(NumericColumn c1, DvBool value) { switch (c1.Kind) { case DataKind.BL: { DvBool[] a; DataColumn <DvBool> res; Operation(c1, out a, out res); for (int i = 0; i < res.Length; ++i) { res.Set(i, a[i] /**/ | value); } return(new NumericColumn(res)); } default: throw new DataTypeError(string.Format("{0} not implemented for column {1}.", OperationName, c1.Kind)); } } #endregion #region Operation between two columns. static void Operation <T1, T2, T3>(NumericColumn c1, NumericColumn c2, out T1[] a, out T2[] b, out DataColumn <T3> res) where T1 : IEquatable <T1>, IComparable <T1> where T2 : IEquatable <T2>, IComparable <T2> where T3 : IEquatable <T3>, IComparable <T3> { var c1o = c1.Column as DataColumn <T1>; var c2o = c2.Column as DataColumn <T2>; if (c1o is null || c2o is null) { throw new DataTypeError(string.Format("{0} not implemented for {1}, {2}.", OperationName, c1.Kind, c2.Kind)); } res = new DataColumn <T3>(c1.Length); a = c1o.Data; b = c2o.Data; } public static NumericColumn Operation(NumericColumn c1, NumericColumn c2) { switch (c1.Kind) { case DataKind.BL: switch (c2.Kind) { case DataKind.BL: { DvBool[] a; DvBool[] b; DataColumn <DvBool> res; Operation(c1, c2, out a, out b, out res); for (int i = 0; i < res.Length; ++i) { res.Set(i, a[i] /**/ | b[i]); } return(new NumericColumn(res)); } default: throw new DataTypeError(string.Format("{0} not implemented for {1}, {2}.", OperationName, c1.Kind, c2.Kind)); }
public void TestComparableDvText() { const int count = 100; var rand = RandomUtils.Create(42); var chars = new char[2000]; for (int i = 0; i < chars.Length; i++) { chars[i] = (char)rand.Next(128); } var str = new string(chars); var values = new DvText[2 * count]; for (int i = 0; i < count; i++) { int len = rand.Next(20); int ich = rand.Next(str.Length - len + 1); var v = values[i] = new DvText(str, ich, ich + len); values[values.Length - i - 1] = v; } // Assign two NA's and an empty at random. int iv1 = rand.Next(values.Length); int iv2 = rand.Next(values.Length - 1); if (iv2 >= iv1) { iv2++; } int iv3 = rand.Next(values.Length - 2); if (iv3 >= iv1) { iv3++; } if (iv3 >= iv2) { iv3++; } values[iv1] = DvText.NA; values[iv2] = DvText.NA; values[iv3] = DvText.Empty; Array.Sort(values); Assert.True(values[0].IsNA); Assert.True(values[1].IsNA); Assert.True(values[2].IsEmpty); Assert.True((values[0] == values[1]).IsNA); Assert.True((values[0] != values[1]).IsNA); Assert.True(values[0].Equals(values[1])); Assert.True(values[0].CompareTo(values[1]) == 0); Assert.True((values[1] == values[2]).IsNA); Assert.True((values[1] != values[2]).IsNA); Assert.True(!values[1].Equals(values[2])); Assert.True(values[1].CompareTo(values[2]) < 0); for (int i = 3; i < values.Length; i++) { DvBool eq = values[i - 1] == values[i]; DvBool ne = values[i - 1] != values[i]; bool feq = values[i - 1].Equals(values[i]); int cmp = values[i - 1].CompareTo(values[i]); Assert.True(!eq.IsNA); Assert.True(!ne.IsNA); Assert.True(eq.IsTrue == ne.IsFalse); Assert.True(feq == eq.IsTrue); Assert.True(cmp <= 0); Assert.True(feq == (cmp == 0)); } }