#!/usr/bin/env python3

import sys

sys.set_int_max_str_digits(10000000)


# ============================================================
# QCA ORIGIN TEST v20
#
# Normalized operator comparison
#
# Z[φ]
#
# φ² = φ + 1
#
# ============================================================


class Phi:

    def __init__(self, a=0, b=0):
        self.a = int(a)
        self.b = int(b)

    def __add__(self, other):
        return Phi(
            self.a + other.a,
            self.b + other.b
        )

    def __sub__(self, other):
        return Phi(
            self.a - other.a,
            self.b - other.b
        )

    def __mul__(self, other):

        return Phi(
            self.a * other.a +
            self.b * other.b,

            self.a * other.b +
            self.b * other.a +
            self.b * other.b
        )

    def square(self):
        return self * self

    def norm(self):

        return (
            self.a*self.a +
            self.a*self.b -
            self.b*self.b
        )

    def __repr__(self):

        return f"({self.a}+{self.b}φ)"



ONE = Phi(1,0)



# ============================================================
# Fibonacci φ orbit
#
# φ^n = F(n-1)+F(n)φ
#
# ============================================================


def phi_power(n):

    if n == 0:
        return Phi(1,0)

    if n == 1:
        return Phi(0,1)

    a = 0
    b = 1

    for _ in range(1,n):

        a,b = b,a+b

    return Phi(a,b)



# ============================================================
# Operators
# ============================================================


def primitive(x):

    # X+1
    return x + ONE



def inverse(x):

    # X-1
    return x - ONE



def fixed(x):

    # X²-X-1
    return x.square() - x - ONE



# ============================================================
# Normalized comparison
# ============================================================


def ratio(num,den):

    if den == 0:
        return float("inf")

    return abs(num)/abs(den)



def dominant(values):

    return min(
        values,
        key=values.get
    )



# ============================================================
# Tests
# ============================================================


tests=[
    (2,True),
    (3,True),
    (4,False),
    (5,True),
    (6,False),
    (7,True),
    (8,False),
    (9,False),
    (11,True),
    (13,True),
    (17,True),
    (19,True),
    (23,True),
    (29,True),
    (31,True)
]



# ============================================================
# Header
# ============================================================


print("""
============================================================

QCA ORIGIN TEST v20

============================================================

SUBSTRATE:

X = 0


NON-DUALITY:

X+1=0


FIXED POINT:

Ω = α = φ


IDENTITY:

φ²-φ-1=0


TRINITY:

alpha-1=(-1+1φ)
alpha  =(0+1φ)
alpha+1=(1+1φ)


OPERATORS:

Primitive:
Δ0=X+1

Inverse:
Δ-0=X-1

Fixed:
Δ1=X²-X-1


NORMALIZATION:

Primitive:
|N(X+1)| / |N(X)|

Inverse:
|N(X-1)| / |N(X)|

Fixed:
|N(X²-X-1)| / |N(X)|²


============================================================
""")



# ============================================================
# Execution
# ============================================================


for n,isprime in tests:

    label="prime" if isprime else "composite"


    print()
    print(f"{n:3d} {label}")

    print()


    X=phi_power(n)


    nx=X.norm()


    print("INPUT:")
    print("Π =",X)
    print("N =",nx)



    d0=primitive(X)
    dm=inverse(X)
    d1=fixed(X)



    r0=ratio(
        d0.norm(),
        nx
    )


    rm=ratio(
        dm.norm(),
        nx
    )


    r1=ratio(
        d1.norm(),
        nx*nx
    )


    print()

    print("PRIMITIVE:")
    print("Δ0 =",d0)
    print("N  =",d0.norm())
    print("R  =",r0)


    print()

    print("INVERSE:")
    print("Δ-0=",dm)
    print("N  =",dm.norm())
    print("R  =",rm)


    print()

    print("FIXED:")
    print("Δ1 =",d1)
    print("N  =",d1.norm())
    print("R  =",r1)


    results={
        "primitive":r0,
        "inverse":rm,
        "fixed":r1
    }


    print()

    print("DOMINANT NORMALIZED OPERATOR:")
    print(
        dominant(results)
    )

    print()

    print("-"*60)



print("""
============================================================

END

============================================================
""")