package formulae

/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
 * CONSTANTS
 * ---------------------------------------------------------------- */

var Tautology = Formula{
	kind:        "taut",
	expr:        "1",
	valence:     0,
	subformulae: [](*Formula){},
}

var Contradiction = Formula{
	kind:        "contradiction",
	expr:        "0",
	valence:     0,
	subformulae: [](*Formula){},
}

/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
 * METHODS: Constructions
 * ---------------------------------------------------------------- */

func Atom(name string) Formula {
	return Formula{
		kind:        "atom",
		name:        name,
		expr:        name,
		valence:     0,
		subformulae: [](*Formula){},
	}
}

func NegatedAtom(name string) Formula {
	return Negation(Atom(name))
}

func Generic(name string) Formula {
	return Formula{
		kind:        "generic",
		name:        name,
		expr:        "{" + name + "}",
		valence:     0,
		subformulae: [](*Formula){},
	}
}

func Negation(fml Formula) Formula {
	var name string
	if fml.IsAtom() {
		name = fml.GetName()
	}
	var expr = fml.expr
	expr = "!" + expr
	return Formula{
		kind:        "not",
		name:        name, // preserves name of negated atoms
		expr:        expr,
		valence:     1,
		subformulae: [](*Formula){&fml},
	}
}

func Conjunction2(fml1 Formula, fml2 Formula) Formula {
	return Formula{
		kind:        "and2",
		expr:        "(" + fml1.expr + " && " + fml2.expr + ")",
		valence:     2,
		subformulae: [](*Formula){&fml1, &fml2},
	}
}

func Conjunction(fmls []Formula) Formula {
	switch len(fmls) {
	case 0:
		return Tautology
	case 1:
		return fmls[0]
	}
	var expr string = ""
	var subFmls = make([](*Formula), len(fmls))
	for i, fml := range fmls {
		(func(i int, subFml Formula) { subFmls[i] = &subFml })(i, fml)
		if i > 0 {
			expr += " && "
		}
		expr += fml.expr
	}
	return Formula{
		kind:        "and",
		expr:        "(" + expr + ")",
		valence:     len(subFmls),
		subformulae: subFmls,
	}
}

func Disjunction2(fml1 Formula, fml2 Formula) Formula {
	return Formula{
		kind:        "or2",
		expr:        "(" + fml1.expr + " || " + fml2.expr + ")",
		valence:     2,
		subformulae: [](*Formula){&fml1, &fml2},
	}
}

func Disjunction(fmls []Formula) Formula {
	switch len(fmls) {
	case 0:
		return Contradiction
	case 1:
		return fmls[0]
	}
	var expr string = ""
	var subFmls = make([](*Formula), len(fmls))
	for i, fml := range fmls {
		(func(i int, subFml Formula) { subFmls[i] = &subFml })(i, fml)
		if i > 0 {
			expr += " || "
		}
		expr += fml.expr
	}
	return Formula{
		kind:        "or",
		expr:        "(" + expr + ")",
		valence:     len(subFmls),
		subformulae: subFmls,
	}
}

func Implies(fml1 Formula, fml2 Formula) Formula {
	return Formula{
		kind:        "implies",
		expr:        "(" + fml1.expr + " -> " + fml2.expr + ")",
		valence:     2,
		subformulae: [](*Formula){&fml1, &fml2},
	}
}

func DoubleImplication(fml1 Formula, fml2 Formula) Formula {
	return Formula{
		kind:        "iff",
		expr:        "(" + fml1.expr + " <-> " + fml2.expr + ")",
		valence:     2,
		subformulae: [](*Formula){&fml1, &fml2},
	}
}
