ts-fsrs 2.0.2 → 2.0.3

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/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # About The
2
2
 
3
- ts-fsrs is a TypeScript package used to implement the Free Spaced Repetition Scheduler (FSRS) algorithm. It helps
4
- developers apply FSRS to their flashcard applications, thereby improving the user learning experience.
3
+ ts-fsrs is a TypeScript package used to implement the [Free Spaced Repetition Scheduler (FSRS) algorithm](https://github.com/open-spaced-repetition/free-spaced-repetition-scheduler). It helps
4
+ developers apply FSRS to their flashcard applications, there by improving the user learning experience.
5
5
 
6
6
  # Usage
7
7
 
package/lib/fsrs.d.ts CHANGED
@@ -1,126 +1,126 @@
1
- import { Card, FSRSParameters, Rating, SchedulingCard, State } from "./index";
2
- import { int } from './help';
3
- export default class FSRS {
4
- private param;
5
- private readonly intervalModifier;
6
- private seed?;
7
- constructor(param?: FSRSParameters);
8
- repeat: (card: Card, now: Date) => {
9
- 0: {
10
- card: Card;
11
- log: {
12
- rating: Rating;
13
- state: State;
14
- elapsed_days: number;
15
- scheduled_days: number;
16
- review: Date;
17
- };
18
- };
19
- 1: {
20
- card: Card;
21
- log: {
22
- rating: Rating;
23
- state: State;
24
- elapsed_days: number;
25
- scheduled_days: number;
26
- review: Date;
27
- };
28
- };
29
- 2: {
30
- card: Card;
31
- log: {
32
- rating: Rating;
33
- state: State;
34
- elapsed_days: number;
35
- scheduled_days: number;
36
- review: Date;
37
- };
38
- };
39
- 3: {
40
- card: Card;
41
- log: {
42
- rating: Rating;
43
- state: State;
44
- elapsed_days: number;
45
- scheduled_days: number;
46
- review: Date;
47
- };
48
- };
49
- };
50
- get_retrievability: (card: Card, now: Date) => undefined | string;
51
- init_ds(s: SchedulingCard): void;
52
- /**
53
- *
54
- * @param s scheduling Card
55
- * @param last_d Difficulty
56
- * @param last_s Stability
57
- * @param retrievability Retrievability
58
- */
59
- next_ds(s: SchedulingCard, last_d: number, last_s: number, retrievability: number): void;
60
- /**
61
- * The formula used is :
62
- * $$S_0(G) = w_0 + G \cdot w_1$$
63
- * $$\max \{S_0,0.1\}$$
64
- * @param g Grade (rating at Anki) [0.again,1.hard,2.good,3.easy]
65
- * @return Stability (interval when R=90%)
66
- */
67
- init_stability(g: number): number;
68
- /**
69
- * The formula used is :
70
- * $$D_0(G) = w_2 + (G-2) \cdot w_3$$
71
- * $$\min \{\max \{D_0(G),1\},10\}$$
72
- * @param g Grade (rating at Anki) [0.again,1.hard,2.good,3.easy]
73
- * @return Difficulty D \in [1,10]
74
- */
75
- init_difficulty(g: number): number;
76
- apply_fuzz(ivl: number): number;
77
- next_interval(s: number): int;
78
- /**
79
- * The formula used is :
80
- * $$next_d = D + w_4 \cdot (R - 2)$$
81
- * $$D^\prime(D,R) = w_5 \cdot D_0(2) +(1 - w_5) \cdot next_d$$
82
- * @param d
83
- * @param g Grade (Rating[0.again,1.hard,2.good,3.easy])
84
- * @return next_D
85
- */
86
- next_difficulty(d: number, g: number): number;
87
- /**
88
- * The formula used is :
89
- * $$\min \{\max \{D_0,1\},10\}$$
90
- */
91
- constrain_difficulty(difficulty: number): number;
92
- /**
93
- * The formula used is :
94
- * $$w_5 \cdot init +(1 - w_5) \cdot current$$
95
- * @param init $$w_2 : D_0(2) = w_2 + (R-2) \cdot w_3= w_2$$
96
- * @param current $$D + w_4 \cdot (R - 2)$$
97
- * @return difficulty
98
- */
99
- mean_reversion(init: number, current: number): number;
100
- /**
101
- * The formula used is :
102
- * $$S^\prime_r(D,S,R) = S\cdot(e^{w_6}\cdot (11-D)\cdot S^{w_7}\cdot(e^{w_8\cdot(1-R)}-1)+1)$$
103
- * @param d Difficulty D \in [1,10]
104
- * @param s Stability (interval when R=90%)
105
- * @param r Retrievability (probability of recall)
106
- * @return S^\prime_r new stability after recall
107
- */
108
- next_recall_stability(d: number, s: number, r: number): number;
109
- /**
110
- * The formula used is :
111
- * $$S^\prime_f(D,S,R) = w_9\cdot D^{w_{10}}\cdot S^{w_{11}}\cdot e^{w_{12}\cdot(1-R)}.$$
112
- * @param d Difficulty D \in [1,10]
113
- * @param s Stability (interval when R=90%)
114
- * @param r Retrievability (probability of recall)
115
- * @return S^\prime_f new stability after forgetting
116
- */
117
- next_forget_stability(d: number, s: number, r: number): number;
118
- /**
119
- * The formula used is :
120
- * $$R(t,S) = 0.9^{\frac{t}{S}}$$
121
- * @param t t days since the last review
122
- * @param s Stability (interval when R=90%)
123
- * @return r Retrievability (probability of recall)
124
- */
125
- current_retrievability(t: number, s: number): number;
126
- }
1
+ import { Card, FSRSParameters, Rating, SchedulingCard, State } from "./index";
2
+ import { int } from './help';
3
+ export default class FSRS {
4
+ private param;
5
+ private readonly intervalModifier;
6
+ private seed?;
7
+ constructor(param?: FSRSParameters);
8
+ repeat: (card: Card, now: Date) => {
9
+ 0: {
10
+ card: Card;
11
+ log: {
12
+ rating: Rating;
13
+ state: State;
14
+ elapsed_days: number;
15
+ scheduled_days: number;
16
+ review: Date;
17
+ };
18
+ };
19
+ 1: {
20
+ card: Card;
21
+ log: {
22
+ rating: Rating;
23
+ state: State;
24
+ elapsed_days: number;
25
+ scheduled_days: number;
26
+ review: Date;
27
+ };
28
+ };
29
+ 2: {
30
+ card: Card;
31
+ log: {
32
+ rating: Rating;
33
+ state: State;
34
+ elapsed_days: number;
35
+ scheduled_days: number;
36
+ review: Date;
37
+ };
38
+ };
39
+ 3: {
40
+ card: Card;
41
+ log: {
42
+ rating: Rating;
43
+ state: State;
44
+ elapsed_days: number;
45
+ scheduled_days: number;
46
+ review: Date;
47
+ };
48
+ };
49
+ };
50
+ get_retrievability: (card: Card, now: Date) => undefined | string;
51
+ init_ds(s: SchedulingCard): void;
52
+ /**
53
+ *
54
+ * @param s scheduling Card
55
+ * @param last_d Difficulty
56
+ * @param last_s Stability
57
+ * @param retrievability Retrievability
58
+ */
59
+ next_ds(s: SchedulingCard, last_d: number, last_s: number, retrievability: number): void;
60
+ /**
61
+ * The formula used is :
62
+ * $$S_0(G) = w_0 + G \cdot w_1$$
63
+ * $$\max \{S_0,0.1\}$$
64
+ * @param g Grade (rating at Anki) [0.again,1.hard,2.good,3.easy]
65
+ * @return Stability (interval when R=90%)
66
+ */
67
+ init_stability(g: number): number;
68
+ /**
69
+ * The formula used is :
70
+ * $$D_0(G) = w_2 + (G-2) \cdot w_3$$
71
+ * $$\min \{\max \{D_0(G),1\},10\}$$
72
+ * @param g Grade (rating at Anki) [0.again,1.hard,2.good,3.easy]
73
+ * @return Difficulty D \in [1,10]
74
+ */
75
+ init_difficulty(g: number): number;
76
+ apply_fuzz(ivl: number): number;
77
+ next_interval(s: number): int;
78
+ /**
79
+ * The formula used is :
80
+ * $$next_d = D + w_4 \cdot (R - 2)$$
81
+ * $$D^\prime(D,R) = w_5 \cdot D_0(2) +(1 - w_5) \cdot next_d$$
82
+ * @param d
83
+ * @param g Grade (Rating[0.again,1.hard,2.good,3.easy])
84
+ * @return next_D
85
+ */
86
+ next_difficulty(d: number, g: number): number;
87
+ /**
88
+ * The formula used is :
89
+ * $$\min \{\max \{D_0,1\},10\}$$
90
+ */
91
+ constrain_difficulty(difficulty: number): number;
92
+ /**
93
+ * The formula used is :
94
+ * $$w_5 \cdot init +(1 - w_5) \cdot current$$
95
+ * @param init $$w_2 : D_0(2) = w_2 + (R-2) \cdot w_3= w_2$$
96
+ * @param current $$D + w_4 \cdot (R - 2)$$
97
+ * @return difficulty
98
+ */
99
+ mean_reversion(init: number, current: number): number;
100
+ /**
101
+ * The formula used is :
102
+ * $$S^\prime_r(D,S,R) = S\cdot(e^{w_6}\cdot (11-D)\cdot S^{w_7}\cdot(e^{w_8\cdot(1-R)}-1)+1)$$
103
+ * @param d Difficulty D \in [1,10]
104
+ * @param s Stability (interval when R=90%)
105
+ * @param r Retrievability (probability of recall)
106
+ * @return S^\prime_r new stability after recall
107
+ */
108
+ next_recall_stability(d: number, s: number, r: number): number;
109
+ /**
110
+ * The formula used is :
111
+ * $$S^\prime_f(D,S,R) = w_9\cdot D^{w_{10}}\cdot S^{w_{11}}\cdot e^{w_{12}\cdot(1-R)}.$$
112
+ * @param d Difficulty D \in [1,10]
113
+ * @param s Stability (interval when R=90%)
114
+ * @param r Retrievability (probability of recall)
115
+ * @return S^\prime_f new stability after forgetting
116
+ */
117
+ next_forget_stability(d: number, s: number, r: number): number;
118
+ /**
119
+ * The formula used is :
120
+ * $$R(t,S) = 0.9^{\frac{t}{S}}$$
121
+ * @param t t days since the last review
122
+ * @param s Stability (interval when R=90%)
123
+ * @return r Retrievability (probability of recall)
124
+ */
125
+ current_retrievability(t: number, s: number): number;
126
+ }
package/lib/fsrs.js CHANGED
@@ -1,189 +1,189 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const seedrandom_1 = __importDefault(require("seedrandom"));
7
- const index_1 = require("./index");
8
- class FSRS {
9
- constructor(param) {
10
- this.repeat = (card, now) => {
11
- card = Object.assign({}, card);
12
- now = new Date(now.getTime());
13
- card.elapsed_days = card.state === index_1.State.New ? 0 : now.diff(card.last_review, "days"); //相距时间
14
- card.last_review = now; // 上次复习时间
15
- card.reps += 1;
16
- const s = new index_1.SchedulingCard(card);
17
- s.update_state(card.state);
18
- this.seed = String(card.last_review.getTime()) + String(card.elapsed_days);
19
- let easy_interval, good_interval, hard_interval;
20
- switch (card.state) {
21
- case index_1.State.New:
22
- this.init_ds(s);
23
- s.again.due = now.scheduler(1);
24
- s.hard.due = now.scheduler(5);
25
- s.good.due = now.scheduler(10);
26
- easy_interval = this.next_interval(s.easy.stability * this.param.easy_bonus);
27
- s.easy.scheduled_days = easy_interval;
28
- s.easy.due = now.scheduler(easy_interval, true);
29
- break;
30
- case index_1.State.Learning:
31
- case index_1.State.Relearning:
32
- hard_interval = 0;
33
- good_interval = this.next_interval(s.good.stability);
34
- easy_interval = Math.max(this.next_interval(s.easy.stability * this.param.easy_bonus), good_interval + 1);
35
- s.schedule(now, hard_interval, good_interval, easy_interval);
36
- break;
37
- case index_1.State.Review:
38
- const interval = card.elapsed_days;
39
- const last_d = card.difficulty;
40
- const last_s = card.stability;
41
- const retrievability = this.current_retrievability(interval, last_s);
42
- this.next_ds(s, last_d, last_s, retrievability);
43
- hard_interval = this.next_interval(last_s * this.param.hard_factor);
44
- good_interval = this.next_interval(s.good.stability);
45
- hard_interval = Math.min(hard_interval, good_interval);
46
- good_interval = Math.max(good_interval, hard_interval + 1);
47
- easy_interval = Math.max(this.next_interval(s.easy.stability * this.param.easy_bonus), good_interval + 1);
48
- s.schedule(now, hard_interval, good_interval, easy_interval);
49
- break;
50
- }
51
- return s.record_log(card, now);
52
- };
53
- this.get_retrievability = (card, now) => {
54
- if (card.state !== index_1.State.Review) {
55
- return undefined;
56
- }
57
- const t = Math.max(now.diff(card.last_review, "days"), 0);
58
- return (this.current_retrievability(t, card.stability) * 100).toFixed(2) + '%';
59
- };
60
- this.param = param || (0, index_1.generatorParameters)();
61
- this.intervalModifier = Math.log(this.param.request_retention) / Math.log(0.9);
62
- }
63
- init_ds(s) {
64
- s.again.difficulty = this.init_difficulty(index_1.Rating.Again);
65
- s.again.stability = this.init_stability(index_1.Rating.Again);
66
- s.hard.difficulty = this.init_difficulty(index_1.Rating.Hard);
67
- s.hard.stability = this.init_stability(index_1.Rating.Hard);
68
- s.good.difficulty = this.init_difficulty(index_1.Rating.Good);
69
- s.good.stability = this.init_stability(index_1.Rating.Good);
70
- s.easy.difficulty = this.init_difficulty(index_1.Rating.Easy);
71
- s.easy.stability = this.init_stability(index_1.Rating.Easy);
72
- }
73
- /**
74
- *
75
- * @param s scheduling Card
76
- * @param last_d Difficulty
77
- * @param last_s Stability
78
- * @param retrievability Retrievability
79
- */
80
- next_ds(s, last_d, last_s, retrievability) {
81
- s.again.difficulty = this.next_difficulty(last_d, index_1.Rating.Again);
82
- s.again.stability = this.next_forget_stability(s.again.difficulty, last_s, retrievability);
83
- s.hard.difficulty = this.next_difficulty(last_d, index_1.Rating.Hard);
84
- s.hard.stability = this.next_recall_stability(s.hard.difficulty, last_s, retrievability);
85
- s.good.difficulty = this.next_difficulty(last_d, index_1.Rating.Good);
86
- s.good.stability = this.next_recall_stability(s.good.difficulty, last_s, retrievability);
87
- s.easy.difficulty = this.next_difficulty(last_d, index_1.Rating.Easy);
88
- s.easy.stability = this.next_recall_stability(s.easy.difficulty, last_s, retrievability);
89
- }
90
- /**
91
- * The formula used is :
92
- * $$S_0(G) = w_0 + G \cdot w_1$$
93
- * $$\max \{S_0,0.1\}$$
94
- * @param g Grade (rating at Anki) [0.again,1.hard,2.good,3.easy]
95
- * @return Stability (interval when R=90%)
96
- */
97
- init_stability(g) {
98
- return Math.max(this.param.w[0] + this.param.w[1] * g, 0.1);
99
- }
100
- /**
101
- * The formula used is :
102
- * $$D_0(G) = w_2 + (G-2) \cdot w_3$$
103
- * $$\min \{\max \{D_0(G),1\},10\}$$
104
- * @param g Grade (rating at Anki) [0.again,1.hard,2.good,3.easy]
105
- * @return Difficulty D \in [1,10]
106
- */
107
- init_difficulty(g) {
108
- return Math.min(Math.max(this.param.w[2] + this.param.w[3] * (g - 2), 1), 10);
109
- }
110
- apply_fuzz(ivl) {
111
- if (!this.param.enable_fuzz || ivl < 2.5)
112
- return ivl;
113
- const generator = (0, seedrandom_1.default)(this.seed);
114
- const fuzz_factor = generator();
115
- ivl = Math.round(ivl);
116
- const min_ivl = Math.max(2, Math.round(ivl * 0.95 - 1));
117
- const max_ivl = Math.round(ivl * 1.05 + 1);
118
- return Math.floor(fuzz_factor * (max_ivl - min_ivl + 1) + min_ivl);
119
- }
120
- next_interval(s) {
121
- const newInterval = this.apply_fuzz(s * this.intervalModifier);
122
- return Math.min(Math.max(Math.round(newInterval), 1), this.param.maximum_interval);
123
- }
124
- /**
125
- * The formula used is :
126
- * $$next_d = D + w_4 \cdot (R - 2)$$
127
- * $$D^\prime(D,R) = w_5 \cdot D_0(2) +(1 - w_5) \cdot next_d$$
128
- * @param d
129
- * @param g Grade (Rating[0.again,1.hard,2.good,3.easy])
130
- * @return next_D
131
- */
132
- next_difficulty(d, g) {
133
- const next_d = d + this.param.w[4] * (g - 2);
134
- return this.constrain_difficulty(this.mean_reversion(this.param.w[2], next_d));
135
- }
136
- /**
137
- * The formula used is :
138
- * $$\min \{\max \{D_0,1\},10\}$$
139
- */
140
- constrain_difficulty(difficulty) {
141
- return Math.min(Math.max(Number(difficulty.toFixed(2)), 1), 10);
142
- }
143
- /**
144
- * The formula used is :
145
- * $$w_5 \cdot init +(1 - w_5) \cdot current$$
146
- * @param init $$w_2 : D_0(2) = w_2 + (R-2) \cdot w_3= w_2$$
147
- * @param current $$D + w_4 \cdot (R - 2)$$
148
- * @return difficulty
149
- */
150
- mean_reversion(init, current) {
151
- return this.param.w[5] * init + (1 - this.param.w[5]) * current;
152
- }
153
- /**
154
- * The formula used is :
155
- * $$S^\prime_r(D,S,R) = S\cdot(e^{w_6}\cdot (11-D)\cdot S^{w_7}\cdot(e^{w_8\cdot(1-R)}-1)+1)$$
156
- * @param d Difficulty D \in [1,10]
157
- * @param s Stability (interval when R=90%)
158
- * @param r Retrievability (probability of recall)
159
- * @return S^\prime_r new stability after recall
160
- */
161
- next_recall_stability(d, s, r) {
162
- return s * (1 + Math.exp(this.param.w[6]) *
163
- (11 - d) *
164
- Math.pow(s, this.param.w[7]) *
165
- (Math.exp((1 - r) * this.param.w[8]) - 1));
166
- }
167
- /**
168
- * The formula used is :
169
- * $$S^\prime_f(D,S,R) = w_9\cdot D^{w_{10}}\cdot S^{w_{11}}\cdot e^{w_{12}\cdot(1-R)}.$$
170
- * @param d Difficulty D \in [1,10]
171
- * @param s Stability (interval when R=90%)
172
- * @param r Retrievability (probability of recall)
173
- * @return S^\prime_f new stability after forgetting
174
- */
175
- next_forget_stability(d, s, r) {
176
- return this.param.w[9] * Math.pow(d, this.param.w[10]) * Math.pow(s, this.param.w[11]) * Math.exp((1 - r) * this.param.w[12]);
177
- }
178
- /**
179
- * The formula used is :
180
- * $$R(t,S) = 0.9^{\frac{t}{S}}$$
181
- * @param t t days since the last review
182
- * @param s Stability (interval when R=90%)
183
- * @return r Retrievability (probability of recall)
184
- */
185
- current_retrievability(t, s) {
186
- return Math.exp(Math.log(0.9) * t / s);
187
- }
188
- }
189
- exports.default = FSRS;
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const seedrandom_1 = __importDefault(require("seedrandom"));
7
+ const index_1 = require("./index");
8
+ class FSRS {
9
+ constructor(param) {
10
+ this.repeat = (card, now) => {
11
+ card = Object.assign({}, card);
12
+ now = new Date(now.getTime());
13
+ card.elapsed_days = card.state === index_1.State.New ? 0 : now.diff(card.last_review, "days"); //相距时间
14
+ card.last_review = now; // 上次复习时间
15
+ card.reps += 1;
16
+ const s = new index_1.SchedulingCard(card);
17
+ s.update_state(card.state);
18
+ this.seed = String(card.last_review.getTime()) + String(card.elapsed_days);
19
+ let easy_interval, good_interval, hard_interval;
20
+ switch (card.state) {
21
+ case index_1.State.New:
22
+ this.init_ds(s);
23
+ s.again.due = now.scheduler(1);
24
+ s.hard.due = now.scheduler(5);
25
+ s.good.due = now.scheduler(10);
26
+ easy_interval = this.next_interval(s.easy.stability * this.param.easy_bonus);
27
+ s.easy.scheduled_days = easy_interval;
28
+ s.easy.due = now.scheduler(easy_interval, true);
29
+ break;
30
+ case index_1.State.Learning:
31
+ case index_1.State.Relearning:
32
+ hard_interval = 0;
33
+ good_interval = this.next_interval(s.good.stability);
34
+ easy_interval = Math.max(this.next_interval(s.easy.stability * this.param.easy_bonus), good_interval + 1);
35
+ s.schedule(now, hard_interval, good_interval, easy_interval);
36
+ break;
37
+ case index_1.State.Review:
38
+ const interval = card.elapsed_days;
39
+ const last_d = card.difficulty;
40
+ const last_s = card.stability;
41
+ const retrievability = this.current_retrievability(interval, last_s);
42
+ this.next_ds(s, last_d, last_s, retrievability);
43
+ hard_interval = this.next_interval(last_s * this.param.hard_factor);
44
+ good_interval = this.next_interval(s.good.stability);
45
+ hard_interval = Math.min(hard_interval, good_interval);
46
+ good_interval = Math.max(good_interval, hard_interval + 1);
47
+ easy_interval = Math.max(this.next_interval(s.easy.stability * this.param.easy_bonus), good_interval + 1);
48
+ s.schedule(now, hard_interval, good_interval, easy_interval);
49
+ break;
50
+ }
51
+ return s.record_log(card, now);
52
+ };
53
+ this.get_retrievability = (card, now) => {
54
+ if (card.state !== index_1.State.Review) {
55
+ return undefined;
56
+ }
57
+ const t = Math.max(now.diff(card.last_review, "days"), 0);
58
+ return (this.current_retrievability(t, card.stability) * 100).toFixed(2) + '%';
59
+ };
60
+ this.param = param || (0, index_1.generatorParameters)();
61
+ this.intervalModifier = Math.log(this.param.request_retention) / Math.log(0.9);
62
+ }
63
+ init_ds(s) {
64
+ s.again.difficulty = this.init_difficulty(index_1.Rating.Again);
65
+ s.again.stability = this.init_stability(index_1.Rating.Again);
66
+ s.hard.difficulty = this.init_difficulty(index_1.Rating.Hard);
67
+ s.hard.stability = this.init_stability(index_1.Rating.Hard);
68
+ s.good.difficulty = this.init_difficulty(index_1.Rating.Good);
69
+ s.good.stability = this.init_stability(index_1.Rating.Good);
70
+ s.easy.difficulty = this.init_difficulty(index_1.Rating.Easy);
71
+ s.easy.stability = this.init_stability(index_1.Rating.Easy);
72
+ }
73
+ /**
74
+ *
75
+ * @param s scheduling Card
76
+ * @param last_d Difficulty
77
+ * @param last_s Stability
78
+ * @param retrievability Retrievability
79
+ */
80
+ next_ds(s, last_d, last_s, retrievability) {
81
+ s.again.difficulty = this.next_difficulty(last_d, index_1.Rating.Again);
82
+ s.again.stability = this.next_forget_stability(s.again.difficulty, last_s, retrievability);
83
+ s.hard.difficulty = this.next_difficulty(last_d, index_1.Rating.Hard);
84
+ s.hard.stability = this.next_recall_stability(s.hard.difficulty, last_s, retrievability);
85
+ s.good.difficulty = this.next_difficulty(last_d, index_1.Rating.Good);
86
+ s.good.stability = this.next_recall_stability(s.good.difficulty, last_s, retrievability);
87
+ s.easy.difficulty = this.next_difficulty(last_d, index_1.Rating.Easy);
88
+ s.easy.stability = this.next_recall_stability(s.easy.difficulty, last_s, retrievability);
89
+ }
90
+ /**
91
+ * The formula used is :
92
+ * $$S_0(G) = w_0 + G \cdot w_1$$
93
+ * $$\max \{S_0,0.1\}$$
94
+ * @param g Grade (rating at Anki) [0.again,1.hard,2.good,3.easy]
95
+ * @return Stability (interval when R=90%)
96
+ */
97
+ init_stability(g) {
98
+ return Math.max(this.param.w[0] + this.param.w[1] * g, 0.1);
99
+ }
100
+ /**
101
+ * The formula used is :
102
+ * $$D_0(G) = w_2 + (G-2) \cdot w_3$$
103
+ * $$\min \{\max \{D_0(G),1\},10\}$$
104
+ * @param g Grade (rating at Anki) [0.again,1.hard,2.good,3.easy]
105
+ * @return Difficulty D \in [1,10]
106
+ */
107
+ init_difficulty(g) {
108
+ return Math.min(Math.max(this.param.w[2] + this.param.w[3] * (g - 2), 1), 10);
109
+ }
110
+ apply_fuzz(ivl) {
111
+ if (!this.param.enable_fuzz || ivl < 2.5)
112
+ return ivl;
113
+ const generator = (0, seedrandom_1.default)(this.seed);
114
+ const fuzz_factor = generator();
115
+ ivl = Math.round(ivl);
116
+ const min_ivl = Math.max(2, Math.round(ivl * 0.95 - 1));
117
+ const max_ivl = Math.round(ivl * 1.05 + 1);
118
+ return Math.floor(fuzz_factor * (max_ivl - min_ivl + 1) + min_ivl);
119
+ }
120
+ next_interval(s) {
121
+ const newInterval = this.apply_fuzz(s * this.intervalModifier);
122
+ return Math.min(Math.max(Math.round(newInterval), 1), this.param.maximum_interval);
123
+ }
124
+ /**
125
+ * The formula used is :
126
+ * $$next_d = D + w_4 \cdot (R - 2)$$
127
+ * $$D^\prime(D,R) = w_5 \cdot D_0(2) +(1 - w_5) \cdot next_d$$
128
+ * @param d
129
+ * @param g Grade (Rating[0.again,1.hard,2.good,3.easy])
130
+ * @return next_D
131
+ */
132
+ next_difficulty(d, g) {
133
+ const next_d = d + this.param.w[4] * (g - 2);
134
+ return this.constrain_difficulty(this.mean_reversion(this.param.w[2], next_d));
135
+ }
136
+ /**
137
+ * The formula used is :
138
+ * $$\min \{\max \{D_0,1\},10\}$$
139
+ */
140
+ constrain_difficulty(difficulty) {
141
+ return Math.min(Math.max(Number(difficulty.toFixed(2)), 1), 10);
142
+ }
143
+ /**
144
+ * The formula used is :
145
+ * $$w_5 \cdot init +(1 - w_5) \cdot current$$
146
+ * @param init $$w_2 : D_0(2) = w_2 + (R-2) \cdot w_3= w_2$$
147
+ * @param current $$D + w_4 \cdot (R - 2)$$
148
+ * @return difficulty
149
+ */
150
+ mean_reversion(init, current) {
151
+ return this.param.w[5] * init + (1 - this.param.w[5]) * current;
152
+ }
153
+ /**
154
+ * The formula used is :
155
+ * $$S^\prime_r(D,S,R) = S\cdot(e^{w_6}\cdot (11-D)\cdot S^{w_7}\cdot(e^{w_8\cdot(1-R)}-1)+1)$$
156
+ * @param d Difficulty D \in [1,10]
157
+ * @param s Stability (interval when R=90%)
158
+ * @param r Retrievability (probability of recall)
159
+ * @return S^\prime_r new stability after recall
160
+ */
161
+ next_recall_stability(d, s, r) {
162
+ return s * (1 + Math.exp(this.param.w[6]) *
163
+ (11 - d) *
164
+ Math.pow(s, this.param.w[7]) *
165
+ (Math.exp((1 - r) * this.param.w[8]) - 1));
166
+ }
167
+ /**
168
+ * The formula used is :
169
+ * $$S^\prime_f(D,S,R) = w_9\cdot D^{w_{10}}\cdot S^{w_{11}}\cdot e^{w_{12}\cdot(1-R)}.$$
170
+ * @param d Difficulty D \in [1,10]
171
+ * @param s Stability (interval when R=90%)
172
+ * @param r Retrievability (probability of recall)
173
+ * @return S^\prime_f new stability after forgetting
174
+ */
175
+ next_forget_stability(d, s, r) {
176
+ return this.param.w[9] * Math.pow(d, this.param.w[10]) * Math.pow(s, this.param.w[11]) * Math.exp((1 - r) * this.param.w[12]);
177
+ }
178
+ /**
179
+ * The formula used is :
180
+ * $$R(t,S) = 0.9^{\frac{t}{S}}$$
181
+ * @param t t days since the last review
182
+ * @param s Stability (interval when R=90%)
183
+ * @return r Retrievability (probability of recall)
184
+ */
185
+ current_retrievability(t, s) {
186
+ return Math.exp(Math.log(0.9) * t / s);
187
+ }
188
+ }
189
+ exports.default = FSRS;