<jupyter_start><jupyter_text>**nocut**The boolean function `nocut` determines if there are no cuts for a given height $x$ on any number of pipes $n$, where $b$ is the bottom position (not relative) of the pipe, $d$ is the diameter, and $v$ is the minimum required space around the pipe. The variable $b_{\text{min}}$ is the lowest pipe, and is used to make $b$ relative.$$ \text{nocut}(x) = \left|\ x - \left(b_n - b_{min} + \dfrac{d_n}{2}\right)\ \right| \geq \dfrac{d_n}{2} + v_n \quad\text{for}\ n=0,1,2,\ldots,7$$If there is an $\text{OB}$ or $\text{TS}$, it becomes:$$ \text{nocut}(x) = \left|\ x - \left(b_n - \left(b_{min} + \text{OB} + \text{TS}_k\right) + \dfrac{d_n}{2}\right)\ \right| \geq \dfrac{d_n}{2} + v_n, $$where $k$ is the number of $\text{TS}$. Both equations can be simplified to $x-b_n+b_\text{min}-\frac{d_n}{2}$, and $x-b_n+b_\text{min}+\text{OB} + \text{TS}_k-\frac{d_n}{2}$ respectively.<jupyter_code>def nocut(p, bmin, x): return abs(x - (p.b - bmin + p.d / 2)) >= p.d / 2 + p.v    
def pad(x, n): return pad("0" + str(x), n) if len(str(x)) != n else str(x)<jupyter_output><empty_output><jupyter_text>The following algorithm finds the optimal possible values for $\text{OB}_{\text{min}}$, $\text{TSk}_{\text{max}}$, and $\text{KS}_{\text{min}}$ under the constraints that: 1. $\text{OB}$ is as small as possible. 2. $\text{TS}$ is as large as possible, but $ R - \text{TS} \geq \text{cover plate}$. 3. $\text{KS}$ is as large as possible, but $ \text{KS} \leq R$.<jupyter_code># Input
A = 8000
bodem = 200
stellaag = 220
B = [1000, 2000, 3000, 4000, 1250, 5000, 6500]
D = [500, 500, 500, 500, 500, 850]
V = [100, 100, 100, 100, 100, 100]

# Casting
P = list(map(lambda p: Pipe(p[0],p[1],p[2]), zip(B,D,V)))

# Put onderdelen
OB = [50*x + 400 for x in range(46)]
TS = [250*x + 500 for x in range(8)]
KS = [250] + [150*x + 400 for x in range(14)]
print('OB: {} ... {}'.format(OB[:3], OB[-3:]))
print('TS: {} ... {}'.format(TS[:3], OB[-3:]))
print('KS: {} ... {}'.format(KS[:3], KS[-3:]))

# Uitgeschakelde putten wegfilteren
B = list(map(lambda x: x if x > 0 else math.inf, B))
print('B (buis bob): {}'.format(B))
print('D (buis diameter): {}'.format(D))
print('V (buis vleesmaat): {}'.format(V))

# Laagste buis bepalen
bmin = min(B)
print('bmin (laagste buis): {}'.format(bmin))

print('A (putrandhoogte NAP): {}'.format(A))

# Puthoogte bepalen 
H = A - bmin - stellaag + bodem
print('H (puthoogte): {}'.format(H))

# Buizen met de onderbakken snijden en de kleinst beschikbare zoeken.
OB_satisfied = [ob for ob in OB if reduce(lambda x, y: x and y, [nocut(p, bmin, ob) 
                                                                 for p in P])]
OBmin = min(OB_satisfied + [math.inf])
print('Onderbak: {}'.format(OBmin))

print('Beschikbare onderbakken (OB {}/{}): '.format(len(OB_satisfied), len(OB)), end='')
print(', '.join(map(lambda x: str(x), OB_satisfied)))

# Resthoogte (0) bepalen.
R0 = H - OBmin
print('R0 (resthoogte 0): {}'.format(R0))

# Kijken of een kegelstuk past, en anders tussenstukken plaatsen.
# ;; kan beter met een while loop (en oneindig veel tussenstukken), maar de inrichting
#    gaat uit van 2 tussenstukken.
if R0 <= max(KS):
    
    # Kegelstuk past, dus we kunnen het kegelstuk bepalen.
    KSmax = max([ks for ks in KS if ks <= R0] + [-math.inf])
    print('Kegelstuk: {}'.format(KSmax))
    print('Aangepaste stellaag: {}'.format(R0 - KSmax))
    print('Totale lengte put: {}'.format(OBmin + KSmax))
    
else:
        
    # Tussenstuk nodig, dus deze bepalen (snijlijnen checken en er moet ook nog een KS op)
    TS0_satisfied = [ts for ts in TS if reduce(lambda x, y: x and y, [nocut(p, bmin + OBmin, ts) # geen snede
                                                                      and R0 - ts >= min(KS)     # kegelstuk overhouden
                                                                      for p in P])]
    print('Beschikbare tussenstukken (TS0 {}/{}): '.format(len(TS0_satisfied), len(TS)), end='')
    print(', '.join(map(lambda x: str(x), TS0_satisfied)))
    
    TS0max = max(TS0_satisfied)
    print('Tussenstuk (TS0): {}'.format(TS0max))
    
    R1 = R0 - TS0max
    print('R1 (resthoogte 1): {}'.format(R1))
    
    # Kijken of een kegelstuk past, en anders tussenstukken plaatsen.
    if R1 <= max(KS):

        # Kegelstuk past, dus we kunnen het kegelstuk bepalen.
        KSmax = max([ks for ks in KS if ks <= R1] + [-math.inf])
        print('Kegelstuk: {}'.format(KSmax))
        print('Aangepaste stellaag: {}'.format(R1 - KSmax))
        print('Totale lengte put: {}'.format(OBmin + TS0max + KSmax))
        
    else:

        # Nog een tussenstuk nodig, dus deze bepalen (snijlijnen checken en er moet ook nog een KS op)
        TS1_satisfied = [ts for ts in TS if reduce(lambda x, y: x and y, [nocut(p, bmin + OBmin + TS0max, ts) # geen snede
                                                                          and R1 - ts >= min(KS)     # kegelstuk overhouden
                                                                          for p in P])]
        print('Beschikbare tussenstukken (TS1 {}/{}): '.format(len(TS1_satisfied), len(TS)), end='')
        print(', '.join(map(lambda x: str(x), TS1_satisfied)))

        TS1max = max(TS1_satisfied)
        print('Tussenstuk (TS1): {}'.format(TS1max))

        R2 = R1 - TS1max
        print('R2 (resthoogte 2): {}'.format(R2))
        
        # Kijken of een kegelstuk past, en anders tussenstukken plaatsen.
        if R2 <= max(KS):

            # Kegelstuk past, dus we kunnen het kegelstuk bepalen.
            KSmax = max([ks for ks in KS if ks <= R2] + [-math.inf])
            print('Kegelstuk: {}'.format(KSmax))
            print('Aangepaste stellaag: {}'.format(R2 - KSmax))
            print('Totale lengte put: {}'.format(OBmin + TS0max + TS1max + KSmax))
            
        else:
            print('Put is te hoog, kan niet worden opgevuld.')<jupyter_output>OB: [400, 450, 500] ... [2550, 2600, 2650]
TS: [500, 750, 1000] ... [2550, 2600, 2650]
KS: [250, 400, 550] ... [2050, 2200, 2350]
B (buis bob): [1000, 2000, 3000, 4000, 1250, 5000, 6500]
D (buis diameter): [500, 500, 500, 500, 500, 850]
V (buis vleesmaat): [100, 100, 100, 100, 100, 100]
bmin (laagste buis): 1000
A (putrandhoogte NAP): 8000
H (puthoogte): 6980
Onderbak: 850
Beschikbare onderbakken (OB 11/46): 850, 900, 1600, 1650, 1700, 1750, 1800, 1850, 1900, 2600, 2650
R0 (resthoogte 0): 6130
Beschikbare tussenstukken (TS0 4/8): 750, 1000, 1750, 2000
Tussenstuk (TS0): 2000
R1 (resthoogte 1): 4130
Beschikbare tussenstukken (TS1 3/8): 750, 1000, 2250
Tussenstuk (TS1): 2250
R2 (resthoogte 2): 1880
Kegelstuk: 1750
Aangepaste stellaag: 130
Totale lengte put: 6850<jupyter_text>The algorithm is a bit weird, but the layout is such that it can be converted to feature model conditions more easily. Below is a version that supports infinite $\text{TS}$ it will return `inf` if there is no solution.<jupyter_code>def nocut(p, bmin, x): return abs(x - (p.b - bmin + p.d / 2)) >= p.d / 2 + p.v  
def maxinf(x): return max(x + [-math.inf]) # always returns a result, even if x is empty.
def mininf(x): return min(x + [math.inf]) # always returns a result, even if x is empty.

A = 8000;bodem = 200;stellaag = 220
B = [1200, 4000, 5000, 0, 7000, 0, 0]
D = [1000, 1000, 500, 1000, 200, 850]
#V = [100, 100, 100, 100, 100, 100]
P = list(map(lambda p: Pipe(p[0],p[1],p[2]), zip(B,D,V)))
OB = [50*x + 400 for x in range(46)]
TS = [250*x + 500 for x in range(8)]
KS = [250] + [150*x + 400 for x in range(14)]
B = list(map(lambda x: x if x > 0 else math.inf, B))
bmin = min(B)
H = A - bmin - stellaag + bodem
Plen = 0

# onderbak
OB_satisfied = [ob for ob in OB if reduce(lambda x, y: x and y, [nocut(p, bmin, ob) 
                                                                 for p in P])]
OBmin = mininf(OB_satisfied); Plen = OBmin
print('Onderbak   (OB): {}'.format(OBmin))

# tussenstukken
R = H - OBmin
while R > max(KS):
    TS_satisfied = [ts for ts in TS if reduce(lambda x, y: x and y, [nocut(p, bmin + H - R, ts) for p in P]) 
                                       and R - ts >= min(KS)]
    if not TS_satisfied: 
        # can't find a TS, abort.
        TS_satisfied = [math.inf]; 
        OBmin = math.inf 
        
    TSmax = maxinf(TS_satisfied); Plen += TSmax
    R = R - TSmax
    print('Tussenstuk (TS): {}'.format(TSmax))

# kegelstuk
KSmax = maxinf([ks for ks in KS if ks <= R])
Plen += KSmax
print('Kegelstuk  (KS): {}'.format(KSmax))

# resultaat
print('Aangepaste stellaag: {}'.format(H-Plen))
print('Totale lengte put: {}'.format(Plen))
print('H (puthoogte): {}'.format(H))
if OBmin == math.inf: print('No solution')
def head(s): return s[0:0]
def tail(s): return s[1:]
def reverse(s): return s[::-1]
def last(s): return s[-1:]
def init(s): return s[0:len(s)-1]
def take(s, n): return s[:n]
def drop(s, n): return s[n:]
def product(s): return reduce(lambda x, y: x * y, s)
B = [1000,2300,0,0,0,0,0,0]
D = [600,600,0,0,0,0,0,0]
V = [210,210,0,0,0,0,0,0]
B = list(map(lambda x: x if x > 0 else math.inf, B))
P = list(map(lambda p: Pipe(p[0], p[1], p[2]), zip(B,D,V)))
A = 6000
stellaag = 220
D = 200
H = A - stellaag - min(B) + D
OB = 850
TS = [250*x + 500 for x in range(8)]
R = H - OB
print('Puthoogte: {}'.format(H))
print('Resthoogte: {}'.format(R))
[ts for ts in TS if reduce(lambda x, y: x and y, [nocut(p, min(B) + OB, ts) for p in P])]
TS<jupyter_output><empty_output><jupyter_text>Formula generation<jupyter_code>f = """
@R2 - x <= 150 && @R2 - x >= 0
"""

for ks in KS:
    print(f.replace('x', str(ks)))
f = """
math.abs( x - (@b0 - @bmin + @d0 / 2) ) >= @d0 / 2 + @v0 &&
math.abs( x - (@b1 - @bmin + @d1 / 2) ) >= @d1 / 2 + @v1 &&
math.abs( x - (@b2 - @bmin + @d2 / 2) ) >= @d2 / 2 + @v2 &&
math.abs( x - (@b3 - @bmin + @d3 / 2) ) >= @d3 / 2 + @v3 &&
math.abs( x - (@b4 - @bmin + @d4 / 2) ) >= @d4 / 2 + @v4 &&
math.abs( x - (@b5 - @bmin + @d5 / 2) ) >= @d5 / 2 + @v5 &&
math.abs( x - (@b6 - @bmin + @d6 / 2) ) >= @d6 / 2 + @v6 &&
math.abs( x - (@b7 - @bmin + @d7 / 2) ) >= @d7 / 2 + @v7
"""

for ob in OB:
    print(f.replace('x', str(ob)))<jupyter_output>math.abs( 400 - (@b0 - @bmin + @d0 / 2) ) >= @d0 / 2 + @v0 &&
math.abs( 400 - (@b1 - @bmin + @d1 / 2) ) >= @d1 / 2 + @v1 &&
math.abs( 400 - (@b2 - @bmin + @d2 / 2) ) >= @d2 / 2 + @v2 &&
math.abs( 400 - (@b3 - @bmin + @d3 / 2) ) >= @d3 / 2 + @v3 &&
math.abs( 400 - (@b4 - @bmin + @d4 / 2) ) >= @d4 / 2 + @v4 &&
math.abs( 400 - (@b5 - @bmin + @d5 / 2) ) >= @d5 / 2 + @v5 &&
math.abs( 400 - (@b6 - @bmin + @d6 / 2) ) >= @d6 / 2 + @v6 &&
math.abs( 400 - (@b7 - @bmin + @d7 / 2) ) >= @d7 / 2 + @v7


math.abs( 450 - (@b0 - @bmin + @d0 / 2) ) >= @d0 / 2 + @v0 &&
math.abs( 450 - (@b1 - @bmin + @d1 / 2) ) >= @d1 / 2 + @v1 &&
math.abs( 450 - (@b2 - @bmin + @d2 / 2) ) >= @d2 / 2 + @v2 &&
math.abs( 450 - (@b3 - @bmin + @d3 / 2) ) >= @d3 / 2 + @v3 &&
math.abs( 450 - (@b4 - @bmin + @d4 / 2) ) >= @d4 / 2 + @v4 &&
math.abs( 450 - (@b5 - @bmin + @d5 / 2) ) >= @d5 / 2 + @v5 &&
math.abs( 450 - (@b6 - @bmin + @d6 / 2) ) >= @d6 / 2 + @v6 &&
math.abs( 450 - (@b7 - @bmin + @d7 / 2) ) >= @d7 / 2 + @v7


math.abs([...]
{'fraction_non_alphanumeric': 0.19355128435423027, 'fraction_numerical': 0.07194179989222202, 'mean_word_length': 2.6724934036939314, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 20, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '153005', 'license': 'Unlicense', 'n_tokens_mistral': 5161, 'n_tokens_neox': 4528, 'n_words': 1373, 'path': '.ipynb_checkpoints/Put problem-checkpoint.ipynb', 'repo_name': 'darkeclipz/jupyter-notebooks', 'size': '11134'}