"""BTVN, Python3: ADN, hoán vị và đa thức theo mô hình SGK.

python3 g11-cs-thuc-hanh-quay-lui.py adn 2
python3 g11-cs-thuc-hanh-quay-lui.py adn-xau 2
python3 g11-cs-thuc-hanh-quay-lui.py cau mây qua núi
python3 g11-cs-thuc-hanh-quay-lui.py hoan-vi 3
python3 g11-cs-thuc-hanh-quay-lui.py da-thuc 3 0 -2
Mỗi dòng ADN là một dãy trên A,T,G,X. Chế độ cau sinh hoán vị từ,
chưa kiểm ngữ pháp hoặc ngữ nghĩa. Hệ số nhận số nguyên/thập phân hữu hạn.
"""
import argparse
from decimal import Decimal, InvalidOperation

count = 0


def genDNASection(A, k, n, as_string=False):
    global count
    if k == n:
        print(''.join(A) if as_string else A)
        count += 1
    else:
        for value in 'ATGX':
            A[k] = value
            genDNASection(A, k + 1, n, as_string)


def genSentence(words, dictionary, k):
    global count
    if k == len(dictionary):
        print(' '.join(words))
        count += 1
    else:
        for word in dictionary:
            if word not in words:
                words.append(word)
                genSentence(words, dictionary, k + 1)
                words.pop()


def genPermutations(permutation, elements, k):
    global count
    if k == len(elements):
        print(permutation)
        count += 1
    else:
        for value in elements:
            if value not in permutation:
                permutation.append(value)
                genPermutations(permutation, elements, k + 1)
                permutation.pop()


def format_polynomial(coefs):
    terms = []
    degree = len(coefs) - 1
    for i, value in enumerate(coefs):
        if value == 0:
            continue
        power = degree - i
        magnitude = format(value, 'f').lstrip('-')
        if '.' in magnitude:
            magnitude = magnitude.rstrip('0').rstrip('.')
        coefficient = '' if power > 0 and value in (1, -1) else magnitude
        variable = '' if power == 0 else ('x' if power == 1 else f'x^{power}')
        sign = (' - ' if terms else '-') if value < 0 else (' + ' if terms else '')
        terms.append(sign + coefficient + variable)
    return ''.join(terms) if terms else '0'


def genPolynomials(coefs, k):
    global count
    if k == len(coefs):
        if coefs and coefs[0] != 0:
            print(format_polynomial(coefs))
            count += 1
        return
    used = []
    for i in range(k, len(coefs)):
        if coefs[i] in used:
            continue
        used.append(coefs[i])
        coefs[k], coefs[i] = coefs[i], coefs[k]
        genPolynomials(coefs, k + 1)
        coefs[k], coefs[i] = coefs[i], coefs[k]


def main():
    global count
    parser = argparse.ArgumentParser(description=__doc__)
    modes = parser.add_subparsers(dest='mode', required=True)
    for mode in ['adn', 'adn-xau', 'hoan-vi']:
        sub = modes.add_parser(mode)
        sub.add_argument('n', type=int)
    sentence = modes.add_parser('cau')
    sentence.add_argument('words', nargs='+')
    poly = modes.add_parser('da-thuc')
    poly.add_argument('coefficients', nargs='+')
    args = parser.parse_args()
    try:
        count = 0
        if args.mode in ['adn', 'adn-xau']:
            if not 0 <= args.n <= 6:
                raise ValueError('Bản thực hành ADN nhận n từ 0 đến 6 để xuất tối đa 4096 nghiệm.')
            genDNASection([''] * args.n, 0, args.n, args.mode == 'adn-xau')
        elif args.mode == 'hoan-vi':
            if not 0 <= args.n <= 7:
                raise ValueError('Bản thực hành hoán vị nhận n từ 0 đến 7.')
            genPermutations([], list(range(1, args.n + 1)), 0)
        elif args.mode == 'cau':
            if not 1 <= len(args.words) <= 7 or len(set(args.words)) != len(args.words) or any(not x.strip() for x in args.words):
                raise ValueError('Nhập từ 1 đến 7 từ khác nhau, không có từ trống.')
            genSentence([], args.words, 0)
        else:
            if not 1 <= len(args.coefficients) <= 7:
                raise ValueError('Nhập từ 1 đến 7 hệ số.')
            coefs = [Decimal(value) for value in args.coefficients]
            if any(not x.is_finite() for x in coefs):
                raise ValueError('Hệ số cần là số hữu hạn.')
            genPolynomials(coefs, 0)
        print('Tổng số nghiệm:', count)
    except (ValueError, InvalidOperation) as error:
        parser.exit(2, f'Dữ liệu không phù hợp: {error}\n')


if __name__ == '__main__':
    main()
