ts-fsrs 5.2.3 → 5.3.0
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/CHANGELOG.md +11 -0
- package/dist/index.cjs +110 -191
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.mjs +110 -192
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +110 -191
- package/dist/index.umd.js.map +1 -1
- package/package.json +25 -36
- package/README.md +0 -174
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# ts-fsrs
|
|
2
|
+
|
|
3
|
+
## 5.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#266](https://github.com/open-spaced-repetition/ts-fsrs/pull/266) [`17fb34d`](https://github.com/open-spaced-repetition/ts-fsrs/commit/17fb34d849c66f2035c5a239e2dfd64ed40055c9) Thanks [@ishiko732](https://github.com/ishiko732)! - Non-decreasing SInc(Hard) in same-day reviews — sync with fsrs-rs#376 changes
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- [#314](https://github.com/open-spaced-repetition/ts-fsrs/pull/314) [`87f94f2`](https://github.com/open-spaced-repetition/ts-fsrs/commit/87f94f277f7030a449490cef943e1aebe1585b64) Thanks [@user1823](https://github.com/user1823)! - Performance improvements
|
package/dist/index.cjs
CHANGED
|
@@ -54,6 +54,9 @@ class TypeConvert {
|
|
|
54
54
|
throw new Error(`Invalid state:[${value}]`);
|
|
55
55
|
}
|
|
56
56
|
static time(value) {
|
|
57
|
+
if (value instanceof Date) {
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
57
60
|
const date = new Date(value);
|
|
58
61
|
if (typeof value === "object" && value !== null && !Number.isNaN(Date.parse(value) || +date)) {
|
|
59
62
|
return date;
|
|
@@ -80,15 +83,19 @@ class TypeConvert {
|
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
85
|
|
|
86
|
+
/* istanbul ignore next -- @preserve */
|
|
83
87
|
Date.prototype.scheduler = function(t, isDay) {
|
|
84
88
|
return date_scheduler(this, t, isDay);
|
|
85
89
|
};
|
|
90
|
+
/* istanbul ignore next -- @preserve */
|
|
86
91
|
Date.prototype.diff = function(pre, unit) {
|
|
87
92
|
return date_diff(this, pre, unit);
|
|
88
93
|
};
|
|
94
|
+
/* istanbul ignore next -- @preserve */
|
|
89
95
|
Date.prototype.format = function() {
|
|
90
96
|
return formatDate(this);
|
|
91
97
|
};
|
|
98
|
+
/* istanbul ignore next -- @preserve */
|
|
92
99
|
Date.prototype.dueFormat = function(last_review, unit, timeUnit) {
|
|
93
100
|
return show_diff_message(this, last_review, unit, timeUnit);
|
|
94
101
|
};
|
|
@@ -148,12 +155,15 @@ function show_diff_message(due, last_review, unit, timeUnit = TIMEUNITFORMAT) {
|
|
|
148
155
|
}
|
|
149
156
|
return `${Math.floor(diff)}${unit ? timeUnit[i] : ""}`;
|
|
150
157
|
}
|
|
158
|
+
/* istanbul ignore next -- @preserve */
|
|
151
159
|
function fixDate(value) {
|
|
152
160
|
return TypeConvert.time(value);
|
|
153
161
|
}
|
|
162
|
+
/* istanbul ignore next -- @preserve */
|
|
154
163
|
function fixState(value) {
|
|
155
164
|
return TypeConvert.state(value);
|
|
156
165
|
}
|
|
166
|
+
/* istanbul ignore next -- @preserve */
|
|
157
167
|
function fixRating(value) {
|
|
158
168
|
return TypeConvert.rating(value);
|
|
159
169
|
}
|
|
@@ -197,6 +207,10 @@ function get_fuzz_range(interval, elapsed_days, maximum_interval) {
|
|
|
197
207
|
function clamp(value, min, max) {
|
|
198
208
|
return Math.min(Math.max(value, min), max);
|
|
199
209
|
}
|
|
210
|
+
function roundTo(num, decimals) {
|
|
211
|
+
const factor = 10 ** decimals;
|
|
212
|
+
return Math.round(num * factor) / factor;
|
|
213
|
+
}
|
|
200
214
|
function dateDiffInDays(last, cur) {
|
|
201
215
|
const utc1 = Date.UTC(
|
|
202
216
|
last.getUTCFullYear(),
|
|
@@ -469,7 +483,7 @@ function alea(seed) {
|
|
|
469
483
|
return prng;
|
|
470
484
|
}
|
|
471
485
|
|
|
472
|
-
const version="5.
|
|
486
|
+
const version="5.3.0";
|
|
473
487
|
|
|
474
488
|
const default_request_retention = 0.9;
|
|
475
489
|
const default_maximum_interval = 36500;
|
|
@@ -643,11 +657,11 @@ function createEmptyCard(now, afterHandler) {
|
|
|
643
657
|
const computeDecayFactor = (decayOrParams) => {
|
|
644
658
|
const decay = typeof decayOrParams === "number" ? -decayOrParams : -decayOrParams[20];
|
|
645
659
|
const factor = Math.exp(Math.pow(decay, -1) * Math.log(0.9)) - 1;
|
|
646
|
-
return { decay, factor:
|
|
660
|
+
return { decay, factor: roundTo(factor, 8) };
|
|
647
661
|
};
|
|
648
662
|
function forgetting_curve(decayOrParams, elapsed_days, stability) {
|
|
649
663
|
const { decay, factor } = computeDecayFactor(decayOrParams);
|
|
650
|
-
return
|
|
664
|
+
return roundTo(Math.pow(1 + factor * elapsed_days / stability, decay), 8);
|
|
651
665
|
}
|
|
652
666
|
class FSRSAlgorithm {
|
|
653
667
|
param;
|
|
@@ -681,7 +695,7 @@ class FSRSAlgorithm {
|
|
|
681
695
|
throw new Error("Requested retention rate should be in the range (0,1]");
|
|
682
696
|
}
|
|
683
697
|
const { decay, factor } = computeDecayFactor(this.param.w);
|
|
684
|
-
return
|
|
698
|
+
return roundTo((Math.pow(request_retention, 1 / decay) - 1) / factor, 8);
|
|
685
699
|
}
|
|
686
700
|
/**
|
|
687
701
|
* Get the parameters of the algorithm.
|
|
@@ -748,8 +762,9 @@ class FSRSAlgorithm {
|
|
|
748
762
|
* @return {number} Difficulty $$D \in [1,10]$$
|
|
749
763
|
*/
|
|
750
764
|
init_difficulty(g) {
|
|
751
|
-
const
|
|
752
|
-
|
|
765
|
+
const w = this.param.w;
|
|
766
|
+
const d = w[4] - Math.exp((g - 1) * w[5]) + 1;
|
|
767
|
+
return roundTo(d, 8);
|
|
753
768
|
}
|
|
754
769
|
/**
|
|
755
770
|
* If fuzzing is disabled or ivl is less than 2.5, it returns the original interval.
|
|
@@ -784,7 +799,7 @@ class FSRSAlgorithm {
|
|
|
784
799
|
* @see https://github.com/open-spaced-repetition/fsrs4anki/issues/697
|
|
785
800
|
*/
|
|
786
801
|
linear_damping(delta_d, old_d) {
|
|
787
|
-
return
|
|
802
|
+
return roundTo(delta_d * (10 - old_d) / 9, 8);
|
|
788
803
|
}
|
|
789
804
|
/**
|
|
790
805
|
* The formula used is :
|
|
@@ -812,9 +827,8 @@ class FSRSAlgorithm {
|
|
|
812
827
|
* @return {number} difficulty
|
|
813
828
|
*/
|
|
814
829
|
mean_reversion(init, current) {
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
);
|
|
830
|
+
const w = this.param.w;
|
|
831
|
+
return roundTo(w[7] * init + (1 - w[7]) * current, 8);
|
|
818
832
|
}
|
|
819
833
|
/**
|
|
820
834
|
* The formula used is :
|
|
@@ -826,13 +840,17 @@ class FSRSAlgorithm {
|
|
|
826
840
|
* @return {number} S^\prime_r new stability after recall
|
|
827
841
|
*/
|
|
828
842
|
next_recall_stability(d, s, r, g) {
|
|
829
|
-
const
|
|
830
|
-
const
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
843
|
+
const w = this.param.w;
|
|
844
|
+
const hard_penalty = Rating.Hard === g ? w[15] : 1;
|
|
845
|
+
const easy_bound = Rating.Easy === g ? w[16] : 1;
|
|
846
|
+
return roundTo(
|
|
847
|
+
clamp(
|
|
848
|
+
s * (1 + Math.exp(w[8]) * (11 - d) * Math.pow(s, -w[9]) * (Math.exp((1 - r) * w[10]) - 1) * hard_penalty * easy_bound),
|
|
849
|
+
S_MIN,
|
|
850
|
+
36500
|
|
851
|
+
),
|
|
852
|
+
8
|
|
853
|
+
);
|
|
836
854
|
}
|
|
837
855
|
/**
|
|
838
856
|
* The formula used is :
|
|
@@ -845,11 +863,15 @@ class FSRSAlgorithm {
|
|
|
845
863
|
* @return {number} S^\prime_f new stability after forgetting
|
|
846
864
|
*/
|
|
847
865
|
next_forget_stability(d, s, r) {
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
866
|
+
const w = this.param.w;
|
|
867
|
+
return roundTo(
|
|
868
|
+
clamp(
|
|
869
|
+
w[11] * Math.pow(d, -w[12]) * (Math.pow(s + 1, w[13]) - 1) * Math.exp((1 - r) * w[14]),
|
|
870
|
+
S_MIN,
|
|
871
|
+
36500
|
|
872
|
+
),
|
|
873
|
+
8
|
|
874
|
+
);
|
|
853
875
|
}
|
|
854
876
|
/**
|
|
855
877
|
* The formula used is :
|
|
@@ -858,9 +880,10 @@ class FSRSAlgorithm {
|
|
|
858
880
|
* @param {Grade} g Grade (Rating[0.again,1.hard,2.good,3.easy])
|
|
859
881
|
*/
|
|
860
882
|
next_short_term_stability(s, g) {
|
|
861
|
-
const
|
|
862
|
-
const
|
|
863
|
-
|
|
883
|
+
const w = this.param.w;
|
|
884
|
+
const sinc = Math.pow(s, -w[19]) * Math.exp(w[17] * (g - 3 + w[18]));
|
|
885
|
+
const maskedSinc = g >= Rating.Hard ? Math.max(sinc, 1) : sinc;
|
|
886
|
+
return roundTo(clamp(s * maskedSinc, S_MIN, 36500), 8);
|
|
864
887
|
}
|
|
865
888
|
/**
|
|
866
889
|
* The formula used is :
|
|
@@ -876,9 +899,10 @@ class FSRSAlgorithm {
|
|
|
876
899
|
* @param memory_state - The current state of memory, which can be null.
|
|
877
900
|
* @param t - The time elapsed since the last review.
|
|
878
901
|
* @param {Rating} g Grade (Rating[0.Manual,1.Again,2.Hard,3.Good,4.Easy])
|
|
902
|
+
* @param r - Optional retrievability value. If not provided, it will be calculated.
|
|
879
903
|
* @returns The next state of memory with updated difficulty and stability.
|
|
880
904
|
*/
|
|
881
|
-
next_state(memory_state, t, g) {
|
|
905
|
+
next_state(memory_state, t, g, r) {
|
|
882
906
|
const { difficulty: d, stability: s } = memory_state ?? {
|
|
883
907
|
difficulty: 0,
|
|
884
908
|
stability: 0
|
|
@@ -906,22 +930,22 @@ class FSRSAlgorithm {
|
|
|
906
930
|
`Invalid memory state { difficulty: ${d}, stability: ${s} }`
|
|
907
931
|
);
|
|
908
932
|
}
|
|
909
|
-
const
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
if (g === 1) {
|
|
933
|
+
const w = this.param.w;
|
|
934
|
+
r = typeof r === "number" ? r : this.forgetting_curve(t, s);
|
|
935
|
+
let new_s;
|
|
936
|
+
if (t === 0 && this.param.enable_short_term) {
|
|
937
|
+
new_s = this.next_short_term_stability(s, g);
|
|
938
|
+
} else if (g === 1) {
|
|
939
|
+
const s_after_fail = this.next_forget_stability(d, s, r);
|
|
915
940
|
let [w_17, w_18] = [0, 0];
|
|
916
941
|
if (this.param.enable_short_term) {
|
|
917
|
-
w_17 =
|
|
918
|
-
w_18 =
|
|
942
|
+
w_17 = w[17];
|
|
943
|
+
w_18 = w[18];
|
|
919
944
|
}
|
|
920
945
|
const next_s_min = s / Math.exp(w_17 * w_18);
|
|
921
|
-
new_s = clamp(
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
new_s = s_after_short_term;
|
|
946
|
+
new_s = clamp(roundTo(next_s_min, 8), S_MIN, s_after_fail);
|
|
947
|
+
} else {
|
|
948
|
+
new_s = this.next_recall_stability(d, s, r, g);
|
|
925
949
|
}
|
|
926
950
|
const new_d = this.next_difficulty(d, g);
|
|
927
951
|
return { difficulty: new_d, stability: new_s };
|
|
@@ -1006,9 +1030,7 @@ class BasicScheduler extends AbstractScheduler {
|
|
|
1006
1030
|
if (exist) {
|
|
1007
1031
|
return exist;
|
|
1008
1032
|
}
|
|
1009
|
-
const next =
|
|
1010
|
-
next.difficulty = clamp(this.algorithm.init_difficulty(grade), 1, 10);
|
|
1011
|
-
next.stability = this.algorithm.init_stability(grade);
|
|
1033
|
+
const next = this.next_ds(this.elapsed_days, grade);
|
|
1012
1034
|
this.applyLearningSteps(next, grade, State.Learning);
|
|
1013
1035
|
const item = {
|
|
1014
1036
|
card: next,
|
|
@@ -1022,14 +1044,11 @@ class BasicScheduler extends AbstractScheduler {
|
|
|
1022
1044
|
if (exist) {
|
|
1023
1045
|
return exist;
|
|
1024
1046
|
}
|
|
1025
|
-
const
|
|
1026
|
-
const next = TypeConvert.card(this.current);
|
|
1027
|
-
next.difficulty = this.algorithm.next_difficulty(difficulty, grade);
|
|
1028
|
-
next.stability = this.algorithm.next_short_term_stability(stability, grade);
|
|
1047
|
+
const next = this.next_ds(this.elapsed_days, grade);
|
|
1029
1048
|
this.applyLearningSteps(
|
|
1030
1049
|
next,
|
|
1031
1050
|
grade,
|
|
1032
|
-
state
|
|
1051
|
+
this.last.state
|
|
1033
1052
|
/** Learning or Relearning */
|
|
1034
1053
|
);
|
|
1035
1054
|
const item = {
|
|
@@ -1045,21 +1064,14 @@ class BasicScheduler extends AbstractScheduler {
|
|
|
1045
1064
|
return exist;
|
|
1046
1065
|
}
|
|
1047
1066
|
const interval = this.elapsed_days;
|
|
1048
|
-
const
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
const next_hard = TypeConvert.card(this.current);
|
|
1052
|
-
const next_good = TypeConvert.card(this.current);
|
|
1053
|
-
const next_easy = TypeConvert.card(this.current);
|
|
1054
|
-
this.next_ds(
|
|
1055
|
-
next_again,
|
|
1056
|
-
next_hard,
|
|
1057
|
-
next_good,
|
|
1058
|
-
next_easy,
|
|
1059
|
-
difficulty,
|
|
1060
|
-
stability,
|
|
1061
|
-
retrievability
|
|
1067
|
+
const retrievability = this.algorithm.forgetting_curve(
|
|
1068
|
+
interval,
|
|
1069
|
+
this.current.stability
|
|
1062
1070
|
);
|
|
1071
|
+
const next_again = this.next_ds(interval, Rating.Again, retrievability);
|
|
1072
|
+
const next_hard = this.next_ds(interval, Rating.Hard, retrievability);
|
|
1073
|
+
const next_good = this.next_ds(interval, Rating.Good, retrievability);
|
|
1074
|
+
const next_easy = this.next_ds(interval, Rating.Easy, retrievability);
|
|
1063
1075
|
this.next_interval(next_hard, next_good, next_easy, interval);
|
|
1064
1076
|
this.next_state(next_hard, next_good, next_easy);
|
|
1065
1077
|
this.applyLearningSteps(next_again, Rating.Again, State.Relearning);
|
|
@@ -1089,50 +1101,20 @@ class BasicScheduler extends AbstractScheduler {
|
|
|
1089
1101
|
/**
|
|
1090
1102
|
* Review next_ds
|
|
1091
1103
|
*/
|
|
1092
|
-
next_ds(
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
difficulty,
|
|
1102
|
-
stability,
|
|
1103
|
-
retrievability
|
|
1104
|
-
);
|
|
1105
|
-
next_again.stability = clamp(+nextSMin.toFixed(8), S_MIN, s_after_fail);
|
|
1106
|
-
next_hard.difficulty = this.algorithm.next_difficulty(
|
|
1107
|
-
difficulty,
|
|
1108
|
-
Rating.Hard
|
|
1109
|
-
);
|
|
1110
|
-
next_hard.stability = this.algorithm.next_recall_stability(
|
|
1111
|
-
difficulty,
|
|
1112
|
-
stability,
|
|
1113
|
-
retrievability,
|
|
1114
|
-
Rating.Hard
|
|
1115
|
-
);
|
|
1116
|
-
next_good.difficulty = this.algorithm.next_difficulty(
|
|
1117
|
-
difficulty,
|
|
1118
|
-
Rating.Good
|
|
1119
|
-
);
|
|
1120
|
-
next_good.stability = this.algorithm.next_recall_stability(
|
|
1121
|
-
difficulty,
|
|
1122
|
-
stability,
|
|
1123
|
-
retrievability,
|
|
1124
|
-
Rating.Good
|
|
1125
|
-
);
|
|
1126
|
-
next_easy.difficulty = this.algorithm.next_difficulty(
|
|
1127
|
-
difficulty,
|
|
1128
|
-
Rating.Easy
|
|
1129
|
-
);
|
|
1130
|
-
next_easy.stability = this.algorithm.next_recall_stability(
|
|
1131
|
-
difficulty,
|
|
1132
|
-
stability,
|
|
1133
|
-
retrievability,
|
|
1134
|
-
Rating.Easy
|
|
1104
|
+
next_ds(t, g, r) {
|
|
1105
|
+
const next_state = this.algorithm.next_state(
|
|
1106
|
+
{
|
|
1107
|
+
difficulty: this.current.difficulty,
|
|
1108
|
+
stability: this.current.stability
|
|
1109
|
+
},
|
|
1110
|
+
t,
|
|
1111
|
+
g,
|
|
1112
|
+
r
|
|
1135
1113
|
);
|
|
1114
|
+
const card = TypeConvert.card(this.current);
|
|
1115
|
+
card.difficulty = next_state.difficulty;
|
|
1116
|
+
card.stability = next_state.stability;
|
|
1117
|
+
return card;
|
|
1136
1118
|
}
|
|
1137
1119
|
/**
|
|
1138
1120
|
* Review next_interval
|
|
@@ -1175,12 +1157,11 @@ class LongTermScheduler extends AbstractScheduler {
|
|
|
1175
1157
|
}
|
|
1176
1158
|
this.current.scheduled_days = 0;
|
|
1177
1159
|
this.current.elapsed_days = 0;
|
|
1178
|
-
const next_again = TypeConvert.card(this.current);
|
|
1179
|
-
const next_hard = TypeConvert.card(this.current);
|
|
1180
|
-
const next_good = TypeConvert.card(this.current);
|
|
1181
|
-
const next_easy = TypeConvert.card(this.current);
|
|
1182
|
-
this.init_ds(next_again, next_hard, next_good, next_easy);
|
|
1183
1160
|
const first_interval = 0;
|
|
1161
|
+
const next_again = this.next_ds(first_interval, Rating.Again);
|
|
1162
|
+
const next_hard = this.next_ds(first_interval, Rating.Hard);
|
|
1163
|
+
const next_good = this.next_ds(first_interval, Rating.Good);
|
|
1164
|
+
const next_easy = this.next_ds(first_interval, Rating.Easy);
|
|
1184
1165
|
this.next_interval(
|
|
1185
1166
|
next_again,
|
|
1186
1167
|
next_hard,
|
|
@@ -1192,31 +1173,20 @@ class LongTermScheduler extends AbstractScheduler {
|
|
|
1192
1173
|
this.update_next(next_again, next_hard, next_good, next_easy);
|
|
1193
1174
|
return this.next.get(grade);
|
|
1194
1175
|
}
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
1,
|
|
1205
|
-
10
|
|
1206
|
-
);
|
|
1207
|
-
next_hard.stability = this.algorithm.init_stability(Rating.Hard);
|
|
1208
|
-
next_good.difficulty = clamp(
|
|
1209
|
-
this.algorithm.init_difficulty(Rating.Good),
|
|
1210
|
-
1,
|
|
1211
|
-
10
|
|
1212
|
-
);
|
|
1213
|
-
next_good.stability = this.algorithm.init_stability(Rating.Good);
|
|
1214
|
-
next_easy.difficulty = clamp(
|
|
1215
|
-
this.algorithm.init_difficulty(Rating.Easy),
|
|
1216
|
-
1,
|
|
1217
|
-
10
|
|
1176
|
+
next_ds(t, g, r) {
|
|
1177
|
+
const next_state = this.algorithm.next_state(
|
|
1178
|
+
{
|
|
1179
|
+
difficulty: this.current.difficulty,
|
|
1180
|
+
stability: this.current.stability
|
|
1181
|
+
},
|
|
1182
|
+
t,
|
|
1183
|
+
g,
|
|
1184
|
+
r
|
|
1218
1185
|
);
|
|
1219
|
-
|
|
1186
|
+
const card = TypeConvert.card(this.current);
|
|
1187
|
+
card.difficulty = next_state.difficulty;
|
|
1188
|
+
card.stability = next_state.stability;
|
|
1189
|
+
return card;
|
|
1220
1190
|
}
|
|
1221
1191
|
/**
|
|
1222
1192
|
* @see https://github.com/open-spaced-repetition/ts-fsrs/issues/98#issuecomment-2241923194
|
|
@@ -1230,72 +1200,20 @@ class LongTermScheduler extends AbstractScheduler {
|
|
|
1230
1200
|
return exist;
|
|
1231
1201
|
}
|
|
1232
1202
|
const interval = this.elapsed_days;
|
|
1233
|
-
const
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
const next_hard = TypeConvert.card(this.current);
|
|
1237
|
-
const next_good = TypeConvert.card(this.current);
|
|
1238
|
-
const next_easy = TypeConvert.card(this.current);
|
|
1239
|
-
this.next_ds(
|
|
1240
|
-
next_again,
|
|
1241
|
-
next_hard,
|
|
1242
|
-
next_good,
|
|
1243
|
-
next_easy,
|
|
1244
|
-
difficulty,
|
|
1245
|
-
stability,
|
|
1246
|
-
retrievability
|
|
1203
|
+
const retrievability = this.algorithm.forgetting_curve(
|
|
1204
|
+
interval,
|
|
1205
|
+
this.current.stability
|
|
1247
1206
|
);
|
|
1207
|
+
const next_again = this.next_ds(interval, Rating.Again, retrievability);
|
|
1208
|
+
const next_hard = this.next_ds(interval, Rating.Hard, retrievability);
|
|
1209
|
+
const next_good = this.next_ds(interval, Rating.Good, retrievability);
|
|
1210
|
+
const next_easy = this.next_ds(interval, Rating.Easy, retrievability);
|
|
1248
1211
|
this.next_interval(next_again, next_hard, next_good, next_easy, interval);
|
|
1249
1212
|
this.next_state(next_again, next_hard, next_good, next_easy);
|
|
1250
1213
|
next_again.lapses += 1;
|
|
1251
1214
|
this.update_next(next_again, next_hard, next_good, next_easy);
|
|
1252
1215
|
return this.next.get(grade);
|
|
1253
1216
|
}
|
|
1254
|
-
/**
|
|
1255
|
-
* Review next_ds
|
|
1256
|
-
*/
|
|
1257
|
-
next_ds(next_again, next_hard, next_good, next_easy, difficulty, stability, retrievability) {
|
|
1258
|
-
next_again.difficulty = this.algorithm.next_difficulty(
|
|
1259
|
-
difficulty,
|
|
1260
|
-
Rating.Again
|
|
1261
|
-
);
|
|
1262
|
-
const s_after_fail = this.algorithm.next_forget_stability(
|
|
1263
|
-
difficulty,
|
|
1264
|
-
stability,
|
|
1265
|
-
retrievability
|
|
1266
|
-
);
|
|
1267
|
-
next_again.stability = clamp(stability, S_MIN, s_after_fail);
|
|
1268
|
-
next_hard.difficulty = this.algorithm.next_difficulty(
|
|
1269
|
-
difficulty,
|
|
1270
|
-
Rating.Hard
|
|
1271
|
-
);
|
|
1272
|
-
next_hard.stability = this.algorithm.next_recall_stability(
|
|
1273
|
-
difficulty,
|
|
1274
|
-
stability,
|
|
1275
|
-
retrievability,
|
|
1276
|
-
Rating.Hard
|
|
1277
|
-
);
|
|
1278
|
-
next_good.difficulty = this.algorithm.next_difficulty(
|
|
1279
|
-
difficulty,
|
|
1280
|
-
Rating.Good
|
|
1281
|
-
);
|
|
1282
|
-
next_good.stability = this.algorithm.next_recall_stability(
|
|
1283
|
-
difficulty,
|
|
1284
|
-
stability,
|
|
1285
|
-
retrievability,
|
|
1286
|
-
Rating.Good
|
|
1287
|
-
);
|
|
1288
|
-
next_easy.difficulty = this.algorithm.next_difficulty(
|
|
1289
|
-
difficulty,
|
|
1290
|
-
Rating.Easy
|
|
1291
|
-
);
|
|
1292
|
-
next_easy.stability = this.algorithm.next_recall_stability(
|
|
1293
|
-
difficulty,
|
|
1294
|
-
stability,
|
|
1295
|
-
retrievability,
|
|
1296
|
-
Rating.Easy
|
|
1297
|
-
);
|
|
1298
|
-
}
|
|
1299
1217
|
/**
|
|
1300
1218
|
* Review/New next_interval
|
|
1301
1219
|
*/
|
|
@@ -1983,6 +1901,7 @@ exports.fsrs = fsrs;
|
|
|
1983
1901
|
exports.generatorParameters = generatorParameters;
|
|
1984
1902
|
exports.get_fuzz_range = get_fuzz_range;
|
|
1985
1903
|
exports.migrateParameters = migrateParameters;
|
|
1904
|
+
exports.roundTo = roundTo;
|
|
1986
1905
|
exports.show_diff_message = show_diff_message;
|
|
1987
1906
|
module.exports = Object.assign(exports.default || {}, exports)
|
|
1988
1907
|
//# sourceMappingURL=index.cjs.map
|