(* :Title: Difference *)

(* :Author: Mark Fisher *)

(* :Summary: Code for differencing vectors (and higher order tensors). *)

(* :Discussion:
	Difference is implemented using polynomials in the lag operator to
	compute the kernel for ListConvolve.
*)

Difference::usage = "Difference[list] returns the first differnce of
	the list. Difference[list, d] returns the d-th difference of the
	list (d >= 0). (For negative d, Difference returns the accumulated
	sum.) Difference[list, d, s] returns the d-th difference of the
	list with a span of s (s >= 1). The list can be a matrix or a
	tensor. Difference[list, polyfun] takes a polynominal function as
	a second argument to specify the differencing kernel symbolically.
	In fact, Difference[list, d, s] calls Difference[list, (1-#^s)^d
	&]. For another example, Difference[list, (1-#^s)^d (1-#)^n &]
	performs d-th seasonal differencing at span s and n-th
	differencing at span 1."

Difference[list_List, d_Integer:1, s_Integer:1] /; Positive[s] :=
	Switch[{d, s},
	{1, 1}, 			Rest[list] - Drop[list, -1],
	{0, _}, 			list,
	{_?Negative, _}, 	Nest[Rest @ FoldList[Plus, 0, #] &, list, -d],
	{_, _}, 			Difference[list, (1 - #^s)^d &]
	]

Difference[list_List, poly_, var_] :=
	Difference[list, Function[var, poly]]

(* # is used purely as a dummy variable in p[#] *)
Difference[list_List, p_Function] /; PolynomialQ[p[#], #] :=
	ListConvolve[DifferenceKernel[p, TensorRank[list]], list]

DifferenceKernel[poly_, var_Symbol, rank_Intger:1] :=
	DifferenceKernel[Function[var, poly], rank]

DifferenceKernel[p_Function, rank_Integer:1] :=
	With[{ker = CoefficientList[p[#], #]},
	If[rank > 1,
		(Composition @@ Table[List, {rank - 1}]) /@ ker,
		ker]
	]

