#!/usr/bin/env catnip
# fold et reduce

print("⇒ fold : agrégation avec valeur initiale")

# Somme
total = fold(list(1, 2, 3, 4, 5), 0, (acc, x) => { acc + x })
print("Somme 1..5:", total)

# Produit
produit = fold(list(1, 2, 3, 4), 1, (acc, x) => { acc * x })
print("Produit 1..4:", produit)

# Concaténation
mot = fold(list("c", "a", "t"), "", (acc, x) => { acc + x })
print("Concat:", mot)

print()
print("⇒ fold sur collection vide")

# fold est total : sur une liste vide, retourne init
vide = fold(list(), 0, (acc, x) => { acc + x })
print("fold(list(), 0, +):", vide)

print()
print("⇒ fold un seul niveau")

# fold n'effectue pas de descente récursive
taille_totale = fold(
    list(list(1, 2), list(3, 4)),
    0,
    (acc, row) => {
        acc + len(row)
    }
)
print("Éléments dans sous-listes:", taille_totale)

print()
print("⇒ reduce : sans valeur initiale")

# Le premier élément sert d'accumulateur
somme = reduce(list(10, 20, 30), (acc, x) => { acc + x })
print("reduce sum:", somme)

# Maximum artisanal
maximum = reduce(
    list(3, 1, 4, 1, 5, 9, 2, 6),
    (acc, x) => {
        if x > acc { x } else { acc }
    }
)
print("Maximum:", maximum)

print()
print("⇒ Composition broadcast + fold")

# broadcast transforme, fold agrège
resultat = fold(list(1, 2, 3).[* 10], 0, (acc, x) => { acc + x })
print("list(1,2,3).[*10] puis fold +:", resultat)

# Pipeline complet : transformer puis agréger
cubes = fold(list(1, 2, 3, 4).[** 3], 0, (acc, x) => { acc + x })
print("Somme des cubes 1..4:", cubes)

print()
print("⇒ reduce par monoïdes nommés")

data = list(3, -1, 4, -1, 5, 9, -2, 6)

# Un monoïde : la valeur agrégée
print("sum:", reduce(data, sum))
print("len:", reduce(data, len))
print("max:", reduce(data, max))

# Plusieurs monoïdes : un tuple, en une passe
print("sum, len, max:", reduce(data, sum, len, max))

# concat aplatit un niveau ; any/all réduisent des booléens
print("concat:", reduce(list(list(1, 2), list(3), list(4, 5)), concat))
print("all:", reduce(list(True, True, False), all))

# Un callback lambda reste sur le reduce classique
print("callback:", reduce(list(1, 2, 3, 4), (a, b) => { a + b }))