Wave 3: refactor generators batch 1 to crypto random

- refactor(generators): migrate Card, Coin, Color, Country to crypto random
- refactor(generators): migrate Date, Emoji, FontPair to crypto random
- refactor(dice): deduplicate DiceGenerator by importing from dice-engine.ts
This commit is contained in:
emil
2026-05-13 20:16:40 +03:00
parent e24a8b744d
commit 3d94c99ff9
10 changed files with 128 additions and 271 deletions
+54 -2
View File
@@ -8,7 +8,7 @@
"plan_name": "critical-refactor",
"status": "active",
"started_at": "2026-05-13T16:25:19.266Z",
"updated_at": "2026-05-13T16:42:05.595Z",
"updated_at": "2026-05-13T17:16:10.363Z",
"session_ids": [
"ses_1de6ede59ffexUwt5C0sddEWwR"
],
@@ -42,6 +42,32 @@
"status": "completed",
"ended_at": "2026-05-13T16:42:05.595Z",
"elapsed_ms": 68147
},
"todo:6": {
"task_key": "todo:6",
"task_label": "6",
"task_title": "Refactor CardGenerator.astro",
"session_id": "ses_1ddc76889ffe240ehScl9taAza",
"agent": "Sisyphus-Junior",
"category": "unspecified-high",
"updated_at": "2026-05-13T16:47:10.433Z",
"started_at": "2026-05-13T16:44:57.812Z",
"status": "completed",
"ended_at": "2026-05-13T16:47:10.433Z",
"elapsed_ms": 132621
},
"todo:10": {
"task_key": "todo:10",
"task_label": "10",
"task_title": "Refactor DateGenerator.astro",
"session_id": "ses_1ddc380cbffe0YA1da3ew6QlNf",
"agent": "Sisyphus-Junior",
"category": "unspecified-high",
"updated_at": "2026-05-13T17:16:10.363Z",
"started_at": "2026-05-13T16:49:43.859Z",
"status": "completed",
"ended_at": "2026-05-13T17:16:10.363Z",
"elapsed_ms": 1586504
}
}
}
@@ -49,7 +75,7 @@
"active_plan": "/home/emil/Desktop/Coding/AI/Randify.pro/.sisyphus/plans/critical-refactor.md",
"started_at": "2026-05-13T16:25:19.266Z",
"status": "active",
"updated_at": "2026-05-13T16:42:05.595Z",
"updated_at": "2026-05-13T17:16:10.363Z",
"session_ids": [
"ses_1de6ede59ffexUwt5C0sddEWwR"
],
@@ -83,6 +109,32 @@
"status": "completed",
"ended_at": "2026-05-13T16:42:05.595Z",
"elapsed_ms": 68147
},
"todo:6": {
"task_key": "todo:6",
"task_label": "6",
"task_title": "Refactor CardGenerator.astro",
"session_id": "ses_1ddc76889ffe240ehScl9taAza",
"agent": "Sisyphus-Junior",
"category": "unspecified-high",
"updated_at": "2026-05-13T16:47:10.433Z",
"started_at": "2026-05-13T16:44:57.812Z",
"status": "completed",
"ended_at": "2026-05-13T16:47:10.433Z",
"elapsed_ms": 132621
},
"todo:10": {
"task_key": "todo:10",
"task_label": "10",
"task_title": "Refactor DateGenerator.astro",
"session_id": "ses_1ddc380cbffe0YA1da3ew6QlNf",
"agent": "Sisyphus-Junior",
"category": "unspecified-high",
"updated_at": "2026-05-13T17:16:10.363Z",
"started_at": "2026-05-13T16:49:43.859Z",
"status": "completed",
"ended_at": "2026-05-13T17:16:10.363Z",
"elapsed_ms": 1586504
}
},
"agent": "atlas"
+8 -8
View File
@@ -427,7 +427,7 @@ Max Concurrent: 8 (Wave 3)
- Message: `feat(components): add ResultBox and CopyButton shared components`
- Files: `src/components/ResultBox.astro`, `src/components/CopyButton.astro`
- [ ] 6. Refactor CardGenerator.astro
- [x] 6. Refactor CardGenerator.astro
**What to do**:
- Replace `Math.random()` with `random.ts` functions
@@ -457,7 +457,7 @@ Max Concurrent: 8 (Wave 3)
**Commit**: NO (groups with batch)
- [ ] 7. Refactor CoinGenerator.astro
- [x] 7. Refactor CoinGenerator.astro
**What to do**: Same pattern as Task 6.
**Recommended Agent Profile**: `unspecified-high`
@@ -467,7 +467,7 @@ Max Concurrent: 8 (Wave 3)
**QA Scenarios**: grep Math.random → empty
**Commit**: NO
- [ ] 8. Refactor ColorGenerator.astro
- [x] 8. Refactor ColorGenerator.astro
**What to do**: Same pattern as Task 6.
**Recommended Agent Profile**: `unspecified-high`
@@ -477,7 +477,7 @@ Max Concurrent: 8 (Wave 3)
**QA Scenarios**: grep Math.random → empty
**Commit**: NO
- [ ] 9. Refactor CountryGenerator.astro
- [x] 9. Refactor CountryGenerator.astro
**What to do**: Same pattern as Task 6.
**Recommended Agent Profile**: `unspecified-high`
@@ -487,7 +487,7 @@ Max Concurrent: 8 (Wave 3)
**QA Scenarios**: grep Math.random → empty
**Commit**: NO
- [ ] 10. Refactor DateGenerator.astro
- [x] 10. Refactor DateGenerator.astro
**What to do**: Same pattern as Task 6.
**Recommended Agent Profile**: `unspecified-high`
@@ -497,7 +497,7 @@ Max Concurrent: 8 (Wave 3)
**QA Scenarios**: grep Math.random → empty
**Commit**: NO
- [ ] 11. Refactor EmojiGenerator.astro
- [x] 11. Refactor EmojiGenerator.astro
**What to do**: Same pattern as Task 6.
**Recommended Agent Profile**: `unspecified-high`
@@ -507,7 +507,7 @@ Max Concurrent: 8 (Wave 3)
**QA Scenarios**: grep Math.random → empty
**Commit**: NO
- [ ] 12. Refactor FontPairGenerator.astro
- [x] 12. Refactor FontPairGenerator.astro
**What to do**: Same pattern as Task 6.
**Recommended Agent Profile**: `unspecified-high`
@@ -685,7 +685,7 @@ Max Concurrent: 8 (Wave 3)
- Message: `refactor(generators): migrate batch 4 to crypto random (Teams, Time, Uuid, Weighted, WheelSpinner, YesNo)`
- Files: `src/components/generators/{Teams,Time,Uuid,Weighted,YesNo}Generator.astro`, `src/components/generators/WheelSpinner.astro`
- [ ] 33. Refactor DiceGenerator to import dice-engine.ts
- [x] 33. Refactor DiceGenerator to import dice-engine.ts
**What to do**:
- Replace inline dice engine code in `DiceGenerator.astro:274-958` with imports from `src/lib/dice-engine.ts`
+10 -19
View File
@@ -116,19 +116,15 @@ const T = useT(isRu ? "ru" : "en");
<script>
import { CopyFeedback } from "@/lib/client/clipboard";
import { createErrorDisplay } from "@/lib/client/validation";
import { randomInt, shuffleArray } from "@/lib/client/random";
import { getClientT } from "@/lib/client/i18n";
const isRu = document.documentElement.lang === "ru";
const COPY_LABEL = isRu ? "Копировать" : "Copy";
const COPIED_LABEL = isRu ? "Скопировано" : "Copied";
const ERR_AT_LEAST = isRu
? "Вытащите хотя бы 1 карту."
: "Draw at least 1 card.";
const ERR_UNIQUE = isRu
? "Нельзя вытащить более 52 уникальных карт."
: "Cannot draw more than 52 unique cards.";
const ERR_MAX52 = isRu
? "Максимум 52 карты одновременно."
: "Maximum 52 cards at once.";
const T = getClientT();
const COPY_LABEL = T.copy;
const COPIED_LABEL = T.copied;
const ERR_AT_LEAST = T.errDrawAtLeast;
const ERR_UNIQUE = T.errMaxUniqueCards;
const ERR_MAX52 = T.errMax52Cards;
const countInput = document.getElementById("crd-count") as HTMLInputElement;
const replaceCb = document.getElementById("crd-replace") as HTMLInputElement;
@@ -195,15 +191,10 @@ const T = useT(isRu ? "ru" : "en");
if (withReplacement) {
lastCards = Array.from(
{ length: count },
() => DECK[Math.floor(Math.random() * DECK.length)],
() => DECK[randomInt(0, DECK.length - 1)],
);
} else {
const deck = [...DECK];
for (let i = 0; i < count; i++) {
const j = i + Math.floor(Math.random() * (deck.length - i));
[deck[i], deck[j]] = [deck[j], deck[i]];
}
lastCards = deck.slice(0, count);
lastCards = shuffleArray(DECK).slice(0, count);
}
renderCards();
+12 -10
View File
@@ -100,16 +100,18 @@ const T = useT(isRu ? "ru" : "en");
<script>
import { CopyFeedback } from "@/lib/client/clipboard";
import { randomFloat } from "@/lib/client/random";
import { getClientT } from "@/lib/client/i18n";
const isRu = document.documentElement.lang === "ru";
const COPY_LABEL = isRu ? "Копировать" : "Copy";
const COPIED_LABEL = isRu ? "Скопировано" : "Copied";
const HEADS_LABEL = isRu ? "О" : "H";
const TAILS_LABEL = isRu ? "Р" : "T";
const HEADS_WORD = isRu ? "орёл" : "Heads";
const TAILS_WORD = isRu ? "решка" : "Tails";
const HEADS_SUMMARY = isRu ? "орёл" : "heads";
const TAILS_SUMMARY = isRu ? "решка" : "tails";
const T = getClientT();
const COPY_LABEL = T.copy;
const COPIED_LABEL = T.copied;
const HEADS_LABEL = T.headsLabel;
const TAILS_LABEL = T.tailsLabel;
const HEADS_WORD = T.headsWord;
const TAILS_WORD = T.tailsWord;
const HEADS_SUMMARY = T.headsWord;
const TAILS_SUMMARY = T.tailsWord;
const countInput = document.getElementById("coin-count") as HTMLInputElement;
const countVal = document.getElementById("coin-count-val") as HTMLSpanElement;
@@ -135,7 +137,7 @@ const T = useT(isRu ? "ru" : "en");
function flip() {
const count = parseInt(countInput.value, 10);
lastResults = Array.from({ length: count }, () => Math.random() < 0.5);
lastResults = Array.from({ length: count }, () => randomFloat() < 0.5);
const heads = lastResults.filter(Boolean).length;
const tails = count - heads;
@@ -95,6 +95,10 @@ const T = useT(isRu ? "ru" : "en");
</div>
<script>
import { randomInt } from "@/lib/client/random";
import { getClientT } from "@/lib/client/i18n";
const T = getClientT();
const swatch = document.getElementById("cg-swatch") as HTMLDivElement;
const valuesEl = document.getElementById("cg-values") as HTMLDivElement;
const hexEl = document.getElementById("cg-hex") as HTMLSpanElement;
@@ -166,9 +170,9 @@ const T = useT(isRu ? "ru" : "en");
);
function generate() {
const r = Math.floor(Math.random() * 256);
const g = Math.floor(Math.random() * 256);
const b = Math.floor(Math.random() * 256);
const r = randomInt(0, 255);
const g = randomInt(0, 255);
const b = randomInt(0, 255);
const [h, s, l] = rgbToHsl(r, g, b);
const hex = `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();
@@ -124,6 +124,9 @@ const regions = isRu
</div>
<script>
import { randomInt } from "@/lib/client/random";
import { getClientLang, getClientT } from "@/lib/client/i18n";
interface Country {
name: string;
ruName: string;
@@ -190,7 +193,9 @@ const regions = isRu
const checkIcon = document.getElementById("cg-check-icon") as SVGElement;
const copyText = document.getElementById("cg-copy-text") as HTMLSpanElement;
const isRu = document.documentElement.lang === "ru";
const lang = getClientLang();
const isRu = lang === "ru";
const T = getClientT();
function formatPopulation(n: number): string {
if (n >= 1_000_000_000) return (n / 1_000_000_000).toFixed(1).replace(/\.0$/, "") + "B";
@@ -208,7 +213,7 @@ const regions = isRu
function generate() {
const list = getFilteredCountries();
if (list.length === 0) return;
const country = list[Math.floor(Math.random() * list.length)];
const country = list[randomInt(0, list.length - 1)];
flagEl.textContent = country.flagEmoji;
nameEl.textContent = isRu ? country.ruName : country.name;
@@ -235,13 +240,13 @@ const regions = isRu
copyIcon.classList.add("hidden");
checkIcon.classList.remove("hidden");
copyText.textContent = isRu ? "Скопировано" : "Copied";
copyText.textContent = T.copied;
if (copyTimer) clearTimeout(copyTimer);
copyTimer = setTimeout(() => {
checkIcon.classList.add("hidden");
copyIcon.classList.remove("hidden");
copyText.textContent = isRu ? "Копировать" : "Copy";
copyText.textContent = T.copy;
}, 1500);
});
@@ -113,8 +113,12 @@ const T = useT(isRu ? "ru" : "en");
import { CopyFeedback } from "@/lib/client/clipboard";
import { createErrorDisplay } from "@/lib/client/validation";
import { popElement } from "@/lib/client/animations";
import { randomFloat } from "@/lib/client/random";
import { getClientLang, getClientT } from "@/lib/client/i18n";
const isRu = document.documentElement.lang === "ru";
const lang = getClientLang();
const isRu = lang === "ru";
const T = getClientT();
const ERR_BOTH_DATES = isRu
? "Укажите обе даты."
: "Please enter both dates.";
@@ -158,7 +162,7 @@ const T = useT(isRu ? "ru" : "en");
const fromMs = fromDate.getTime();
const toMs = toDate.getTime();
const randomMs = fromMs + Math.random() * (toMs - fromMs);
const randomMs = fromMs + randomFloat() * (toMs - fromMs);
const resultDate = new Date(randomMs);
const formatted = resultDate.toLocaleDateString(isRu ? "ru-RU" : "en-US", {
+9 -207
View File
@@ -270,217 +270,19 @@ const presets = [
<!-- Script section appended to DiceGenerator.astro -->
<script type="module">
// ═══ Inline Dice Engine ═══════════════════════════════════════════════════════
<script>
import { parseDiceNotation, rollDice, rollAdvantage, buildNotation } from "@/lib/dice-engine";
import { getClientLang, getClientT } from "@/lib/client/i18n";
const EXPLODE_CAP = 100;
const REROLL_CAP = 1000;
function rollDie(sides, explode, reroll) {
let value = Math.floor(Math.random() * sides) + 1;
const original = value;
let rerolledFrom = null;
const explosions = [];
let exploded = false;
if (reroll.active && reroll.values.has(value)) {
rerolledFrom = value;
let rc = 0;
while (reroll.values.has(value) && rc < REROLL_CAP) {
value = Math.floor(Math.random() * sides) + 1;
rc++;
if (reroll.once) break;
}
}
const isRu = getClientLang() === "ru";
const T = getClientT();
const threshold = explode.threshold ?? sides;
if (explode.active && value >= threshold) {
exploded = true;
let chain = 0;
while (chain < EXPLODE_CAP) {
let next = Math.floor(Math.random() * sides) + 1;
if (explode.penetrating) next = Math.max(1, next - 1);
explosions.push(next);
if (next < threshold) break;
chain++;
}
}
return {
value,
original,
exploded,
explosions,
rerolledFrom,
dropped: false,
};
}
function parseDiceNotation(raw) {
const s = raw.replace(/\s+/g, "").toLowerCase();
if (!s) return null;
const main = s.match(/^(\d*)d(\d+)(.*)$/);
if (!main) return null;
const count = main[1] === "" ? 1 : parseInt(main[1], 10);
const sides = parseInt(main[2], 10);
let remaining = main[3];
if (count < 1 || count > 20 || sides < 1 || sides > 9999) return null;
let keepDrop = null;
const kd = remaining.match(/^(kh|kl|dh|dl)(\d+)/);
if (kd) {
const kc = parseInt(kd[2], 10);
if (kc < 1 || kc >= count) return null;
keepDrop = { type: kd[1], count: kc };
remaining = remaining.slice(kd[0].length);
}
const explode = { active: false, threshold: null, penetrating: false };
const em = remaining.match(/^(!p|!)(>?)(\d*)/);
if (em) {
explode.active = true;
explode.penetrating = em[1] === "!p";
if (em[2] === ">" && em[3]) {
explode.threshold = parseInt(em[3], 10);
if (explode.threshold < 2 || explode.threshold > sides) return null;
}
remaining = remaining.slice(em[0].length);
}
const reroll = {
active: false,
values: new Set(),
once: false,
operator: "eq",
};
const rm = remaining.match(/^(ro|r)([<>]?)(\d+)/);
if (rm) {
reroll.active = true;
reroll.once = rm[1] === "ro";
const op = rm[2],
val = parseInt(rm[3], 10);
if (op === "<") {
reroll.operator = "lt";
for (let i = 1; i < val && i < sides; i++) reroll.values.add(i);
} else if (op === ">") {
reroll.operator = "gt";
for (let i = val + 1; i <= sides; i++) reroll.values.add(i);
} else {
reroll.operator = "eq";
reroll.values.add(val);
}
remaining = remaining.slice(rm[0].length);
}
let modifier = 0;
const mm = remaining.match(/^([+-]\d+)$/);
if (mm) {
modifier = parseInt(mm[1], 10);
if (Math.abs(modifier) > 999) return null;
remaining = remaining.slice(mm[0].length);
}
if (remaining.length > 0) return null;
return {
count,
sides,
modifier,
keepDrop,
explode,
reroll,
advantage: false,
disadvantage: false,
};
}
function rollDice(parsed) {
const dice = [];
for (let i = 0; i < parsed.count; i++)
dice.push(rollDie(parsed.sides, parsed.explode, parsed.reroll));
let kept = [...dice];
let dropped = [];
if (parsed.keepDrop) {
const indexed = dice.map((d, i) => ({ die: d, idx: i }));
if (parsed.keepDrop.type === "kh")
indexed.sort((a, b) => b.die.value - a.die.value);
else if (parsed.keepDrop.type === "kl")
indexed.sort((a, b) => a.die.value - b.die.value);
else if (parsed.keepDrop.type === "dh")
indexed.sort((a, b) => b.die.value - a.die.value);
else if (parsed.keepDrop.type === "dl")
indexed.sort((a, b) => a.die.value - b.die.value);
const keepN = parsed.keepDrop.type.startsWith("k")
? parsed.keepDrop.count
: parsed.count - parsed.keepDrop.count;
const keepSet = new Set(indexed.slice(0, keepN).map((x) => x.idx));
kept = dice.filter((_, i) => keepSet.has(i));
dropped = dice.filter((_, i) => !keepSet.has(i));
}
dropped.forEach((d) => (d.dropped = true));
let total = kept.reduce((s, d) => s + d.value, 0);
total += kept.reduce(
(s, d) => s + d.explosions.reduce((ss, v) => ss + v, 0),
0,
);
total += parsed.modifier;
return {
dice,
kept,
dropped,
modifier: parsed.modifier,
total,
notation: "",
advantageRolls: null,
};
}
function rollAdvantage(sides, modifier, advantage) {
const r1 = Math.floor(Math.random() * sides) + 1;
const r2 = Math.floor(Math.random() * sides) + 1;
const kv = advantage ? Math.max(r1, r2) : Math.min(r1, r2);
const die = {
value: kv,
original: kv,
exploded: false,
explosions: [],
rerolledFrom: null,
dropped: false,
};
return {
dice: [die],
kept: [die],
dropped: [],
modifier,
total: kv + modifier,
notation: "",
advantageRolls: [r1, r2],
};
}
function buildNotation(count, sides, modifier) {
let s = `${count}d${sides}`;
if (modifier > 0) s += `+${modifier}`;
else if (modifier < 0) s += modifier;
return s;
}
// ═══ DOM + Logic ══════════════════════════════════════════════════════════════
const isRu = document.documentElement.lang === "ru";
const COPY_LABEL = isRu ? "Копировать" : "Copy";
const COPIED_LABEL = isRu ? "Скопировано" : "Copied";
const ERR_COUNT = isRu
? "Количество кубиков должно быть не менее 1."
: "Number of dice must be at least 1.";
const ERR_MAX = isRu
? "Максимум 20 кубиков одновременно."
: "Maximum 20 dice at once.";
const COPY_LABEL = T.copy;
const COPIED_LABEL = T.copied;
const ERR_COUNT = T.errDiceAtLeast;
const ERR_MAX = T.errDiceMax;
const ERR_NOTATION = isRu
? "Неверный формат — попробуйте 2d6+3"
: "Invalid notation — try 2d6+3";
+7 -13
View File
@@ -139,12 +139,15 @@ const clickToCopyLabel = isRu ? "Нажмите, чтобы скопироват
import { createErrorDisplay } from "@/lib/client/validation";
import { CopyFeedback } from "@/lib/client/clipboard";
import { popElement } from "@/lib/client/animations";
import { shuffleArray } from "@/lib/client/random";
import { getClientLang, getClientT } from "@/lib/client/i18n";
const isRu = document.documentElement.lang === "ru";
const COPIED_LABEL = isRu ? "Скопировано!" : "Copied!";
const COPY_ALL_LABEL = isRu ? "Копировать всё" : "Copy all";
const isRu = getClientLang() === "ru";
const T = getClientT();
const COPIED_LABEL = T.copied;
const COPY_ALL_LABEL = T.copyAll;
const ERR_COUNT_RANGE = isRu ? "Укажите количество от 1 до 50." : "Enter a count between 1 and 50.";
const TOOLTIP_COPIED = isRu ? "Скопировано!" : "Copied!";
const TOOLTIP_COPIED = T.copied;
const EMOJIS: Record<string, string[]> = {
smileys: ["😀","😃","😄","😁","😆","😅","😂","🤣","😊","😇","🙂","🙃","😉","😌","😍","🥰","😘","😗","😙","😚","😋","😛","😝","😜","🤪","🤨","🧐","🤓","😎","🥸","🤩","🥳","😏","😒","😞","😔","😟","😕","🙁","☹️","😣","😖","😫","😩","🥺","😢","😭","😤","😠","😡","🤬","🤯","😳","🥵","🥶","😱","😨","😰","😥","😓","🤗","🤔","🤭","🤫","🤥","😶","😐","😑","😬","🙄","😯","😦","😧","😮","😲","🥱","😴","🤤","😪","😵","🤐","🥴","🤢","🤮","🤧","😷","🤒","🤕","🤑","🤠","😈","👿","👹","👺","🤡","💩","👻","💀","☠️","👽","👾","🤖","🎃","😺","😸","😹","😻","😼","😽","🙀","😿","😾"],
@@ -552,15 +555,6 @@ const clickToCopyLabel = isRu ? "Нажмите, чтобы скопироват
const errors = createErrorDisplay(errorEl);
function shuffleArray<T>(arr: T[]): T[] {
const a = [...arr];
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
function getEmojiName(emoji: string): string {
if (isRu) {
return EMOJI_NAMES_RU[emoji] || EMOJI_NAMES_EN[emoji] || emoji;
@@ -170,8 +170,11 @@ const i18n = {
<script>
import { createErrorDisplay } from "@/lib/client/validation";
import { randomInt } from "@/lib/client/random";
import { getClientLang, getClientT } from "@/lib/client/i18n";
const isRu = document.documentElement.lang === "ru";
const isRu = getClientLang() === "ru";
const clientT = getClientT();
const generateBtn = document.getElementById("fp-btn") as HTMLButtonElement;
const shuffleHeadingBtn = document.getElementById("fp-shuffle-heading") as HTMLButtonElement;
@@ -309,7 +312,7 @@ const i18n = {
let loadedFonts = new Set<string>();
function randomItem<T>(arr: T[]): T {
return arr[Math.floor(Math.random() * arr.length)];
return arr[randomInt(0, arr.length - 1)];
}
function getGoogleFontsUrl(heading: Font, body: Font): string {
@@ -472,7 +475,7 @@ const i18n = {
await navigator.clipboard.writeText(fullText);
const originalHtml = copyBtn.innerHTML;
copyBtn.innerHTML = `<span class="inline-flex items-center gap-2">${checkIconSvg} ${isRu ? "Скопировано!" : "Copied!"}</span>`;
copyBtn.innerHTML = `<span class="inline-flex items-center gap-2">${checkIconSvg} ${clientT.copied}</span>`;
setTimeout(() => {
copyBtn.innerHTML = originalHtml;
}, 1500);