// Souffle - A Datalog Compiler
// Copyright (c) 2016, The Souffle Developers. All rights reserved
// Licensed under the Universal Permissive License v 1.0 as shown at:
// - https://opensource.org/licenses/UPL
// - <souffle root>/licenses/SOUFFLE-UPL.txt

//             88                                   88
//             ""                                   ""   ,d
//                                                       88
//   ,adPPYba, 88 8b,dPPYba,  ,adPPYba, 88       88 88 MM88MMM
//  a8"     "" 88 88P'   "Y8 a8"     "" 88       88 88   88
//  8b         88 88         8b         88       88 88   88
//  "8a,   ,aa 88 88         "8a,   ,aa "8a,   ,a88 88   88,
//   `"Ybbd8"' 88 88          `"Ybbd8"'  `"YbbdP'Y8 88   "Y888
//
//                                                                   88
//                                                                   88
//                                                                   88
//  8b,dPPYba,  ,adPPYba,  ,adPPYba,  ,adPPYba,  8b,dPPYba,  ,adPPYb,88
//  88P'   "Y8 a8P_____88 a8"     "" a8"     "8a 88P'   "Y8 a8"    `Y88
//  88         8PP""""""" 8b         8b       d8 88         8b       88
//  88         "8b,   ,aa "8a,   ,aa "8a,   ,a8" 88         "8a,   ,d88
//  88          `"Ybbd8"'  `"Ybbd8"'  `"YbbdP"'  88          `"8bbdP"Y8

// Circuit record
// Determine the required inputs to satisfy the outputs
// Different from circuit_sat, which encoded wire state in integers
// i.e. limited to 32 wires.
// This stores them in recursive records, accessed by treating
// the list as a sort of associative list.
//

.type Wire <: symbol
.type Wire_state = [id : Wire, val : number]
.type Wire_list = [head : Wire_state, tail: Wire_list]

.decl in(a : Wire)
in("a").
in("b").
in("c").

//helpers to determine ordering in the list
.decl less(a: Wire, b: Wire)
less(x, y) :- in(x), in(y), ord(x) < ord(y).

.decl leg(a: Wire, b: Wire)
leg(x, z) :- less(x,y), less(y,z).

//element x is before el y
.decl succ(a: Wire, b: Wire)
succ(x, y) :- less(x, y), !leg(x,y).

//el x is first in list
.decl first(a: Wire)
first(a) :- in(a), !less(_,a).

//el x is last
.decl last(a:Wire)
last(a) :- in(a), !less(a,_).

//boolean states
.decl bin(a : number)
bin(0).
bin(1).

// generate all ordered lists (including list stubs)
.decl gen_wire_states( wl : Wire_list)
gen_wire_states([[x, v], nil]) :-
	first(x),
    bin(v).

gen_wire_states( [[y, v], l]) :-
    gen_wire_states(l),
	succ(x, y),
    bin(v),
    l = [[x,xv],tail].

// collect full length ordered lists (i.e. ignore stubs)
.decl final_gen_wire_states(wl : Wire_list)
final_gen_wire_states(l) :-
    gen_wire_states(l),
    l = [[x,xv],tail],
    last(x).

//extract a wire's value from the array (associative array type)
.decl get_by_key(key : Wire, val : number, rec : Wire_list, conf: Wire_list)
get_by_key(k, v, tail, [[a,av], tail]) :-
	in(a), bin(av), a=k, av=v, gen_wire_states([[a,av], tail]), last(a).
get_by_key(k, v, tail, conf) :-
	in(k), bin(v), get_by_key(_, _, [[k, v], tail], conf).

.decl required_out(ws : Wire_state)
required_out([w, v]) :- required_out_fi(w, v).
//which wires are required to have certain values
.decl required_out_fi(w : Wire, v : number)
.input required_out_fi()

.decl circuit_and(result : Wire, op1 : Wire, op2 : Wire)
.input circuit_and()
.decl circuit_or(result : Wire, op1 : Wire, op2 : Wire)
.input circuit_or()
.decl circuit_not(result : Wire, op1 : Wire)
.input circuit_not()

// evaluate the wire's required value from a set of inputs
.decl evaluate(circuit_state : Wire_list, wire_state : Wire_state)
evaluate(cs, [k, v]) :-
	get_by_key(k,v,  _, cs).
evaluate(cs, [k, av band bv]) :-
	circuit_and(k, a, b),
	evaluate(cs, [a, av]),
	evaluate(cs, [b, bv]).
evaluate(cs, [k, av bor bv]) :-
	circuit_or(k, a, b),
	evaluate(cs, [a, av]),
	evaluate(cs, [b, bv]).
evaluate(cs, [k, lnot av]) :-
	circuit_not(k, a),
	evaluate(cs, [a, av]).

.decl satisfy(conf : Wire_list, ws: Wire_state)
satisfy(conf, [var, val]) :-
	required_out([var, val]),
	evaluate(conf, [var, val]).

// print the required inputs to reach the configuration (turned off atm, as the souffle tester cannot handle the output of records)
.decl sat_print(var : Wire, val : number, conf : Wire_list)
sat_print(var, val, conf) :-
	satisfy(conf,_), get_by_key(var, val, _, conf).

// print the count of total circuits configurations mul inputs
.decl print_states(val : number)
.output print_states()
print_states(val) :-
	val = count : { sat_print(_, _, _) }.

