/*
Anagram Palindrome (Anadrome) from Hackerrank: {@link https://www.hackerrank.com/contests/test1-day12/challenges/check-if-any-anagram-is-palindrome}
*/
type OptionalPropertyNames<T> = {
[K in keyof T]: undefined extends T[K] ? K : never;
}[keyof T];
type SpreadProperties<L, R, K extends keyof L & keyof R> = {
[P in K]: L[P] | Exclude<R[P], undefined>;
};
type Id<T> = { [K in keyof T]: T[K] };
type Spread<L, R> = Id<
Pick<L, Exclude<keyof L, keyof R>> &
Pick<R, Exclude<keyof R, OptionalPropertyNames<R>>> &
Pick<R, Exclude<OptionalPropertyNames<R>, keyof L>> &
SpreadProperties<L, R, OptionalPropertyNames<R> & keyof L>
>;
type Check<T extends string> = Permutation<
TupleToUnion<AddNumbers<StringToArray<T>>>
>;
type ConstructTuple<
L extends number,
Y extends unknown[] = []
> = Y["length"] extends L ? Y : ConstructTuple<L, [...Y, unknown]>;
type LessThanEqual<
Small extends number,
Big extends number
> = ConstructTuple<Big> extends [...ConstructTuple<Small>, ...any[]]
? true
: false;
type UnionToIntersectionFn<U> = (
U extends unknown ? (k: () => U) => void : never
) extends (k: infer I) => void
? I
: never;
type GetUnionLast<U> = UnionToIntersectionFn<U> extends () => infer I
? I
: never;
type Prepend<Tuple extends unknown[], First> = [First, ...Tuple];
type UnionToTuple<
Union,
T extends unknown[] = [],
Last = GetUnionLast<Union>
> = [Union] extends [never]
? T
: UnionToTuple<Exclude<Union, Last>, Prepend<T, Last>>;
type Unique<A extends unknown[], B extends unknown[] = []> = A extends [
infer C,
...infer D
]
? In<C, B> extends false
? Unique<D, [...B, C]>
: Unique<D, B>
: B;
type In<A, B extends unknown[]> = B extends [infer C, ...infer D]
? Equals<A, C> extends true
? true
: In<A, D>
: false;
type Modulo<T extends unknown[], U extends unknown[]> = T extends [
...U,
...infer S
]
? Modulo<S, U>
: T;
type IncludesIndexes<
T extends readonly any[],
U,
P extends any[] = []
> = T extends [...infer F, infer R]
? Equals<U, R> extends true
? IncludesIndexes<F, U, [F["length"], ...P]>
: IncludesIndexes<F, U, P>
: P;
type RemoveNonPalindromes<
Union extends string[],
Item extends string[] = Union
> = Item extends Item
? IsPalindrome<RemoveSecond<Item>> extends true
? Join<RemoveSecond<Item>>
: never
: never;
type Join<
T extends unknown[],
P extends string = "",
JoinedString extends string = ""
> = T extends [infer Head, ...infer C]
? T["length"] extends 1
? Join<C, P, `${JoinedString}${Head & string}`>
: Join<C, P, `${JoinedString}${Head & string}${P}`>
: JoinedString;
type AddNumbers<T extends unknown[], P extends unknown[] = []> = T extends [
infer Head,
...infer Tail
]
? AddNumbers<Tail, [...P, `${Head & string}${T["length"]}`]>
: P;
type TupleToUnion<T> = T[Extract<keyof T, number>];
type StringToArray<T extends string> = T extends `${infer Left}${infer Right}`
? [Left, ...StringToArray<Right>]
: [];
type RemoveSecond<T extends unknown[], P extends unknown[] = []> = T extends [
infer Head,
...infer Tail
]
? RemoveSecond<Tail, [...P, StringToArray<Head & string>[0]]>
: P;
type IsPalindrome<T extends string[]> = Equals<T, Reverse<T>> extends true
? true
: false;
type Permutation<T, K = T> = [T] extends [never]
? []
: K extends K
? [K, ...Permutation<Exclude<T, K>>]
: never;
type Equals<A1 extends any, A2 extends any> = (<A>() => A extends A2
? true
: false) extends <A>() => A extends A1 ? true : false
? true
: false;
type Reverse<T extends unknown[], Res extends unknown[] = []> = T extends [
infer F,
...infer Rest
]
? Reverse<Rest, [F, ...Res]>
: Res;
type CheckPalindrome<
Uniq extends unknown[],
Original extends unknown[],
Odd extends number[] = []
> = Uniq extends [infer Head, ...infer Tail]
? Modulo<
IncludesIndexes<Original, Head>,
[unknown, unknown]
>["length"] extends 1
? CheckPalindrome<Tail, Original, [...Odd, 1]>
: CheckPalindrome<Tail, Original, Odd>
: Modulo<Original, [unknown, unknown]>["length"] extends 1
? Odd["length"] extends 1
? true
: false
: Odd["length"] extends 0
? true
: false;
type CouldBePalindrome<T extends string> = CheckPalindrome<
Unique<StringToArray<Lowercase<T>>>,
StringToArray<Lowercase<T>>
> extends true
/*
We must limit the entry string to 7 characters in order to
show which palindromes are possible. Strings longer than
seven characters produce too many permutations, resulting
in Typescript exploding.
*/
? LessThanEqual<StringToArray<T>["length"], 7> extends true
? PalindromeDisplay<Pass<T>>
: Possible
: Impossible;
type PossiblePalindromeDisplay = {
L00: `╔════════════════════════════╗`;
L01: `║ It can be a palindrome. ║`;
L02: `╚════════════════════════════╝`;
L03: `╔═══╤════════════════════════╗`;
L04: `║ │ Possible palindromes: ║`;
};
type FirstPalindromeDisplay<T extends string, Line extends number> = {
"1": `╟───┼────────────────────────╢`;
"2": `║ ${Line} │ ${AddEmptySpaces<
StringToArray<T>
>} ║`;
};
type LastPalindromeDisplay<T extends string, Line extends number> = {
"1": `╟───┼────────────────────────╢`;
"2": `║ ${Line} │ ${AddEmptySpaces<
StringToArray<T>
>} ║`;
"3": `╚═══╧════════════════════════╝`;
};
type Possible = {
L00: `╔════════════════════════════╗`;
L01: `║ IT IS AN ANADROME 👍 ║`;
L02: `╚════════════════════════════╝`;
};
type Impossible = {
L00: `╔════════════════════════════╗`;
L01: `║ IT IS "NOT" AN ANADROME 👎 ║`;
L02: `╚════════════════════════════╝`;
};
type ConstructPalindromeObjet<T extends Object, P extends string> = {
[K in keyof T as `${P}${K & string}`]: T[K];
};
// @ts-ignore
type Pass<T extends string> = UnionToTuple<RemoveNonPalindromes<Check<T>>>;
type PalindromeDisplay<
T extends unknown[],
Length extends number[] = [1],
Display extends {} = PossiblePalindromeDisplay
> = T extends [infer Head, ...infer Tail]
? Tail["length"] extends 0
? Spread<
Display,
PalindromeDisplay<
Tail,
[...Length, 1],
ConstructPalindromeObjet<LastPalindromeDisplay<Head & string, Length["length"]>,`L${Length['length']}`>
>
>
: PalindromeDisplay<
Tail,
[...Length, 1],
Spread<Display, ConstructPalindromeObjet<FirstPalindromeDisplay<Head & string, Length["length"]>,`L${Length['length']}`>>
>
: Display;
type AddEmptySpaces<
T extends string[],
Length extends number = 7
> = T["length"] extends Length ? Join<T> : AddEmptySpaces<[...T, " "], Length>;
// ───────────────────────── @results ─────────────────────────
// (only the type aliases below resolve in the live view)
type Test = CouldBePalindrome<'ciivc'>
type Test1 = CouldBePalindrome<'carrace'>;
type Test2 = CouldBePalindrome<"spaaps">;
type Test3 = CouldBePalindrome<'MaisannausIma'>
type Test4 = CouldBePalindrome<'amore'>