@sweet-monads/either
Advanced tools
Comparing version 1.3.2 to 2.0.0
@@ -6,5 +6,5 @@ import { Monad } from "@sweet-monads/interfaces"; | ||
} | ||
export default class Either<L, R> implements Monad<R> { | ||
readonly type: EitherType; | ||
readonly value: L | R; | ||
declare class EitherConstructor<L, R, T extends EitherType = EitherType> implements Monad<R> { | ||
private readonly type; | ||
readonly value: T extends EitherType.Left ? L : R; | ||
static mergeInOne<L1, R1>(values: [Either<L1, R1>]): Either<L1, [R1]>; | ||
@@ -20,3 +20,4 @@ static mergeInOne<L1, R1, L2, R2>(values: [Either<L1, R1>, Either<L2, R2>]): Either<L1 | L2, [R1, R2]>; | ||
static mergeInOne<L1, R1, L2, R2, L3, R3, L4, R4, L5, R5, L6, R6, L7, R7, L8, R8, L9, R9, L10, R10>(values: [Either<L1, R1>, Either<L2, R2>, Either<L3, R3>, Either<L4, R4>, Either<L5, R5>, Either<L6, R6>, Either<L7, R7>, Either<L8, R8>, Either<L9, R9>, Either<L10, R10>]): Either<L1 | L2 | L3 | L4 | L5 | L6 | L7 | L8 | L9 | L10, [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10]>; | ||
static merge: typeof Either.mergeInOne; | ||
static mergeInOne<L, R>(either: Array<Either<L, R>>): Either<L, R[]>; | ||
static merge: typeof EitherConstructor.mergeInOne; | ||
static mergeInMany<L1, R1>(values: [Either<L1, R1>]): Either<Array<L1>, [R1]>; | ||
@@ -26,15 +27,15 @@ static mergeInMany<L1, R1, L2, R2>(values: [Either<L1, R1>, Either<L2, R2>]): Either<Array<L1 | L2>, [R1, R2]>; | ||
static mergeInMany<L1, R1, L2, R2, L3, R3, L4, R4>(values: [Either<L1, R1>, Either<L2, R2>, Either<L3, R3>, Either<L4, R4>]): Either<Array<L1 | L2 | L3 | L4>, [R1, R2, R3, R4]>; | ||
static mergeInMany<L1, R1, L2, R2, L3, R3, L4, R4, L5, R5>(values: [Either<L1, R1>, Either<L2, R2>, Either<L3, R3>, Either<L4, R4>, Either<L5, R5>]): Either<Array<L1 | L2 | L3 | L4 | L5>, [R1, R2, R3, R4, R5]>; | ||
static mergeInMany<L1, R1, L2, R2, L3, R3, L4, R4, L5, R5>(values: [Either<L1, R1>, Either<L2, R2>, Either<L3, R3>, Either<L4, R4>, Either<L5, R5>]): EitherConstructor<Array<L1 | L2 | L3 | L4 | L5>, [R1, R2, R3, R4, R5]>; | ||
static mergeInMany<L1, R1, L2, R2, L3, R3, L4, R4, L5, R5, L6, R6>(values: [Either<L1, R1>, Either<L2, R2>, Either<L3, R3>, Either<L4, R4>, Either<L5, R5>, Either<L6, R6>]): Either<Array<L1 | L2 | L3 | L4 | L5 | L6>, [R1, R2, R3, R4, R5, R6]>; | ||
static mergeInMany<L1, R1, L2, R2, L3, R3, L4, R4, L5, R5, L6, R6, L7, R7>(values: [Either<L1, R1>, Either<L2, R2>, Either<L3, R3>, Either<L4, R4>, Either<L5, R5>, Either<L6, R6>, Either<L7, R7>]): Either<Array<L1 | L2 | L3 | L4 | L5 | L6 | L7>, [R1, R2, R3, R4, R5, R6, R7]>; | ||
static mergeInMany<L1, R1, L2, R2, L3, R3, L4, R4, L5, R5, L6, R6, L7, R7, L8, R8>(values: [Either<L1, R1>, Either<L2, R2>, Either<L3, R3>, Either<L4, R4>, Either<L5, R5>, Either<L6, R6>, Either<L7, R7>, Either<L8, R8>]): Either<Array<L1 | L2 | L3 | L4 | L5 | L6 | L7 | L8>, [R1, R2, R3, R4, R5, R6, R7, R8]>; | ||
static mergeInMany<L1, R1, L2, R2, L3, R3, L4, R4, L5, R5, L6, R6, L7, R7, L8, R8>(values: [Either<L1, R1>, Either<L2, R2>, Either<L3, R3>, Either<L4, R4>, Either<L5, R5>, Either<L6, R6>, Either<L7, R7>, Either<L8, R8>]): EitherConstructor<Array<L1 | L2 | L3 | L4 | L5 | L6 | L7 | L8>, [R1, R2, R3, R4, R5, R6, R7, R8]>; | ||
static mergeInMany<L1, R1, L2, R2, L3, R3, L4, R4, L5, R5, L6, R6, L7, R7, L8, R8, L9, R9>(values: [Either<L1, R1>, Either<L2, R2>, Either<L3, R3>, Either<L4, R4>, Either<L5, R5>, Either<L6, R6>, Either<L7, R7>, Either<L8, R8>, Either<L9, R9>]): Either<Array<L1 | L2 | L3 | L4 | L5 | L6 | L7 | L8 | L9>, [R1, R2, R3, R4, R5, R6, R7, R8, R9]>; | ||
static mergeInMany<L1, R1, L2, R2, L3, R3, L4, R4, L5, R5, L6, R6, L7, R7, L8, R8, L9, R9, L10, R10>(values: [Either<L1, R1>, Either<L2, R2>, Either<L3, R3>, Either<L4, R4>, Either<L5, R5>, Either<L6, R6>, Either<L7, R7>, Either<L8, R8>, Either<L9, R9>, Either<L10, R10>]): Either<Array<L1 | L2 | L3 | L4 | L5 | L6 | L7 | L8 | L9 | L10>, [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10]>; | ||
static mergeInMany<L, R>(either: Array<Either<L, R>>): Either<L[], R[]>; | ||
static from<T>(v: T): Either<{}, T>; | ||
static right<L, T>(v: T): Either<L, T>; | ||
static left<T, R>(v: T): Either<T, R>; | ||
constructor(type: EitherType.Left, v: L); | ||
constructor(type: EitherType.Right, v: R); | ||
isLeft(): boolean; | ||
isRight(): boolean; | ||
private constructor(); | ||
isLeft(): this is EitherConstructor<L, R, EitherType.Left>; | ||
isRight(): this is EitherConstructor<L, R, EitherType.Right>; | ||
join<L1, L2, R>(this: Either<L1, Either<L2, R>>): Either<L1 | L2, R>; | ||
@@ -52,2 +53,5 @@ mapRight<T>(f: (r: R) => T): Either<L, T>; | ||
} | ||
export declare type Either<L, R> = EitherConstructor<L, R, EitherType.Right> | EitherConstructor<L, R, EitherType.Left>; | ||
export declare const merge: typeof EitherConstructor.mergeInOne, mergeInOne: typeof EitherConstructor.mergeInOne, mergeInMany: typeof EitherConstructor.mergeInMany, left: typeof EitherConstructor.left, right: typeof EitherConstructor.right, from: typeof EitherConstructor.from; | ||
export declare const isEither: <L, R>(value: unknown) => value is Either<L, R>; | ||
export {}; |
81
index.js
@@ -6,67 +6,69 @@ "use strict"; | ||
} | ||
var Either = /** @class */ (function () { | ||
function Either(type, value) { | ||
var EitherConstructor = /** @class */ (function () { | ||
function EitherConstructor(type, value) { | ||
this.type = type; | ||
this.value = value; | ||
} | ||
Either.mergeInOne = function (eithers) { | ||
EitherConstructor.mergeInOne = function (eithers) { | ||
return eithers.reduce(function (res, v) { | ||
return v.chain(function (v) { return res.map(function (res) { return res.concat([v]); }); }); | ||
}, Either.right([])); | ||
}, EitherConstructor.right([])); | ||
}; | ||
Either.mergeInMany = function (eithers) { | ||
EitherConstructor.mergeInMany = function (eithers) { | ||
return eithers.reduce(function (res, v) { | ||
if (res.isLeft()) { | ||
return v.isLeft() ? Either.left(res.value.concat([v.value])) : res; | ||
return v.isLeft() | ||
? EitherConstructor.left(res.value.concat([v.value])) | ||
: res; | ||
} | ||
return v.isLeft() | ||
? Either.left([v.value]) | ||
? EitherConstructor.left([v.value]) | ||
: v.chain(function (v) { return res.map(function (res) { return res.concat([v]); }); }); | ||
}, Either.right([])); | ||
}, EitherConstructor.right([])); | ||
}; | ||
Either.from = function (v) { | ||
EitherConstructor.from = function (v) { | ||
return this.right(v); | ||
}; | ||
Either.right = function (v) { | ||
return new Either("Right" /* Right */, v); | ||
EitherConstructor.right = function (v) { | ||
return new EitherConstructor("Right" /* Right */, v); | ||
}; | ||
Either.left = function (v) { | ||
return new Either("Left" /* Left */, v); | ||
EitherConstructor.left = function (v) { | ||
return new EitherConstructor("Left" /* Left */, v); | ||
}; | ||
Either.prototype.isLeft = function () { | ||
EitherConstructor.prototype.isLeft = function () { | ||
return this.type === "Left" /* Left */; | ||
}; | ||
Either.prototype.isRight = function () { | ||
EitherConstructor.prototype.isRight = function () { | ||
return this.type === "Right" /* Right */; | ||
}; | ||
Either.prototype.join = function () { | ||
EitherConstructor.prototype.join = function () { | ||
return this.chain(function (x) { return x; }); | ||
}; | ||
Either.prototype.mapRight = function (f) { | ||
EitherConstructor.prototype.mapRight = function (f) { | ||
return this.map(f); | ||
}; | ||
Either.prototype.mapLeft = function (f) { | ||
EitherConstructor.prototype.mapLeft = function (f) { | ||
if (this.isLeft()) { | ||
return Either.left(f(this.value)); | ||
return EitherConstructor.left(f(this.value)); | ||
} | ||
return Either.right(this.value); | ||
return EitherConstructor.right(this.value); | ||
}; | ||
Either.prototype.map = function (f) { | ||
EitherConstructor.prototype.map = function (f) { | ||
if (this.isLeft()) { | ||
return Either.left(this.value); | ||
return EitherConstructor.left(this.value); | ||
} | ||
return Either.right(f(this.value)); | ||
return EitherConstructor.right(f(this.value)); | ||
}; | ||
Either.prototype.asyncMap = function (f) { | ||
EitherConstructor.prototype.asyncMap = function (f) { | ||
if (this.isLeft()) { | ||
return Promise.resolve(Either.left(this.value)); | ||
return Promise.resolve(EitherConstructor.left(this.value)); | ||
} | ||
return f(this.value).then(function (v) { return Either.right(v); }); | ||
return f(this.value).then(function (v) { return EitherConstructor.right(v); }); | ||
}; | ||
Either.prototype.apply = function (argOrFn) { | ||
EitherConstructor.prototype.apply = function (argOrFn) { | ||
if (this.isLeft()) { | ||
return Either.left(this.value); | ||
return EitherConstructor.left(this.value); | ||
} | ||
if (argOrFn.isLeft()) { | ||
return Either.left(argOrFn.value); | ||
return EitherConstructor.left(argOrFn.value); | ||
} | ||
@@ -81,8 +83,8 @@ if (isWrappedFunction(this)) { | ||
}; | ||
Either.prototype.asyncApply = function (argOrFn) { | ||
EitherConstructor.prototype.asyncApply = function (argOrFn) { | ||
if (this.isLeft()) { | ||
return Promise.resolve(Either.left(this.value)); | ||
return Promise.resolve(EitherConstructor.left(this.value)); | ||
} | ||
if (argOrFn.isLeft()) { | ||
return Promise.resolve(Either.left(argOrFn.value)); | ||
return Promise.resolve(EitherConstructor.left(argOrFn.value)); | ||
} | ||
@@ -97,17 +99,18 @@ if (isWrappedFunction(this)) { | ||
}; | ||
Either.prototype.chain = function (f) { | ||
EitherConstructor.prototype.chain = function (f) { | ||
if (this.isLeft()) { | ||
return Either.left(this.value); | ||
return EitherConstructor.left(this.value); | ||
} | ||
return f(this.value); | ||
}; | ||
Either.prototype.asyncChain = function (f) { | ||
EitherConstructor.prototype.asyncChain = function (f) { | ||
if (this.isLeft()) { | ||
return Promise.resolve(Either.left(this.value)); | ||
return Promise.resolve(EitherConstructor.left(this.value)); | ||
} | ||
return f(this.value); | ||
}; | ||
Either.merge = Either.mergeInOne; | ||
return Either; | ||
EitherConstructor.merge = EitherConstructor.mergeInOne; | ||
return EitherConstructor; | ||
}()); | ||
exports["default"] = Either; | ||
exports.merge = EitherConstructor.merge, exports.mergeInOne = EitherConstructor.mergeInOne, exports.mergeInMany = EitherConstructor.mergeInMany, exports.left = EitherConstructor.left, exports.right = EitherConstructor.right, exports.from = EitherConstructor.from; | ||
exports.isEither = function (value) { return value instanceof EitherConstructor; }; |
{ | ||
"name": "@sweet-monads/either", | ||
"version": "1.3.2", | ||
"version": "2.0.0", | ||
"description": "Either monad", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
135
README.md
@@ -22,3 +22,3 @@ # @sweet-monads/either | ||
```typescript | ||
import Either from "@sweet-monads/either"; | ||
import { Either, right } from "@sweet-monads/either"; | ||
@@ -29,3 +29,3 @@ class UserNotFoundError extends Error { name: "UserNotFoundError" }; | ||
function getUser(id: number): Either<UserNotFoundError, User> { | ||
return Either.right({ email: "test@gmail.com", password: "test" }); | ||
return right({ email: "test@gmail.com", password: "test" }); | ||
} | ||
@@ -39,6 +39,7 @@ | ||
- [`Either.merge`](#eithermerge) | ||
- [`Either.left`](#eitherleft) | ||
- [`Either.right`](#eitherright) | ||
- [`Either.from`](#eitherfrom) | ||
- [`merge`](#merge) | ||
- [`left`](#left) | ||
- [`right`](#right) | ||
- [`from`](#from) | ||
- [`isEither`](#iseither) | ||
- [`Either#isLeft`](#eitherisleft) | ||
@@ -57,3 +58,3 @@ - [`Either#isRight`](#eitherisright) | ||
#### `Either.merge` | ||
#### `merge` | ||
```typescript | ||
@@ -70,11 +71,11 @@ function merge<L1, R1>(values: [Either<L1, R1>]): Either<L1, [R1]>; | ||
```typescript | ||
const v1 = Either.right<TypeError, number>(2); // Either<TypeError, number>.Right | ||
const v2 = Either.right<ReferenceError, string>("test"); // Either<ReferenceError, string>.Right | ||
const v3 = Either.left<Error, boolean>(new Error()); // Either<Error, boolean>.Left | ||
const v1 = right<TypeError, number>(2); // Either<TypeError, number>.Right | ||
const v2 = right<ReferenceError, string>("test"); // Either<ReferenceError, string>.Right | ||
const v3 = left<Error, boolean>(new Error()); // Either<Error, boolean>.Left | ||
Either.merge([v1, v2]) // Either<TypeError | ReferenceError, [number, string]>.Right | ||
Either.merge([v1, v2, v3]) // Either<TypeError | ReferenceError | Error, [number, string, boolean]>.Left | ||
merge([v1, v2]) // Either<TypeError | ReferenceError, [number, string]>.Right | ||
merge([v1, v2, v3]) // Either<TypeError | ReferenceError | Error, [number, string, boolean]>.Left | ||
``` | ||
#### `Either.left` | ||
#### `left` | ||
```typescript | ||
@@ -86,7 +87,7 @@ function left<L, R>(value: L): Either<L, R>; | ||
```typescript | ||
const v2 = Either.left(new Error()); // Either<Error, unknown>.Left | ||
const v2 = Either.left<Error, number>(new Error()); // Either<Error, number>.Left | ||
const v2 = left(new Error()); // Either<Error, unknown>.Left | ||
const v2 = left<Error, number>(new Error()); // Either<Error, number>.Left | ||
``` | ||
#### `Either.right` | ||
#### `right` | ||
```typescript | ||
@@ -98,8 +99,10 @@ function right<L, R>(value: R): Either<L, R>; | ||
```typescript | ||
const v2 = Either.right(2); // Either<unknown, number>.Right | ||
const v2 = Either.right<Error, number>(2); // Either<Error, number>.Right | ||
const v2 = right(2); // Either<unknown, number>.Right | ||
const v2 = right<Error, number>(2); // Either<Error, number>.Right | ||
``` | ||
#### `Either.from` | ||
#### `from` | ||
The same as [`right`](#right) | ||
Return only `Right` typed value. | ||
@@ -113,5 +116,19 @@ | ||
```typescript | ||
Either.from(2); // Either<unknown, number>.Right | ||
from(2); // Either<unknown, number>.Right | ||
``` | ||
#### `isEither` | ||
```typescript | ||
function isEither<L, R>(value: unknown | Maybe<L, R>): value is Maybe<L, R>; | ||
``` | ||
- Returns `boolean` if given `value` is instance of Either constructor. | ||
Example: | ||
```typescript | ||
const value: unknown = 2; | ||
if (isEither(value)) { | ||
// ... value is Either<unknown, unknown> at this block | ||
} | ||
``` | ||
#### `Either#isLeft` | ||
@@ -124,4 +141,4 @@ ```typescript | ||
```typescript | ||
const v1 = Either.right(2); | ||
const v2 = Either.left(2); | ||
const v1 = right(2); | ||
const v2 = left(2); | ||
@@ -139,4 +156,4 @@ v1.isLeft() // false | ||
```typescript | ||
const v1 = Either.right(2); | ||
const v2 = Either.left(2); | ||
const v1 = right(2); | ||
const v2 = left(2); | ||
@@ -155,5 +172,5 @@ v1.isRight() // true | ||
```typescript | ||
const v1 = Either.right(Either.right(2)); | ||
const v2 = Either.right(Either.left(new Error())); | ||
const v3 = Either.left<TypeError, Either<Error, number>>(new TypeError()); | ||
const v1 = right(right(2)); | ||
const v2 = right(left(new Error())); | ||
const v3 = left<TypeError, Either<Error, number>>(new TypeError()); | ||
@@ -172,4 +189,4 @@ v1.join() // Either.Right with value 2 | ||
```typescript | ||
const v1 = Either.right<Error, number>(2); | ||
const v2 = Either.left<Error, number>(new Error()); | ||
const v1 = right<Error, number>(2); | ||
const v2 = left<Error, number>(new Error()); | ||
@@ -189,4 +206,4 @@ const newVal1 = v1.map(a => a.toString()); // Either<Error, string>.Right with value "2" | ||
```typescript | ||
const v1 = Either.right<Error, number>(2); | ||
const v2 = Either.left<Error, number>(new Error()); | ||
const v1 = right<Error, number>(2); | ||
const v2 = left<Error, number>(new Error()); | ||
@@ -204,4 +221,4 @@ const newVal1 = v1.map(a => a.toString()); // Either<Error, string>.Right with value "2" | ||
```typescript | ||
const v1 = Either.right<Error, number>(2); | ||
const v2 = Either.left<Error, number>(new Error()); | ||
const v1 = right<Error, number>(2); | ||
const v2 = left<Error, number>(new Error()); | ||
@@ -219,4 +236,4 @@ const newVal1 = v1.mapLeft(a => a.toString()); // Either<string, number>.Right with value 2 | ||
```typescript | ||
const v1 = Either.right<Error, number>(2); | ||
const v2 = Either.left<Error, number>(new Error()); | ||
const v1 = right<Error, number>(2); | ||
const v2 = left<Error, number>(new Error()); | ||
@@ -239,6 +256,6 @@ // Promise<Either<Error, string>.Right> with value "2" | ||
```typescript | ||
const v1 = Either.right<Error, number>(2); | ||
const v2 = Either.left<Error, number>(new Error()); | ||
const fn1 = Either.right<Error, (a: number) => number>((a: number) => a * 2); | ||
const fn2 = Either.left<Error, (a: number) => number>(new Error()); | ||
const v1 = right<Error, number>(2); | ||
const v2 = left<Error, number>(new Error()); | ||
const fn1 = right<Error, (a: number) => number>((a: number) => a * 2); | ||
const fn2 = left<Error, (a: number) => number>(new Error()); | ||
@@ -264,6 +281,6 @@ const newVal1 = fn1.apply(v1); // Either<Error, number>.Right with value 4 | ||
```typescript | ||
const v1 = Either.right<Error, number>(2); | ||
const v2 = Either.left<Error, number>(new Error()); | ||
const fn1 = Either.right<Error, (a: number) => Promise<number>>((a: number) => Promise.resolve(a * 2)); | ||
const fn2 = Either.left<Error, (a: number) => Promise<number>>(new Error()); | ||
const v1 = right<Error, number>(2); | ||
const v2 = left<Error, number>(new Error()); | ||
const fn1 = right<Error, (a: number) => Promise<number>>((a: number) => Promise.resolve(a * 2)); | ||
const fn2 = left<Error, (a: number) => Promise<number>>(new Error()); | ||
@@ -283,13 +300,13 @@ const newVal1 = fn1.apply(v1); // Promise<Either<Error, number>.Right> with value 4 | ||
```typescript | ||
const v1 = Either.right<Error, number>(2); | ||
const v2 = Either.left<Error, number>(new Error()); | ||
const v1 = right<Error, number>(2); | ||
const v2 = left<Error, number>(new Error()); | ||
// Either<Error | TypeError, string>.Right with value "2" | ||
const newVal1 = v1.chain(a => Either.right<TypeError, string>(a.toString())); | ||
const newVal1 = v1.chain(a => right<TypeError, string>(a.toString())); | ||
// Either<Error | TypeError, string>.Left with value new TypeError() | ||
const newVal2 = v1.chain(a => Either.left<TypeError, string>(new TypeError())); | ||
const newVal2 = v1.chain(a => left<TypeError, string>(new TypeError())); | ||
// Either<Error | TypeError, string>.Left with value new Error() | ||
const newVal3 = v2.chain(a => Either.right<TypeError, string>(a.toString())); | ||
const newVal3 = v2.chain(a => right<TypeError, string>(a.toString())); | ||
// Either<Error | TypeError, string>.Left with value new Error() | ||
const newVal4 = v2.chain(a => Either.left<TypeError, string>(new TypeError())); | ||
const newVal4 = v2.chain(a => left<TypeError, string>(new TypeError())); | ||
``` | ||
@@ -304,13 +321,13 @@ | ||
```typescript | ||
const v1 = Either.right<Error, number>(2); | ||
const v2 = Either.left<Error, number>(new Error()); | ||
const v1 = right<Error, number>(2); | ||
const v2 = left<Error, number>(new Error()); | ||
// Promise<Either<Error | TypeError, string>.Right> with value "2" | ||
const newVal1 = v1.chain(a => Either.right<TypeError, string>(a.toString())); | ||
const newVal1 = v1.chain(a => right<TypeError, string>(a.toString())); | ||
// Promise<Either<Error | TypeError, string>.Left> with value new TypeError() | ||
const newVal2 = v1.chain(a => Either.left<TypeError, string>(new TypeError())); | ||
const newVal2 = v1.chain(a => left<TypeError, string>(new TypeError())); | ||
// Promise<Either<Error | TypeError, string>.Left> with value new Error() | ||
const newVal3 = v2.chain(a => Either.right<TypeError, string>(a.toString())); | ||
const newVal3 = v2.chain(a => right<TypeError, string>(a.toString())); | ||
// Promise<Either<Error | TypeError, string>.Left> with value new Error() | ||
const newVal4 = v2.chain(a => Either.left<TypeError, string>(new TypeError())); | ||
const newVal4 = v2.chain(a => left<TypeError, string>(new TypeError())); | ||
``` | ||
@@ -322,6 +339,6 @@ | ||
// Value from Either instance | ||
const { value } = Either.right<Error, number>(2); // number | Error | ||
const { value } = Either.right(2); // any | ||
const { value } = Either.left<Error, number>(new Error()); // number | Error | ||
const { value } = Either.left(2); // any | ||
const { value } = right<Error, number>(2); // number | Error | ||
const { value } = right(2); // any | ||
const { value } = left<Error, number>(new Error()); // number | Error | ||
const { value } = left(2); // any | ||
``` | ||
@@ -328,0 +345,0 @@ |
23850
172
329