@@ -354,7 +354,6 @@ private void EmitV0BaseType(
354354 VectorFormDefinition v0 = dim . Quantities . Vector0 ! ;
355355 string typeName = v0 . Base ;
356356 string fullType = $ "{ typeName } <T>";
357- string ? v1TypeName = dim . Quantities . Vector1 ? . Base ;
358357
359358 using CodeBlocker cb = CodeBlocker . Create ( ) ;
360359
@@ -389,7 +388,9 @@ private void EmitV0BaseType(
389388 Name = "Zero => Create(T.Zero)" ,
390389 } ) ;
391390
392- // Factory methods from available units
391+ // Factory methods from available units. The body wraps Create(...) with
392+ // Vector0Guards.EnsureNonNegative so a negative input throws ArgumentException —
393+ // the V0 non-negativity invariant locked in #50.
393394 if ( dim . AvailableUnits . Count > 0 )
394395 {
395396 string firstUnit = dim . AvailableUnits [ 0 ] ;
@@ -402,36 +403,38 @@ private void EmitV0BaseType(
402403 "/// </summary>" ,
403404 $ "/// <param name=\" value\" >The value in { firstUnit } .</param>",
404405 $ "/// <returns>A new <see cref=\" { typeName } {{T}}\" /> instance.</returns>",
406+ "/// <exception cref=\" System.ArgumentException\" >Thrown when the resulting magnitude would be negative.</exception>" ,
405407 ] ,
406408 Keywords = [ "public" , "static" , fullType ] ,
407409 Name = $ "From{ firstUnit } ",
408410 Parameters = [ new ParameterTemplate { Type = "T" , Name = "value" } ] ,
409- BodyFactory = ( body ) => body . Write ( " => Create(value);" ) ,
411+ BodyFactory = ( body ) => body . Write ( " => Create(Vector0Guards.EnsureNonNegative( value, nameof(value)) );" ) ,
410412 } ) ;
411413 }
412414
413- // V0 subtraction hiding: returns V1 if V1 exists for this dimension
414- if ( v1TypeName != null )
415+ // V0 - V0 returns the same V0 of T.Abs(left - right) (locked decision in #52).
416+ // We emit this on every V0 base type so the derived operator wins overload resolution
417+ // over PhysicalQuantity's plain subtraction (which can produce a negative magnitude
418+ // and would trip the non-negativity guard from #50).
419+ cls . Members . Add ( new MethodTemplate ( )
415420 {
416- cls . Members . Add ( new MethodTemplate ( )
417- {
418- Comments =
419- [
420- "/// <summary>" ,
421- $ "/// Subtracts two { typeName } values, returning a signed { v1TypeName } result.",
422- "/// </summary>" ,
423- ] ,
424- Attributes = [ "System.Diagnostics.CodeAnalysis.SuppressMessage(\" Usage\" , \" CA2225:Operator overloads have named alternates\" , Justification = \" Physics quantity operator\" )" ] ,
425- Keywords = [ "public" , "static" , $ "{ v1TypeName } <T>"] ,
426- Name = "operator -" ,
427- Parameters =
428- [
429- new ParameterTemplate { Type = fullType , Name = "left" } ,
430- new ParameterTemplate { Type = fullType , Name = "right" } ,
431- ] ,
432- BodyFactory = ( body ) => body . Write ( $ " => { v1TypeName } <T>.Create(left.Quantity - right.Quantity);") ,
433- } ) ;
434- }
421+ Comments =
422+ [
423+ "/// <summary>" ,
424+ $ "/// Subtracts two { typeName } values, returning the absolute difference as a non-negative { typeName } .",
425+ "/// Magnitude subtraction stays a magnitude (per the unified-vector model)." ,
426+ "/// </summary>" ,
427+ ] ,
428+ Attributes = [ "System.Diagnostics.CodeAnalysis.SuppressMessage(\" Usage\" , \" CA2225:Operator overloads have named alternates\" , Justification = \" Physics quantity operator\" )" ] ,
429+ Keywords = [ "public" , "static" , fullType ] ,
430+ Name = "operator -" ,
431+ Parameters =
432+ [
433+ new ParameterTemplate { Type = fullType , Name = "left" } ,
434+ new ParameterTemplate { Type = fullType , Name = "right" } ,
435+ ] ,
436+ BodyFactory = ( body ) => body . Write ( " => Create(T.Abs(left.Quantity - right.Quantity));" ) ,
437+ } ) ;
435438
436439 // Cross-dimensional operators
437440 EmitScalarOperators ( cls , typeName , operatorsByOwner , typeFormMap ) ;
@@ -636,7 +639,6 @@ private void EmitOverloadType(
636639 string typeName = overload . Name ;
637640 string fullType = $ "{ typeName } <T>";
638641 string baseFullType = $ "{ baseTypeName } <T>";
639- string ? v1TypeName = dim . Quantities . Vector1 ? . Base ;
640642
641643 // V0/V1 overloads inherit from PhysicalQuantity
642644 if ( vectorForm <= 1 )
@@ -677,17 +679,21 @@ private void EmitOverloadType(
677679 Name = "Zero => Create(T.Zero)" ,
678680 } ) ;
679681
680- // Factory methods
682+ // Factory methods. V0 overloads enforce the same non-negativity invariant as
683+ // their V0 base type (#50); V1 overloads accept any sign.
681684 if ( dim . AvailableUnits . Count > 0 )
682685 {
683686 string firstUnit = dim . AvailableUnits [ 0 ] ;
687+ string body = vectorForm == 0
688+ ? " => Create(Vector0Guards.EnsureNonNegative(value, nameof(value)));"
689+ : " => Create(value);" ;
684690 cls . Members . Add ( new MethodTemplate ( )
685691 {
686692 Comments = [ $ "/// <summary>Creates a new { typeName } from a value in { firstUnit } .</summary>"] ,
687693 Keywords = [ "public" , "static" , fullType ] ,
688694 Name = $ "From{ firstUnit } ",
689695 Parameters = [ new ParameterTemplate { Type = "T" , Name = "value" } ] ,
690- BodyFactory = ( body ) => body . Write ( " => Create(value);" ) ,
696+ BodyFactory = ( b ) => b . Write ( body ) ,
691697 } ) ;
692698 }
693699
@@ -721,21 +727,24 @@ private void EmitOverloadType(
721727 BodyFactory = ( body ) => body . Write ( " => Create(value.Value);" ) ,
722728 } ) ;
723729
724- // V0 overload subtraction hiding (returns V1 base if exists)
725- if ( vectorForm == 0 && v1TypeName != null )
730+ // V0 overload subtraction returns the same V0 of T.Abs(left - right) (locked
731+ // in #52). The overload-typed operator hides the base PhysicalQuantity's plain
732+ // subtraction so overloads stay in their own type and the magnitude invariant
733+ // is preserved.
734+ if ( vectorForm == 0 )
726735 {
727736 cls . Members . Add ( new MethodTemplate ( )
728737 {
729- Comments = [ $ "/// <summary>Subtracts two { typeName } values, returning a signed { v1TypeName } result .</summary>"] ,
738+ Comments = [ $ "/// <summary>Subtracts two { typeName } values, returning the absolute difference as a non-negative { typeName } .</summary>"] ,
730739 Attributes = [ "System.Diagnostics.CodeAnalysis.SuppressMessage(\" Usage\" , \" CA2225:Operator overloads have named alternates\" , Justification = \" Physics quantity operator\" )" ] ,
731- Keywords = [ "public" , "static" , $ " { v1TypeName } <T>" ] ,
740+ Keywords = [ "public" , "static" , fullType ] ,
732741 Name = "operator -" ,
733742 Parameters =
734743 [
735744 new ParameterTemplate { Type = fullType , Name = "left" } ,
736745 new ParameterTemplate { Type = fullType , Name = "right" } ,
737746 ] ,
738- BodyFactory = ( body ) => body . Write ( $ " => { v1TypeName } <T>.Create (left.Quantity - right.Quantity);") ,
747+ BodyFactory = ( body ) => body . Write ( " => Create(T.Abs (left.Quantity - right.Quantity) );" ) ,
739748 } ) ;
740749 }
741750
0 commit comments