Clarify the observable contract
A comparer returns a negative, zero or positive sign describing order; the magnitude is not a distance contract. Antisymmetry of signs and transitivity are necessary for sorting to behave coherently. The fixture checks these laws over five boundary-heavy integer values and demonstrates why subtracting two integers is an unsafe general comparer implementation.
Separate the relation being tested from the algorithm that consumes it. Sorting assumes a coherent order; it does not repair an inconsistent comparator. Sign reversal and transitivity checks deliberately include equal values and numeric extremes because both are common blind spots in comparers written as compact arithmetic expressions.
Walk through the verified program
There are twenty-five ordered pairs, each checking sign reversal, and one hundred twenty-five ordered triples checking the nondecreasing transitivity implication. That totals one hundred fifty checks. Unchecked minimum minus maximum wraps to a positive value, contradicting numeric order. CompareTo returns the correct negative sign for the same pair without overflowing subtraction.
The triple loop increments the check counter for every triple even when the implication's premises are false. Therefore the count is twenty-five pair checks plus one hundred twenty-five implication checks, not the number of times the premise held. The subtraction line is explicitly unchecked so its wraparound is part of the demonstrated counterexample.
using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
int[] values={int.MinValue,-1,0,1,int.MaxValue}; int checks=0;
foreach(int a in values) foreach(int b in values) {
if(Math.Sign(a.CompareTo(b))!=-Math.Sign(b.CompareTo(a))) throw new Exception("antisymmetry"); checks++;
foreach(int c in values) { if(a.CompareTo(b)<=0 && b.CompareTo(c)<=0 && a.CompareTo(c)>0) throw new Exception("transitivity"); checks++; }
}
Console.WriteLine("law checks:"+checks);
Console.WriteLine("subtraction wrong:"+(unchecked(int.MinValue-int.MaxValue)>0).ToString().ToLowerInvariant());
Console.WriteLine("safe sign:"+Math.Sign(int.MinValue.CompareTo(int.MaxValue)));
Account for time, storage and allocations
The explicit law harness takes O(k cubed) time for k selected values and constant auxiliary state, which is appropriate for a small diagnostic fixture rather than a sorting algorithm. Passing these finite tests is evidence, not a proof for every custom type or combination of nullable values.
Finite law tests are most valuable when the selected values target distinct equivalence classes: equal objects, adjacent values, extremes and nullable cases if supported. Exhaustively testing a bounded domain can prove that finite domain's relation, but these five integers do not prove every possible custom comparator or mutable object state.
Review assumptions and likely defects
Using x minus y, inconsistent null placement or culture-dependent text rules can violate an intended ordering contract. A comparator must also agree with equality classes expected by the consuming collection. Random examples alone can miss boundary failures, so retain extremes and equal values.
A comparer whose result changes while objects remain in a sorted collection can invalidate that collection's assumptions even if the comparison formula is otherwise sound. Use immutable ordering keys or rebuild the ordered structure after key changes. This ownership issue is separate from arithmetic overflow and should not be hidden by passing static fixtures.
Practice one: discriminate edge cases
Create a nontransitive cyclic order for three named values and make the triple checks fail. Hint: a compares before b, b before c and c before a cannot form an ordinary total order.
For the cyclic-order exercise, preserve all three named comparisons in the failing output or assertion message. Hint: test sign reversal independently so you can distinguish a comparator that is antisymmetric but nontransitive from one that already fails pairwise consistency. This makes the defect classification more useful than a generic sorting failure.
Practice two: defend an extension
Design a comparer for score descending, timestamp ascending and unique ID ascending. Explain how the final key stabilizes ties and how nulls are ordered. Add focused law fixtures rather than assuming a successful sort proves comparator correctness.
A lexicographic comparator should compare one field at a time and return as soon as a nonzero result appears. Reverse a comparison by swapping operands rather than negating an arbitrary result. A final unique ID removes ambiguous ties, while null placement and case rules remain explicit parts of the ordered-key contract.
Evaluate correctness and interview reasoning
A convincing interview answer treats comparator laws as correctness requirements and uses boundary fixtures to challenge them. It explains the exact one-hundred-fifty check count and why minimum-minus-maximum produces the wrong sign under unchecked arithmetic. It then distinguishes a finite test from a general proof and a static key from a key that changes inside a collection. Senior design discussion should focus on one coherent total order, explicit null semantics and deterministic secondary keys. Short syntax is useful only after those properties are secure; a subtraction-based one-liner can violate them while looking deceptively simple.
A working example
using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
int[] values={int.MinValue,-1,0,1,int.MaxValue}; int checks=0;
foreach(int a in values) foreach(int b in values) {
if(Math.Sign(a.CompareTo(b))!=-Math.Sign(b.CompareTo(a))) throw new Exception("antisymmetry"); checks++;
foreach(int c in values) { if(a.CompareTo(b)<=0 && b.CompareTo(c)<=0 && a.CompareTo(c)>0) throw new Exception("transitivity"); checks++; }
}
Console.WriteLine("law checks:"+checks);
Console.WriteLine("subtraction wrong:"+(unchecked(int.MinValue-int.MaxValue)>0).ToString().ToLowerInvariant());
Console.WriteLine("safe sign:"+Math.Sign(int.MinValue.CompareTo(int.MaxValue)));
Expected output
law checks:150 subtraction wrong:true safe sign:-1
Create a nontransitive cyclic order for three named values and make the triple checks fail. Hint: a compares before b, b before c and c before a cannot form an ordinary total order.
Further reading
Interview questions for this lesson
- Explain: Which comparator laws are exercised, and why is subtraction unsafe even when a sort appears to work on small values?
- Trace: Trace the complete program for test comparator laws instead of subtracting. State the exact stdout, then justify the state transitions and relevant edge conditions.
- Design: Design a comparer for score descending, timestamp ascending and unique ID ascending. Explain how the final key stabilizes ties and how nulls are ordered. Add focused law fixtures rather than assuming a successful sort proves comparator correctness.