;; lib/minikanren/intarith.sx — fast integer arithmetic via project.
;;
;; These are ground-only escapes into host arithmetic. They run at native
;; speed (host ints) but require their arguments to walk to actual numbers
;; — they are not relational the way `pluso` (Peano) is. Use them when
;; the puzzle size makes Peano impractical.
;;
;; Naming: `-i` suffix marks "integer-only" goals.

(define
  pluso-i
  (fn
    (a b c)
    (project
      (a b)
      (if (and (number? a) (number? b)) (== c (+ a b)) fail))))

(define
  minuso-i
  (fn
    (a b c)
    (project
      (a b)
      (if (and (number? a) (number? b)) (== c (- a b)) fail))))

(define
  *o-i
  (fn
    (a b c)
    (project
      (a b)
      (if (and (number? a) (number? b)) (== c (* a b)) fail))))

(define
  lto-i
  (fn
    (a b)
    (project
      (a b)
      (if (and (number? a) (and (number? b) (< a b))) succeed fail))))

(define
  lteo-i
  (fn
    (a b)
    (project
      (a b)
      (if (and (number? a) (and (number? b) (<= a b))) succeed fail))))

(define
  neqo-i
  (fn
    (a b)
    (project
      (a b)
      (if (and (number? a) (and (number? b) (not (= a b)))) succeed fail))))

(define numbero (fn (x) (project (x) (if (number? x) succeed fail))))

(define stringo (fn (x) (project (x) (if (string? x) succeed fail))))

(define symbolo (fn (x) (project (x) (if (symbol? x) succeed fail))))

(define
  even-i
  (fn (n) (project (n) (if (and (number? n) (even? n)) succeed fail))))

(define
  odd-i
  (fn (n) (project (n) (if (and (number? n) (odd? n)) succeed fail))))

(define
  sortedo
  (fn
    (l)
    (conde
      ((nullo l))
      ((fresh (a) (== l (list a))))
      ((fresh (a b rest mid) (conso a mid l) (conso b rest mid) (lteo-i a b) (sortedo mid))))))

(define
  mino
  (fn
    (l m)
    (conde
      ((fresh (a) (== l (list a)) (== m a)))
      ((fresh (a d rest-min) (conso a d l) (mino d rest-min) (conde ((lteo-i a rest-min) (== m a)) ((lto-i rest-min a) (== m rest-min))))))))

(define
  maxo
  (fn
    (l m)
    (conde
      ((fresh (a) (== l (list a)) (== m a)))
      ((fresh (a d rest-max) (conso a d l) (maxo d rest-max) (conde ((lteo-i rest-max a) (== m a)) ((lto-i a rest-max) (== m rest-max))))))))

(define
  sumo
  (fn
    (l total)
    (conde
      ((nullo l) (== total 0))
      ((fresh (a d rest-sum) (conso a d l) (sumo d rest-sum) (pluso-i a rest-sum total))))))

(define
  producto
  (fn
    (l total)
    (conde
      ((nullo l) (== total 1))
      ((fresh (a d rest-prod) (conso a d l) (producto d rest-prod) (*o-i a rest-prod total))))))

(define
  lengtho-i
  (fn
    (l n)
    (conde
      ((nullo l) (== n 0))
      ((fresh (a d n-1) (conso a d l) (lengtho-i d n-1) (pluso-i 1 n-1 n))))))

(define
  enumerate-from-i
  (fn
    (start l result)
    (conde
      ((nullo l) (nullo result))
      ((fresh (a d r-rest start-prime) (conso a d l) (conso (list start a) r-rest result) (pluso-i 1 start start-prime) (enumerate-from-i start-prime d r-rest))))))

(define enumerate-i (fn (l result) (enumerate-from-i 0 l result)))

(define
  counto
  (fn
    (x l n)
    (conde
      ((nullo l) (== n 0))
      ((fresh (a d n-rest) (conso a d l) (conde ((== a x) (counto x d n-rest) (pluso-i 1 n-rest n)) ((nafc (== a x)) (counto x d n))))))))

(define
  mk-arith-prog
  (fn
    (start step len)
    (cond
      ((= len 0) (list))
      (:else (cons start (mk-arith-prog (+ start step) step (- len 1)))))))

(define
  arith-progo
  (fn
    (start step len result)
    (project (start step len) (== result (mk-arith-prog start step len)))))
