mirror of
https://github.com/zrwusa/data-structure-typed.git
synced 2024-11-23 12:54:04 +00:00
fix: Explicitly specify IterationType.
This commit is contained in:
parent
312ba2f492
commit
25de27cf6c
|
@ -8,7 +8,7 @@ All notable changes to this project will be documented in this file.
|
|||
- [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
||||
- [`auto-changelog`](https://github.com/CookPete/auto-changelog)
|
||||
|
||||
## [v1.50.7](https://github.com/zrwusa/data-structure-typed/compare/v1.35.0...main) (upcoming)
|
||||
## [v1.50.8](https://github.com/zrwusa/data-structure-typed/compare/v1.35.0...main) (upcoming)
|
||||
|
||||
### Changes
|
||||
|
||||
|
|
24
README.md
24
README.md
|
@ -16,8 +16,8 @@
|
|||
## Why
|
||||
|
||||
Do you envy C++ with [STL]() (std::), Python with [collections](), and Java with [java.util]() ? Well, no need to envy
|
||||
anymore! JavaScript and TypeScript now have [data-structure-typed]().**`Benchmark`** compared with C++ STL. *
|
||||
*`API standards`** aligned with ES6 and Java. **`Usability`** is comparable to Python
|
||||
anymore! JavaScript and TypeScript now have [data-structure-typed]().**`Benchmark`** compared with C++ STL.
|
||||
**`API standards`** aligned with ES6 and Java. **`Usability`** is comparable to Python
|
||||
|
||||
|
||||
[//]: # (![Branches](https://img.shields.io/badge/branches-55.47%25-red.svg?style=flat))
|
||||
|
@ -45,56 +45,56 @@ Performance surpasses that of native JS/TS
|
|||
<th>Time Taken</th>
|
||||
<th>Data Scale</th>
|
||||
<th>Belongs To</th>
|
||||
<th>Complexity</th>
|
||||
<th>big O</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Queue.push & shift</td>
|
||||
<td>5.83 ms</td>
|
||||
<td>100,000</td>
|
||||
<td>100K</td>
|
||||
<td>Ours</td>
|
||||
<td>O(1)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Array.push & shift</td>
|
||||
<td>2829.59 ms</td>
|
||||
<td>100,000</td>
|
||||
<td>100K</td>
|
||||
<td>Native JS</td>
|
||||
<td>O(n)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Deque.unshift & shift</td>
|
||||
<td>2.44 ms</td>
|
||||
<td>100,000</td>
|
||||
<td>100K</td>
|
||||
<td>Ours</td>
|
||||
<td>O(1)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Array.unshift & shift</td>
|
||||
<td>4750.37 ms</td>
|
||||
<td>100,000</td>
|
||||
<td>100K</td>
|
||||
<td>Native JS</td>
|
||||
<td>O(n)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>HashMap.set</td>
|
||||
<td>122.51 ms</td>
|
||||
<td>1,000,000</td>
|
||||
<td>1M</td>
|
||||
<td>Ours</td>
|
||||
<td>O(1)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Map.set</td>
|
||||
<td>223.80 ms</td>
|
||||
<td>1,000,000</td>
|
||||
<td>1M</td>
|
||||
<td>Native JS</td>
|
||||
<td>O(1)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Set.add</td>
|
||||
<td>185.06 ms</td>
|
||||
<td>1,000,000</td>
|
||||
<td>1M</td>
|
||||
<td>Native JS</td>
|
||||
<td>O(1)</td>
|
||||
</tr>
|
||||
|
@ -102,7 +102,7 @@ Performance surpasses that of native JS/TS
|
|||
</table>
|
||||
|
||||
### Conciseness and uniformity
|
||||
In Java.utils, you need to memorize a table for all sequential data structures(Queue, Deque, LinkedList),
|
||||
In [java.utils](), you need to memorize a table for all sequential data structures(Queue, Deque, LinkedList),
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
|
@ -141,7 +141,7 @@ In Java.utils, you need to memorize a table for all sequential data structures(Q
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
whereas in our data-structure-typed, you **only** need to remember four methods: `push`, `pop`, `shift`, and `unshift` for all sequential data structures.
|
||||
whereas in our [data-structure-typed](), you **only** need to remember four methods: `push`, `pop`, `shift`, and `unshift` for all sequential data structures(Queue, Deque, DoublyLinkedList, SinglyLinkedList and Array).
|
||||
|
||||
## Installation and Usage
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import type {
|
|||
BinaryTreeDeleteResult,
|
||||
BSTNKeyOrNode,
|
||||
BTNCallback,
|
||||
IterationType,
|
||||
KeyOrNodeOrEntry
|
||||
} from '../../types';
|
||||
import { IBinaryTree } from '../../interfaces';
|
||||
|
@ -316,7 +317,7 @@ export class AVLTreeMultiMap<
|
|||
* values:
|
||||
* @returns a boolean value.
|
||||
*/
|
||||
override perfectlyBalance(iterationType = this.iterationType): boolean {
|
||||
override perfectlyBalance(iterationType: IterationType = this.iterationType): boolean {
|
||||
const sorted = this.dfs(node => node, 'in'),
|
||||
n = sorted.length;
|
||||
if (sorted.length < 1) return false;
|
||||
|
|
|
@ -264,7 +264,10 @@ export class BinaryTree<
|
|||
* @returns either the node corresponding to the given key if it is a valid node key, or the key
|
||||
* itself if it is not a valid node key.
|
||||
*/
|
||||
ensureNode(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, NODE>, iterationType = 'ITERATIVE'): NODE | null | undefined {
|
||||
ensureNode(
|
||||
keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, NODE>,
|
||||
iterationType: IterationType = 'ITERATIVE'
|
||||
): NODE | null | undefined {
|
||||
let res: NODE | null | undefined;
|
||||
if (this.isRealNode(keyOrNodeOrEntry)) {
|
||||
res = keyOrNodeOrEntry;
|
||||
|
@ -594,7 +597,7 @@ export class BinaryTree<
|
|||
callback: C = this._defaultOneParamCallback as C,
|
||||
onlyOne = false,
|
||||
beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
|
||||
iterationType = this.iterationType
|
||||
iterationType: IterationType = this.iterationType
|
||||
): NODE[] {
|
||||
if ((!callback || callback === this._defaultOneParamCallback) && (identifier as any) instanceof BinaryTreeNode)
|
||||
callback = (node => node) as C;
|
||||
|
@ -684,7 +687,7 @@ export class BinaryTree<
|
|||
identifier: ReturnType<C> | null | undefined,
|
||||
callback: C = this._defaultOneParamCallback as C,
|
||||
beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
|
||||
iterationType = this.iterationType
|
||||
iterationType: IterationType = this.iterationType
|
||||
): NODE | null | undefined {
|
||||
if ((!callback || callback === this._defaultOneParamCallback) && (identifier as any) instanceof BinaryTreeNode)
|
||||
callback = (node => node) as C;
|
||||
|
@ -711,7 +714,7 @@ export class BinaryTree<
|
|||
* @returns The function `getNodeByKey` returns a node (`NODE`) if a node with the specified key is
|
||||
* found in the binary tree. If no node is found, it returns `undefined`.
|
||||
*/
|
||||
getNodeByKey(key: K, iterationType = 'ITERATIVE'): NODE | undefined {
|
||||
getNodeByKey(key: K, iterationType: IterationType = 'ITERATIVE'): NODE | undefined {
|
||||
if (!this.root) return undefined;
|
||||
if (iterationType === 'RECURSIVE') {
|
||||
const _dfs = (cur: NODE): NODE | undefined => {
|
||||
|
@ -788,7 +791,7 @@ export class BinaryTree<
|
|||
identifier: ReturnType<C> | null | undefined,
|
||||
callback: C = this._defaultOneParamCallback as C,
|
||||
beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
|
||||
iterationType = this.iterationType
|
||||
iterationType: IterationType = this.iterationType
|
||||
): V | undefined {
|
||||
if ((!callback || callback === this._defaultOneParamCallback) && (identifier as any) instanceof BinaryTreeNode)
|
||||
callback = (node => node) as C;
|
||||
|
@ -847,7 +850,7 @@ export class BinaryTree<
|
|||
identifier: ReturnType<C> | null | undefined,
|
||||
callback: C = this._defaultOneParamCallback as C,
|
||||
beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
|
||||
iterationType = this.iterationType
|
||||
iterationType: IterationType = this.iterationType
|
||||
): boolean {
|
||||
if ((!callback || callback === this._defaultOneParamCallback) && (identifier as any) instanceof BinaryTreeNode)
|
||||
callback = (node => node) as C;
|
||||
|
@ -924,7 +927,10 @@ export class BinaryTree<
|
|||
* possible values:
|
||||
* @returns a boolean value.
|
||||
*/
|
||||
isBST(beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root, iterationType = this.iterationType): boolean {
|
||||
isBST(
|
||||
beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
|
||||
iterationType: IterationType = this.iterationType
|
||||
): boolean {
|
||||
// TODO there is a bug
|
||||
beginRoot = this.ensureNode(beginRoot);
|
||||
if (!beginRoot) return true;
|
||||
|
@ -1016,7 +1022,10 @@ export class BinaryTree<
|
|||
* values:
|
||||
* @returns the height of the binary tree.
|
||||
*/
|
||||
getHeight(beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root, iterationType = this.iterationType): number {
|
||||
getHeight(
|
||||
beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
|
||||
iterationType: IterationType = this.iterationType
|
||||
): number {
|
||||
beginRoot = this.ensureNode(beginRoot);
|
||||
if (!this.isRealNode(beginRoot)) return -1;
|
||||
|
||||
|
@ -1064,7 +1073,10 @@ export class BinaryTree<
|
|||
* to calculate the minimum height of a binary tree. It can have two possible values:
|
||||
* @returns The function `getMinHeight` returns the minimum height of a binary tree.
|
||||
*/
|
||||
getMinHeight(beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root, iterationType = this.iterationType): number {
|
||||
getMinHeight(
|
||||
beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
|
||||
iterationType: IterationType = this.iterationType
|
||||
): number {
|
||||
beginRoot = this.ensureNode(beginRoot);
|
||||
if (!beginRoot) return -1;
|
||||
|
||||
|
@ -1164,7 +1176,7 @@ export class BinaryTree<
|
|||
*/
|
||||
getLeftMost(
|
||||
beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
|
||||
iterationType = this.iterationType
|
||||
iterationType: IterationType = this.iterationType
|
||||
): NODE | null | undefined {
|
||||
if (this.isNIL(beginRoot)) return beginRoot as NODE;
|
||||
beginRoot = this.ensureNode(beginRoot);
|
||||
|
@ -1211,7 +1223,7 @@ export class BinaryTree<
|
|||
*/
|
||||
getRightMost(
|
||||
beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
|
||||
iterationType = this.iterationType
|
||||
iterationType: IterationType = this.iterationType
|
||||
): NODE | null | undefined {
|
||||
if (this.isNIL(beginRoot)) return beginRoot as NODE;
|
||||
// TODO support get right most by passing key in
|
||||
|
@ -1476,7 +1488,7 @@ export class BinaryTree<
|
|||
bfs<C extends BTNCallback<NODE | null>>(
|
||||
callback: C = this._defaultOneParamCallback as C,
|
||||
beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
|
||||
iterationType = this.iterationType,
|
||||
iterationType: IterationType = this.iterationType,
|
||||
includeNull = false
|
||||
): ReturnType<C>[] {
|
||||
beginRoot = this.ensureNode(beginRoot);
|
||||
|
@ -1570,7 +1582,7 @@ export class BinaryTree<
|
|||
listLevels<C extends BTNCallback<NODE | null>>(
|
||||
callback: C = this._defaultOneParamCallback as C,
|
||||
beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
|
||||
iterationType = this.iterationType,
|
||||
iterationType: IterationType = this.iterationType,
|
||||
includeNull = false
|
||||
): ReturnType<C>[][] {
|
||||
beginRoot = this.ensureNode(beginRoot);
|
||||
|
|
|
@ -210,7 +210,10 @@ export class BST<
|
|||
* type of iteration to be performed. It has a default value of `'ITERATIVE'`.
|
||||
* @returns either a node object (NODE) or undefined.
|
||||
*/
|
||||
override ensureNode(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, NODE>, iterationType = 'ITERATIVE'): NODE | undefined {
|
||||
override ensureNode(
|
||||
keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, NODE>,
|
||||
iterationType: IterationType = 'ITERATIVE'
|
||||
): NODE | undefined {
|
||||
let res: NODE | undefined;
|
||||
if (this.isRealNode(keyOrNodeOrEntry)) {
|
||||
res = keyOrNodeOrEntry;
|
||||
|
@ -321,7 +324,7 @@ export class BST<
|
|||
keysOrNodesOrEntries: Iterable<KeyOrNodeOrEntry<K, V, NODE>>,
|
||||
values?: Iterable<V | undefined>,
|
||||
isBalanceAdd = true,
|
||||
iterationType = this.iterationType
|
||||
iterationType: IterationType = this.iterationType
|
||||
): boolean[] {
|
||||
const inserted: boolean[] = [];
|
||||
|
||||
|
@ -422,7 +425,8 @@ export class BST<
|
|||
* @returns The function `getNodeByKey` returns a node (`NODE`) if a node with the specified key is
|
||||
* found in the binary tree. If no node is found, it returns `undefined`.
|
||||
*/
|
||||
override getNodeByKey(key: K, iterationType = 'ITERATIVE'): NODE | undefined {
|
||||
override getNodeByKey(key: K, iterationType: IterationType = 'ITERATIVE'): NODE | undefined {
|
||||
// return this.getNodes(key, this._defaultOneParamCallback, true, this.root, iterationType)[0];
|
||||
if (!this.isRealNode(this.root)) return undefined;
|
||||
if (iterationType === 'RECURSIVE') {
|
||||
const _dfs = (cur: NODE): NODE | undefined => {
|
||||
|
@ -480,7 +484,7 @@ export class BST<
|
|||
callback: C = this._defaultOneParamCallback as C,
|
||||
onlyOne = false,
|
||||
beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
|
||||
iterationType = this.iterationType
|
||||
iterationType: IterationType = this.iterationType
|
||||
): NODE[] {
|
||||
beginRoot = this.ensureNode(beginRoot);
|
||||
if (!beginRoot) return [];
|
||||
|
@ -597,7 +601,7 @@ export class BST<
|
|||
override bfs<C extends BTNCallback<NODE>>(
|
||||
callback: C = this._defaultOneParamCallback as C,
|
||||
beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
|
||||
iterationType = this.iterationType
|
||||
iterationType: IterationType = this.iterationType
|
||||
): ReturnType<C>[] {
|
||||
return super.bfs(callback, beginRoot, iterationType, false);
|
||||
}
|
||||
|
@ -628,7 +632,7 @@ export class BST<
|
|||
override listLevels<C extends BTNCallback<NODE>>(
|
||||
callback: C = this._defaultOneParamCallback as C,
|
||||
beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
|
||||
iterationType = this.iterationType
|
||||
iterationType: IterationType = this.iterationType
|
||||
): ReturnType<C>[][] {
|
||||
return super.listLevels(callback, beginRoot, iterationType, false);
|
||||
}
|
||||
|
@ -699,7 +703,7 @@ export class BST<
|
|||
callback: C = this._defaultOneParamCallback as C,
|
||||
lesserOrGreater: CP = 'LT',
|
||||
targetNode: KeyOrNodeOrEntry<K, V, NODE> = this.root,
|
||||
iterationType = this.iterationType
|
||||
iterationType: IterationType = this.iterationType
|
||||
): ReturnType<C>[] {
|
||||
targetNode = this.ensureNode(targetNode);
|
||||
const ans: ReturnType<BTNCallback<NODE>>[] = [];
|
||||
|
@ -751,7 +755,7 @@ export class BST<
|
|||
* values:
|
||||
* @returns The function `perfectlyBalance` returns a boolean value.
|
||||
*/
|
||||
perfectlyBalance(iterationType = this.iterationType): boolean {
|
||||
perfectlyBalance(iterationType: IterationType = this.iterationType): boolean {
|
||||
const sorted = this.dfs(node => node, 'in'),
|
||||
n = sorted.length;
|
||||
this.clear();
|
||||
|
@ -812,7 +816,7 @@ export class BST<
|
|||
* to check if the AVL tree is balanced. It can have two possible values:
|
||||
* @returns a boolean value.
|
||||
*/
|
||||
isAVLBalanced(iterationType = this.iterationType): boolean {
|
||||
isAVLBalanced(iterationType: IterationType = this.iterationType): boolean {
|
||||
if (!this.root) return true;
|
||||
|
||||
let balanced = true;
|
||||
|
|
|
@ -2,6 +2,7 @@ import type {
|
|||
BinaryTreeDeleteResult,
|
||||
BSTNKeyOrNode,
|
||||
BTNCallback,
|
||||
IterationType,
|
||||
KeyOrNodeOrEntry,
|
||||
RBTreeOptions,
|
||||
RedBlackTreeNested,
|
||||
|
@ -232,9 +233,8 @@ export class RedBlackTree<
|
|||
identifier: ReturnType<C> | undefined,
|
||||
callback: C = this._defaultOneParamCallback as C,
|
||||
beginRoot: BSTNKeyOrNode<K, NODE> = this.root,
|
||||
iterationType = this.iterationType
|
||||
iterationType: IterationType = this.iterationType
|
||||
): NODE | null | undefined {
|
||||
// if ((identifier as any) instanceof RedBlackTreeNode) callback = (node => node) as C;
|
||||
return this.getNodes(identifier, callback, true, beginRoot, iterationType)[0] ?? undefined;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import type {
|
|||
BinaryTreeDeleteResult,
|
||||
BSTNKeyOrNode,
|
||||
BTNCallback,
|
||||
IterationType,
|
||||
KeyOrNodeOrEntry,
|
||||
TreeMultiMapNested,
|
||||
TreeMultiMapNodeNested,
|
||||
|
@ -356,7 +357,7 @@ export class TreeMultiMap<
|
|||
* values:
|
||||
* @returns a boolean value.
|
||||
*/
|
||||
override perfectlyBalance(iterationType = this.iterationType): boolean {
|
||||
override perfectlyBalance(iterationType: IterationType = this.iterationType): boolean {
|
||||
const sorted = this.dfs(node => node, 'in'),
|
||||
n = sorted.length;
|
||||
if (sorted.length < 1) return false;
|
||||
|
|
|
@ -3,6 +3,8 @@ import type { BSTOptions } from "./bst";
|
|||
|
||||
export enum RBTNColor { RED = 1, BLACK = 0}
|
||||
|
||||
// export type RBTNColor = 'RED' | 'BLACK';
|
||||
|
||||
export type RedBlackTreeNodeNested<K, V> = RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, RedBlackTreeNode<K, V, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
export type RedBlackTreeNested<K, V, N extends RedBlackTreeNode<K, V, N>> = RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, RedBlackTree<K, V, N, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
|
Loading…
Reference in a new issue