From dc3e512bbf4ccb24a0e1c8ef9fc23156d44c7b25 Mon Sep 17 00:00:00 2001
From: Christian Gram Kalhauge <chrg@dtu.dk>
Date: Thu, 20 Feb 2025 10:02:41 +0100
Subject: [PATCH] Update to work.

---
 flake.lock                              |  25 ++--
 flake.nix                               |  58 +++++-----
 pyrtree/rtree.py                        | 146 +++++++++++++++++++++++-
 rtree-c/bin/Main.hs                     |   2 +-
 rtree/rtree.cabal                       |   3 +-
 rtree/src/Control/Monad/IRTree.hs       |   1 +
 rtree/src/Control/Monad/RTree.hs        |   4 +-
 rtree/src/Control/Monad/RTree/Simple.hs |  29 -----
 8 files changed, 192 insertions(+), 76 deletions(-)
 delete mode 100644 rtree/src/Control/Monad/RTree/Simple.hs

diff --git a/flake.lock b/flake.lock
index 0b9c3a2..25d79f5 100644
--- a/flake.lock
+++ b/flake.lock
@@ -5,17 +5,16 @@
         "systems": "systems"
       },
       "locked": {
-        "lastModified": 1705309234,
-        "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
+        "lastModified": 1731533236,
+        "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
         "owner": "numtide",
         "repo": "flake-utils",
-        "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
+        "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
         "type": "github"
       },
       "original": {
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "type": "github"
+        "id": "flake-utils",
+        "type": "indirect"
       }
     },
     "flake-utils_2": {
@@ -120,18 +119,16 @@
     },
     "nixpkgs_2": {
       "locked": {
-        "lastModified": 1707939175,
-        "narHash": "sha256-D1xan0lgxbmXDyzVqXTiSYHLmAMrMRdD+alKzEO/p3w=",
-        "owner": "nixos",
+        "lastModified": 1739863612,
+        "narHash": "sha256-UbtgxplOhFcyjBcNbTVO8+HUHAl/WXFDOb6LvqShiZo=",
+        "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "f7e8132daca31b1e3859ac0fb49741754375ac3d",
+        "rev": "632f04521e847173c54fa72973ec6c39a371211c",
         "type": "github"
       },
       "original": {
-        "owner": "nixos",
-        "ref": "nixpkgs-unstable",
-        "repo": "nixpkgs",
-        "type": "github"
+        "id": "nixpkgs",
+        "type": "indirect"
       }
     },
     "root": {
diff --git a/flake.nix b/flake.nix
index ba4cb19..9f6a3cf 100644
--- a/flake.nix
+++ b/flake.nix
@@ -1,13 +1,11 @@
 {
   inputs = {
-    nixpkgs.url = github:nixos/nixpkgs/nixpkgs-unstable;
-    flake-utils.url = github:numtide/flake-utils;
-    nix-filter.url = github:numtide/nix-filter;
+    nix-filter.url = "github:numtide/nix-filter";
     language-c = {
-      url = github:kalhauge/language-c;
+      url = "github:kalhauge/language-c";
       flake = false;
     };
-    hspec-glitter.url = github:kalhauge/hspec-glitter;
+    hspec-glitter.url = "github:kalhauge/hspec-glitter";
   };
   outputs = {
     self,
@@ -19,33 +17,41 @@
     load = p: n:
       p.callCabal2nixWithOptions n (nix-filter.lib {root = "${self}/${n}";}) "" {};
 
-    packages = lib: p: {
-      "language-c" = lib.dontCheck (p.callCabal2nixWithOptions "language-c" inputs.language-c "" {});
-      "hspec-glitter" = p.callCabal2nixWithOptions "hspec-glitter" inputs.hspec-glitter "" {};
-      "rtree" = lib.dontCheck (load p "rtree");
-      "rtree-c" = lib.dontCheck (load p "rtree-c");
+    packages = lib: final: prev: {
+      "language-c" = lib.dontCheck (final.callCabal2nixWithOptions "language-c" inputs.language-c "" {});
+      "hspec-glitter" = final.callCabal2nixWithOptions "hspec-glitter" inputs.hspec-glitter "" {};
+      "rtree" = lib.dontCheck (load final "rtree");
+      "rtree-c" = lib.dontCheck (load final "rtree-c");
+    };
+
+    overlay = final: prev: let
+      lib = final.haskell.lib;
+      hpkgs = prev.haskellPackages.override (hpArgs: {
+        overrides = final.lib.composeExtensions (hpArgs.overrides or (_: _: {})) (packages lib);
+      });
+    in {
+      haskellPackages = hpkgs;
+      rtree-c = lib.justStaticExecutables (hpkgs.rtree-c);
+      rtree = hpkgs.rtree;
     };
   in
-    flake-utils.lib.eachDefaultSystem
+    {
+      overlays.default = overlay;
+    }
+    // flake-utils.lib.eachDefaultSystem
     (system: let
       pkgs = import nixpkgs {
         inherit system;
-        overlays = [];
+        overlays = [overlay];
       };
-      lib = pkgs.haskell.lib;
-      hpkgs = pkgs.haskellPackages.override (hpArgs: {
-        overrides = pkgs.lib.composeExtensions (hpArgs.overrides or (_: _: {})) (
-          _hfinal: hprev: packages lib hprev
-        );
-      });
     in {
       packages = {
-        default = lib.justStaticExecutables (hpkgs.rtree-c);
-        rtree = hpkgs.rtree;
-        rtree-c = hpkgs.rtree-c;
+        default = pkgs.rtree-c;
+        rtree = pkgs.rtree;
+        rtree-c = pkgs.rtree-c;
       };
       devShells = let
-        nativeBuildInputs = with hpkgs; [
+        nativeBuildInputs = with pkgs.haskellPackages; [
           cabal-install
           ghcid
           haskell-language-server
@@ -53,7 +59,7 @@
           fourmolu
         ];
         withHoogle = true;
-        profiles = hpkgs.override (hpArgs: {
+        profiles = pkgs.haskellPackages.override (hpArgs: {
           overrides = pkgs.lib.composeExtensions (hpArgs.overrides or (_: _: {})) (
             _hfinal: hprev: {
               mkDerivation = args:
@@ -80,14 +86,14 @@
           );
         });
       in {
-        rtree = hpkgs.rtree;
+        rtree = pkgs.haskellPackages.rtree;
         default =
           profiles.shellFor
           {
             name = "rtree-shells";
             packages = p: [
-              (lib.doCheck p.rtree)
-              (lib.doCheck p.rtree-c)
+              (pkgs.haskell.lib.doCheck p.rtree)
+              (pkgs.haskell.lib.doCheck p.rtree-c)
             ];
             doBenchmark = true;
             genericBuilderArgsModifier = args:
diff --git a/pyrtree/rtree.py b/pyrtree/rtree.py
index ec34513..ac6b969 100644
--- a/pyrtree/rtree.py
+++ b/pyrtree/rtree.py
@@ -1,4 +1,26 @@
 from dataclasses import dataclass
+import math
+from typing import TypeVar
+
+
+def reduce_simp(predicate, rtree):
+    path: list[bool] = []
+    labels: list[str] = []
+
+    def check(label):
+        labels.append(label)
+        return path[len(labels) - 1] if len(path) >= len(labels) else False
+
+    i = rtree(check)
+    while len(path) < len(labels):
+        path.append(True)
+        labels.clear()
+        if t := predicate(j := rtree(check)):
+            i = j
+        else:
+            path[-1] = False
+        print(f"{labels[len(path) - 1]} ... test {j!r:<10} ... {t}")
+    return i
 
 
 class Probe:
@@ -20,6 +42,23 @@ class Probe:
         return len(self.reasons) - len(self.path)
 
 
+def reduce1(predicate, rtree):
+    # Extract the initial reduction probe, from the rightmost branch.
+    rp = Probe(rtree, [])
+    # Run exponential search after the depest sequence of trues
+    # that can be appended to the path without failing the predicate
+    depth = 1
+    # Invariant: predicate(rp) == True
+    while rp.undecided() > 0:
+        # Try to probe the with current path extended by one trues
+        if predicate(rp_ := Probe(rtree, rp.path, depth)):
+            rp = rp_
+            continue
+        rp.path.append(False)
+    # return the input.
+    return rp
+
+
 def reduce(predicate, rtree):
     # Extract the initial reduction probe, from the rightmost branch.
     rp = Probe(rtree, [])
@@ -89,6 +128,29 @@ def latex(
     return newpred
 
 
+def table(
+    predicate,
+    input_format="{}".format,
+    query_format=str,
+    start_count=0,
+):
+    counter = start_count - 1
+
+    def newpred(rp):
+        nonlocal counter
+        counter += 1
+        t = predicate(rp.input)
+        query = ", ".join(
+            query_format(a) for a in rp.reasons[len(rp.path) - rp.depth : len(rp.path)]
+        )
+        theck = "true " if t else "false"
+
+        print(f"{counter:02} - {input_format(rp.input)} - {theck} - {query}")
+        return t
+
+    return newpred
+
+
 def pretty(rp):
     from itertools import zip_longest
 
@@ -103,14 +165,94 @@ def pretty(rp):
 
 def reduce_abc(check) -> str:
     result = ""
-    for x in "abcdefghijklmnopqrstuvxyz":
-        if not check(f"{x}"):
+    for x in "abc":
+        if not check(f"remove {x}?"):
             result += x
         else:
             result += " "
     return result
 
 
+def reduce_dd2(c: list, check) -> list:
+    if check(f"ignore {c}?"):
+        return []
+    if len(c) == 1:
+        return c
+    pivot = len(c) // 2
+    c2 = reduce_dd(c[pivot:], check)
+    c1 = reduce_dd(c[:pivot], check)
+    return c1 + c2
+
+
+def reduce_dd(c: list, check) -> list:
+    if len(c) == 1:
+        return c
+    pivot = len(c) // 2
+    c1 = c[:pivot]
+    c2 = c[pivot:]
+    if check(f"result in c1: {c1}?"):
+        return reduce_dd(c1, check)
+    elif check(f"result in c2: {c2}?"):
+        return reduce_dd(c2, check)
+    else:
+        return reduce_dd(c1, check) + reduce_dd(c2, check)
+
+
+I = TypeVar("I")
+
+
+def reduce_df(i: I, actions, check) -> I:
+    j = None
+    while i != j:
+        for act in actions:
+            if check(f"apply {act}?"):
+                i, j = act(i), i
+        else:
+            break
+    return i
+
+
+def reduce_ddmin(input: list, check, n=2) -> list:
+    def find_next(input, n):
+        step = math.ceil(len(input) / n)
+        subsets = [(step * i, step * (i + 1)) for i in range(n)]
+        for i, (f, t) in enumerate(subsets):
+            if check(f"delta: {i}/{n}"):
+                return input[f:t], 2
+
+        for i, (f, t) in enumerate(subsets) if n != 2 else []:
+            if check(f"complement: {i}/{n}"):
+                return input[:f] + input[t:], max(n - 1, 2)
+
+        return input, n * 2
+
+    while len(input) > 1 and n < 2 * len(input):
+        input, n = find_next(input, n)
+
+    return input
+
+
+def test_reduce_dd():
+    from functools import partial
+
+    p = debug(lambda a: 3 in a and 6 in a)
+    rp = reduce(p, partial(reduce_dd, [1, 2, 3, 4, 5, 6, 7, 8]))
+    print(rp)
+
+
+def test_reduce_ddmin():
+    from functools import partial
+
+    input_format = lambda a: "".join("X" if i in a else "." for i in range(1, 9))
+    p = table(
+        lambda a: 1 in a and 7 in a and 8 in a,
+        input_format=input_format,
+        query_format=str,
+    )
+    rp = reduce1(p, partial(reduce_ddmin, [1, 2, 3, 4, 5, 6, 7, 8]))
+    print(f"   - {input_format(rp.input)}")
+
+
 if __name__ == "__main__":
     p = latex(
         lambda e: "a" in e or "p" in e,
diff --git a/rtree-c/bin/Main.hs b/rtree-c/bin/Main.hs
index d5b9e73..72b2df0 100644
--- a/rtree-c/bin/Main.hs
+++ b/rtree-c/bin/Main.hs
@@ -82,7 +82,7 @@ run = do
       fold
         [ long "keyword"
         , short 'k'
-        , help "don't do any reduction"
+        , help "special versions of the code"
         ]
 
   validity <-
diff --git a/rtree/rtree.cabal b/rtree/rtree.cabal
index 62066a4..dbc30ab 100644
--- a/rtree/rtree.cabal
+++ b/rtree/rtree.cabal
@@ -1,6 +1,6 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.35.2.
+-- This file has been generated from package.yaml by hpack version 0.36.1.
 --
 -- see: https://github.com/sol/hpack
 
@@ -13,7 +13,6 @@ library
       Control.Monad.IRTree
       Control.Monad.Reduce
       Control.Monad.RTree
-      Control.Monad.RTree.Simple
       Data.RPath
       Data.Valuation
   other-modules:
diff --git a/rtree/src/Control/Monad/IRTree.hs b/rtree/src/Control/Monad/IRTree.hs
index 0f92d17..f6d4b2e 100644
--- a/rtree/src/Control/Monad/IRTree.hs
+++ b/rtree/src/Control/Monad/IRTree.hs
@@ -37,6 +37,7 @@ import Control.Monad.Reader
 import Control.Monad.Reduce
 import Data.Bits
 import Data.Foldable
+import Data.Monoid
 import Data.Function
 import Data.Functor
 import Data.Functor.Identity
diff --git a/rtree/src/Control/Monad/RTree.hs b/rtree/src/Control/Monad/RTree.hs
index 2cf8793..2337088 100644
--- a/rtree/src/Control/Monad/RTree.hs
+++ b/rtree/src/Control/Monad/RTree.hs
@@ -36,9 +36,9 @@ module Control.Monad.RTree (
 import Control.Monad
 import Control.Monad.Identity
 import Control.Monad.Reduce
-import Data.Foldable
 import Data.Foldable.WithIndex
-import Data.Function ((&))
+import Data.Function ((&), fix)
+import Data.Foldable
 import Data.RPath
 import qualified Data.Sequence as Seq
 
diff --git a/rtree/src/Control/Monad/RTree/Simple.hs b/rtree/src/Control/Monad/RTree/Simple.hs
deleted file mode 100644
index 73ab863..0000000
--- a/rtree/src/Control/Monad/RTree/Simple.hs
+++ /dev/null
@@ -1,29 +0,0 @@
-{-# LANGUAGE BlockArguments #-}
-{-# LANGUAGE DerivingVia #-}
-
-module Control.Monad.RTree.Simple where
-
-import Control.Monad.Identity
-import Control.Monad.State
-
-type RTree a = State ([Bool], Int) a
-
-check :: RTree Bool
-check = state \(c : cs, n) -> (c, (cs, n + 1))
-
-probe :: RTree a -> [Bool] -> (a, Int)
-probe root path = (extract, depth - length path)
- where
-  (extract, (_, depth)) = runState root (path ++ repeat False, 0)
-
-reduce :: (a -> IO Bool) -> RTree a -> [Bool] -> IO a
-reduce predicate root path
-  | undecided > 0 = do
-      result <- predicate (fst (probe root (path ++ [True])))
-      reduce predicate root (path ++ [result])
-  | otherwise = pure extract
- where
-  (extract, undecided) = probe root path
-
-reduceAbc :: RTree [Char]
-reduceAbc = filterM (\_ -> check) "abc"
-- 
GitLab