; From MathPhys/Riemann/bianchi4.scm
(define d 4)
(define e 4)
(define Do ((fileVal "Do") 'Do))
(define itr4 (lambda (p) (Do 4 p)))
(define (g1 p) (let ((nv (make-vector d)))
     (itr4 (lambda (j) (vector-set! nv j (p j))))
     (lambda (x) (vector-ref nv x))))
(define (gn g) (lambda(p)(let ((a (g1 (lambda(w)
   (g (lambda q (apply p (cons w q))))))))
   (lambda (x . y) (apply (a x) y)))))
(define g2 (gn g1))
(define g3 (gn g2))
(define g4 (gn g3))

(define (eta i j) (if (= i j) (if (zero? i) -1 1) 0))
(define ran (let ((seed 4429)(md (expt 2 20)))(lambda ()
  (set! seed (modulo (* seed 17) md)) (modulo seed 11))))
(define (pgen d n gen z) (if (zero? d) z
  (let ((v (make-vector n)))
    (Do n (lambda (j)(vector-set! v j
      (pgen (- d 1) (- n j) gen (if (zero? j) z (gen)))))) v)))
(define (paddto n k) (lambda (p1 p2)
    (Do k
      (if (= n 1)
        (lambda(j) (vector-set! p2 j
             (+ (vector-ref p1 j)( vector-ref p2 j))))
        (lambda(j) ((paddto (- n 1) (- k j))
          (vector-ref p1 j) (vector-ref p2 j)))))))
(define (padd n k) (lambda (p1 p2) (let ((v (make-vector k)))
   (Do k (lambda(j) (vector-set! v j
      ((if (= n 1) + (padd (- n 1) (- k j)))
       (vector-ref p1 j) (vector-ref p2 j))))) v)))
; The multiply operation required by the current job requires specifying
; the degree of the product and it is assured by the outer logic that
; enough terms of the factors will be present.
; For that matter the code "(padd 2 k)" will yield a polynomial
; of degree k-1 even from inputs of greater degrees.
(define (pmul n k) (lambda (p1 p2) (let ((v (make-vector k)))
   (Do k (lambda(j) (if (= n 1)

  (let ((s 0))(Do (+ j 1) (lambda(i) (set! s (+ s (* (vector-ref p1 i)
    (vector-ref p2 (- j i))))))) (vector-set! v j s)) 

  (let ((s (pgen (- n 1) (- k j) (lambda()0) 0))) (Do (+ j 1) (lambda (i)
   ((paddto (- n 1) (- k j)) ((pmul (- n 1)(- k j)) (vector-ref p1 i)
        (vector-ref p2 (- j i))) s))) (vector-set! v j s))

  ))) v)))

(define (mape ms x)(if (vector? x) (let* ((k (vector-length x))
   (nv (make-vector k)))
   (Do k (lambda(k)(vector-set! nv k (mape ms (vector-ref x k))))) nv)
   (ms x)))

(define (ip e a b)(let ((sum (pgen d e (lambda()0) 0))
    (ad (padd d e))
    (ml (pmul d e)))
  (itr4(lambda (k) (set! sum (ad sum (ml (a k)(b k))))))
 sum))

(define mm (lambda (x y) (g2 (lambda (i k)
      (ip e (lambda( j)(x i j))(lambda (j)(y j k)))))))
(define (inv y)(let* ((zp (pgen d e (lambda()0) 0))
  (op (pgen d e (lambda()0) 1))
  (mop (pgen d e (lambda()0) -1))
  (et (g2 (lambda (i j)
      (case (eta i j) ((0) zp) ((1) op) ((-1) mop)
        (else (pgen d e (lambda()0) (eta i j)))))))
  (ma (lambda (x y) (g2 (lambda (i j) ((padd d e) (x i j) (y i j))))))
  (s0 (g2 (lambda (i j) zp)))
  ; s0 is a mutable reference to an immutable array of mvps.
  ; Successive values are sums of ever more terms.
  (x (g2 (lambda (i j) ((padd d e) (et i j) (mape - (y i j))))))
  ; y = et - x => x = et - y
  (st et) ; will take on term values
  (etx (g2 (lambda (i k)(ip e (lambda( j)(et i j))(lambda (j)(x j k)))))))
 (Do e (lambda (n)(set! s0 (ma s0 st))
    (if (not (zero? n))(set! st (mm etx st)))))s0))
(define (ftl g) (let z ((n d)) (if (zero? n) '()
    (cons (g (- d n)) (z (- n 1))))))
(define (curry g)(lambda(x) (lambda y (apply g (cons x y)))))
(define ml (lambda (k) (lambda (g) (if (zero? k) (g)
   (map (ml (- k 1)) (ftl (curry g)))))))

; To take the partial derivative of a mvp with respect to one of its variables.
; Below (deriv p e j) calls for the derivative of mvp p wrt variable j.
; The input is of degree e (or greater) and the yield will be of degree e-1.
; This is the first function that knows which is the first variable of a mvp!
(define (deriv p e j) (let ((nv (make-vector (- e 1))))
     (Do ( - e 1) (lambda (k) (vector-set! nv k
         (if (zero? j) (if (zero? k) (vector-ref p 1)
            (mape (lambda (z) (* z (+ k 1))) (vector-ref p (+ k 1))))
           (deriv (vector-ref p k) (- e k) (- j 1)))))) nv))

(define (zt d e n v) (let ((zz (pgen d e (lambda()0) 0)))
  (let z ((vl ((ml n) v))) (or 
    (equal? vl zz)
    (and (pair? vl) (z (car vl)) (z (cdr vl)))
    (null? vl)))))

;test:
(define (test j k)
  (let ((acc (pgen j k (lambda()0) 0))) ((paddto j k)(pgen j k
  (let ((x 0))(lambda()(set! x (+ x 1))x)) 0) acc) acc))
(let ((a (pgen 4 5 ran (ran)))(b (pgen 4 5 ran (ran)))) 
  (let ((x ((padd 4 5) a b))(ac (pgen 4 5 (lambda()0) 0)))
    ((paddto 4 5) a ac)((paddto 4 5) b ac)(equal? ac x)))
(let ((x (pgen 4 5 (lambda () 3) 5))(y (pgen 4 5 (lambda () 3) 5)))
   (equal? x y))
(define (gx n) (pgen 4 5 (let ((x 0))(lambda()(set! x (+ x 1))
   (if (= x n) 1 0))) 0))
(let* ((n (lambda ()(pgen 4 5 ran (ran))))(a (n))(b (n))(c (n))
         (t (pmul 4 5))(p (padd 4 5)))
  (list (equal? (t a (p b c))(p (t a b)(t a c)))
     (equal? (t a (t b c)) (t (t a b) c))))


(define Rs
(let* ((gg (g2 (lambda (i j) (pgen d e ran (eta i j)))))
  (g (lambda (i j)(if (< i j) (gg i j) (gg j i))))
  (gi (inv g))
  (gder (g3 (lambda (i j k) (deriv (g j k) 4 i))))
  (2chrst1 (let ((da (padd d (- e 1))))
    (g3 (lambda (i j k) (da (mape - (gder i j k))
          (da (gder j k i) (gder k i j)))))))
  (2chrst (g3 (lambda (i j k)(ip (- e 1)
    (lambda(n) (gi n i)) (lambda (n) (2chrst1 n j k))))))
  (2chder (g4 (lambda (i j k l) (deriv (2chrst j k l) (- e 1) i))))
  (4Riemann (let ((2t (lambda (a)(mape (lambda(x)(* 2 x)) a)))
      (sub (lambda (x y)((padd d (- e 2)) x (mape - y)))))
    (g4 (lambda (m n a b)
     ((padd d (- e 2))(2t (sub (2chder a m n b) (2chder b m n a)))
    (sub (ip (- e 2) (lambda(s)(2chrst m s a))(lambda(s)(2chrst s n b)))
       (ip (- e 2) (lambda(s)(2chrst m s b))(lambda(s)(2chrst s n a)))))))))
  (4R (g4 (lambda (i j k l)
    (ip (- e 2) (lambda(x)(g x i))(lambda(x)(4Riemann x j k l)))))))
(write (let* ((e (- e 1))(pa (padd d e)))
   (zt d e 3 (g3 (lambda (i j k)
      (pa (2chrst i j k) (mape - (2chrst i k j))))))))
(list 2chrst 4R 4Riemann)))

;tests
; Riemann symetries:
(let* ((R (cadr Rs))(e (- e 2))(pa (padd d e))) (list
  (zt d e 4 (g4 (lambda (i j k l) (pa (R i j k l) (R i j l k)))))
  (zt d e 4 (g4 (lambda (i j k l) (pa (R i j k l) (R j i k l)))))
  (zt d e 4 (g4 (lambda (i j k l) (pa (R i j k l) (mape - (R k l i j))))))
  (zt d e 4 (g4 (lambda (i j k l) (pa (pa
     (R i j k l) (R i k l j)) (R i l j k)))))))
; Bianchi identities
(write "Bianchi:")
(let* ((R (caddr Rs))(pa (padd 4 1))
   (Rd ((gn g4) (lambda(i j k l m)(deriv (R i j k l) 2 m)))))
   (zt d 1 5 ((gn g4) (lambda(i j k l m)
     (pa(pa(Rd i j k l m) (Rd i j m k l)) (Rd i j l m k))))))
(write "better Bianchi:")
(let* ((R (caddr Rs))(pa (padd 4 1))(g5 (gn g4))(Gam (car Rs))
   (Rd (g5 (lambda(i j k l m)
     (mape (lambda(a)(* a -2)) (deriv (R i j k l) 2 m))))))
   (Do 4 (lambda(q)
     (set! Rd (g5 (lambda (i j k l m) (pa (Rd i j k l m)
        (ip 1 (lambda (x)
        (Gam x m (cond ((= q 0) i)((= q 1) j)((= q 2) k)((= q 3) l))))
          (lambda (x)
      (Rd (if (= q 0) x i)(if (= q 1) x j)(if (= q 2) x k)(if (= q 3) x l)  
          m)))))))))
   (zt d 1 5 (g5 (lambda(i j k l m)
     (pa(pa(Rd i j k l m) (Rd i j m k l)) (Rd i j l m k))))))
