diff --git a/.claude/rules/vercel-react-best-practices/AGENTS.md b/.claude/rules/vercel-react-best-practices/AGENTS.md deleted file mode 100644 index e7b7ca90df..0000000000 --- a/.claude/rules/vercel-react-best-practices/AGENTS.md +++ /dev/null @@ -1,2719 +0,0 @@ -# React Best Practices - -**Version 1.0.0** -Vercel Engineering -January 2026 - -> **Note:** -> This document is mainly for agents and LLMs to follow when maintaining, -> generating, or refactoring React and Next.js codebases at Vercel. Humans -> may also find it useful, but guidance here is optimized for automation -> and consistency by AI-assisted workflows. - ---- - -## Abstract - -Comprehensive performance optimization guide for React and Next.js applications, designed for AI agents and LLMs. Contains 40+ rules across 8 categories, prioritized by impact from critical (eliminating waterfalls, reducing bundle size) to incremental (advanced patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation. - ---- - -## Table of Contents - -1. [Eliminating Waterfalls](#1-eliminating-waterfalls) — **CRITICAL** - - 1.1 [Defer Await Until Needed](#11-defer-await-until-needed) - - 1.2 [Dependency-Based Parallelization](#12-dependency-based-parallelization) - - 1.3 [Prevent Waterfall Chains in API Routes](#13-prevent-waterfall-chains-in-api-routes) - - 1.4 [Promise.all() for Independent Operations](#14-promiseall-for-independent-operations) - - 1.5 [Strategic Suspense Boundaries](#15-strategic-suspense-boundaries) -2. [Bundle Size Optimization](#2-bundle-size-optimization) — **CRITICAL** - - 2.1 [Avoid Barrel File Imports](#21-avoid-barrel-file-imports) - - 2.2 [Conditional Module Loading](#22-conditional-module-loading) - - 2.3 [Defer Non-Critical Third-Party Libraries](#23-defer-non-critical-third-party-libraries) - - 2.4 [Dynamic Imports for Heavy Components](#24-dynamic-imports-for-heavy-components) - - 2.5 [Preload Based on User Intent](#25-preload-based-on-user-intent) -3. [Server-Side Performance](#3-server-side-performance) — **HIGH** - - 3.1 [Authenticate Server Actions Like API Routes](#31-authenticate-server-actions-like-api-routes) - - 3.2 [Avoid Duplicate Serialization in RSC Props](#32-avoid-duplicate-serialization-in-rsc-props) - - 3.3 [Cross-Request LRU Caching](#33-cross-request-lru-caching) - - 3.4 [Minimize Serialization at RSC Boundaries](#34-minimize-serialization-at-rsc-boundaries) - - 3.5 [Parallel Data Fetching with Component Composition](#35-parallel-data-fetching-with-component-composition) - - 3.6 [Per-Request Deduplication with React.cache()](#36-per-request-deduplication-with-reactcache) - - 3.7 [Use after() for Non-Blocking Operations](#37-use-after-for-non-blocking-operations) -4. [Client-Side Data Fetching](#4-client-side-data-fetching) — **MEDIUM-HIGH** - - 4.1 [Deduplicate Global Event Listeners](#41-deduplicate-global-event-listeners) - - 4.2 [Use Passive Event Listeners for Scrolling Performance](#42-use-passive-event-listeners-for-scrolling-performance) - - 4.3 [Use SWR for Automatic Deduplication](#43-use-swr-for-automatic-deduplication) - - 4.4 [Version and Minimize localStorage Data](#44-version-and-minimize-localstorage-data) -5. [Re-render Optimization](#5-re-render-optimization) — **MEDIUM** - - 5.1 [Defer State Reads to Usage Point](#51-defer-state-reads-to-usage-point) - - 5.2 [Do not wrap a simple expression with a primitive result type in useMemo](#52-do-not-wrap-a-simple-expression-with-a-primitive-result-type-in-usememo) - - 5.3 [Extract Default Non-primitive Parameter Value from Memoized Component to Constant](#53-extract-default-non-primitive-parameter-value-from-memoized-component-to-constant) - - 5.4 [Extract to Memoized Components](#54-extract-to-memoized-components) - - 5.5 [Narrow Effect Dependencies](#55-narrow-effect-dependencies) - - 5.6 [Subscribe to Derived State](#56-subscribe-to-derived-state) - - 5.7 [Use Functional setState Updates](#57-use-functional-setstate-updates) - - 5.8 [Use Lazy State Initialization](#58-use-lazy-state-initialization) - - 5.9 [Use Transitions for Non-Urgent Updates](#59-use-transitions-for-non-urgent-updates) -6. [Rendering Performance](#6-rendering-performance) — **MEDIUM** - - 6.1 [Animate SVG Wrapper Instead of SVG Element](#61-animate-svg-wrapper-instead-of-svg-element) - - 6.2 [CSS content-visibility for Long Lists](#62-css-content-visibility-for-long-lists) - - 6.3 [Hoist Static JSX Elements](#63-hoist-static-jsx-elements) - - 6.4 [Optimize SVG Precision](#64-optimize-svg-precision) - - 6.5 [Prevent Hydration Mismatch Without Flickering](#65-prevent-hydration-mismatch-without-flickering) - - 6.6 [Use Activity Component for Show/Hide](#66-use-activity-component-for-showhide) - - 6.7 [Use Explicit Conditional Rendering](#67-use-explicit-conditional-rendering) - - 6.8 [Use useTransition Over Manual Loading States](#68-use-usetransition-over-manual-loading-states) -7. [JavaScript Performance](#7-javascript-performance) — **LOW-MEDIUM** - - 7.1 [Avoid Layout Thrashing](#71-avoid-layout-thrashing) - - 7.2 [Build Index Maps for Repeated Lookups](#72-build-index-maps-for-repeated-lookups) - - 7.3 [Cache Property Access in Loops](#73-cache-property-access-in-loops) - - 7.4 [Cache Repeated Function Calls](#74-cache-repeated-function-calls) - - 7.5 [Cache Storage API Calls](#75-cache-storage-api-calls) - - 7.6 [Combine Multiple Array Iterations](#76-combine-multiple-array-iterations) - - 7.7 [Early Length Check for Array Comparisons](#77-early-length-check-for-array-comparisons) - - 7.8 [Early Return from Functions](#78-early-return-from-functions) - - 7.9 [Hoist RegExp Creation](#79-hoist-regexp-creation) - - 7.10 [Use Loop for Min/Max Instead of Sort](#710-use-loop-for-minmax-instead-of-sort) - - 7.11 [Use Set/Map for O(1) Lookups](#711-use-setmap-for-o1-lookups) - - 7.12 [Use toSorted() Instead of sort() for Immutability](#712-use-tosorted-instead-of-sort-for-immutability) -8. [Advanced Patterns](#8-advanced-patterns) — **LOW** - - 8.1 [Store Event Handlers in Refs](#81-store-event-handlers-in-refs) - - 8.2 [useEffectEvent for Stable Callback Refs](#82-useeffectevent-for-stable-callback-refs) - ---- - -## 1. Eliminating Waterfalls - -**Impact: CRITICAL** - -Waterfalls are the #1 performance killer. Each sequential await adds full network latency. Eliminating them yields the largest gains. - -### 1.1 Defer Await Until Needed - -**Impact: HIGH (avoids blocking unused code paths)** - -Move `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them. - -**Incorrect: blocks both branches** - -```typescript -async function handleRequest(userId: string, skipProcessing: boolean) { - const userData = await fetchUserData(userId) - - if (skipProcessing) { - // Returns immediately but still waited for userData - return { skipped: true } - } - - // Only this branch uses userData - return processUserData(userData) -} -``` - -**Correct: only blocks when needed** - -```typescript -async function handleRequest(userId: string, skipProcessing: boolean) { - if (skipProcessing) { - // Returns immediately without waiting - return { skipped: true } - } - - // Fetch only when needed - const userData = await fetchUserData(userId) - return processUserData(userData) -} -``` - -**Another example: early return optimization** - -```typescript -// Incorrect: always fetches permissions -async function updateResource(resourceId: string, userId: string) { - const permissions = await fetchPermissions(userId) - const resource = await getResource(resourceId) - - if (!resource) { - return { error: 'Not found' } - } - - if (!permissions.canEdit) { - return { error: 'Forbidden' } - } - - return await updateResourceData(resource, permissions) -} - -// Correct: fetches only when needed -async function updateResource(resourceId: string, userId: string) { - const resource = await getResource(resourceId) - - if (!resource) { - return { error: 'Not found' } - } - - const permissions = await fetchPermissions(userId) - - if (!permissions.canEdit) { - return { error: 'Forbidden' } - } - - return await updateResourceData(resource, permissions) -} -``` - -This optimization is especially valuable when the skipped branch is frequently taken, or when the deferred operation is expensive. - -### 1.2 Dependency-Based Parallelization - -**Impact: CRITICAL (2-10× improvement)** - -For operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment. - -**Incorrect: profile waits for config unnecessarily** - -```typescript -const [user, config] = await Promise.all([ - fetchUser(), - fetchConfig() -]) -const profile = await fetchProfile(user.id) -``` - -**Correct: config and profile run in parallel** - -```typescript -import { all } from 'better-all' - -const { user, config, profile } = await all({ - async user() { return fetchUser() }, - async config() { return fetchConfig() }, - async profile() { - return fetchProfile((await this.$.user).id) - } -}) -``` - -**Alternative without extra dependencies:** - -```typescript -const userPromise = fetchUser() -const profilePromise = userPromise.then(user => fetchProfile(user.id)) - -const [user, config, profile] = await Promise.all([ - userPromise, - fetchConfig(), - profilePromise -]) -``` - -We can also create all the promises first, and do `Promise.all()` at the end. - -Reference: [https://github.com/shuding/better-all](https://github.com/shuding/better-all) - -### 1.3 Prevent Waterfall Chains in API Routes - -**Impact: CRITICAL (2-10× improvement)** - -In API routes and Server Actions, start independent operations immediately, even if you don't await them yet. - -**Incorrect: config waits for auth, data waits for both** - -```typescript -export async function GET(request: Request) { - const session = await auth() - const config = await fetchConfig() - const data = await fetchData(session.user.id) - return Response.json({ data, config }) -} -``` - -**Correct: auth and config start immediately** - -```typescript -export async function GET(request: Request) { - const sessionPromise = auth() - const configPromise = fetchConfig() - const session = await sessionPromise - const [config, data] = await Promise.all([ - configPromise, - fetchData(session.user.id) - ]) - return Response.json({ data, config }) -} -``` - -For operations with more complex dependency chains, use `better-all` to automatically maximize parallelism (see Dependency-Based Parallelization). - -### 1.4 Promise.all() for Independent Operations - -**Impact: CRITICAL (2-10× improvement)** - -When async operations have no interdependencies, execute them concurrently using `Promise.all()`. - -**Incorrect: sequential execution, 3 round trips** - -```typescript -const user = await fetchUser() -const posts = await fetchPosts() -const comments = await fetchComments() -``` - -**Correct: parallel execution, 1 round trip** - -```typescript -const [user, posts, comments] = await Promise.all([ - fetchUser(), - fetchPosts(), - fetchComments() -]) -``` - -### 1.5 Strategic Suspense Boundaries - -**Impact: HIGH (faster initial paint)** - -Instead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads. - -**Incorrect: wrapper blocked by data fetching** - -```tsx -async function Page() { - const data = await fetchData() // Blocks entire page - - return ( -
0&&x(De)?v>1?qi(De,v-1,x,P,W):Dn(W,De):P||(W[W.length]=De)}return W}var g=gc(),y=gc(!0);function R(p,v){return p&&g(p,v,No)}function F(p,v){return p&&y(p,v,No)}function b(p,v){return It(v,function(x){return Ra(p[x])})}function J(p,v){v=Vs(v,p);for(var x=0,P=v.length;p!=null&&x
v}function Lt(p,v){return p!=null&&li.call(p,v)}function xr(p,v){return p!=null&&v in xn(p)}function io(p,v,x){return p>=Kn(v,x)&&p 1),ee}),ko(p,sr(p),x),P&&(x=zo(x,T|B|H,cm));for(var W=v.length;W--;)A2(x,v[W]);return x});function A1(p,v){return tf(p,Od(Vn(v)))}var Ap=cl(function(p,v){return p==null?{}:vh(p,v)});function tf(p,v){if(p==null)return{};var x=Ft(sr(p),function(P){return[P]});return v=Vn(v),mh(p,x,function(P,W){return v(P,W[0])})}function Hm(p,v,x){v=Vs(v,p);var P=-1,W=v.length;for(W||(W=1,p=i);++P 0||v<0)?new at(x):(p<0?x=x.takeRight(-p):p&&(x=x.drop(p)),v!==i&&(v=Mr(v),x=v<0?x.dropRight(-v):x.take(v-p)),x)},at.prototype.takeRightWhile=function(p){return this.reverse().takeWhile(p).reverse()},at.prototype.toArray=function(){return this.take(fe)},R(at.prototype,function(p,v){var x=/^(?:filter|find|map|reject)|While$/.test(v),P=/^(?:head|last)$/.test(v),W=Y[P?"take"+(v=="last"?"Right":""):v],ee=P||/^find/.test(v);!W||(Y.prototype[v]=function(){var he=this.__wrapped__,De=P?[1]:arguments,be=he instanceof at,Et=De[0],St=be||tr(he),At=function(ui){var di=W.apply(Y,Dn([ui],De));return P&&on?di[0]:di};St&&x&&typeof Et=="function"&&Et.length!=1&&(be=St=!1);var on=this.__chain__,kn=!!this.__actions__.length,rr=ee&&!on,br=be&&!kn;if(!ee&&St){he=br?he:new at(this);var ar=p.apply(he,De);return ar.__actions__.push({func:g1,args:[At],thisArg:i}),new Vr(ar,on)}return rr&&br?p.apply(this,De):(ar=this.thru(At),rr?P?ar.value()[0]:ar.value():ar)})}),it(["pop","push","shift","sort","splice","unshift"],function(p){var v=Jr[p],x=/^(?:push|sort|unshift)$/.test(p)?"tap":"thru",P=/^(?:pop|shift)$/.test(p);Y.prototype[p]=function(){var W=arguments;if(P&&!this.__chain__){var ee=this.value();return v.apply(tr(ee)?ee:[],W)}return this[x](function(he){return v.apply(tr(he)?he:[],W)})}}),R(at.prototype,function(p,v){var x=Y[v];if(x){var P=x.name+"";li.call(On,P)||(On[P]=[]),On[P].push({name:v,func:x})}}),On[ga(i,pe).name]=[{name:"wrapper",func:i}],at.prototype.clone=Di,at.prototype.reverse=ru,at.prototype.value=wo,Y.prototype.at=$2,Y.prototype.chain=qh,Y.prototype.commit=ep,Y.prototype.next=Wh,Y.prototype.plant=Em,Y.prototype.reverse=If,Y.prototype.toJSON=Y.prototype.valueOf=Y.prototype.value=bf,Y.prototype.first=Y.prototype.head,Pu&&(Y.prototype[Pu]=_m),Y},n0=t0();typeof define=="function"&&typeof define.amd=="object"&&define.amd?(ji._=n0,define(function(){return n0})):z?((z.exports=n0)._=n0,U._=n0):ji._=n0}).call(Wv)});var yD=Ke((wW,mD)=>{"use strict";var Pi=mD.exports;mD.exports.default=Pi;var Du="[",jy="]",Vv="\x07",P_=";",LS=process.env.TERM_PROGRAM==="Apple_Terminal";Pi.cursorTo=(i,o)=>{if(typeof i!="number")throw new TypeError("The `x` argument is required");return typeof o!="number"?Du+(i+1)+"G":Du+(o+1)+";"+(i+1)+"H"};Pi.cursorMove=(i,o)=>{if(typeof i!="number")throw new TypeError("The `x` argument is required");let a="";return i<0?a+=Du+-i+"D":i>0&&(a+=Du+i+"C"),o<0?a+=Du+-o+"A":o>0&&(a+=Du+o+"B"),a};Pi.cursorUp=(i=1)=>Du+i+"A";Pi.cursorDown=(i=1)=>Du+i+"B";Pi.cursorForward=(i=1)=>Du+i+"C";Pi.cursorBackward=(i=1)=>Du+i+"D";Pi.cursorLeft=Du+"G";Pi.cursorSavePosition=LS?"7":Du+"s";Pi.cursorRestorePosition=LS?"8":Du+"u";Pi.cursorGetPosition=Du+"6n";Pi.cursorNextLine=Du+"E";Pi.cursorPrevLine=Du+"F";Pi.cursorHide=Du+"?25l";Pi.cursorShow=Du+"?25h";Pi.eraseLines=i=>{let o="";for(let a=0;a[jy,"8",P_,P_,o,Vv,i,jy,"8",P_,P_,Vv].join("");Pi.image=(i,o={})=>{let a=`${jy}1337;File=inline=1`;return o.width&&(a+=`;width=${o.width}`),o.height&&(a+=`;height=${o.height}`),o.preserveAspectRatio===!1&&(a+=";preserveAspectRatio=0"),a+":"+i.toString("base64")+Vv};Pi.iTerm={setCwd:(i=process.cwd())=>`${jy}50;CurrentDir=${i}${Vv}`,annotation:(i,o={})=>{let a=`${jy}1337;`,c=typeof o.x!="undefined",_=typeof o.y!="undefined";if((c||_)&&!(c&&_&&typeof o.length!="undefined"))throw new Error("`x`, `y` and `length` must be defined when `x` or `y` is defined");return i=i.replace(/\|/g,""),a+=o.isHidden?"AddHiddenAnnotation=":"AddAnnotation=",o.length>0?a+=(c?[i,o.length,o.x,o.y]:[o.length,i]).join("|"):a+=i,a+Vv}}});var PS=Ke((SW,gD)=>{"use strict";var NS=(i,o)=>{for(let a of Reflect.ownKeys(o))Object.defineProperty(i,a,Object.getOwnPropertyDescriptor(o,a));return i};gD.exports=NS;gD.exports.default=NS});var bS=Ke((TW,I_)=>{"use strict";var AI=PS(),b_=new WeakMap,IS=(i,o={})=>{if(typeof i!="function")throw new TypeError("Expected a function");let a,c=0,_=i.displayName||i.name||"=0){var E=c1()-d1;f.actualDuration+=E,d&&(f.selfBaseDuration=E),d1=-1}}var bl=null,$a=null,wa=!1;function V2(){wa&&Qt(!1,"We should not be hydrating here. This is a bug in React. Please file a bug.")}function G2(f){if(!Se)return!1;var d=f.stateNode.containerInfo;return $a=U(d),bl=f,wa=!0,!0}function hm(f,d){return Se?($a=ji(d),X2(f),wa=!0,!0):!1}function Y2(f,d){switch(f.tag){case B:oe(f.stateNode.containerInfo,d);break;case q:We(f.type,f.memoizedProps,f.stateNode,d);break}var E=rE();E.stateNode=d,E.return=f,E.effectTag=Ko,f.lastEffect!==null?(f.lastEffect.nextEffect=E,f.lastEffect=E):f.firstEffect=f.lastEffect=E}function Fh(f,d){switch(d.effectTag=d.effectTag&~au|mi,f.tag){case B:{var E=f.stateNode.containerInfo;switch(d.tag){case q:var C=d.type,A=d.pendingProps;it(E,C,A);break;case ne:var j=d.pendingProps;Ct(E,j);break;case ce:Mt(E);break}break}case q:{var V=f.type,te=f.memoizedProps,se=f.stateNode;switch(d.tag){case q:var Ue=d.type,Qe=d.pendingProps;It(V,te,se,Ue,Qe);break;case ne:var vt=d.pendingProps;sn(V,te,se,vt);break;case ce:rn(V,te,se);break}break}default:return}}function Ph(f,d){switch(f.tag){case q:{var E=f.type,C=f.pendingProps,A=hf(d,E,C);return A!==null?(f.stateNode=A,!0):!1}case ne:{var j=f.pendingProps,V=Bs(d,j);return V!==null?(f.stateNode=V,!0):!1}case ce:{if(Ai){var te=Ba(d);if(te!==null){var se={dehydrated:te,retryTime:Di};f.memoizedState=se;var Ue=iE(te);return Ue.return=f,f.child=Ue,!0}}return!1}default:return!1}}function K2(f){if(!!wa){var d=$a;if(!d){Fh(bl,f),wa=!1,bl=f;return}var E=d;if(!Ph(f,d)){if(d=ji(E),!d||!Ph(f,d)){Fh(bl,f),wa=!1,bl=f;return}Y2(bl,E)}bl=f,$a=U(d)}}function vm(f,d,E){if(!Se)throw Error("Expected prepareToHydrateHostInstance() to never be called. This error is likely caused by a bug in React. Please file an issue.");var C=f.stateNode,A=z(C,f.type,f.memoizedProps,d,E,f);return f.updateQueue=A,A!==null}function mm(f){if(!Se)throw Error("Expected prepareToHydrateHostTextInstance() to never be called. This error is likely caused by a bug in React. Please file an issue.");var d=f.stateNode,E=f.memoizedProps,C=G(d,E,f);if(C){var A=bl;if(A!==null)switch(A.tag){case B:{var j=A.stateNode.containerInfo;Je(j,d,E);break}case q:{var V=A.type,te=A.memoizedProps,se=A.stateNode;mt(V,te,se,d,E);break}}}return C}function Ih(f){if(!Se)throw Error("Expected prepareToHydrateHostSuspenseInstance() to never be called. This error is likely caused by a bug in React. Please file an issue.");var d=f.memoizedState,E=d!==null?d.dehydrated:null;if(!E)throw Error("Expected to have a hydrated suspense instance. This error is likely caused by a bug in React. Please file an issue.");$(E,f)}function ym(f){if(!Se)throw Error("Expected skipPastDehydratedSuspenseInstance() to never be called. This error is likely caused by a bug in React. Please file an issue.");var d=f.memoizedState,E=d!==null?d.dehydrated:null;if(!E)throw Error("Expected to have a hydrated suspense instance. This error is likely caused by a bug in React. Please file an issue.");return Ce(E)}function X2(f){for(var d=f.return;d!==null&&d.tag!==q&&d.tag!==B&&d.tag!==ce;)d=d.return;bl=d}function h1(f){if(!Se||f!==bl)return!1;if(!wa)return X2(f),wa=!0,!1;var d=f.type;if(f.tag!==q||d!=="head"&&d!=="body"&&!Li(d,f.memoizedProps))for(var E=$a;E;)Y2(f,E),E=ji(E);return X2(f),f.tag===ce?$a=ym(f):$a=bl?ji(f.stateNode):null,!0}function v1(){!Se||(bl=null,$a=null,wa=!1)}var m1=nt.ReactCurrentOwner,Sa=!1,Q2,Ks,Xs,Qs,J2,Ta,y1,Td,Tc,Z2;Q2={},Ks={},Xs={},Qs={},J2={},Ta=!1,y1=!1,Td={},Tc={},Z2={};function w0(f,d,E,C){f===null?d.child=$c(d,null,E,C):d.child=Cf(d,f.child,E,C)}function bh(f,d,E,C){d.child=Cf(d,f.child,null,C),d.child=Cf(d,null,E,C)}function Bh(f,d,E,C,A){if(d.type!==d.elementType){var j=E.propTypes;j&&_(j,C,"prop",Wt(E),Nr)}var V=E.render,te=d.ref,se;return uo(d,A),m1.current=d,et("render"),se=Ff(f,d,V,C,te,A),Ri&&d.mode&mr&&d.memoizedState!==null&&(se=Ff(f,d,V,C,te,A)),et(null),f!==null&&!Sa?(_d(f,d,A),Ca(f,d,A)):(d.effectTag|=su,w0(f,d,se,A),d.child)}function Uh(f,d,E,C,A,j){if(f===null){var V=E.type;if(ao(V)&&E.compare===null&&E.defaultProps===void 0){var te=V;return te=ro(V),d.tag=re,d.type=te,tp(d,V),jh(f,d,te,C,A,j)}{var se=V.propTypes;se&&_(se,C,"prop",Wt(V),Nr)}var Ue=yy(E.type,null,C,null,d.mode,j);return Ue.ref=d.ref,Ue.return=d,d.child=Ue,Ue}{var Qe=E.type,vt=Qe.propTypes;vt&&_(vt,C,"prop",Wt(Qe),Nr)}var Nt=f.child;if(A ru&&(Ul=f),d!==null&&f
>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?I>>>0>>1>>>0?S>>>0>>1>>>0?S>>>0