1414using HonkSharp . Laziness ;
1515using Complex = AngouriMath . Entity . Number . Complex ;
1616using System . Linq . Expressions ;
17+ using System . Diagnostics . CodeAnalysis ;
1718
1819namespace AngouriMath
1920{
@@ -53,7 +54,7 @@ public FiniteSet Apply(Func<Entity, Entity> func)
5354 var changed = func ( el ) ;
5455 if ( ! ReferenceEquals ( changed , el ) )
5556 hasAnythingChanged = true ;
56- newElements . Add ( changed ) ;
57+ if ( changed != MathS . NaN ) newElements . Add ( changed ) ;
5758 }
5859 if ( hasAnythingChanged )
5960 return newElements . ToFiniteSet ( ) ;
@@ -123,31 +124,26 @@ internal static FiniteSet Unite(FiniteSet A, FiniteSet B)
123124 => new FiniteSet ( A . Elements . Concat ( B . Elements ) ) ;
124125
125126 // It could be written with one chain request, but readability > one line
126- internal static FiniteSet Subtract ( FiniteSet A , FiniteSet B )
127+ internal static bool TryFullSubtract ( FiniteSet A , FiniteSet B , [ NotNullWhen ( true ) ] out FiniteSet ? resultSet )
127128 {
129+ var constantSets = A . Vars . Count == 0 && B . Vars . Count == 0 ;
128130 var dict = BuildDictionaryFromElements ( A . Elements , noCheck : true ) ;
129131 foreach ( var el in B )
130- dict . Remove ( el . Evaled ) ;
131- return new FiniteSet ( dict . Values , noCheck : true ) ; // we didn't add anything
132- }
133-
134- internal static FiniteSet Intersect ( FiniteSet A , FiniteSet B )
135- {
136- var dict = BuildDictionaryFromElements ( A . Elements , noCheck : true ) ;
137- foreach ( var el in A . elements )
138- if ( ! B . Contains ( el . Key ) )
139- dict . Remove ( el . Key ) ;
140- return new FiniteSet ( dict . Values , noCheck : true ) ; // we didn't add anything
132+ if ( ! dict . Remove ( el . Evaled ) && ! constantSets ) { resultSet = null ; return false ; }
133+ resultSet = new FiniteSet ( dict . Values , noCheck : true ) ; // we didn't add anything
134+ return true ;
141135 }
142136
143137 /// <inheritdoc/>
144138 public override bool TryContains ( Entity entity , out bool contains )
145139 {
140+ if ( IsSetEmpty ) { contains = false ; return true ; } // a in {} is unambiguously false
146141 contains = elements . ContainsKey ( entity . Evaled ) ;
147- // a in { 2, 3 } is false
148- // 4 in { a, 3 } is false
149- // TODO: should we return false when there are symbolic expressions?
150- return true ;
142+ // x in { x, 2 } and 2 in { x, 2 } are both unambiguously true
143+ if ( contains ) return true ;
144+ // however, 1 in { x, 2 }, y in { x, 2 } are both unknown
145+ // meanwhile, 2 in { 1, 3 } is unambiguously false
146+ return entity . Vars . Count == 0 && elements . Keys . All ( el => el . Vars . Count == 0 ) ;
151147 }
152148
153149 /// <inheritdoc/>
@@ -168,7 +164,7 @@ public bool Equals(FiniteSet? other)
168164 if ( other . Count != Count )
169165 return false ;
170166 foreach ( var pair in elements )
171- if ( ! other . Contains ( pair . Key ) )
167+ if ( ! other . TryContains ( pair . Key , out var contains ) || ! contains )
172168 return false ;
173169 return true ;
174170 }
@@ -653,13 +649,9 @@ public override Entity Replace(Func<Entity, Entity> func)
653649 public override bool TryContains ( Entity entity , out bool contains )
654650 {
655651 contains = false ;
656- if ( Left is not Set left || Right is not Set right )
657- return false ;
658- if ( left . TryContains ( entity , out var leftContains ) && right . TryContains ( entity , out var rightContains ) )
659- {
660- contains = leftContains || rightContains ;
661- return true ;
662- }
652+ if ( Left is not Set left || Right is not Set right ) return false ;
653+ if ( left . TryContains ( entity , out contains ) && contains ) return true ;
654+ if ( right . TryContains ( entity , out contains ) ) return true ;
663655 return false ;
664656 }
665657
0 commit comments