@@ -237,17 +237,23 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
237237 /// simplifying and a change count is therefore never still.
238238 /// </para>
239239 /// <para>
240- /// <b>A decline is held with the scope it was made in.</b> Five rules answer only the
241- /// question asked (<see cref="AnsweringTheQuestionAsked"/>) and decline the same
242- /// integrand one level down, so a <see langword="null"/> computed at depth two says
243- /// nothing about depth one — and once held without the scope it was served to the
244- /// top-level ask: <c>sec(x)^3</c>, tried and declined inside another rule's search, then
245- /// asked for directly and declined from the cache in two milliseconds. The key carries
246- /// the scope, and a lookup takes a decline only from its own scope and an answer from
247- /// either, since an antiderivative that was found is right wherever it is asked for.
240+ /// <b>A decline is held with the scope it was made in.</b> Rules answer only the question
241+ /// asked (<see cref="AnsweringTheQuestionAsked"/>), or it and one or two levels below it
242+ /// (<see cref="AnsweringTheQuestionAskedOrOneBelow"/>,
243+ /// <see cref="AnsweringTheQuestionAskedOrTwoBelow"/>), and decline the same integrand
244+ /// deeper, so a <see langword="null"/> computed at one depth says nothing about a
245+ /// shallower one. Held without the scope it was served there: <c>sec(x)^3</c>, tried and
246+ /// declined inside another rule's search, then asked for directly and declined from the
247+ /// cache in two milliseconds. And held with the top told only from the rest, a decline at
248+ /// depth three was served at depth two: the search for <c>1/(x^(3/2) (a + c x^4))</c>
249+ /// declines the root's substitution <c>2/(w^2 (a + c w^8))</c> at depth three and asks it
250+ /// again at depth two, and the integral was declined. The key carries the depth as far as
251+ /// a scope reaches, <see cref="ScopeDepth"/>, and a lookup takes a decline only from its
252+ /// own depth and an answer from any, since an antiderivative that was found is right
253+ /// wherever it is asked for.
248254 /// </para>
249255 /// </remarks>
250- [ System . ThreadStatic ] private static Dictionary < ( Entity , Entity . Variable , bool , bool ) , Entity ? > ? answered ;
256+ [ System . ThreadStatic ] private static Dictionary < ( Entity , Entity . Variable , bool , int ) , Entity ? > ? answered ;
251257
252258 /// <summary>The settings <see cref="answered"/> was filled under.</summary>
253259 [ System . ThreadStatic ] private static object ? [ ] ? answeredUnder ;
@@ -346,6 +352,17 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
346352 /// </summary>
347353 internal static bool AnsweringTheQuestionAskedOrTwoBelow => descentDepth <= 3 ;
348354
355+ /// <summary>
356+ /// The depth as far as a scope tells one depth from another: 1 for the question asked, 2
357+ /// and 3 for the levels <see cref="AnsweringTheQuestionAskedOrOneBelow"/> and
358+ /// <see cref="AnsweringTheQuestionAskedOrTwoBelow"/> reach, and 4 for every level past
359+ /// them, where every scoped rule declines alike.
360+ /// </summary>
361+ private static int ScopeDepth => System . Math . Min ( descentDepth , DeepestScope ) ;
362+
363+ /// <summary>The deepest <see cref="ScopeDepth"/>.</summary>
364+ private const int DeepestScope = 4 ;
365+
349366 /// <summary>
350367 /// <paramref name="expr"/> integrated as a question in its own right rather than as a
351368 /// step in the search for the current one: the term of a sum asked at the top is asked at
@@ -485,7 +502,7 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
485502 // would otherwise be answered with, without the rule ever being reached to say that a
486503 // second pass could answer it.
487504 if ( answer is not null && memo is not null && stamp is not null && SettingsState . StillHolds ( stamp ) )
488- memo [ ( Normalized ( expr , x ) , x , integrateByParts , true ) ] = answer ;
505+ memo [ ( Normalized ( expr , x ) , x , integrateByParts , 1 ) ] = answer ;
489506 return answer ;
490507 }
491508
@@ -549,12 +566,13 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
549566 var into = answered ;
550567 var stamp = answeredUnder ;
551568
552- var key = ( expr , x , integrateByParts , AnsweringTheQuestionAsked ) ;
569+ var depth = ScopeDepth ;
570+ var key = ( expr , x , integrateByParts , depth ) ;
553571 if ( into . TryGetValue ( key , out var already ) )
554572 return already ;
555- var otherScope = ( expr , x , integrateByParts , ! AnsweringTheQuestionAsked ) ;
556- if ( into . TryGetValue ( otherScope , out var elsewhere ) && elsewhere is not null )
557- return elsewhere ;
573+ for ( var other = 1 ; other <= DeepestScope ; other ++ )
574+ if ( other != depth && into . TryGetValue ( ( expr , x , integrateByParts , other ) , out var elsewhere ) && elsewhere is not null )
575+ return elsewhere ;
558576
559577 // Whether the descent ran out *inside this computation*, which is the only truncation
560578 // that says anything about this key. The flag is one per thread, so it is cleared
0 commit comments