title: Unable to sort because the IComparer.Compare() method returns inconsistent results. Again
body: <p><a href="https://github.com/Mirandatz/ComplicatedComparisonAlgorithm/archive/master.zip" rel="noreferrer"><strong>Minimal reproductible example</strong></a></p>
  
  <p>Before move people vote to close this question, would you please, kindly check the minimal reproducible example?</p>
  
  <p>This question has been asked a thousand times, but this time it really doesn't make any sense.</p>
  
  <p>I'm getting the following exception message:</p>
  
  <pre><code>System.ArgumentException
    HResult=0x80070057
    Message=Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results. IComparer: 'Minotaur.GeneticAlgorithms.LexicographicalFitnessComparer'.
    Source=System.Private.CoreLib
    StackTrace:
     at System.Collections.Generic.IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(Object comparer)
     at System.Collections.Generic.ArraySortHelper`2.Sort(TKey[] keys, TValue[] values, Int32 index, Int32 length, IComparer`1 comparer)
     at System.Array.Sort[TKey,TValue](TKey[] keys, TValue[] items, IComparer`1 comparer)
     at Minotaur.FitnessReportMaker.MakeReport(Array`1 fitnesses) in C:\Source\minotaur\Minotaur\Minotaur\FitnessReportMaker.cs:line 18
     at Minotaur.Theseus.EvolutionEngine.Run(IEnumerable`1 initialPopulation) in C:\Source\minotaur\Minotaur\Minotaur\Theseus\EvolutionEngine.cs:line 63
     at Minotaur.Program.Run(ProgramSettings settings) in C:\Source\minotaur\Minotaur\Minotaur\Program.cs:line 148
     at Minotaur.ProgramSettings.OnExecute() in C:\Source\minotaur\Minotaur\Minotaur\ProgramSettings.cs:line 14
  </code></pre>
  
  <p>When I call this method:</p>
  
  <pre><code>Array.Sort(
      keys: sortedFitnesses,
      items: sortedFitnesses,
      comparer: new LexicographicalFitnessComparer());
  </code></pre>
  
  <p>And this is my <code>IComparer&lt;Fitness&gt;</code> implementation:</p>
  
  <pre><code>namespace Minotaur.GeneticAlgorithms {
      using System;
      using System.Collections.Generic;
  
      public sealed class LexicographicalFitnessComparer: IComparer&lt;Fitness&gt; {
  
          public int Compare(Fitness lhs, Fitness rhs) {
              var a = CompareImpl(lhs, lhs);
              if (a != 0)
                  throw new InvalidOperationException();
  
              var b = CompareImpl(rhs, rhs);
              if (b != 0)
                  throw new InvalidOperationException();
  
              var c = CompareImpl(lhs, rhs);
              var d = CompareImpl(rhs, lhs);
              if (c != -d)
                  throw new InvalidOperationException();
  
              return c;
          }
  
          public int CompareImpl(Fitness lhs, Fitness rhs) {
              if (lhs is null)
                  throw new ArgumentNullException(nameof(lhs));
              if (rhs is null)
                  throw new ArgumentNullException(nameof(rhs));
              if (lhs.Count != rhs.Count)
                  throw new ArgumentException(nameof(lhs) + " and " + nameof(rhs) + " must have the same length.");
  
              for (int i = 0; i &lt; lhs.Count; i++) {
                  if (lhs[i] &lt; rhs[i])
                      return -1;
                  else if (lhs[i] &gt; rhs[i])
                      return 1;
              }
  
              return 0;
          }
      }
  }
  </code></pre>
  
  <p>As you can see, the <code>Compare</code> method actually calls <code>CompareImpl</code> and performs many tests with the results.
  No <code>InvalidOperationException</code> is ever thrown. So I have no idea what is going on...</p>
  
  <p>And for completeness sake, here is my <code>Fitness</code> implementation:</p>
  
  <pre><code>namespace Minotaur.GeneticAlgorithms {
      using System;
      using System.Collections;
      using System.Collections.Generic;
      using System.Linq;
      using Minotaur.ExtensionMethods.SystemArray;
      using Newtonsoft.Json;
  
      [JsonObject(MemberSerialization.OptIn)]
      public sealed class Fitness: IEquatable&lt;Fitness&gt;, IReadOnlyList&lt;float&gt; {
  
          [JsonProperty] private readonly float[] _objectives;
  
          private readonly int _precomputedHashCode;
  
          [JsonConstructor]
          private Fitness(float[] objectives) {
              for (int i = 0; i &lt; objectives.Length; i++) {
                  if (float.IsNaN(objectives[i]))
                      throw new ArgumentException(nameof(objectives) + " can't contain NaNs.");
              }
  
              _objectives = objectives;
              Count = objectives.Length;
  
              var hash = new HashCode();
              for (int i = 0; i &lt; _objectives.Length; i++)
                  hash.Add(_objectives[i]);
  
              _precomputedHashCode = hash.ToHashCode();
          }
  
          public static Fitness WrapAndCopy(float[] objectives) {
              if (objectives == null)
                  throw new ArgumentNullException(nameof(objectives));
              if (objectives.Length == 0)
                  throw new ArgumentException(nameof(objectives) + " can't be empty");
  
              return new Fitness(objectives.ToArray());
          }
  
          public float this[int index] =&gt; _objectives[index];
  
          public int Count { get; }
  
          public override string ToString() =&gt; "[" + string.Join(", ", _objectives) + "]";
  
          public override int GetHashCode() =&gt; _precomputedHashCode;
  
          public override bool Equals(object obj) =&gt; Equals(obj as Fitness);
  
          public bool Equals(Fitness other) {
              if (other == null)
                  return false;
  
              if (object.ReferenceEquals(this, other))
                  return true;
  
              // Again, fitnesses should all have the same length
              // finding one with different length indicates a critical error
              if (Count != other.Count)
                  throw new InvalidOperationException($"Fitness should ALWAYS have the same {nameof(Count)}");
  
              var lhs = _objectives;
              var rhs = other._objectives;
  
              for (int i = 0; i &lt; lhs.Length; i++) {
                  if (lhs[i] != rhs[i])
                      return false;
              }
  
              return true;
          }
  
          public IEnumerator&lt;float&gt; GetEnumerator() =&gt; _objectives.GetGenericEnumerator();
  
          IEnumerator IEnumerable.GetEnumerator() =&gt; _objectives.GetEnumerator();
      }
  }
  </code></pre>
  
  <p>As you can see, Fitness is immutable and doesn't allow NaNs.
  The array being sorted is a local variable (hence is not being modified by other threadds) and contains no nulls.</p>
  
  <p>Is this a framework bug?</p>
  
  <p>Edit: the target framework is .NET Core 2.2. The project is being built for x64 on Windows.</p>
  
  <p>Sample input:</p>
  
  <pre><code>{Minotaur.GeneticAlgorithms.Fitness[50]}
  {[0.4032445, 144]}
  {[0.3778533, 144]}
  {[0.1739699, 144]}
  {[0.3778533, 144]}
  {[0.4032445, 144]}
  {[0.1962067, 144]}
  {[0.2236756, 144]}
  {[0.376882, 144]}
  {[0.275862, 144]}
  {[0.3972802, 144]}
  {[0.2236756, 144]}
  {[0.1962067, 144]}
  {[0.376882, 144]}
  {[0.2236756, 144]}
  {[0.4032445, 144]}
  {[0.3684821, 144]}
  {[0.3603691, 144]}
  {[0.4032445, 144]}
  {[0.3113146, 144]}
  {[0.3176299, 144]}
  {[0.2236756, 144]}
  {[0.3972802, 144]}
  {[0.4325995, 144]}
  {[0.275862, 144]}
  {[0.2972316, 144]}
  {[0.376882, 144]}
  {[0.3603691, 144]}
  {[0.275862, 144]}
  {[0.2236756, 144]}
  {[0.2040549, 144]}
  {[0.4032445, 144]}
  {[0.3113146, 144]}
  {[0.2040549, 144]}
  {[0.2236756, 144]}
  {[0.275862, 144]}
  {[0.4032445, 144]}
  {[0.3113146, 144]}
  {[0.3113146, 144]}
  {[0.3176299, 144]}
  {[0.3118019, 144]}
  {[0.3778533, 144]}
  {[0.4032445, 144]}
  {[0.3127732, 144]}
  {[0.3176299, 144]}
  {[0.3603691, 144]}
  {[0.275862, 144]}
  {[0.2236756, 144]}
  {[0.376882, 144]}
  {[0.3176299, 144]}
  {[0.3603691, 144]}
  </code></pre>