Section ListFacts.
Definition sat φ l := Forall (h10uc_sem φ) l.
(* needs 2 fresh variables *)
(* x is 0 if 1 + x + x * x = y and 1 + x + y * y = z and 1 + y + x * x = z *)
Definition express_0 (x: nat) (f: nat -> nat) := [(x, x, f 0); (x, f 0, f 1); (f 0, x, f 1)].
Definition express_0f := [1; 2].
(* needs 2 fresh variables *)
(* x is 1 if 1 + y + y * y = x and 1 + y + x * x = z and 1 + x + y * y = z *)
Definition express_1 (x: nat) (f: nat -> nat) := [(f 0, f 0, x); (f 0, x, f 1); (x, f 0, f 1)].
Definition express_1f := [0; 2].
(* needs 3 fresh variables *)
(* x is 1 + y if 1 + y + 0 * 0 = x *)
Definition express_succ (x y: nat) (f: nat -> nat) := (express_0 (f 2) f) ++ [(y, f 2, x)].
Definition express_succf := express_0f ++ [0].
(* needs 7 fresh variables *)
(* x = y * y if 1 + 0 + y * y = 1 + x *)
Definition express_square (x y: nat) (f: nat -> nat) :=
(express_0 (f 5) f) ++ (express_succ (f 6) x (fun i => f (2+i))) ++ [(f 5, y, f 6)].
Definition express_squaref φx :=
express_0f ++ express_succf ++ [0; 1 + φx].
(* needs 35 fresh variables *)
(* x = x + z if 1 + (y + y) + (1 + z) * (1 + z) = 1 + (1 + (x + x)) + z * z *)
Definition express_sum (x y z: nat) (f: nat -> nat) :=
(express_succ (f 33) z f) ++
(express_double (f 32) y (fun i => f (3+i))) ++
(express_double (f 31) x (fun i => f (15+i))) ++
(express_succ (f 34) (f 31) (fun i => f (27+i))) ++
[((f 32), (f 33), (f 30)); ((f 34), z, (f 30))].
Definition express_sumf φx φy φz :=
express_succf ++
(express_doublef φy) ++
(express_doublef φx) ++
express_succf ++
[1 + (φy + φy) + (1 + φz) * (1 + φz); φx + φx ; φy + φy; 1 + φz; 1 + φx + φx].
Definition embed '(x, y, z, i, j) : nat := encode (encode (encode (x, y), z), encode (i, j)).
Definition unembed (n: nat) : (nat * nat * nat * nat * nat) :=
let (xyz, ij) := decode n in
let (xy, z) := decode xyz in
let (x, y) := decode xy in
let (i, j) := decode ij in
(x, y, z, i, j).
(* encode an h10 constraint as a list of uniform h10 constraints *)
Definition h10c_to_h10uc (c : h10c) : list h10uc :=
let h x := embed (x, 0, 0, 0, 0) in
match c with
| h10c_one x => express_1 (h x) (fun i => embed (x, 0, 0, 1, i))
| h10c_plus x y z => express_sum (h z) (h x) (h y) (fun i => embed (z, x, y, 2, i))
| h10c_mult x y z => express_prod (h z) (h x) (h y) (fun i => embed (z, x, y, 3, i))
end.
End Argument.

(* many-one reduction from H10C_SAT to H10UC_SAT *)
Theorem reduction : H10C_SAT ⪯ H10UC_SAT.