ts-fsrs 5.2.0 → 5.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.umd.js CHANGED
@@ -1,2 +1,2061 @@
1
- (function(o,c){typeof exports=="object"&&typeof module!="undefined"?c(exports):typeof define=="function"&&define.amd?define(["exports"],c):(o=typeof globalThis!="undefined"?globalThis:o||self,c(o.FSRS={}))})(this,function(o){"use strict";var c=(i=>(i[i.New=0]="New",i[i.Learning=1]="Learning",i[i.Review=2]="Review",i[i.Relearning=3]="Relearning",i))(c||{}),l=(i=>(i[i.Manual=0]="Manual",i[i.Again=1]="Again",i[i.Hard=2]="Hard",i[i.Good=3]="Good",i[i.Easy=4]="Easy",i))(l||{}),pt=Object.defineProperty,vt=Object.defineProperties,wt=Object.getOwnPropertyDescriptors,U=Object.getOwnPropertySymbols,bt=Object.prototype.hasOwnProperty,St=Object.prototype.propertyIsEnumerable,k=(i,t,e)=>t in i?pt(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e,q=(i,t)=>{for(var e in t||(t={}))bt.call(t,e)&&k(i,e,t[e]);if(U)for(var e of U(t))St.call(t,e)&&k(i,e,t[e]);return i},Y=(i,t)=>vt(i,wt(t));class d{static card(t){return Y(q({},t),{state:d.state(t.state),due:d.time(t.due),last_review:t.last_review?d.time(t.last_review):void 0})}static rating(t){if(typeof t=="string"){const e=t.charAt(0).toUpperCase(),r=t.slice(1).toLowerCase(),a=l[`${e}${r}`];if(a===void 0)throw new Error(`Invalid rating:[${t}]`);return a}else if(typeof t=="number")return t;throw new Error(`Invalid rating:[${t}]`)}static state(t){if(typeof t=="string"){const e=t.charAt(0).toUpperCase(),r=t.slice(1).toLowerCase(),a=c[`${e}${r}`];if(a===void 0)throw new Error(`Invalid state:[${t}]`);return a}else if(typeof t=="number")return t;throw new Error(`Invalid state:[${t}]`)}static time(t){if(typeof t=="object"&&t instanceof Date)return t;if(typeof t=="string"){const e=Date.parse(t);if(isNaN(e))throw new Error(`Invalid date:[${t}]`);return new Date(e)}else if(typeof t=="number")return new Date(t);throw new Error(`Invalid date:[${t}]`)}static review_log(t){return Y(q({},t),{due:d.time(t.due),rating:d.rating(t.rating),state:d.state(t.state),review:d.time(t.review)})}}Date.prototype.scheduler=function(i,t){return p(this,i,t)},Date.prototype.diff=function(i,t){return b(this,i,t)},Date.prototype.format=function(){return W(this)},Date.prototype.dueFormat=function(i,t,e){return X(this,i,t,e)};function p(i,t,e){return new Date(e?d.time(i).getTime()+t*24*60*60*1e3:d.time(i).getTime()+t*60*1e3)}function b(i,t,e){if(!i||!t)throw new Error("Invalid date");const r=d.time(i).getTime()-d.time(t).getTime();let a=0;switch(e){case"days":a=Math.floor(r/(24*60*60*1e3));break;case"minutes":a=Math.floor(r/(60*1e3));break}return a}function W(i){const t=d.time(i),e=t.getFullYear(),r=t.getMonth()+1,a=t.getDate(),s=t.getHours(),n=t.getMinutes(),u=t.getSeconds();return`${e}-${M(r)}-${M(a)} ${M(s)}:${M(n)}:${M(u)}`}function M(i){return i<10?`0${i}`:`${i}`}const N=[60,60,24,31,12],O=["second","min","hour","day","month","year"];function X(i,t,e,r=O){i=d.time(i),t=d.time(t),r.length!==O.length&&(r=O);let a=i.getTime()-t.getTime(),s;for(a/=1e3,s=0;s<N.length&&!(a<N[s]);s++)a/=N[s];return`${Math.floor(a)}${e?r[s]:""}`}function xt(i){return d.time(i)}function Mt(i){return d.state(i)}function Et(i){return d.rating(i)}const B=Object.freeze([l.Again,l.Hard,l.Good,l.Easy]),Rt=[{start:2.5,end:7,factor:.15},{start:7,end:20,factor:.1},{start:20,end:1/0,factor:.05}];function V(i,t,e){let r=1;for(const n of Rt)r+=n.factor*Math.max(Math.min(i,n.end)-n.start,0);i=Math.min(i,e);let a=Math.max(2,Math.round(i-r));const s=Math.min(Math.round(i+r),e);return i>t&&(a=Math.max(a,t+1)),a=Math.min(a,s),{min_ivl:a,max_ivl:s}}function y(i,t,e){return Math.min(Math.max(i,t),e)}function J(i,t){const e=Date.UTC(i.getUTCFullYear(),i.getUTCMonth(),i.getUTCDate()),r=Date.UTC(t.getUTCFullYear(),t.getUTCMonth(),t.getUTCDate());return Math.floor((r-e)/864e5)}const $t="5.2.0",K=.9,Q=36500,Z=!1,tt=!0,et=Object.freeze(["1m","10m"]),it=Object.freeze(["10m"]),Ft=`v${$t} using FSRS-6.0`,v=.001,At=36500,E=100,P=.5,rt=.1542,j=Object.freeze([.212,1.2931,2.3065,8.2956,6.4133,.8334,3.0194,.001,1.8722,.1666,.796,1.4835,.0614,.2629,1.6483,.6014,1.8729,.5425,.0912,.0658,rt]),at=2,st=i=>[[v,E],[v,E],[v,E],[v,E],[1,10],[.001,4],[.001,4],[.001,.75],[0,4.5],[0,.8],[.001,3.5],[.001,5],[.001,.25],[.001,.9],[0,4],[0,1],[1,6],[0,i],[0,i],[0,.8],[.1,.8]],$=(i,t)=>{let e=at;if(Math.max(0,t)>1){const r=-(Math.log(i[11])+Math.log(Math.pow(2,i[13])-1)+i[14]*.3)/t;e=y(+r.toFixed(8),.01,2)}return st(e).map(([r,a],s)=>y(i[s],r,a))},Dt=i=>{if(i.find(t=>!isFinite(t)&&!isNaN(t))!==void 0)throw Error(`Non-finite or NaN value in parameters ${i}`);if(![17,19,21].includes(i.length))throw Error(`Invalid parameter length: ${i.length}. Must be 17, 19 or 21 for FSRSv4, 5 and 6 respectively.`);return i},F=i=>{if(i===void 0)return[...j];switch(i.length){case 21:return[...i];case 19:return console.debug("[FSRS-6]auto fill w from 19 to 21 length"),[...i,0,P];case 17:{const t=[...i];return t[4]=+(t[5]*2+t[4]).toFixed(8),t[5]=+(Math.log(t[5]*3+1)/3).toFixed(8),t[6]=+(t[6]+.5).toFixed(8),console.debug("[FSRS-6]auto fill w from 17 to 21 length"),t.concat([0,0,0,P])}default:return console.warn("[FSRS]Invalid parameters length, using default parameters"),[...j]}},C=i=>{var t,e;const r=Array.isArray(i==null?void 0:i.learning_steps)?i.learning_steps:et,a=Array.isArray(i==null?void 0:i.relearning_steps)?i.relearning_steps:it,s=$(F(i==null?void 0:i.w),a.length);return{request_retention:(i==null?void 0:i.request_retention)||K,maximum_interval:(i==null?void 0:i.maximum_interval)||Q,w:s,enable_fuzz:(t=i==null?void 0:i.enable_fuzz)!=null?t:Z,enable_short_term:(e=i==null?void 0:i.enable_short_term)!=null?e:tt,learning_steps:r,relearning_steps:a}};function A(i,t){const e={due:i?d.time(i):new Date,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:0,lapses:0,learning_steps:0,state:c.New,last_review:void 0};return t&&typeof t=="function"?t(e):e}var Lt=Object.defineProperty,It=(i,t,e)=>t in i?Lt(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e,D=(i,t,e)=>It(i,typeof t!="symbol"?t+"":t,e);let Nt=class{constructor(t){D(this,"c"),D(this,"s0"),D(this,"s1"),D(this,"s2");const e=Ot();this.c=1,this.s0=e(" "),this.s1=e(" "),this.s2=e(" "),t==null&&(t=+new Date),this.s0-=e(t),this.s0<0&&(this.s0+=1),this.s1-=e(t),this.s1<0&&(this.s1+=1),this.s2-=e(t),this.s2<0&&(this.s2+=1)}next(){const t=2091639*this.s0+this.c*23283064365386963e-26;return this.s0=this.s1,this.s1=this.s2,this.s2=t-(this.c=t|0),this.s2}set state(t){this.c=t.c,this.s0=t.s0,this.s1=t.s1,this.s2=t.s2}get state(){return{c:this.c,s0:this.s0,s1:this.s1,s2:this.s2}}};function Ot(){let i=4022871197;return function(t){t=String(t);for(let e=0;e<t.length;e++){i+=t.charCodeAt(e);let r=.02519603282416938*i;i=r>>>0,r-=i,r*=i,i=r>>>0,r-=i,i+=r*4294967296}return(i>>>0)*23283064365386963e-26}}function Pt(i){const t=new Nt(i),e=()=>t.next();return e.int32=()=>t.next()*4294967296|0,e.double=()=>e()+(e()*2097152|0)*11102230246251565e-32,e.state=()=>t.state,e.importState=r=>(t.state=r,e),e}var jt=Object.defineProperty,Ct=(i,t,e)=>t in i?jt(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e,L=(i,t,e)=>Ct(i,typeof t!="symbol"?t+"":t,e);const H=i=>{const t=typeof i=="number"?-i:-i[20],e=Math.exp(Math.pow(t,-1)*Math.log(.9))-1;return{decay:t,factor:+e.toFixed(8)}};function I(i,t,e){const{decay:r,factor:a}=H(i);return+Math.pow(1+a*t/e,r).toFixed(8)}class nt{constructor(t){L(this,"param"),L(this,"intervalModifier"),L(this,"_seed"),L(this,"forgetting_curve"),this.param=new Proxy(C(t),this.params_handler_proxy()),this.intervalModifier=this.calculate_interval_modifier(this.param.request_retention),this.forgetting_curve=I.bind(this,this.param.w)}get interval_modifier(){return this.intervalModifier}set seed(t){this._seed=t}calculate_interval_modifier(t){if(t<=0||t>1)throw new Error("Requested retention rate should be in the range (0,1]");const{decay:e,factor:r}=H(this.param.w);return+((Math.pow(t,1/e)-1)/r).toFixed(8)}get parameters(){return this.param}set parameters(t){this.update_parameters(t)}params_handler_proxy(){const t=this;return{set:function(e,r,a){return r==="request_retention"&&Number.isFinite(a)?t.intervalModifier=t.calculate_interval_modifier(Number(a)):r==="w"&&(a=$(F(a),e.relearning_steps.length),t.forgetting_curve=I.bind(this,a),t.intervalModifier=t.calculate_interval_modifier(Number(e.request_retention))),Reflect.set(e,r,a),!0}}}update_parameters(t){const e=C(t);for(const r in e)if(r in this.param){const a=r;this.param[a]=e[a]}}init_stability(t){return Math.max(this.param.w[t-1],.1)}init_difficulty(t){return+(this.param.w[4]-Math.exp((t-1)*this.param.w[5])+1).toFixed(8)}apply_fuzz(t,e){if(!this.param.enable_fuzz||t<2.5)return Math.round(t);const r=Pt(this._seed)(),{min_ivl:a,max_ivl:s}=V(t,e,this.param.maximum_interval);return Math.floor(r*(s-a+1)+a)}next_interval(t,e){const r=Math.min(Math.max(1,Math.round(t*this.intervalModifier)),this.param.maximum_interval);return this.apply_fuzz(r,e)}linear_damping(t,e){return+(t*(10-e)/9).toFixed(8)}next_difficulty(t,e){const r=-this.param.w[6]*(e-3),a=t+this.linear_damping(r,t);return y(this.mean_reversion(this.init_difficulty(l.Easy),a),1,10)}mean_reversion(t,e){return+(this.param.w[7]*t+(1-this.param.w[7])*e).toFixed(8)}next_recall_stability(t,e,r,a){const s=l.Hard===a?this.param.w[15]:1,n=l.Easy===a?this.param.w[16]:1;return+y(e*(1+Math.exp(this.param.w[8])*(11-t)*Math.pow(e,-this.param.w[9])*(Math.exp((1-r)*this.param.w[10])-1)*s*n),v,36500).toFixed(8)}next_forget_stability(t,e,r){return+y(this.param.w[11]*Math.pow(t,-this.param.w[12])*(Math.pow(e+1,this.param.w[13])-1)*Math.exp((1-r)*this.param.w[14]),v,36500).toFixed(8)}next_short_term_stability(t,e){const r=Math.pow(t,-this.param.w[19])*Math.exp(this.param.w[17]*(e-3+this.param.w[18])),a=e>=3?Math.max(r,1):r;return+y(t*a,v,36500).toFixed(8)}next_state(t,e,r){const{difficulty:a,stability:s}=t!=null?t:{difficulty:0,stability:0};if(e<0)throw new Error(`Invalid delta_t "${e}"`);if(r<0||r>4)throw new Error(`Invalid grade "${r}"`);if(a===0&&s===0)return{difficulty:y(this.init_difficulty(r),1,10),stability:this.init_stability(r)};if(r===0)return{difficulty:a,stability:s};if(a<1||s<v)throw new Error(`Invalid memory state { difficulty: ${a}, stability: ${s} }`);const n=this.forgetting_curve(e,s),u=this.next_recall_stability(a,s,n,r),h=this.next_forget_stability(a,s,n),f=this.next_short_term_stability(s,r);let _=u;if(r===1){let[m,w]=[0,0];this.param.enable_short_term&&(m=this.param.w[17],w=this.param.w[18]);const g=s/Math.exp(m*w);_=y(+g.toFixed(8),v,h)}return e===0&&this.param.enable_short_term&&(_=f),{difficulty:this.next_difficulty(a,r),stability:_}}}function lt(){const i=this.review_time.getTime(),t=this.current.reps,e=this.current.difficulty*this.current.stability;return`${i}_${t}_${e}`}function Ht(i){return function(){var t;const e=(t=Reflect.get(this.current,i))!=null?t:0,r=this.current.reps;return String(e+r||0)}}const ut=i=>{const t=i.slice(-1),e=parseInt(i.slice(0,-1),10);if(isNaN(e)||!Number.isFinite(e)||e<0)throw new Error(`Invalid step value: ${i}`);switch(t){case"m":return e;case"h":return e*60;case"d":return e*1440;default:throw new Error(`Invalid step unit: ${i}, expected m/h/d`)}},ot=(i,t,e)=>{const r=t===c.Relearning||t===c.Review?i.relearning_steps:i.learning_steps,a=r.length;if(a===0||e>=a)return{};const s=r[0],n=ut,u=()=>n(s),h=()=>{if(a===1)return Math.round(n(s)*1.5);const g=r[1];return Math.round((n(s)+n(g))/2)},f=g=>g<0||g>=a?null:r[g],_=g=>n(g),m={},w=f(Math.max(0,e));if(t===c.Review)return m[l.Again]={scheduled_minutes:n(w),next_step:0},m;{m[l.Again]={scheduled_minutes:u(),next_step:0},m[l.Hard]={scheduled_minutes:h(),next_step:e};const g=f(e+1);if(g){const x=_(g);x&&(m[l.Good]={scheduled_minutes:Math.round(x),next_step:e+1})}}return m};var R=(i=>(i.SCHEDULER="Scheduler",i.LEARNING_STEPS="LearningSteps",i.SEED="Seed",i))(R||{}),Tt=Object.defineProperty,Gt=(i,t,e)=>t in i?Tt(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e,S=(i,t,e)=>Gt(i,typeof t!="symbol"?t+"":t,e);class T{constructor(t,e,r,a){S(this,"last"),S(this,"current"),S(this,"review_time"),S(this,"next",new Map),S(this,"algorithm"),S(this,"strategies"),S(this,"elapsed_days",0),this.algorithm=r,this.last=d.card(t),this.current=d.card(t),this.review_time=d.time(e),this.strategies=a,this.init()}checkGrade(t){if(!Number.isFinite(t)||t<0||t>4)throw new Error(`Invalid grade "${t}",expected 1-4`)}init(){const{state:t,last_review:e}=this.current;let r=0;t!==c.New&&e&&(r=J(e,this.review_time)),this.current.last_review=this.review_time,this.elapsed_days=r,this.current.elapsed_days=r,this.current.reps+=1;let a=lt;if(this.strategies){const s=this.strategies.get(R.SEED);s&&(a=s)}this.algorithm.seed=a.call(this)}preview(){return{[l.Again]:this.review(l.Again),[l.Hard]:this.review(l.Hard),[l.Good]:this.review(l.Good),[l.Easy]:this.review(l.Easy),[Symbol.iterator]:this.previewIterator.bind(this)}}*previewIterator(){for(const t of B)yield this.review(t)}review(t){const{state:e}=this.last;let r;switch(this.checkGrade(t),e){case c.New:r=this.newState(t);break;case c.Learning:case c.Relearning:r=this.learningState(t);break;case c.Review:r=this.reviewState(t);break}return r}buildLog(t){const{last_review:e,due:r,elapsed_days:a}=this.last;return{rating:t,state:this.current.state,due:e||r,stability:this.current.stability,difficulty:this.current.difficulty,elapsed_days:this.elapsed_days,last_elapsed_days:a,scheduled_days:this.current.scheduled_days,learning_steps:this.current.learning_steps,review:this.review_time}}}var zt=Object.defineProperty,Ut=(i,t,e)=>t in i?zt(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e,kt=(i,t,e)=>Ut(i,t+"",e);class dt extends T{constructor(t,e,r,a){super(t,e,r,a),kt(this,"learningStepsStrategy");let s=ot;if(this.strategies){const n=this.strategies.get(R.LEARNING_STEPS);n&&(s=n)}this.learningStepsStrategy=s}getLearningInfo(t,e){var r,a,s,n;const u=this.algorithm.parameters;t.learning_steps=t.learning_steps||0;const h=this.learningStepsStrategy(u,t.state,this.current.state===c.Learning?t.learning_steps+1:t.learning_steps),f=Math.max(0,(a=(r=h[e])==null?void 0:r.scheduled_minutes)!=null?a:0),_=Math.max(0,(n=(s=h[e])==null?void 0:s.next_step)!=null?n:0);return{scheduled_minutes:f,next_steps:_}}applyLearningSteps(t,e,r){const{scheduled_minutes:a,next_steps:s}=this.getLearningInfo(this.current,e);if(a>0&&a<1440)t.learning_steps=s,t.scheduled_days=0,t.state=r,t.due=p(this.review_time,Math.round(a),!1);else if(t.state=c.Review,a>=1440)t.learning_steps=s,t.due=p(this.review_time,Math.round(a),!1),t.scheduled_days=Math.floor(a/1440);else{t.learning_steps=0;const n=this.algorithm.next_interval(t.stability,this.elapsed_days);t.scheduled_days=n,t.due=p(this.review_time,n,!0)}}newState(t){const e=this.next.get(t);if(e)return e;const r=d.card(this.current);r.difficulty=y(this.algorithm.init_difficulty(t),1,10),r.stability=this.algorithm.init_stability(t),this.applyLearningSteps(r,t,c.Learning);const a={card:r,log:this.buildLog(t)};return this.next.set(t,a),a}learningState(t){const e=this.next.get(t);if(e)return e;const{state:r,difficulty:a,stability:s}=this.last,n=d.card(this.current);n.difficulty=this.algorithm.next_difficulty(a,t),n.stability=this.algorithm.next_short_term_stability(s,t),this.applyLearningSteps(n,t,r);const u={card:n,log:this.buildLog(t)};return this.next.set(t,u),u}reviewState(t){const e=this.next.get(t);if(e)return e;const r=this.elapsed_days,{difficulty:a,stability:s}=this.last,n=this.algorithm.forgetting_curve(r,s),u=d.card(this.current),h=d.card(this.current),f=d.card(this.current),_=d.card(this.current);this.next_ds(u,h,f,_,a,s,n),this.next_interval(h,f,_,r),this.next_state(h,f,_),this.applyLearningSteps(u,l.Again,c.Relearning),u.lapses+=1;const m={card:u,log:this.buildLog(l.Again)},w={card:h,log:super.buildLog(l.Hard)},g={card:f,log:super.buildLog(l.Good)},x={card:_,log:super.buildLog(l.Easy)};return this.next.set(l.Again,m),this.next.set(l.Hard,w),this.next.set(l.Good,g),this.next.set(l.Easy,x),this.next.get(t)}next_ds(t,e,r,a,s,n,u){t.difficulty=this.algorithm.next_difficulty(s,l.Again);const h=n/Math.exp(this.algorithm.parameters.w[17]*this.algorithm.parameters.w[18]),f=this.algorithm.next_forget_stability(s,n,u);t.stability=y(+h.toFixed(8),v,f),e.difficulty=this.algorithm.next_difficulty(s,l.Hard),e.stability=this.algorithm.next_recall_stability(s,n,u,l.Hard),r.difficulty=this.algorithm.next_difficulty(s,l.Good),r.stability=this.algorithm.next_recall_stability(s,n,u,l.Good),a.difficulty=this.algorithm.next_difficulty(s,l.Easy),a.stability=this.algorithm.next_recall_stability(s,n,u,l.Easy)}next_interval(t,e,r,a){let s,n;s=this.algorithm.next_interval(t.stability,a),n=this.algorithm.next_interval(e.stability,a),s=Math.min(s,n),n=Math.max(n,s+1);const u=Math.max(this.algorithm.next_interval(r.stability,a),n+1);t.scheduled_days=s,t.due=p(this.review_time,s,!0),e.scheduled_days=n,e.due=p(this.review_time,n,!0),r.scheduled_days=u,r.due=p(this.review_time,u,!0)}next_state(t,e,r){t.state=c.Review,t.learning_steps=0,e.state=c.Review,e.learning_steps=0,r.state=c.Review,r.learning_steps=0}}let ht=class extends T{newState(t){const e=this.next.get(t);if(e)return e;this.current.scheduled_days=0,this.current.elapsed_days=0;const r=d.card(this.current),a=d.card(this.current),s=d.card(this.current),n=d.card(this.current);return this.init_ds(r,a,s,n),this.next_interval(r,a,s,n,0),this.next_state(r,a,s,n),this.update_next(r,a,s,n),this.next.get(t)}init_ds(t,e,r,a){t.difficulty=y(this.algorithm.init_difficulty(l.Again),1,10),t.stability=this.algorithm.init_stability(l.Again),e.difficulty=y(this.algorithm.init_difficulty(l.Hard),1,10),e.stability=this.algorithm.init_stability(l.Hard),r.difficulty=y(this.algorithm.init_difficulty(l.Good),1,10),r.stability=this.algorithm.init_stability(l.Good),a.difficulty=y(this.algorithm.init_difficulty(l.Easy),1,10),a.stability=this.algorithm.init_stability(l.Easy)}learningState(t){return this.reviewState(t)}reviewState(t){const e=this.next.get(t);if(e)return e;const r=this.elapsed_days,{difficulty:a,stability:s}=this.last,n=this.algorithm.forgetting_curve(r,s),u=d.card(this.current),h=d.card(this.current),f=d.card(this.current),_=d.card(this.current);return this.next_ds(u,h,f,_,a,s,n),this.next_interval(u,h,f,_,r),this.next_state(u,h,f,_),u.lapses+=1,this.update_next(u,h,f,_),this.next.get(t)}next_ds(t,e,r,a,s,n,u){t.difficulty=this.algorithm.next_difficulty(s,l.Again);const h=this.algorithm.next_forget_stability(s,n,u);t.stability=y(n,v,h),e.difficulty=this.algorithm.next_difficulty(s,l.Hard),e.stability=this.algorithm.next_recall_stability(s,n,u,l.Hard),r.difficulty=this.algorithm.next_difficulty(s,l.Good),r.stability=this.algorithm.next_recall_stability(s,n,u,l.Good),a.difficulty=this.algorithm.next_difficulty(s,l.Easy),a.stability=this.algorithm.next_recall_stability(s,n,u,l.Easy)}next_interval(t,e,r,a,s){let n,u,h,f;n=this.algorithm.next_interval(t.stability,s),u=this.algorithm.next_interval(e.stability,s),h=this.algorithm.next_interval(r.stability,s),f=this.algorithm.next_interval(a.stability,s),n=Math.min(n,u),u=Math.max(u,n+1),h=Math.max(h,u+1),f=Math.max(f,h+1),t.scheduled_days=n,t.due=p(this.review_time,n,!0),e.scheduled_days=u,e.due=p(this.review_time,u,!0),r.scheduled_days=h,r.due=p(this.review_time,h,!0),a.scheduled_days=f,a.due=p(this.review_time,f,!0)}next_state(t,e,r,a){t.state=c.Review,t.learning_steps=0,e.state=c.Review,e.learning_steps=0,r.state=c.Review,r.learning_steps=0,a.state=c.Review,a.learning_steps=0}update_next(t,e,r,a){const s={card:t,log:this.buildLog(l.Again)},n={card:e,log:super.buildLog(l.Hard)},u={card:r,log:super.buildLog(l.Good)},h={card:a,log:super.buildLog(l.Easy)};this.next.set(l.Again,s),this.next.set(l.Hard,n),this.next.set(l.Good,u),this.next.set(l.Easy,h)}};var qt=Object.defineProperty,Yt=Object.defineProperties,Wt=Object.getOwnPropertyDescriptors,ct=Object.getOwnPropertySymbols,Xt=Object.prototype.hasOwnProperty,Bt=Object.prototype.propertyIsEnumerable,G=(i,t,e)=>t in i?qt(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e,Vt=(i,t)=>{for(var e in t||(t={}))Xt.call(t,e)&&G(i,e,t[e]);if(ct)for(var e of ct(t))Bt.call(t,e)&&G(i,e,t[e]);return i},Jt=(i,t)=>Yt(i,Wt(t)),Kt=(i,t,e)=>G(i,t+"",e);class Qt{constructor(t){Kt(this,"fsrs"),this.fsrs=t}replay(t,e,r){return this.fsrs.next(t,e,r)}handleManualRating(t,e,r,a,s,n,u){if(typeof e=="undefined")throw new Error("reschedule: state is required for manual rating");let h,f;if(e===c.New)h={rating:l.Manual,state:e,due:u!=null?u:r,stability:t.stability,difficulty:t.difficulty,elapsed_days:a,last_elapsed_days:t.elapsed_days,scheduled_days:t.scheduled_days,learning_steps:t.learning_steps,review:r},f=A(r),f.last_review=r;else{if(typeof u=="undefined")throw new Error("reschedule: due is required for manual rating");const _=b(u,r,"days");h={rating:l.Manual,state:t.state,due:t.last_review||t.due,stability:t.stability,difficulty:t.difficulty,elapsed_days:a,last_elapsed_days:t.elapsed_days,scheduled_days:t.scheduled_days,learning_steps:t.learning_steps,review:r},f=Jt(Vt({},t),{state:e,due:u,last_review:r,stability:s||t.stability,difficulty:n||t.difficulty,elapsed_days:a,scheduled_days:_,reps:t.reps+1})}return{card:f,log:h}}reschedule(t,e){const r=[];let a=A(t.due);for(const s of e){let n;if(s.review=d.time(s.review),s.rating===l.Manual){let u=0;a.state!==c.New&&a.last_review&&(u=b(s.review,a.last_review,"days")),n=this.handleManualRating(a,s.state,s.review,u,s.stability,s.difficulty,s.due?d.time(s.due):void 0)}else n=this.replay(a,s.review,s.rating);r.push(n),a=n.card}return r}calculateManualRecord(t,e,r,a){if(!r)return null;const{card:s,log:n}=r,u=d.card(t);return u.due.getTime()===s.due.getTime()?null:(u.scheduled_days=b(s.due,u.due,"days"),this.handleManualRating(u,s.state,d.time(e),n.elapsed_days,a?s.stability:void 0,a?s.difficulty:void 0,s.due))}}var Zt=Object.defineProperty,te=Object.defineProperties,ee=Object.getOwnPropertyDescriptors,ft=Object.getOwnPropertySymbols,ie=Object.prototype.hasOwnProperty,re=Object.prototype.propertyIsEnumerable,z=(i,t,e)=>t in i?Zt(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e,_t=(i,t)=>{for(var e in t||(t={}))ie.call(t,e)&&z(i,e,t[e]);if(ft)for(var e of ft(t))re.call(t,e)&&z(i,e,t[e]);return i},gt=(i,t)=>te(i,ee(t)),yt=(i,t,e)=>z(i,typeof t!="symbol"?t+"":t,e);class mt extends nt{constructor(t){super(t),yt(this,"strategyHandler",new Map),yt(this,"Scheduler");const{enable_short_term:e}=this.parameters;this.Scheduler=e?dt:ht}params_handler_proxy(){const t=this;return{set:function(e,r,a){return r==="request_retention"&&Number.isFinite(a)?t.intervalModifier=t.calculate_interval_modifier(Number(a)):r==="enable_short_term"?t.Scheduler=a===!0?dt:ht:r==="w"&&(a=$(F(a),e.relearning_steps.length),t.forgetting_curve=I.bind(this,a),t.intervalModifier=t.calculate_interval_modifier(Number(e.request_retention))),Reflect.set(e,r,a),!0}}}useStrategy(t,e){return this.strategyHandler.set(t,e),this}clearStrategy(t){return t?this.strategyHandler.delete(t):this.strategyHandler.clear(),this}getScheduler(t,e){const r=this.strategyHandler.get(R.SCHEDULER)||this.Scheduler;return new r(t,e,this,this.strategyHandler)}repeat(t,e,r){const a=this.getScheduler(t,e).preview();return r&&typeof r=="function"?r(a):a}next(t,e,r,a){const s=this.getScheduler(t,e),n=d.rating(r);if(n===l.Manual)throw new Error("Cannot review a manual rating");const u=s.review(n);return a&&typeof a=="function"?a(u):u}get_retrievability(t,e,r=!0){const a=d.card(t);e=e?d.time(e):new Date;const s=a.state!==c.New?Math.max(b(e,a.last_review,"days"),0):0,n=a.state!==c.New?this.forgetting_curve(s,+a.stability.toFixed(8)):0;return r?`${(n*100).toFixed(2)}%`:n}rollback(t,e,r){const a=d.card(t),s=d.review_log(e);if(s.rating===l.Manual)throw new Error("Cannot rollback a manual rating");let n,u,h;switch(s.state){case c.New:n=s.due,u=void 0,h=0;break;case c.Learning:case c.Relearning:case c.Review:n=s.review,u=s.due,h=a.lapses-(s.rating===l.Again&&s.state===c.Review?1:0);break}const f=gt(_t({},a),{due:n,stability:s.stability,difficulty:s.difficulty,elapsed_days:s.last_elapsed_days,scheduled_days:s.scheduled_days,reps:Math.max(0,a.reps-1),lapses:Math.max(0,h),learning_steps:s.learning_steps,state:s.state,last_review:u});return r&&typeof r=="function"?r(f):f}forget(t,e,r=!1,a){const s=d.card(t);e=d.time(e);const n=s.state===c.New?0:b(e,s.due,"days"),u={rating:l.Manual,state:s.state,due:s.due,stability:s.stability,difficulty:s.difficulty,elapsed_days:0,last_elapsed_days:s.elapsed_days,scheduled_days:n,learning_steps:s.learning_steps,review:e},h={card:gt(_t({},s),{due:e,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:r?0:s.reps,lapses:r?0:s.lapses,learning_steps:0,state:c.New,last_review:s.last_review}),log:u};return a&&typeof a=="function"?a(h):h}reschedule(t,e=[],r={}){const{recordLogHandler:a,reviewsOrderBy:s,skipManual:n=!0,now:u=new Date,update_memory_state:h=!1}=r;s&&typeof s=="function"&&e.sort(s),n&&(e=e.filter(x=>x.rating!==l.Manual));const f=new Qt(this),_=f.reschedule(r.first_card||A(),e),m=_.length,w=d.card(t),g=f.calculateManualRecord(w,u,m?_[m-1]:void 0,h);return a&&typeof a=="function"?{collections:_.map(a),reschedule_item:g?a(g):null}:{collections:_,reschedule_item:g}}}const ae=i=>new mt(i||{});o.AbstractScheduler=T,o.BasicLearningStepsStrategy=ot,o.CLAMP_PARAMETERS=st,o.ConvertStepUnitToMinutes=ut,o.DefaultInitSeedStrategy=lt,o.FSRS=mt,o.FSRS5_DEFAULT_DECAY=P,o.FSRS6_DEFAULT_DECAY=rt,o.FSRSAlgorithm=nt,o.FSRSVersion=Ft,o.GenSeedStrategyWithCardId=Ht,o.Grades=B,o.INIT_S_MAX=E,o.Rating=l,o.S_MAX=At,o.S_MIN=v,o.State=c,o.StrategyMode=R,o.TypeConvert=d,o.W17_W18_Ceiling=at,o.checkParameters=Dt,o.clamp=y,o.clipParameters=$,o.computeDecayFactor=H,o.createEmptyCard=A,o.dateDiffInDays=J,o.date_diff=b,o.date_scheduler=p,o.default_enable_fuzz=Z,o.default_enable_short_term=tt,o.default_learning_steps=et,o.default_maximum_interval=Q,o.default_relearning_steps=it,o.default_request_retention=K,o.default_w=j,o.fixDate=xt,o.fixRating=Et,o.fixState=Mt,o.forgetting_curve=I,o.formatDate=W,o.fsrs=ae,o.generatorParameters=C,o.get_fuzz_range=V,o.migrateParameters=F,o.show_diff_message=X});
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.FSRS = {}));
5
+ })(this, (function (exports) { 'use strict';
6
+
7
+ var State = /* @__PURE__ */ ((State2) => {
8
+ State2[State2["New"] = 0] = "New";
9
+ State2[State2["Learning"] = 1] = "Learning";
10
+ State2[State2["Review"] = 2] = "Review";
11
+ State2[State2["Relearning"] = 3] = "Relearning";
12
+ return State2;
13
+ })(State || {});
14
+ var Rating = /* @__PURE__ */ ((Rating2) => {
15
+ Rating2[Rating2["Manual"] = 0] = "Manual";
16
+ Rating2[Rating2["Again"] = 1] = "Again";
17
+ Rating2[Rating2["Hard"] = 2] = "Hard";
18
+ Rating2[Rating2["Good"] = 3] = "Good";
19
+ Rating2[Rating2["Easy"] = 4] = "Easy";
20
+ return Rating2;
21
+ })(Rating || {});
22
+
23
+ var __defProp$6 = Object.defineProperty;
24
+ var __defProps$2 = Object.defineProperties;
25
+ var __getOwnPropDescs$2 = Object.getOwnPropertyDescriptors;
26
+ var __getOwnPropSymbols$2 = Object.getOwnPropertySymbols;
27
+ var __hasOwnProp$2 = Object.prototype.hasOwnProperty;
28
+ var __propIsEnum$2 = Object.prototype.propertyIsEnumerable;
29
+ var __defNormalProp$6 = (obj, key, value) => key in obj ? __defProp$6(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
30
+ var __spreadValues$2 = (a, b) => {
31
+ for (var prop in b || (b = {}))
32
+ if (__hasOwnProp$2.call(b, prop))
33
+ __defNormalProp$6(a, prop, b[prop]);
34
+ if (__getOwnPropSymbols$2)
35
+ for (var prop of __getOwnPropSymbols$2(b)) {
36
+ if (__propIsEnum$2.call(b, prop))
37
+ __defNormalProp$6(a, prop, b[prop]);
38
+ }
39
+ return a;
40
+ };
41
+ var __spreadProps$2 = (a, b) => __defProps$2(a, __getOwnPropDescs$2(b));
42
+ class TypeConvert {
43
+ static card(card) {
44
+ return __spreadProps$2(__spreadValues$2({}, card), {
45
+ state: TypeConvert.state(card.state),
46
+ due: TypeConvert.time(card.due),
47
+ last_review: card.last_review ? TypeConvert.time(card.last_review) : void 0
48
+ });
49
+ }
50
+ static rating(value) {
51
+ if (typeof value === "string") {
52
+ const firstLetter = value.charAt(0).toUpperCase();
53
+ const restOfString = value.slice(1).toLowerCase();
54
+ const ret = Rating[`${firstLetter}${restOfString}`];
55
+ if (ret === void 0) {
56
+ throw new Error(`Invalid rating:[${value}]`);
57
+ }
58
+ return ret;
59
+ } else if (typeof value === "number") {
60
+ return value;
61
+ }
62
+ throw new Error(`Invalid rating:[${value}]`);
63
+ }
64
+ static state(value) {
65
+ if (typeof value === "string") {
66
+ const firstLetter = value.charAt(0).toUpperCase();
67
+ const restOfString = value.slice(1).toLowerCase();
68
+ const ret = State[`${firstLetter}${restOfString}`];
69
+ if (ret === void 0) {
70
+ throw new Error(`Invalid state:[${value}]`);
71
+ }
72
+ return ret;
73
+ } else if (typeof value === "number") {
74
+ return value;
75
+ }
76
+ throw new Error(`Invalid state:[${value}]`);
77
+ }
78
+ static time(value) {
79
+ if (typeof value === "object" && value instanceof Date) {
80
+ return value;
81
+ } else if (typeof value === "string") {
82
+ const timestamp = Date.parse(value);
83
+ if (!Number.isNaN(timestamp)) {
84
+ return new Date(timestamp);
85
+ } else {
86
+ throw new Error(`Invalid date:[${value}]`);
87
+ }
88
+ } else if (typeof value === "number") {
89
+ return new Date(value);
90
+ }
91
+ throw new Error(`Invalid date:[${value}]`);
92
+ }
93
+ static review_log(log) {
94
+ return __spreadProps$2(__spreadValues$2({}, log), {
95
+ due: TypeConvert.time(log.due),
96
+ rating: TypeConvert.rating(log.rating),
97
+ state: TypeConvert.state(log.state),
98
+ review: TypeConvert.time(log.review)
99
+ });
100
+ }
101
+ }
102
+
103
+ Date.prototype.scheduler = function(t, isDay) {
104
+ return date_scheduler(this, t, isDay);
105
+ };
106
+ Date.prototype.diff = function(pre, unit) {
107
+ return date_diff(this, pre, unit);
108
+ };
109
+ Date.prototype.format = function() {
110
+ return formatDate(this);
111
+ };
112
+ Date.prototype.dueFormat = function(last_review, unit, timeUnit) {
113
+ return show_diff_message(this, last_review, unit, timeUnit);
114
+ };
115
+ function date_scheduler(now, t, isDay) {
116
+ return new Date(
117
+ isDay ? TypeConvert.time(now).getTime() + t * 24 * 60 * 60 * 1e3 : TypeConvert.time(now).getTime() + t * 60 * 1e3
118
+ );
119
+ }
120
+ function date_diff(now, pre, unit) {
121
+ if (!now || !pre) {
122
+ throw new Error("Invalid date");
123
+ }
124
+ const diff = TypeConvert.time(now).getTime() - TypeConvert.time(pre).getTime();
125
+ let r = 0;
126
+ switch (unit) {
127
+ case "days":
128
+ r = Math.floor(diff / (24 * 60 * 60 * 1e3));
129
+ break;
130
+ case "minutes":
131
+ r = Math.floor(diff / (60 * 1e3));
132
+ break;
133
+ }
134
+ return r;
135
+ }
136
+ function formatDate(dateInput) {
137
+ const date = TypeConvert.time(dateInput);
138
+ const year = date.getFullYear();
139
+ const month = date.getMonth() + 1;
140
+ const day = date.getDate();
141
+ const hours = date.getHours();
142
+ const minutes = date.getMinutes();
143
+ const seconds = date.getSeconds();
144
+ return `${year}-${padZero(month)}-${padZero(day)} ${padZero(hours)}:${padZero(
145
+ minutes
146
+ )}:${padZero(seconds)}`;
147
+ }
148
+ function padZero(num) {
149
+ return num < 10 ? `0${num}` : `${num}`;
150
+ }
151
+ const TIMEUNIT = [60, 60, 24, 31, 12];
152
+ const TIMEUNITFORMAT = ["second", "min", "hour", "day", "month", "year"];
153
+ function show_diff_message(due, last_review, unit, timeUnit = TIMEUNITFORMAT) {
154
+ due = TypeConvert.time(due);
155
+ last_review = TypeConvert.time(last_review);
156
+ if (timeUnit.length !== TIMEUNITFORMAT.length) {
157
+ timeUnit = TIMEUNITFORMAT;
158
+ }
159
+ let diff = due.getTime() - last_review.getTime();
160
+ let i = 0;
161
+ diff /= 1e3;
162
+ for (i = 0; i < TIMEUNIT.length; i++) {
163
+ if (diff < TIMEUNIT[i]) {
164
+ break;
165
+ } else {
166
+ diff /= TIMEUNIT[i];
167
+ }
168
+ }
169
+ return `${Math.floor(diff)}${unit ? timeUnit[i] : ""}`;
170
+ }
171
+ function fixDate(value) {
172
+ return TypeConvert.time(value);
173
+ }
174
+ function fixState(value) {
175
+ return TypeConvert.state(value);
176
+ }
177
+ function fixRating(value) {
178
+ return TypeConvert.rating(value);
179
+ }
180
+ const Grades = Object.freeze([
181
+ Rating.Again,
182
+ Rating.Hard,
183
+ Rating.Good,
184
+ Rating.Easy
185
+ ]);
186
+ const FUZZ_RANGES = [
187
+ {
188
+ start: 2.5,
189
+ end: 7,
190
+ factor: 0.15
191
+ },
192
+ {
193
+ start: 7,
194
+ end: 20,
195
+ factor: 0.1
196
+ },
197
+ {
198
+ start: 20,
199
+ end: Infinity,
200
+ factor: 0.05
201
+ }
202
+ ];
203
+ function get_fuzz_range(interval, elapsed_days, maximum_interval) {
204
+ let delta = 1;
205
+ for (const range of FUZZ_RANGES) {
206
+ delta += range.factor * Math.max(Math.min(interval, range.end) - range.start, 0);
207
+ }
208
+ interval = Math.min(interval, maximum_interval);
209
+ let min_ivl = Math.max(2, Math.round(interval - delta));
210
+ const max_ivl = Math.min(Math.round(interval + delta), maximum_interval);
211
+ if (interval > elapsed_days) {
212
+ min_ivl = Math.max(min_ivl, elapsed_days + 1);
213
+ }
214
+ min_ivl = Math.min(min_ivl, max_ivl);
215
+ return { min_ivl, max_ivl };
216
+ }
217
+ function clamp(value, min, max) {
218
+ return Math.min(Math.max(value, min), max);
219
+ }
220
+ function dateDiffInDays(last, cur) {
221
+ const utc1 = Date.UTC(
222
+ last.getUTCFullYear(),
223
+ last.getUTCMonth(),
224
+ last.getUTCDate()
225
+ );
226
+ const utc2 = Date.UTC(
227
+ cur.getUTCFullYear(),
228
+ cur.getUTCMonth(),
229
+ cur.getUTCDate()
230
+ );
231
+ return Math.floor(
232
+ (utc2 - utc1) / 864e5
233
+ /** 1000 * 60 * 60 * 24*/
234
+ );
235
+ }
236
+
237
+ const ConvertStepUnitToMinutes = (step) => {
238
+ const unit = step.slice(-1);
239
+ const value = parseInt(step.slice(0, -1), 10);
240
+ if (Number.isNaN(value) || !Number.isFinite(value) || value < 0) {
241
+ throw new Error(`Invalid step value: ${step}`);
242
+ }
243
+ switch (unit) {
244
+ case "m":
245
+ return value;
246
+ case "h":
247
+ return value * 60;
248
+ case "d":
249
+ return value * 1440;
250
+ default:
251
+ throw new Error(`Invalid step unit: ${step}, expected m/h/d`);
252
+ }
253
+ };
254
+ const BasicLearningStepsStrategy = (params, state, cur_step) => {
255
+ const learning_steps = state === State.Relearning || state === State.Review ? params.relearning_steps : params.learning_steps;
256
+ const steps_length = learning_steps.length;
257
+ if (steps_length === 0 || cur_step >= steps_length) return {};
258
+ const firstStep = learning_steps[0];
259
+ const toMinutes = ConvertStepUnitToMinutes;
260
+ const getAgainInterval = () => {
261
+ return toMinutes(firstStep);
262
+ };
263
+ const getHardInterval = () => {
264
+ if (steps_length === 1) return Math.round(toMinutes(firstStep) * 1.5);
265
+ const nextStep = learning_steps[1];
266
+ return Math.round((toMinutes(firstStep) + toMinutes(nextStep)) / 2);
267
+ };
268
+ const getStepInfo = (index) => {
269
+ if (index < 0 || index >= steps_length) {
270
+ return null;
271
+ } else {
272
+ return learning_steps[index];
273
+ }
274
+ };
275
+ const getGoodMinutes = (step) => {
276
+ return toMinutes(step);
277
+ };
278
+ const result = {};
279
+ const step_info = getStepInfo(Math.max(0, cur_step));
280
+ if (state === State.Review) {
281
+ result[Rating.Again] = {
282
+ scheduled_minutes: toMinutes(step_info),
283
+ next_step: 0
284
+ };
285
+ return result;
286
+ } else {
287
+ result[Rating.Again] = {
288
+ scheduled_minutes: getAgainInterval(),
289
+ next_step: 0
290
+ };
291
+ result[Rating.Hard] = {
292
+ scheduled_minutes: getHardInterval(),
293
+ next_step: cur_step
294
+ };
295
+ const next_info = getStepInfo(cur_step + 1);
296
+ if (next_info) {
297
+ const nextMin = getGoodMinutes(next_info);
298
+ if (nextMin) {
299
+ result[Rating.Good] = {
300
+ scheduled_minutes: Math.round(nextMin),
301
+ next_step: cur_step + 1
302
+ };
303
+ }
304
+ }
305
+ }
306
+ return result;
307
+ };
308
+
309
+ function DefaultInitSeedStrategy() {
310
+ const time = this.review_time.getTime();
311
+ const reps = this.current.reps;
312
+ const mul = this.current.difficulty * this.current.stability;
313
+ return `${time}_${reps}_${mul}`;
314
+ }
315
+ function GenSeedStrategyWithCardId(card_id_field) {
316
+ return function() {
317
+ var _a;
318
+ const card_id = (_a = Reflect.get(this.current, card_id_field)) != null ? _a : 0;
319
+ const reps = this.current.reps;
320
+ return String(card_id + reps || 0);
321
+ };
322
+ }
323
+
324
+ var StrategyMode = /* @__PURE__ */ ((StrategyMode2) => {
325
+ StrategyMode2["SCHEDULER"] = "Scheduler";
326
+ StrategyMode2["LEARNING_STEPS"] = "LearningSteps";
327
+ StrategyMode2["SEED"] = "Seed";
328
+ return StrategyMode2;
329
+ })(StrategyMode || {});
330
+
331
+ var __defProp$5 = Object.defineProperty;
332
+ var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
333
+ var __publicField$5 = (obj, key, value) => __defNormalProp$5(obj, typeof key !== "symbol" ? key + "" : key, value);
334
+ class AbstractScheduler {
335
+ // init
336
+ constructor(card, now, algorithm, strategies) {
337
+ __publicField$5(this, "last");
338
+ __publicField$5(this, "current");
339
+ __publicField$5(this, "review_time");
340
+ __publicField$5(this, "next", /* @__PURE__ */ new Map());
341
+ __publicField$5(this, "algorithm");
342
+ __publicField$5(this, "strategies");
343
+ __publicField$5(this, "elapsed_days", 0);
344
+ this.algorithm = algorithm;
345
+ this.last = TypeConvert.card(card);
346
+ this.current = TypeConvert.card(card);
347
+ this.review_time = TypeConvert.time(now);
348
+ this.strategies = strategies;
349
+ this.init();
350
+ }
351
+ checkGrade(grade) {
352
+ if (!Number.isFinite(grade) || grade < 0 || grade > 4) {
353
+ throw new Error(`Invalid grade "${grade}",expected 1-4`);
354
+ }
355
+ }
356
+ init() {
357
+ const { state, last_review } = this.current;
358
+ let interval = 0;
359
+ if (state !== State.New && last_review) {
360
+ interval = dateDiffInDays(last_review, this.review_time);
361
+ }
362
+ this.current.last_review = this.review_time;
363
+ this.elapsed_days = interval;
364
+ this.current.elapsed_days = interval;
365
+ this.current.reps += 1;
366
+ let seed_strategy = DefaultInitSeedStrategy;
367
+ if (this.strategies) {
368
+ const custom_strategy = this.strategies.get(StrategyMode.SEED);
369
+ if (custom_strategy) {
370
+ seed_strategy = custom_strategy;
371
+ }
372
+ }
373
+ this.algorithm.seed = seed_strategy.call(this);
374
+ }
375
+ preview() {
376
+ return {
377
+ [Rating.Again]: this.review(Rating.Again),
378
+ [Rating.Hard]: this.review(Rating.Hard),
379
+ [Rating.Good]: this.review(Rating.Good),
380
+ [Rating.Easy]: this.review(Rating.Easy),
381
+ [Symbol.iterator]: this.previewIterator.bind(this)
382
+ };
383
+ }
384
+ *previewIterator() {
385
+ for (const grade of Grades) {
386
+ yield this.review(grade);
387
+ }
388
+ }
389
+ review(grade) {
390
+ const { state } = this.last;
391
+ let item;
392
+ this.checkGrade(grade);
393
+ switch (state) {
394
+ case State.New:
395
+ item = this.newState(grade);
396
+ break;
397
+ case State.Learning:
398
+ case State.Relearning:
399
+ item = this.learningState(grade);
400
+ break;
401
+ case State.Review:
402
+ item = this.reviewState(grade);
403
+ break;
404
+ }
405
+ return item;
406
+ }
407
+ buildLog(rating) {
408
+ const { last_review, due, elapsed_days } = this.last;
409
+ return {
410
+ rating,
411
+ state: this.current.state,
412
+ due: last_review || due,
413
+ stability: this.current.stability,
414
+ difficulty: this.current.difficulty,
415
+ elapsed_days: this.elapsed_days,
416
+ last_elapsed_days: elapsed_days,
417
+ scheduled_days: this.current.scheduled_days,
418
+ learning_steps: this.current.learning_steps,
419
+ review: this.review_time
420
+ };
421
+ }
422
+ }
423
+
424
+ var __defProp$4 = Object.defineProperty;
425
+ var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
426
+ var __publicField$4 = (obj, key, value) => __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
427
+ class Alea {
428
+ constructor(seed) {
429
+ __publicField$4(this, "c");
430
+ __publicField$4(this, "s0");
431
+ __publicField$4(this, "s1");
432
+ __publicField$4(this, "s2");
433
+ const mash = Mash();
434
+ this.c = 1;
435
+ this.s0 = mash(" ");
436
+ this.s1 = mash(" ");
437
+ this.s2 = mash(" ");
438
+ if (seed == null) seed = Date.now();
439
+ this.s0 -= mash(seed);
440
+ if (this.s0 < 0) this.s0 += 1;
441
+ this.s1 -= mash(seed);
442
+ if (this.s1 < 0) this.s1 += 1;
443
+ this.s2 -= mash(seed);
444
+ if (this.s2 < 0) this.s2 += 1;
445
+ }
446
+ next() {
447
+ const t = 2091639 * this.s0 + this.c * 23283064365386963e-26;
448
+ this.s0 = this.s1;
449
+ this.s1 = this.s2;
450
+ this.c = t | 0;
451
+ this.s2 = t - this.c;
452
+ return this.s2;
453
+ }
454
+ set state(state) {
455
+ this.c = state.c;
456
+ this.s0 = state.s0;
457
+ this.s1 = state.s1;
458
+ this.s2 = state.s2;
459
+ }
460
+ get state() {
461
+ return {
462
+ c: this.c,
463
+ s0: this.s0,
464
+ s1: this.s1,
465
+ s2: this.s2
466
+ };
467
+ }
468
+ }
469
+ function Mash() {
470
+ let n = 4022871197;
471
+ return function mash(data) {
472
+ data = String(data);
473
+ for (let i = 0; i < data.length; i++) {
474
+ n += data.charCodeAt(i);
475
+ let h = 0.02519603282416938 * n;
476
+ n = h >>> 0;
477
+ h -= n;
478
+ h *= n;
479
+ n = h >>> 0;
480
+ h -= n;
481
+ n += h * 4294967296;
482
+ }
483
+ return (n >>> 0) * 23283064365386963e-26;
484
+ };
485
+ }
486
+ function alea(seed) {
487
+ const xg = new Alea(seed);
488
+ const prng = () => xg.next();
489
+ prng.int32 = () => xg.next() * 4294967296 | 0;
490
+ prng.double = () => prng() + (prng() * 2097152 | 0) * 11102230246251565e-32;
491
+ prng.state = () => xg.state;
492
+ prng.importState = (state) => {
493
+ xg.state = state;
494
+ return prng;
495
+ };
496
+ return prng;
497
+ }
498
+
499
+ const version="5.2.2";
500
+
501
+ const default_request_retention = 0.9;
502
+ const default_maximum_interval = 36500;
503
+ const default_enable_fuzz = false;
504
+ const default_enable_short_term = true;
505
+ const default_learning_steps = Object.freeze([
506
+ "1m",
507
+ "10m"
508
+ ]);
509
+ const default_relearning_steps = Object.freeze([
510
+ "10m"
511
+ ]);
512
+ const FSRSVersion = `v${version} using FSRS-6.0`;
513
+ const S_MIN = 1e-3;
514
+ const S_MAX = 36500;
515
+ const INIT_S_MAX = 100;
516
+ const FSRS5_DEFAULT_DECAY = 0.5;
517
+ const FSRS6_DEFAULT_DECAY = 0.1542;
518
+ const default_w = Object.freeze([
519
+ 0.212,
520
+ 1.2931,
521
+ 2.3065,
522
+ 8.2956,
523
+ 6.4133,
524
+ 0.8334,
525
+ 3.0194,
526
+ 1e-3,
527
+ 1.8722,
528
+ 0.1666,
529
+ 0.796,
530
+ 1.4835,
531
+ 0.0614,
532
+ 0.2629,
533
+ 1.6483,
534
+ 0.6014,
535
+ 1.8729,
536
+ 0.5425,
537
+ 0.0912,
538
+ 0.0658,
539
+ FSRS6_DEFAULT_DECAY
540
+ ]);
541
+ const W17_W18_Ceiling = 2;
542
+ const CLAMP_PARAMETERS = (w17_w18_ceiling, enable_short_term = default_enable_short_term) => [
543
+ [S_MIN, INIT_S_MAX],
544
+ [S_MIN, INIT_S_MAX],
545
+ [S_MIN, INIT_S_MAX],
546
+ [S_MIN, INIT_S_MAX],
547
+ [1, 10],
548
+ [1e-3, 4],
549
+ [1e-3, 4],
550
+ [1e-3, 0.75],
551
+ [0, 4.5],
552
+ [0, 0.8],
553
+ [1e-3, 3.5],
554
+ [1e-3, 5],
555
+ [1e-3, 0.25],
556
+ [1e-3, 0.9],
557
+ [0, 4],
558
+ [0, 1],
559
+ [1, 6],
560
+ [0, w17_w18_ceiling],
561
+ [0, w17_w18_ceiling],
562
+ [
563
+ enable_short_term ? 0.01 : 0,
564
+ 0.8
565
+ ],
566
+ [0.1, 0.8]
567
+ ];
568
+
569
+ const clipParameters = (parameters, numRelearningSteps, enableShortTerm = default_enable_short_term) => {
570
+ let w17_w18_ceiling = W17_W18_Ceiling;
571
+ if (Math.max(0, numRelearningSteps) > 1) {
572
+ const value = -(Math.log(parameters[11]) + Math.log(Math.pow(2, parameters[13]) - 1) + parameters[14] * 0.3) / numRelearningSteps;
573
+ w17_w18_ceiling = clamp(+value.toFixed(8), 0.01, 2);
574
+ }
575
+ const clip = CLAMP_PARAMETERS(w17_w18_ceiling, enableShortTerm).slice(
576
+ 0,
577
+ parameters.length
578
+ );
579
+ return clip.map(
580
+ ([min, max], index) => clamp(parameters[index] || 0, min, max)
581
+ );
582
+ };
583
+ const checkParameters = (parameters) => {
584
+ const invalid = parameters.find(
585
+ (param) => !Number.isFinite(param) && !Number.isNaN(param)
586
+ );
587
+ if (invalid !== void 0) {
588
+ throw Error(`Non-finite or NaN value in parameters ${parameters}`);
589
+ } else if (![17, 19, 21].includes(parameters.length)) {
590
+ throw Error(
591
+ `Invalid parameter length: ${parameters.length}. Must be 17, 19 or 21 for FSRSv4, 5 and 6 respectively.`
592
+ );
593
+ }
594
+ return parameters;
595
+ };
596
+ const migrateParameters = (parameters, numRelearningSteps = 0, enableShortTerm = default_enable_short_term) => {
597
+ if (parameters === void 0) {
598
+ return [...default_w];
599
+ }
600
+ switch (parameters.length) {
601
+ case 21:
602
+ return clipParameters(
603
+ Array.from(parameters),
604
+ numRelearningSteps,
605
+ enableShortTerm
606
+ );
607
+ case 19:
608
+ console.debug("[FSRS-6]auto fill w from 19 to 21 length");
609
+ return clipParameters(
610
+ Array.from(parameters),
611
+ numRelearningSteps,
612
+ enableShortTerm
613
+ ).concat([0, FSRS5_DEFAULT_DECAY]);
614
+ case 17: {
615
+ const w = clipParameters(
616
+ Array.from(parameters),
617
+ numRelearningSteps,
618
+ enableShortTerm
619
+ );
620
+ w[4] = +(w[5] * 2 + w[4]).toFixed(8);
621
+ w[5] = +(Math.log(w[5] * 3 + 1) / 3).toFixed(8);
622
+ w[6] = +(w[6] + 0.5).toFixed(8);
623
+ console.debug("[FSRS-6]auto fill w from 17 to 21 length");
624
+ return w.concat([0, 0, 0, FSRS5_DEFAULT_DECAY]);
625
+ }
626
+ default:
627
+ console.warn("[FSRS]Invalid parameters length, using default parameters");
628
+ return [...default_w];
629
+ }
630
+ };
631
+ const generatorParameters = (props) => {
632
+ var _a, _b;
633
+ const learning_steps = Array.isArray(props == null ? void 0 : props.learning_steps) ? props.learning_steps : default_learning_steps;
634
+ const relearning_steps = Array.isArray(props == null ? void 0 : props.relearning_steps) ? props.relearning_steps : default_relearning_steps;
635
+ const enable_short_term = (_a = props == null ? void 0 : props.enable_short_term) != null ? _a : default_enable_short_term;
636
+ const w = migrateParameters(
637
+ props == null ? void 0 : props.w,
638
+ relearning_steps.length,
639
+ enable_short_term
640
+ );
641
+ return {
642
+ request_retention: (props == null ? void 0 : props.request_retention) || default_request_retention,
643
+ maximum_interval: (props == null ? void 0 : props.maximum_interval) || default_maximum_interval,
644
+ w,
645
+ enable_fuzz: (_b = props == null ? void 0 : props.enable_fuzz) != null ? _b : default_enable_fuzz,
646
+ enable_short_term,
647
+ learning_steps,
648
+ relearning_steps
649
+ };
650
+ };
651
+ function createEmptyCard(now, afterHandler) {
652
+ const emptyCard = {
653
+ due: now ? TypeConvert.time(now) : /* @__PURE__ */ new Date(),
654
+ stability: 0,
655
+ difficulty: 0,
656
+ elapsed_days: 0,
657
+ scheduled_days: 0,
658
+ reps: 0,
659
+ lapses: 0,
660
+ learning_steps: 0,
661
+ state: State.New,
662
+ last_review: void 0
663
+ };
664
+ if (afterHandler && typeof afterHandler === "function") {
665
+ return afterHandler(emptyCard);
666
+ } else {
667
+ return emptyCard;
668
+ }
669
+ }
670
+
671
+ var __defProp$3 = Object.defineProperty;
672
+ var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
673
+ var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
674
+ const computeDecayFactor = (decayOrParams) => {
675
+ const decay = typeof decayOrParams === "number" ? -decayOrParams : -decayOrParams[20];
676
+ const factor = Math.exp(Math.pow(decay, -1) * Math.log(0.9)) - 1;
677
+ return { decay, factor: +factor.toFixed(8) };
678
+ };
679
+ function forgetting_curve(decayOrParams, elapsed_days, stability) {
680
+ const { decay, factor } = computeDecayFactor(decayOrParams);
681
+ return +Math.pow(1 + factor * elapsed_days / stability, decay).toFixed(8);
682
+ }
683
+ class FSRSAlgorithm {
684
+ constructor(params) {
685
+ __publicField$3(this, "param");
686
+ __publicField$3(this, "intervalModifier");
687
+ __publicField$3(this, "_seed");
688
+ /**
689
+ * The formula used is :
690
+ * $$R(t,S) = (1 + \text{FACTOR} \times \frac{t}{9 \cdot S})^{\text{DECAY}}$$
691
+ * @param {number} elapsed_days t days since the last review
692
+ * @param {number} stability Stability (interval when R=90%)
693
+ * @return {number} r Retrievability (probability of recall)
694
+ */
695
+ __publicField$3(this, "forgetting_curve");
696
+ this.param = new Proxy(
697
+ generatorParameters(params),
698
+ this.params_handler_proxy()
699
+ );
700
+ this.intervalModifier = this.calculate_interval_modifier(
701
+ this.param.request_retention
702
+ );
703
+ this.forgetting_curve = forgetting_curve.bind(this, this.param.w);
704
+ }
705
+ get interval_modifier() {
706
+ return this.intervalModifier;
707
+ }
708
+ set seed(seed) {
709
+ this._seed = seed;
710
+ }
711
+ /**
712
+ * @see https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-5
713
+ *
714
+ * The formula used is: $$I(r,s) = (r^{\frac{1}{DECAY}} - 1) / FACTOR \times s$$
715
+ * @param request_retention 0<request_retention<=1,Requested retention rate
716
+ * @throws {Error} Requested retention rate should be in the range (0,1]
717
+ */
718
+ calculate_interval_modifier(request_retention) {
719
+ if (request_retention <= 0 || request_retention > 1) {
720
+ throw new Error("Requested retention rate should be in the range (0,1]");
721
+ }
722
+ const { decay, factor } = computeDecayFactor(this.param.w);
723
+ return +((Math.pow(request_retention, 1 / decay) - 1) / factor).toFixed(8);
724
+ }
725
+ /**
726
+ * Get the parameters of the algorithm.
727
+ */
728
+ get parameters() {
729
+ return this.param;
730
+ }
731
+ /**
732
+ * Set the parameters of the algorithm.
733
+ * @param params Partial<FSRSParameters>
734
+ */
735
+ set parameters(params) {
736
+ this.update_parameters(params);
737
+ }
738
+ params_handler_proxy() {
739
+ const _this = this;
740
+ return {
741
+ set: function(target, prop, value) {
742
+ if (prop === "request_retention" && Number.isFinite(value)) {
743
+ _this.intervalModifier = _this.calculate_interval_modifier(
744
+ Number(value)
745
+ );
746
+ } else if (prop === "w") {
747
+ value = migrateParameters(
748
+ value,
749
+ target.relearning_steps.length,
750
+ target.enable_short_term
751
+ );
752
+ _this.forgetting_curve = forgetting_curve.bind(this, value);
753
+ _this.intervalModifier = _this.calculate_interval_modifier(
754
+ Number(target.request_retention)
755
+ );
756
+ }
757
+ Reflect.set(target, prop, value);
758
+ return true;
759
+ }
760
+ };
761
+ }
762
+ update_parameters(params) {
763
+ const _params = generatorParameters(params);
764
+ for (const key in _params) {
765
+ const paramKey = key;
766
+ this.param[paramKey] = _params[paramKey];
767
+ }
768
+ }
769
+ /**
770
+ * The formula used is :
771
+ * $$ S_0(G) = w_{G-1}$$
772
+ * $$S_0 = \max \lbrace S_0,0.1\rbrace $$
773
+
774
+ * @param g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
775
+ * @return Stability (interval when R=90%)
776
+ */
777
+ init_stability(g) {
778
+ return Math.max(this.param.w[g - 1], 0.1);
779
+ }
780
+ /**
781
+ * The formula used is :
782
+ * $$D_0(G) = w_4 - e^{(G-1) \cdot w_5} + 1 $$
783
+ * $$D_0 = \min \lbrace \max \lbrace D_0(G),1 \rbrace,10 \rbrace$$
784
+ * where the $$D_0(1)=w_4$$ when the first rating is good.
785
+ *
786
+ * @param {Grade} g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
787
+ * @return {number} Difficulty $$D \in [1,10]$$
788
+ */
789
+ init_difficulty(g) {
790
+ const d = this.param.w[4] - Math.exp((g - 1) * this.param.w[5]) + 1;
791
+ return +d.toFixed(8);
792
+ }
793
+ /**
794
+ * If fuzzing is disabled or ivl is less than 2.5, it returns the original interval.
795
+ * @param {number} ivl - The interval to be fuzzed.
796
+ * @param {number} elapsed_days t days since the last review
797
+ * @return {number} - The fuzzed interval.
798
+ **/
799
+ apply_fuzz(ivl, elapsed_days) {
800
+ if (!this.param.enable_fuzz || ivl < 2.5) return Math.round(ivl);
801
+ const generator = alea(this._seed);
802
+ const fuzz_factor = generator();
803
+ const { min_ivl, max_ivl } = get_fuzz_range(
804
+ ivl,
805
+ elapsed_days,
806
+ this.param.maximum_interval
807
+ );
808
+ return Math.floor(fuzz_factor * (max_ivl - min_ivl + 1) + min_ivl);
809
+ }
810
+ /**
811
+ * @see The formula used is : {@link FSRSAlgorithm.calculate_interval_modifier}
812
+ * @param {number} s - Stability (interval when R=90%)
813
+ * @param {number} elapsed_days t days since the last review
814
+ */
815
+ next_interval(s, elapsed_days) {
816
+ const newInterval = Math.min(
817
+ Math.max(1, Math.round(s * this.intervalModifier)),
818
+ this.param.maximum_interval
819
+ );
820
+ return this.apply_fuzz(newInterval, elapsed_days);
821
+ }
822
+ /**
823
+ * @see https://github.com/open-spaced-repetition/fsrs4anki/issues/697
824
+ */
825
+ linear_damping(delta_d, old_d) {
826
+ return +(delta_d * (10 - old_d) / 9).toFixed(8);
827
+ }
828
+ /**
829
+ * The formula used is :
830
+ * $$\text{delta}_d = -w_6 \cdot (g - 3)$$
831
+ * $$\text{next}_d = D + \text{linear damping}(\text{delta}_d , D)$$
832
+ * $$D^\prime(D,R) = w_7 \cdot D_0(4) +(1 - w_7) \cdot \text{next}_d$$
833
+ * @param {number} d Difficulty $$D \in [1,10]$$
834
+ * @param {Grade} g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
835
+ * @return {number} $$\text{next}_D$$
836
+ */
837
+ next_difficulty(d, g) {
838
+ const delta_d = -this.param.w[6] * (g - 3);
839
+ const next_d = d + this.linear_damping(delta_d, d);
840
+ return clamp(
841
+ this.mean_reversion(this.init_difficulty(Rating.Easy), next_d),
842
+ 1,
843
+ 10
844
+ );
845
+ }
846
+ /**
847
+ * The formula used is :
848
+ * $$w_7 \cdot \text{init} +(1 - w_7) \cdot \text{current}$$
849
+ * @param {number} init $$w_2 : D_0(3) = w_2 + (R-2) \cdot w_3= w_2$$
850
+ * @param {number} current $$D - w_6 \cdot (R - 2)$$
851
+ * @return {number} difficulty
852
+ */
853
+ mean_reversion(init, current) {
854
+ return +(this.param.w[7] * init + (1 - this.param.w[7]) * current).toFixed(
855
+ 8
856
+ );
857
+ }
858
+ /**
859
+ * The formula used is :
860
+ * $$S^\prime_r(D,S,R,G) = S\cdot(e^{w_8}\cdot (11-D)\cdot S^{-w_9}\cdot(e^{w_{10}\cdot(1-R)}-1)\cdot w_{15}(\text{if} G=2) \cdot w_{16}(\text{if} G=4)+1)$$
861
+ * @param {number} d Difficulty D \in [1,10]
862
+ * @param {number} s Stability (interval when R=90%)
863
+ * @param {number} r Retrievability (probability of recall)
864
+ * @param {Grade} g Grade (Rating[0.again,1.hard,2.good,3.easy])
865
+ * @return {number} S^\prime_r new stability after recall
866
+ */
867
+ next_recall_stability(d, s, r, g) {
868
+ const hard_penalty = Rating.Hard === g ? this.param.w[15] : 1;
869
+ const easy_bound = Rating.Easy === g ? this.param.w[16] : 1;
870
+ return +clamp(
871
+ s * (1 + Math.exp(this.param.w[8]) * (11 - d) * Math.pow(s, -this.param.w[9]) * (Math.exp((1 - r) * this.param.w[10]) - 1) * hard_penalty * easy_bound),
872
+ S_MIN,
873
+ 36500
874
+ ).toFixed(8);
875
+ }
876
+ /**
877
+ * The formula used is :
878
+ * $$S^\prime_f(D,S,R) = w_{11}\cdot D^{-w_{12}}\cdot ((S+1)^{w_{13}}-1) \cdot e^{w_{14}\cdot(1-R)}$$
879
+ * enable_short_term = true : $$S^\prime_f \in \min \lbrace \max \lbrace S^\prime_f,0.01\rbrace, \frac{S}{e^{w_{17} \cdot w_{18}}} \rbrace$$
880
+ * enable_short_term = false : $$S^\prime_f \in \min \lbrace \max \lbrace S^\prime_f,0.01\rbrace, S \rbrace$$
881
+ * @param {number} d Difficulty D \in [1,10]
882
+ * @param {number} s Stability (interval when R=90%)
883
+ * @param {number} r Retrievability (probability of recall)
884
+ * @return {number} S^\prime_f new stability after forgetting
885
+ */
886
+ next_forget_stability(d, s, r) {
887
+ return +clamp(
888
+ this.param.w[11] * Math.pow(d, -this.param.w[12]) * (Math.pow(s + 1, this.param.w[13]) - 1) * Math.exp((1 - r) * this.param.w[14]),
889
+ S_MIN,
890
+ 36500
891
+ ).toFixed(8);
892
+ }
893
+ /**
894
+ * The formula used is :
895
+ * $$S^\prime_s(S,G) = S \cdot e^{w_{17} \cdot (G-3+w_{18})}$$
896
+ * @param {number} s Stability (interval when R=90%)
897
+ * @param {Grade} g Grade (Rating[0.again,1.hard,2.good,3.easy])
898
+ */
899
+ next_short_term_stability(s, g) {
900
+ const sinc = Math.pow(s, -this.param.w[19]) * Math.exp(this.param.w[17] * (g - 3 + this.param.w[18]));
901
+ const maskedSinc = g >= 3 ? Math.max(sinc, 1) : sinc;
902
+ return +clamp(s * maskedSinc, S_MIN, 36500).toFixed(8);
903
+ }
904
+ /**
905
+ * Calculates the next state of memory based on the current state, time elapsed, and grade.
906
+ *
907
+ * @param memory_state - The current state of memory, which can be null.
908
+ * @param t - The time elapsed since the last review.
909
+ * @param {Rating} g Grade (Rating[0.Manual,1.Again,2.Hard,3.Good,4.Easy])
910
+ * @returns The next state of memory with updated difficulty and stability.
911
+ */
912
+ next_state(memory_state, t, g) {
913
+ const { difficulty: d, stability: s } = memory_state != null ? memory_state : {
914
+ difficulty: 0,
915
+ stability: 0
916
+ };
917
+ if (t < 0) {
918
+ throw new Error(`Invalid delta_t "${t}"`);
919
+ }
920
+ if (g < 0 || g > 4) {
921
+ throw new Error(`Invalid grade "${g}"`);
922
+ }
923
+ if (d === 0 && s === 0) {
924
+ return {
925
+ difficulty: clamp(this.init_difficulty(g), 1, 10),
926
+ stability: this.init_stability(g)
927
+ };
928
+ }
929
+ if (g === 0) {
930
+ return {
931
+ difficulty: d,
932
+ stability: s
933
+ };
934
+ }
935
+ if (d < 1 || s < S_MIN) {
936
+ throw new Error(
937
+ `Invalid memory state { difficulty: ${d}, stability: ${s} }`
938
+ );
939
+ }
940
+ const r = this.forgetting_curve(t, s);
941
+ const s_after_success = this.next_recall_stability(d, s, r, g);
942
+ const s_after_fail = this.next_forget_stability(d, s, r);
943
+ const s_after_short_term = this.next_short_term_stability(s, g);
944
+ let new_s = s_after_success;
945
+ if (g === 1) {
946
+ let [w_17, w_18] = [0, 0];
947
+ if (this.param.enable_short_term) {
948
+ w_17 = this.param.w[17];
949
+ w_18 = this.param.w[18];
950
+ }
951
+ const next_s_min = s / Math.exp(w_17 * w_18);
952
+ new_s = clamp(+next_s_min.toFixed(8), S_MIN, s_after_fail);
953
+ }
954
+ if (t === 0 && this.param.enable_short_term) {
955
+ new_s = s_after_short_term;
956
+ }
957
+ const new_d = this.next_difficulty(d, g);
958
+ return { difficulty: new_d, stability: new_s };
959
+ }
960
+ }
961
+
962
+ var __defProp$2 = Object.defineProperty;
963
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
964
+ var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, key + "" , value);
965
+ class BasicScheduler extends AbstractScheduler {
966
+ constructor(card, now, algorithm, strategies) {
967
+ super(card, now, algorithm, strategies);
968
+ __publicField$2(this, "learningStepsStrategy");
969
+ let learningStepStrategy = BasicLearningStepsStrategy;
970
+ if (this.strategies) {
971
+ const custom_strategy = this.strategies.get(StrategyMode.LEARNING_STEPS);
972
+ if (custom_strategy) {
973
+ learningStepStrategy = custom_strategy;
974
+ }
975
+ }
976
+ this.learningStepsStrategy = learningStepStrategy;
977
+ }
978
+ getLearningInfo(card, grade) {
979
+ var _a, _b, _c, _d;
980
+ const parameters = this.algorithm.parameters;
981
+ card.learning_steps = card.learning_steps || 0;
982
+ const steps_strategy = this.learningStepsStrategy(
983
+ parameters,
984
+ card.state,
985
+ // In the original learning steps setup (Again = 5m, Hard = 10m, Good = FSRS),
986
+ // not adding 1 can cause slight variations in the memory state’s ds.
987
+ this.current.state === State.Learning && grade !== Rating.Again && grade !== Rating.Hard ? card.learning_steps + 1 : card.learning_steps
988
+ );
989
+ const scheduled_minutes = Math.max(
990
+ 0,
991
+ (_b = (_a = steps_strategy[grade]) == null ? void 0 : _a.scheduled_minutes) != null ? _b : 0
992
+ );
993
+ const next_steps = Math.max(0, (_d = (_c = steps_strategy[grade]) == null ? void 0 : _c.next_step) != null ? _d : 0);
994
+ return {
995
+ scheduled_minutes,
996
+ next_steps
997
+ };
998
+ }
999
+ /**
1000
+ * @description This function applies the learning steps based on the current card's state and grade.
1001
+ */
1002
+ applyLearningSteps(nextCard, grade, to_state) {
1003
+ const { scheduled_minutes, next_steps } = this.getLearningInfo(
1004
+ this.current,
1005
+ grade
1006
+ );
1007
+ if (scheduled_minutes > 0 && scheduled_minutes < 1440) {
1008
+ nextCard.learning_steps = next_steps;
1009
+ nextCard.scheduled_days = 0;
1010
+ nextCard.state = to_state;
1011
+ nextCard.due = date_scheduler(
1012
+ this.review_time,
1013
+ Math.round(scheduled_minutes),
1014
+ false
1015
+ /** true:days false: minute */
1016
+ );
1017
+ } else {
1018
+ nextCard.state = State.Review;
1019
+ if (scheduled_minutes >= 1440) {
1020
+ nextCard.learning_steps = next_steps;
1021
+ nextCard.due = date_scheduler(
1022
+ this.review_time,
1023
+ Math.round(scheduled_minutes),
1024
+ false
1025
+ /** true:days false: minute */
1026
+ );
1027
+ nextCard.scheduled_days = Math.floor(scheduled_minutes / 1440);
1028
+ } else {
1029
+ nextCard.learning_steps = 0;
1030
+ const interval = this.algorithm.next_interval(
1031
+ nextCard.stability,
1032
+ this.elapsed_days
1033
+ );
1034
+ nextCard.scheduled_days = interval;
1035
+ nextCard.due = date_scheduler(this.review_time, interval, true);
1036
+ }
1037
+ }
1038
+ }
1039
+ newState(grade) {
1040
+ const exist = this.next.get(grade);
1041
+ if (exist) {
1042
+ return exist;
1043
+ }
1044
+ const next = TypeConvert.card(this.current);
1045
+ next.difficulty = clamp(this.algorithm.init_difficulty(grade), 1, 10);
1046
+ next.stability = this.algorithm.init_stability(grade);
1047
+ this.applyLearningSteps(next, grade, State.Learning);
1048
+ const item = {
1049
+ card: next,
1050
+ log: this.buildLog(grade)
1051
+ };
1052
+ this.next.set(grade, item);
1053
+ return item;
1054
+ }
1055
+ learningState(grade) {
1056
+ const exist = this.next.get(grade);
1057
+ if (exist) {
1058
+ return exist;
1059
+ }
1060
+ const { state, difficulty, stability } = this.last;
1061
+ const next = TypeConvert.card(this.current);
1062
+ next.difficulty = this.algorithm.next_difficulty(difficulty, grade);
1063
+ next.stability = this.algorithm.next_short_term_stability(stability, grade);
1064
+ this.applyLearningSteps(
1065
+ next,
1066
+ grade,
1067
+ state
1068
+ /** Learning or Relearning */
1069
+ );
1070
+ const item = {
1071
+ card: next,
1072
+ log: this.buildLog(grade)
1073
+ };
1074
+ this.next.set(grade, item);
1075
+ return item;
1076
+ }
1077
+ reviewState(grade) {
1078
+ const exist = this.next.get(grade);
1079
+ if (exist) {
1080
+ return exist;
1081
+ }
1082
+ const interval = this.elapsed_days;
1083
+ const { difficulty, stability } = this.last;
1084
+ const retrievability = this.algorithm.forgetting_curve(interval, stability);
1085
+ const next_again = TypeConvert.card(this.current);
1086
+ const next_hard = TypeConvert.card(this.current);
1087
+ const next_good = TypeConvert.card(this.current);
1088
+ const next_easy = TypeConvert.card(this.current);
1089
+ this.next_ds(
1090
+ next_again,
1091
+ next_hard,
1092
+ next_good,
1093
+ next_easy,
1094
+ difficulty,
1095
+ stability,
1096
+ retrievability
1097
+ );
1098
+ this.next_interval(next_hard, next_good, next_easy, interval);
1099
+ this.next_state(next_hard, next_good, next_easy);
1100
+ this.applyLearningSteps(next_again, Rating.Again, State.Relearning);
1101
+ next_again.lapses += 1;
1102
+ const item_again = {
1103
+ card: next_again,
1104
+ log: this.buildLog(Rating.Again)
1105
+ };
1106
+ const item_hard = {
1107
+ card: next_hard,
1108
+ log: super.buildLog(Rating.Hard)
1109
+ };
1110
+ const item_good = {
1111
+ card: next_good,
1112
+ log: super.buildLog(Rating.Good)
1113
+ };
1114
+ const item_easy = {
1115
+ card: next_easy,
1116
+ log: super.buildLog(Rating.Easy)
1117
+ };
1118
+ this.next.set(Rating.Again, item_again);
1119
+ this.next.set(Rating.Hard, item_hard);
1120
+ this.next.set(Rating.Good, item_good);
1121
+ this.next.set(Rating.Easy, item_easy);
1122
+ return this.next.get(grade);
1123
+ }
1124
+ /**
1125
+ * Review next_ds
1126
+ */
1127
+ next_ds(next_again, next_hard, next_good, next_easy, difficulty, stability, retrievability) {
1128
+ next_again.difficulty = this.algorithm.next_difficulty(
1129
+ difficulty,
1130
+ Rating.Again
1131
+ );
1132
+ const nextSMin = stability / Math.exp(
1133
+ this.algorithm.parameters.w[17] * this.algorithm.parameters.w[18]
1134
+ );
1135
+ const s_after_fail = this.algorithm.next_forget_stability(
1136
+ difficulty,
1137
+ stability,
1138
+ retrievability
1139
+ );
1140
+ next_again.stability = clamp(+nextSMin.toFixed(8), S_MIN, s_after_fail);
1141
+ next_hard.difficulty = this.algorithm.next_difficulty(
1142
+ difficulty,
1143
+ Rating.Hard
1144
+ );
1145
+ next_hard.stability = this.algorithm.next_recall_stability(
1146
+ difficulty,
1147
+ stability,
1148
+ retrievability,
1149
+ Rating.Hard
1150
+ );
1151
+ next_good.difficulty = this.algorithm.next_difficulty(
1152
+ difficulty,
1153
+ Rating.Good
1154
+ );
1155
+ next_good.stability = this.algorithm.next_recall_stability(
1156
+ difficulty,
1157
+ stability,
1158
+ retrievability,
1159
+ Rating.Good
1160
+ );
1161
+ next_easy.difficulty = this.algorithm.next_difficulty(
1162
+ difficulty,
1163
+ Rating.Easy
1164
+ );
1165
+ next_easy.stability = this.algorithm.next_recall_stability(
1166
+ difficulty,
1167
+ stability,
1168
+ retrievability,
1169
+ Rating.Easy
1170
+ );
1171
+ }
1172
+ /**
1173
+ * Review next_interval
1174
+ */
1175
+ next_interval(next_hard, next_good, next_easy, interval) {
1176
+ let hard_interval, good_interval;
1177
+ hard_interval = this.algorithm.next_interval(next_hard.stability, interval);
1178
+ good_interval = this.algorithm.next_interval(next_good.stability, interval);
1179
+ hard_interval = Math.min(hard_interval, good_interval);
1180
+ good_interval = Math.max(good_interval, hard_interval + 1);
1181
+ const easy_interval = Math.max(
1182
+ this.algorithm.next_interval(next_easy.stability, interval),
1183
+ good_interval + 1
1184
+ );
1185
+ next_hard.scheduled_days = hard_interval;
1186
+ next_hard.due = date_scheduler(this.review_time, hard_interval, true);
1187
+ next_good.scheduled_days = good_interval;
1188
+ next_good.due = date_scheduler(this.review_time, good_interval, true);
1189
+ next_easy.scheduled_days = easy_interval;
1190
+ next_easy.due = date_scheduler(this.review_time, easy_interval, true);
1191
+ }
1192
+ /**
1193
+ * Review next_state
1194
+ */
1195
+ next_state(next_hard, next_good, next_easy) {
1196
+ next_hard.state = State.Review;
1197
+ next_hard.learning_steps = 0;
1198
+ next_good.state = State.Review;
1199
+ next_good.learning_steps = 0;
1200
+ next_easy.state = State.Review;
1201
+ next_easy.learning_steps = 0;
1202
+ }
1203
+ }
1204
+
1205
+ class LongTermScheduler extends AbstractScheduler {
1206
+ newState(grade) {
1207
+ const exist = this.next.get(grade);
1208
+ if (exist) {
1209
+ return exist;
1210
+ }
1211
+ this.current.scheduled_days = 0;
1212
+ this.current.elapsed_days = 0;
1213
+ const next_again = TypeConvert.card(this.current);
1214
+ const next_hard = TypeConvert.card(this.current);
1215
+ const next_good = TypeConvert.card(this.current);
1216
+ const next_easy = TypeConvert.card(this.current);
1217
+ this.init_ds(next_again, next_hard, next_good, next_easy);
1218
+ const first_interval = 0;
1219
+ this.next_interval(
1220
+ next_again,
1221
+ next_hard,
1222
+ next_good,
1223
+ next_easy,
1224
+ first_interval
1225
+ );
1226
+ this.next_state(next_again, next_hard, next_good, next_easy);
1227
+ this.update_next(next_again, next_hard, next_good, next_easy);
1228
+ return this.next.get(grade);
1229
+ }
1230
+ init_ds(next_again, next_hard, next_good, next_easy) {
1231
+ next_again.difficulty = clamp(
1232
+ this.algorithm.init_difficulty(Rating.Again),
1233
+ 1,
1234
+ 10
1235
+ );
1236
+ next_again.stability = this.algorithm.init_stability(Rating.Again);
1237
+ next_hard.difficulty = clamp(
1238
+ this.algorithm.init_difficulty(Rating.Hard),
1239
+ 1,
1240
+ 10
1241
+ );
1242
+ next_hard.stability = this.algorithm.init_stability(Rating.Hard);
1243
+ next_good.difficulty = clamp(
1244
+ this.algorithm.init_difficulty(Rating.Good),
1245
+ 1,
1246
+ 10
1247
+ );
1248
+ next_good.stability = this.algorithm.init_stability(Rating.Good);
1249
+ next_easy.difficulty = clamp(
1250
+ this.algorithm.init_difficulty(Rating.Easy),
1251
+ 1,
1252
+ 10
1253
+ );
1254
+ next_easy.stability = this.algorithm.init_stability(Rating.Easy);
1255
+ }
1256
+ /**
1257
+ * @see https://github.com/open-spaced-repetition/ts-fsrs/issues/98#issuecomment-2241923194
1258
+ */
1259
+ learningState(grade) {
1260
+ return this.reviewState(grade);
1261
+ }
1262
+ reviewState(grade) {
1263
+ const exist = this.next.get(grade);
1264
+ if (exist) {
1265
+ return exist;
1266
+ }
1267
+ const interval = this.elapsed_days;
1268
+ const { difficulty, stability } = this.last;
1269
+ const retrievability = this.algorithm.forgetting_curve(interval, stability);
1270
+ const next_again = TypeConvert.card(this.current);
1271
+ const next_hard = TypeConvert.card(this.current);
1272
+ const next_good = TypeConvert.card(this.current);
1273
+ const next_easy = TypeConvert.card(this.current);
1274
+ this.next_ds(
1275
+ next_again,
1276
+ next_hard,
1277
+ next_good,
1278
+ next_easy,
1279
+ difficulty,
1280
+ stability,
1281
+ retrievability
1282
+ );
1283
+ this.next_interval(next_again, next_hard, next_good, next_easy, interval);
1284
+ this.next_state(next_again, next_hard, next_good, next_easy);
1285
+ next_again.lapses += 1;
1286
+ this.update_next(next_again, next_hard, next_good, next_easy);
1287
+ return this.next.get(grade);
1288
+ }
1289
+ /**
1290
+ * Review next_ds
1291
+ */
1292
+ next_ds(next_again, next_hard, next_good, next_easy, difficulty, stability, retrievability) {
1293
+ next_again.difficulty = this.algorithm.next_difficulty(
1294
+ difficulty,
1295
+ Rating.Again
1296
+ );
1297
+ const s_after_fail = this.algorithm.next_forget_stability(
1298
+ difficulty,
1299
+ stability,
1300
+ retrievability
1301
+ );
1302
+ next_again.stability = clamp(stability, S_MIN, s_after_fail);
1303
+ next_hard.difficulty = this.algorithm.next_difficulty(
1304
+ difficulty,
1305
+ Rating.Hard
1306
+ );
1307
+ next_hard.stability = this.algorithm.next_recall_stability(
1308
+ difficulty,
1309
+ stability,
1310
+ retrievability,
1311
+ Rating.Hard
1312
+ );
1313
+ next_good.difficulty = this.algorithm.next_difficulty(
1314
+ difficulty,
1315
+ Rating.Good
1316
+ );
1317
+ next_good.stability = this.algorithm.next_recall_stability(
1318
+ difficulty,
1319
+ stability,
1320
+ retrievability,
1321
+ Rating.Good
1322
+ );
1323
+ next_easy.difficulty = this.algorithm.next_difficulty(
1324
+ difficulty,
1325
+ Rating.Easy
1326
+ );
1327
+ next_easy.stability = this.algorithm.next_recall_stability(
1328
+ difficulty,
1329
+ stability,
1330
+ retrievability,
1331
+ Rating.Easy
1332
+ );
1333
+ }
1334
+ /**
1335
+ * Review/New next_interval
1336
+ */
1337
+ next_interval(next_again, next_hard, next_good, next_easy, interval) {
1338
+ let again_interval, hard_interval, good_interval, easy_interval;
1339
+ again_interval = this.algorithm.next_interval(
1340
+ next_again.stability,
1341
+ interval
1342
+ );
1343
+ hard_interval = this.algorithm.next_interval(next_hard.stability, interval);
1344
+ good_interval = this.algorithm.next_interval(next_good.stability, interval);
1345
+ easy_interval = this.algorithm.next_interval(next_easy.stability, interval);
1346
+ again_interval = Math.min(again_interval, hard_interval);
1347
+ hard_interval = Math.max(hard_interval, again_interval + 1);
1348
+ good_interval = Math.max(good_interval, hard_interval + 1);
1349
+ easy_interval = Math.max(easy_interval, good_interval + 1);
1350
+ next_again.scheduled_days = again_interval;
1351
+ next_again.due = date_scheduler(this.review_time, again_interval, true);
1352
+ next_hard.scheduled_days = hard_interval;
1353
+ next_hard.due = date_scheduler(this.review_time, hard_interval, true);
1354
+ next_good.scheduled_days = good_interval;
1355
+ next_good.due = date_scheduler(this.review_time, good_interval, true);
1356
+ next_easy.scheduled_days = easy_interval;
1357
+ next_easy.due = date_scheduler(this.review_time, easy_interval, true);
1358
+ }
1359
+ /**
1360
+ * Review/New next_state
1361
+ */
1362
+ next_state(next_again, next_hard, next_good, next_easy) {
1363
+ next_again.state = State.Review;
1364
+ next_again.learning_steps = 0;
1365
+ next_hard.state = State.Review;
1366
+ next_hard.learning_steps = 0;
1367
+ next_good.state = State.Review;
1368
+ next_good.learning_steps = 0;
1369
+ next_easy.state = State.Review;
1370
+ next_easy.learning_steps = 0;
1371
+ }
1372
+ update_next(next_again, next_hard, next_good, next_easy) {
1373
+ const item_again = {
1374
+ card: next_again,
1375
+ log: this.buildLog(Rating.Again)
1376
+ };
1377
+ const item_hard = {
1378
+ card: next_hard,
1379
+ log: super.buildLog(Rating.Hard)
1380
+ };
1381
+ const item_good = {
1382
+ card: next_good,
1383
+ log: super.buildLog(Rating.Good)
1384
+ };
1385
+ const item_easy = {
1386
+ card: next_easy,
1387
+ log: super.buildLog(Rating.Easy)
1388
+ };
1389
+ this.next.set(Rating.Again, item_again);
1390
+ this.next.set(Rating.Hard, item_hard);
1391
+ this.next.set(Rating.Good, item_good);
1392
+ this.next.set(Rating.Easy, item_easy);
1393
+ }
1394
+ }
1395
+
1396
+ var __defProp$1 = Object.defineProperty;
1397
+ var __defProps$1 = Object.defineProperties;
1398
+ var __getOwnPropDescs$1 = Object.getOwnPropertyDescriptors;
1399
+ var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols;
1400
+ var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
1401
+ var __propIsEnum$1 = Object.prototype.propertyIsEnumerable;
1402
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1403
+ var __spreadValues$1 = (a, b) => {
1404
+ for (var prop in b || (b = {}))
1405
+ if (__hasOwnProp$1.call(b, prop))
1406
+ __defNormalProp$1(a, prop, b[prop]);
1407
+ if (__getOwnPropSymbols$1)
1408
+ for (var prop of __getOwnPropSymbols$1(b)) {
1409
+ if (__propIsEnum$1.call(b, prop))
1410
+ __defNormalProp$1(a, prop, b[prop]);
1411
+ }
1412
+ return a;
1413
+ };
1414
+ var __spreadProps$1 = (a, b) => __defProps$1(a, __getOwnPropDescs$1(b));
1415
+ var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, key + "" , value);
1416
+ class Reschedule {
1417
+ /**
1418
+ * Creates an instance of the `Reschedule` class.
1419
+ * @param fsrs - An instance of the FSRS class used for scheduling.
1420
+ */
1421
+ constructor(fsrs) {
1422
+ __publicField$1(this, "fsrs");
1423
+ this.fsrs = fsrs;
1424
+ }
1425
+ /**
1426
+ * Replays a review for a card and determines the next review date based on the given rating.
1427
+ * @param card - The card being reviewed.
1428
+ * @param reviewed - The date the card was reviewed.
1429
+ * @param rating - The grade given to the card during the review.
1430
+ * @returns A `RecordLogItem` containing the updated card and review log.
1431
+ */
1432
+ replay(card, reviewed, rating) {
1433
+ return this.fsrs.next(card, reviewed, rating);
1434
+ }
1435
+ /**
1436
+ * Processes a manual review for a card, allowing for custom state, stability, difficulty, and due date.
1437
+ * @param card - The card being reviewed.
1438
+ * @param state - The state of the card after the review.
1439
+ * @param reviewed - The date the card was reviewed.
1440
+ * @param elapsed_days - The number of days since the last review.
1441
+ * @param stability - (Optional) The stability of the card.
1442
+ * @param difficulty - (Optional) The difficulty of the card.
1443
+ * @param due - (Optional) The due date for the next review.
1444
+ * @returns A `RecordLogItem` containing the updated card and review log.
1445
+ * @throws Will throw an error if the state or due date is not provided when required.
1446
+ */
1447
+ handleManualRating(card, state, reviewed, elapsed_days, stability, difficulty, due) {
1448
+ if (typeof state === "undefined") {
1449
+ throw new Error("reschedule: state is required for manual rating");
1450
+ }
1451
+ let log;
1452
+ let next_card;
1453
+ if (state === State.New) {
1454
+ log = {
1455
+ rating: Rating.Manual,
1456
+ state,
1457
+ due: due != null ? due : reviewed,
1458
+ stability: card.stability,
1459
+ difficulty: card.difficulty,
1460
+ elapsed_days,
1461
+ last_elapsed_days: card.elapsed_days,
1462
+ scheduled_days: card.scheduled_days,
1463
+ learning_steps: card.learning_steps,
1464
+ review: reviewed
1465
+ };
1466
+ next_card = createEmptyCard(reviewed);
1467
+ next_card.last_review = reviewed;
1468
+ } else {
1469
+ if (typeof due === "undefined") {
1470
+ throw new Error("reschedule: due is required for manual rating");
1471
+ }
1472
+ const scheduled_days = date_diff(due, reviewed, "days");
1473
+ log = {
1474
+ rating: Rating.Manual,
1475
+ state: card.state,
1476
+ due: card.last_review || card.due,
1477
+ stability: card.stability,
1478
+ difficulty: card.difficulty,
1479
+ elapsed_days,
1480
+ last_elapsed_days: card.elapsed_days,
1481
+ scheduled_days: card.scheduled_days,
1482
+ learning_steps: card.learning_steps,
1483
+ review: reviewed
1484
+ };
1485
+ next_card = __spreadProps$1(__spreadValues$1({}, card), {
1486
+ state,
1487
+ due,
1488
+ last_review: reviewed,
1489
+ stability: stability || card.stability,
1490
+ difficulty: difficulty || card.difficulty,
1491
+ elapsed_days,
1492
+ scheduled_days,
1493
+ reps: card.reps + 1
1494
+ });
1495
+ }
1496
+ return { card: next_card, log };
1497
+ }
1498
+ /**
1499
+ * Reschedules a card based on its review history.
1500
+ *
1501
+ * @param current_card - The card to be rescheduled.
1502
+ * @param reviews - An array of review history objects.
1503
+ * @returns An array of record log items representing the rescheduling process.
1504
+ */
1505
+ reschedule(current_card, reviews) {
1506
+ const collections = [];
1507
+ let cur_card = createEmptyCard(current_card.due);
1508
+ for (const review of reviews) {
1509
+ let item;
1510
+ review.review = TypeConvert.time(review.review);
1511
+ if (review.rating === Rating.Manual) {
1512
+ let interval = 0;
1513
+ if (cur_card.state !== State.New && cur_card.last_review) {
1514
+ interval = date_diff(review.review, cur_card.last_review, "days");
1515
+ }
1516
+ item = this.handleManualRating(
1517
+ cur_card,
1518
+ review.state,
1519
+ review.review,
1520
+ interval,
1521
+ review.stability,
1522
+ review.difficulty,
1523
+ review.due ? TypeConvert.time(review.due) : void 0
1524
+ );
1525
+ } else {
1526
+ item = this.replay(cur_card, review.review, review.rating);
1527
+ }
1528
+ collections.push(item);
1529
+ cur_card = item.card;
1530
+ }
1531
+ return collections;
1532
+ }
1533
+ calculateManualRecord(current_card, now, record_log_item, update_memory) {
1534
+ if (!record_log_item) {
1535
+ return null;
1536
+ }
1537
+ const { card: reschedule_card, log } = record_log_item;
1538
+ const cur_card = TypeConvert.card(current_card);
1539
+ if (cur_card.due.getTime() === reschedule_card.due.getTime()) {
1540
+ return null;
1541
+ }
1542
+ cur_card.scheduled_days = date_diff(
1543
+ reschedule_card.due,
1544
+ cur_card.due,
1545
+ "days"
1546
+ );
1547
+ return this.handleManualRating(
1548
+ cur_card,
1549
+ reschedule_card.state,
1550
+ TypeConvert.time(now),
1551
+ log.elapsed_days,
1552
+ update_memory ? reschedule_card.stability : void 0,
1553
+ update_memory ? reschedule_card.difficulty : void 0,
1554
+ reschedule_card.due
1555
+ );
1556
+ }
1557
+ }
1558
+
1559
+ var __defProp = Object.defineProperty;
1560
+ var __defProps = Object.defineProperties;
1561
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
1562
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
1563
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
1564
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
1565
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1566
+ var __spreadValues = (a, b) => {
1567
+ for (var prop in b || (b = {}))
1568
+ if (__hasOwnProp.call(b, prop))
1569
+ __defNormalProp(a, prop, b[prop]);
1570
+ if (__getOwnPropSymbols)
1571
+ for (var prop of __getOwnPropSymbols(b)) {
1572
+ if (__propIsEnum.call(b, prop))
1573
+ __defNormalProp(a, prop, b[prop]);
1574
+ }
1575
+ return a;
1576
+ };
1577
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
1578
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
1579
+ class FSRS extends FSRSAlgorithm {
1580
+ constructor(param) {
1581
+ super(param);
1582
+ __publicField(this, "strategyHandler", /* @__PURE__ */ new Map());
1583
+ __publicField(this, "Scheduler");
1584
+ const { enable_short_term } = this.parameters;
1585
+ this.Scheduler = enable_short_term ? BasicScheduler : LongTermScheduler;
1586
+ }
1587
+ params_handler_proxy() {
1588
+ const _this = this;
1589
+ return {
1590
+ set: function(target, prop, value) {
1591
+ if (prop === "request_retention" && Number.isFinite(value)) {
1592
+ _this.intervalModifier = _this.calculate_interval_modifier(
1593
+ Number(value)
1594
+ );
1595
+ } else if (prop === "enable_short_term") {
1596
+ _this.Scheduler = value === true ? BasicScheduler : LongTermScheduler;
1597
+ } else if (prop === "w") {
1598
+ value = migrateParameters(
1599
+ value,
1600
+ target.relearning_steps.length,
1601
+ target.enable_short_term
1602
+ );
1603
+ _this.forgetting_curve = forgetting_curve.bind(this, value);
1604
+ _this.intervalModifier = _this.calculate_interval_modifier(
1605
+ Number(target.request_retention)
1606
+ );
1607
+ }
1608
+ Reflect.set(target, prop, value);
1609
+ return true;
1610
+ }
1611
+ };
1612
+ }
1613
+ useStrategy(mode, handler) {
1614
+ this.strategyHandler.set(mode, handler);
1615
+ return this;
1616
+ }
1617
+ clearStrategy(mode) {
1618
+ if (mode) {
1619
+ this.strategyHandler.delete(mode);
1620
+ } else {
1621
+ this.strategyHandler.clear();
1622
+ }
1623
+ return this;
1624
+ }
1625
+ getScheduler(card, now) {
1626
+ const schedulerStrategy = this.strategyHandler.get(
1627
+ StrategyMode.SCHEDULER
1628
+ );
1629
+ const Scheduler = schedulerStrategy || this.Scheduler;
1630
+ const instance = new Scheduler(card, now, this, this.strategyHandler);
1631
+ return instance;
1632
+ }
1633
+ /**
1634
+ * Display the collection of cards and logs for the four scenarios after scheduling the card at the current time.
1635
+ * @param card Card to be processed
1636
+ * @param now Current time or scheduled time
1637
+ * @param afterHandler Convert the result to another type. (Optional)
1638
+ * @example
1639
+ * ```typescript
1640
+ * const card: Card = createEmptyCard(new Date());
1641
+ * const f = fsrs();
1642
+ * const recordLog = f.repeat(card, new Date());
1643
+ * ```
1644
+ * @example
1645
+ * ```typescript
1646
+ * interface RevLogUnchecked
1647
+ * extends Omit<ReviewLog, "due" | "review" | "state" | "rating"> {
1648
+ * cid: string;
1649
+ * due: Date | number;
1650
+ * state: StateType;
1651
+ * review: Date | number;
1652
+ * rating: RatingType;
1653
+ * }
1654
+ *
1655
+ * interface RepeatRecordLog {
1656
+ * card: CardUnChecked; //see method: createEmptyCard
1657
+ * log: RevLogUnchecked;
1658
+ * }
1659
+ *
1660
+ * function repeatAfterHandler(recordLog: RecordLog) {
1661
+ * const record: { [key in Grade]: RepeatRecordLog } = {} as {
1662
+ * [key in Grade]: RepeatRecordLog;
1663
+ * };
1664
+ * for (const grade of Grades) {
1665
+ * record[grade] = {
1666
+ * card: {
1667
+ * ...(recordLog[grade].card as Card & { cid: string }),
1668
+ * due: recordLog[grade].card.due.getTime(),
1669
+ * state: State[recordLog[grade].card.state] as StateType,
1670
+ * last_review: recordLog[grade].card.last_review
1671
+ * ? recordLog[grade].card.last_review!.getTime()
1672
+ * : null,
1673
+ * },
1674
+ * log: {
1675
+ * ...recordLog[grade].log,
1676
+ * cid: (recordLog[grade].card as Card & { cid: string }).cid,
1677
+ * due: recordLog[grade].log.due.getTime(),
1678
+ * review: recordLog[grade].log.review.getTime(),
1679
+ * state: State[recordLog[grade].log.state] as StateType,
1680
+ * rating: Rating[recordLog[grade].log.rating] as RatingType,
1681
+ * },
1682
+ * };
1683
+ * }
1684
+ * return record;
1685
+ * }
1686
+ * const card: Card = createEmptyCard(new Date(), cardAfterHandler); //see method: createEmptyCard
1687
+ * const f = fsrs();
1688
+ * const recordLog = f.repeat(card, new Date(), repeatAfterHandler);
1689
+ * ```
1690
+ */
1691
+ repeat(card, now, afterHandler) {
1692
+ const instance = this.getScheduler(card, now);
1693
+ const recordLog = instance.preview();
1694
+ if (afterHandler && typeof afterHandler === "function") {
1695
+ return afterHandler(recordLog);
1696
+ } else {
1697
+ return recordLog;
1698
+ }
1699
+ }
1700
+ /**
1701
+ * Display the collection of cards and logs for the card scheduled at the current time, after applying a specific grade rating.
1702
+ * @param card Card to be processed
1703
+ * @param now Current time or scheduled time
1704
+ * @param grade Rating of the review (Again, Hard, Good, Easy)
1705
+ * @param afterHandler Convert the result to another type. (Optional)
1706
+ * @example
1707
+ * ```typescript
1708
+ * const card: Card = createEmptyCard(new Date());
1709
+ * const f = fsrs();
1710
+ * const recordLogItem = f.next(card, new Date(), Rating.Again);
1711
+ * ```
1712
+ * @example
1713
+ * ```typescript
1714
+ * interface RevLogUnchecked
1715
+ * extends Omit<ReviewLog, "due" | "review" | "state" | "rating"> {
1716
+ * cid: string;
1717
+ * due: Date | number;
1718
+ * state: StateType;
1719
+ * review: Date | number;
1720
+ * rating: RatingType;
1721
+ * }
1722
+ *
1723
+ * interface NextRecordLog {
1724
+ * card: CardUnChecked; //see method: createEmptyCard
1725
+ * log: RevLogUnchecked;
1726
+ * }
1727
+ *
1728
+ function nextAfterHandler(recordLogItem: RecordLogItem) {
1729
+ const recordItem = {
1730
+ card: {
1731
+ ...(recordLogItem.card as Card & { cid: string }),
1732
+ due: recordLogItem.card.due.getTime(),
1733
+ state: State[recordLogItem.card.state] as StateType,
1734
+ last_review: recordLogItem.card.last_review
1735
+ ? recordLogItem.card.last_review!.getTime()
1736
+ : null,
1737
+ },
1738
+ log: {
1739
+ ...recordLogItem.log,
1740
+ cid: (recordLogItem.card as Card & { cid: string }).cid,
1741
+ due: recordLogItem.log.due.getTime(),
1742
+ review: recordLogItem.log.review.getTime(),
1743
+ state: State[recordLogItem.log.state] as StateType,
1744
+ rating: Rating[recordLogItem.log.rating] as RatingType,
1745
+ },
1746
+ };
1747
+ return recordItem
1748
+ }
1749
+ * const card: Card = createEmptyCard(new Date(), cardAfterHandler); //see method: createEmptyCard
1750
+ * const f = fsrs();
1751
+ * const recordLogItem = f.repeat(card, new Date(), Rating.Again, nextAfterHandler);
1752
+ * ```
1753
+ */
1754
+ next(card, now, grade, afterHandler) {
1755
+ const instance = this.getScheduler(card, now);
1756
+ const g = TypeConvert.rating(grade);
1757
+ if (g === Rating.Manual) {
1758
+ throw new Error("Cannot review a manual rating");
1759
+ }
1760
+ const recordLogItem = instance.review(g);
1761
+ if (afterHandler && typeof afterHandler === "function") {
1762
+ return afterHandler(recordLogItem);
1763
+ } else {
1764
+ return recordLogItem;
1765
+ }
1766
+ }
1767
+ /**
1768
+ * Get the retrievability of the card
1769
+ * @param card Card to be processed
1770
+ * @param now Current time or scheduled time
1771
+ * @param format default:true , Convert the result to another type. (Optional)
1772
+ * @returns The retrievability of the card,if format is true, the result is a string, otherwise it is a number
1773
+ */
1774
+ get_retrievability(card, now, format = true) {
1775
+ const processedCard = TypeConvert.card(card);
1776
+ now = now ? TypeConvert.time(now) : /* @__PURE__ */ new Date();
1777
+ const t = processedCard.state !== State.New ? Math.max(date_diff(now, processedCard.last_review, "days"), 0) : 0;
1778
+ const r = processedCard.state !== State.New ? this.forgetting_curve(t, +processedCard.stability.toFixed(8)) : 0;
1779
+ return format ? `${(r * 100).toFixed(2)}%` : r;
1780
+ }
1781
+ /**
1782
+ *
1783
+ * @param card Card to be processed
1784
+ * @param log last review log
1785
+ * @param afterHandler Convert the result to another type. (Optional)
1786
+ * @example
1787
+ * ```typescript
1788
+ * const now = new Date();
1789
+ * const f = fsrs();
1790
+ * const emptyCardFormAfterHandler = createEmptyCard(now);
1791
+ * const repeatFormAfterHandler = f.repeat(emptyCardFormAfterHandler, now);
1792
+ * const { card, log } = repeatFormAfterHandler[Rating.Hard];
1793
+ * const rollbackFromAfterHandler = f.rollback(card, log);
1794
+ * ```
1795
+ *
1796
+ * @example
1797
+ * ```typescript
1798
+ * const now = new Date();
1799
+ * const f = fsrs();
1800
+ * const emptyCardFormAfterHandler = createEmptyCard(now, cardAfterHandler); //see method: createEmptyCard
1801
+ * const repeatFormAfterHandler = f.repeat(emptyCardFormAfterHandler, now, repeatAfterHandler); //see method: fsrs.repeat()
1802
+ * const { card, log } = repeatFormAfterHandler[Rating.Hard];
1803
+ * const rollbackFromAfterHandler = f.rollback(card, log, cardAfterHandler);
1804
+ * ```
1805
+ */
1806
+ rollback(card, log, afterHandler) {
1807
+ const processedCard = TypeConvert.card(card);
1808
+ const processedLog = TypeConvert.review_log(log);
1809
+ if (processedLog.rating === Rating.Manual) {
1810
+ throw new Error("Cannot rollback a manual rating");
1811
+ }
1812
+ let last_due;
1813
+ let last_review;
1814
+ let last_lapses;
1815
+ switch (processedLog.state) {
1816
+ case State.New:
1817
+ last_due = processedLog.due;
1818
+ last_review = void 0;
1819
+ last_lapses = 0;
1820
+ break;
1821
+ case State.Learning:
1822
+ case State.Relearning:
1823
+ case State.Review:
1824
+ last_due = processedLog.review;
1825
+ last_review = processedLog.due;
1826
+ last_lapses = processedCard.lapses - (processedLog.rating === Rating.Again && processedLog.state === State.Review ? 1 : 0);
1827
+ break;
1828
+ }
1829
+ const prevCard = __spreadProps(__spreadValues({}, processedCard), {
1830
+ due: last_due,
1831
+ stability: processedLog.stability,
1832
+ difficulty: processedLog.difficulty,
1833
+ elapsed_days: processedLog.last_elapsed_days,
1834
+ scheduled_days: processedLog.scheduled_days,
1835
+ reps: Math.max(0, processedCard.reps - 1),
1836
+ lapses: Math.max(0, last_lapses),
1837
+ learning_steps: processedLog.learning_steps,
1838
+ state: processedLog.state,
1839
+ last_review
1840
+ });
1841
+ if (afterHandler && typeof afterHandler === "function") {
1842
+ return afterHandler(prevCard);
1843
+ } else {
1844
+ return prevCard;
1845
+ }
1846
+ }
1847
+ /**
1848
+ *
1849
+ * @param card Card to be processed
1850
+ * @param now Current time or scheduled time
1851
+ * @param reset_count Should the review count information(reps,lapses) be reset. (Optional)
1852
+ * @param afterHandler Convert the result to another type. (Optional)
1853
+ * @example
1854
+ * ```typescript
1855
+ * const now = new Date();
1856
+ * const f = fsrs();
1857
+ * const emptyCard = createEmptyCard(now);
1858
+ * const scheduling_cards = f.repeat(emptyCard, now);
1859
+ * const { card, log } = scheduling_cards[Rating.Hard];
1860
+ * const forgetCard = f.forget(card, new Date(), true);
1861
+ * ```
1862
+ *
1863
+ * @example
1864
+ * ```typescript
1865
+ * interface RepeatRecordLog {
1866
+ * card: CardUnChecked; //see method: createEmptyCard
1867
+ * log: RevLogUnchecked; //see method: fsrs.repeat()
1868
+ * }
1869
+ *
1870
+ * function forgetAfterHandler(recordLogItem: RecordLogItem): RepeatRecordLog {
1871
+ * return {
1872
+ * card: {
1873
+ * ...(recordLogItem.card as Card & { cid: string }),
1874
+ * due: recordLogItem.card.due.getTime(),
1875
+ * state: State[recordLogItem.card.state] as StateType,
1876
+ * last_review: recordLogItem.card.last_review
1877
+ * ? recordLogItem.card.last_review!.getTime()
1878
+ * : null,
1879
+ * },
1880
+ * log: {
1881
+ * ...recordLogItem.log,
1882
+ * cid: (recordLogItem.card as Card & { cid: string }).cid,
1883
+ * due: recordLogItem.log.due.getTime(),
1884
+ * review: recordLogItem.log.review.getTime(),
1885
+ * state: State[recordLogItem.log.state] as StateType,
1886
+ * rating: Rating[recordLogItem.log.rating] as RatingType,
1887
+ * },
1888
+ * };
1889
+ * }
1890
+ * const now = new Date();
1891
+ * const f = fsrs();
1892
+ * const emptyCardFormAfterHandler = createEmptyCard(now, cardAfterHandler); //see method: createEmptyCard
1893
+ * const repeatFormAfterHandler = f.repeat(emptyCardFormAfterHandler, now, repeatAfterHandler); //see method: fsrs.repeat()
1894
+ * const { card } = repeatFormAfterHandler[Rating.Hard];
1895
+ * const forgetFromAfterHandler = f.forget(card, date_scheduler(now, 1, true), false, forgetAfterHandler);
1896
+ * ```
1897
+ */
1898
+ forget(card, now, reset_count = false, afterHandler) {
1899
+ const processedCard = TypeConvert.card(card);
1900
+ now = TypeConvert.time(now);
1901
+ const scheduled_days = processedCard.state === State.New ? 0 : date_diff(now, processedCard.due, "days");
1902
+ const forget_log = {
1903
+ rating: Rating.Manual,
1904
+ state: processedCard.state,
1905
+ due: processedCard.due,
1906
+ stability: processedCard.stability,
1907
+ difficulty: processedCard.difficulty,
1908
+ elapsed_days: 0,
1909
+ last_elapsed_days: processedCard.elapsed_days,
1910
+ scheduled_days,
1911
+ learning_steps: processedCard.learning_steps,
1912
+ review: now
1913
+ };
1914
+ const forget_card = __spreadProps(__spreadValues({}, processedCard), {
1915
+ due: now,
1916
+ stability: 0,
1917
+ difficulty: 0,
1918
+ elapsed_days: 0,
1919
+ scheduled_days: 0,
1920
+ reps: reset_count ? 0 : processedCard.reps,
1921
+ lapses: reset_count ? 0 : processedCard.lapses,
1922
+ learning_steps: 0,
1923
+ state: State.New,
1924
+ last_review: processedCard.last_review
1925
+ });
1926
+ const recordLogItem = { card: forget_card, log: forget_log };
1927
+ if (afterHandler && typeof afterHandler === "function") {
1928
+ return afterHandler(recordLogItem);
1929
+ } else {
1930
+ return recordLogItem;
1931
+ }
1932
+ }
1933
+ /**
1934
+ * Reschedules the current card and returns the rescheduled collections and reschedule item.
1935
+ *
1936
+ * @template T - The type of the record log item.
1937
+ * @param {CardInput | Card} current_card - The current card to be rescheduled.
1938
+ * @param {Array<FSRSHistory>} reviews - The array of FSRSHistory objects representing the reviews.
1939
+ * @param {Partial<RescheduleOptions<T>>} options - The optional reschedule options.
1940
+ * @returns {IReschedule<T>} - The rescheduled collections and reschedule item.
1941
+ *
1942
+ * @example
1943
+ * ```typescript
1944
+ * const f = fsrs()
1945
+ * const grades: Grade[] = [Rating.Good, Rating.Good, Rating.Good, Rating.Good]
1946
+ * const reviews_at = [
1947
+ * new Date(2024, 8, 13),
1948
+ * new Date(2024, 8, 13),
1949
+ * new Date(2024, 8, 17),
1950
+ * new Date(2024, 8, 28),
1951
+ * ]
1952
+ *
1953
+ * const reviews: FSRSHistory[] = []
1954
+ * for (let i = 0; i < grades.length; i++) {
1955
+ * reviews.push({
1956
+ * rating: grades[i],
1957
+ * review: reviews_at[i],
1958
+ * })
1959
+ * }
1960
+ *
1961
+ * const results_short = scheduler.reschedule(
1962
+ * createEmptyCard(),
1963
+ * reviews,
1964
+ * {
1965
+ * skipManual: false,
1966
+ * }
1967
+ * )
1968
+ * console.log(results_short)
1969
+ * ```
1970
+ */
1971
+ reschedule(current_card, reviews = [], options = {}) {
1972
+ const {
1973
+ recordLogHandler,
1974
+ reviewsOrderBy,
1975
+ skipManual = true,
1976
+ now = /* @__PURE__ */ new Date(),
1977
+ update_memory_state: updateMemoryState = false
1978
+ } = options;
1979
+ if (reviewsOrderBy && typeof reviewsOrderBy === "function") {
1980
+ reviews.sort(reviewsOrderBy);
1981
+ }
1982
+ if (skipManual) {
1983
+ reviews = reviews.filter((review) => review.rating !== Rating.Manual);
1984
+ }
1985
+ const rescheduleSvc = new Reschedule(this);
1986
+ const collections = rescheduleSvc.reschedule(
1987
+ options.first_card || createEmptyCard(),
1988
+ reviews
1989
+ );
1990
+ const len = collections.length;
1991
+ const cur_card = TypeConvert.card(current_card);
1992
+ const manual_item = rescheduleSvc.calculateManualRecord(
1993
+ cur_card,
1994
+ now,
1995
+ len ? collections[len - 1] : void 0,
1996
+ updateMemoryState
1997
+ );
1998
+ if (recordLogHandler && typeof recordLogHandler === "function") {
1999
+ return {
2000
+ collections: collections.map(recordLogHandler),
2001
+ reschedule_item: manual_item ? recordLogHandler(manual_item) : null
2002
+ };
2003
+ }
2004
+ return {
2005
+ collections,
2006
+ reschedule_item: manual_item
2007
+ };
2008
+ }
2009
+ }
2010
+ const fsrs = (params) => {
2011
+ return new FSRS(params || {});
2012
+ };
2013
+
2014
+ exports.AbstractScheduler = AbstractScheduler;
2015
+ exports.BasicLearningStepsStrategy = BasicLearningStepsStrategy;
2016
+ exports.CLAMP_PARAMETERS = CLAMP_PARAMETERS;
2017
+ exports.ConvertStepUnitToMinutes = ConvertStepUnitToMinutes;
2018
+ exports.DefaultInitSeedStrategy = DefaultInitSeedStrategy;
2019
+ exports.FSRS = FSRS;
2020
+ exports.FSRS5_DEFAULT_DECAY = FSRS5_DEFAULT_DECAY;
2021
+ exports.FSRS6_DEFAULT_DECAY = FSRS6_DEFAULT_DECAY;
2022
+ exports.FSRSAlgorithm = FSRSAlgorithm;
2023
+ exports.FSRSVersion = FSRSVersion;
2024
+ exports.GenSeedStrategyWithCardId = GenSeedStrategyWithCardId;
2025
+ exports.Grades = Grades;
2026
+ exports.INIT_S_MAX = INIT_S_MAX;
2027
+ exports.Rating = Rating;
2028
+ exports.S_MAX = S_MAX;
2029
+ exports.S_MIN = S_MIN;
2030
+ exports.State = State;
2031
+ exports.StrategyMode = StrategyMode;
2032
+ exports.TypeConvert = TypeConvert;
2033
+ exports.W17_W18_Ceiling = W17_W18_Ceiling;
2034
+ exports.checkParameters = checkParameters;
2035
+ exports.clamp = clamp;
2036
+ exports.clipParameters = clipParameters;
2037
+ exports.computeDecayFactor = computeDecayFactor;
2038
+ exports.createEmptyCard = createEmptyCard;
2039
+ exports.dateDiffInDays = dateDiffInDays;
2040
+ exports.date_diff = date_diff;
2041
+ exports.date_scheduler = date_scheduler;
2042
+ exports.default_enable_fuzz = default_enable_fuzz;
2043
+ exports.default_enable_short_term = default_enable_short_term;
2044
+ exports.default_learning_steps = default_learning_steps;
2045
+ exports.default_maximum_interval = default_maximum_interval;
2046
+ exports.default_relearning_steps = default_relearning_steps;
2047
+ exports.default_request_retention = default_request_retention;
2048
+ exports.default_w = default_w;
2049
+ exports.fixDate = fixDate;
2050
+ exports.fixRating = fixRating;
2051
+ exports.fixState = fixState;
2052
+ exports.forgetting_curve = forgetting_curve;
2053
+ exports.formatDate = formatDate;
2054
+ exports.fsrs = fsrs;
2055
+ exports.generatorParameters = generatorParameters;
2056
+ exports.get_fuzz_range = get_fuzz_range;
2057
+ exports.migrateParameters = migrateParameters;
2058
+ exports.show_diff_message = show_diff_message;
2059
+
2060
+ }));
2
2061
  //# sourceMappingURL=index.umd.js.map