import z3

import problems
from common.axioms import total_strict_order
from problems.z3_utils import fresh_const

from . import Node, NodeType, is_slist, less_than, list_next, lte, nil


def desired_property(x: NodeType) -> z3.BoolRef:
    return z3.Implies(is_slist(x), lte(nil, x))


def linked_list() -> z3.BoolRef:
    n = fresh_const(Node)
    return z3.ForAll(n, (n == nil) == (list_next(n) == n))


def linked_sorted() -> z3.BoolRef:
    n = fresh_const(Node)

    return z3.ForAll(
        n,
        is_slist(n)
        == z3.Or(n == nil, z3.And(is_slist(list_next(n)), less_than(list_next(n), n))),
    )


@problems.mark(
    model=True,
    osc=True,
    popl24=True,
    bruteforce_axiom=True,
    guided_axiom=True,
    popl25=True,
)
def sorted_list_max() -> z3.BoolRef:
    n = fresh_const(Node)
    return z3.And(
        total_strict_order(less_than),
        linked_list(),
        linked_sorted(),
        z3.Exists(n, z3.Not(desired_property(n))),
    )
