SAS-Taper · how it works

Choosing the fold

A buprenorphine taper asks you to take a little less film each cycle. Today's dose might be 15.28 mm of a 22 mm strip. Measuring that needs a steel rule, good light and a steady hand, at six in the morning, every day for two months.

Folding does not. Halve the film, halve it again, take five of the six parts: no ruler, no arithmetic, and the same answer every time. The catch is that a fold can only land on certain amounts, so the tool has to find the closest one you can actually make. This page is how it does that: which grid to fold into, which parts to take, where the blade goes, and how far off the answer is.

Every diagram below is live: move the slider and the whole thing runs again on the new number.

Dose wanted, off one 8 mg film
5.56mg
Examples
Start here

The words this page uses

All of it is about one rectangle of film and how you divide it up, so there are only a handful of things to name. Every one of them is on the picture below.

A film folded into 4 along its length and 3 across its width, with 5 of the 12 parts taken. Every term on this page is labelled here.

And the names in the code

The listing below spells names out, parts_taken rather than cells, because it is there to be read. The calculator's own two copies stay short. Here is every name the listing uses.

NameMeansIn the picture above
wantThe dose you are trying to hit, written as a fraction of one whole film. 0 is nothing, 1 is the entire film.n/a
film_mgHow many milligrams the whole film is. Turns a fraction back into a dose.8 mg
full_mmThe film's long side in millimetres.22.0
wide_mmThe film's short side in millimetres.12.8
long_divHow many parts the long side is folded into. "div" is short for divisions.4
short_divHow many parts the short side is folded into.3
parts_in_allHow many little rectangles the grid makes altogether, the two divisions multiplied. This is the bottom of the fraction.12
parts_takenHow many of the little rectangles you are taking. Not how many there are. How many you keep.5 of 12
columnsHow many full-height strips of the film that adds up to.1
tabThe leftover cells that do not make a full column, the little bit hanging off the end.2
tab_mmThe tab's smaller side in millimetres, the actual size of the little bit your fingers have to hold. Under 2 mm and the fold is priced as awkward.5.5
cutsHow many times you put a blade to the film.3
cut_along_the_length1 if one of those strokes runs the long way down the film, 0 if not. That is the freehand one, and the expensive one.1
cut_across_the_widthHow many strokes run the short way across instead, guided by the film's own straight edge. The cheap ones.2
piecesHow many separate bits of film end up being your dose.2
error_mgHow far this fold's dose is from the one you wanted, in milligrams. Never hidden from the reader.n/a
difficultyA made-up score for how hard this fold is to do by hand: the strokes, extra pieces, a tab too small to hold. Lower is easier. Only ever compared against other folds.n/a
finenessHow fine the grid is, as a small extra ranking after the error. A coarser grid wins only when two folds already take the same strokes and land equally close. It is not part of the difficulty score.n/a
tolerance_mgYour own cutting tolerance in milligrams: how far out you would be anyway, using a ruler.n/a
candidatesThe list of every fold the grid can make. 180 of them.n/a
closest_possibleThe smallest error any fold in that list manages.n/a
worst_allowedThe most error we will accept. A fold has to be at least this close to stay in the running.n/a
shortlistThe folds that are close enough, the ones still in the running once that ceiling is applied.n/a
chosenThe one fold that wins, and the one the tool draws.n/a

One piece of Python: divmod

divmod(a, b) does one division and hands back both answers at once: how many times b goes into a, and what is left over. divmod(5, 3) is 1 and 2. Three goes into five once, with two left over.

That is exactly the question being asked of the grid. Five cells, three to a column: one full column, and two cells left over. Those two leftovers are the tab. One line of arithmetic decides the whole shape of the piece.

The whole algorithm

All of it, in one place

This is the search the tool runs, with the comments written for someone meeting it for the first time. Everything after it on this page is these same steps, one at a time, with a picture for each. If you would rather see the pictures first, skip past it. Nothing below depends on having read it.

Lines
# ── The grid ────────────────────────────────────────────────────────────────
# A fold is only useful if a person can make it by eye, so the film may only be
# divided these ways. Along its 22 mm length: into 1, 2, 3, 4 or 8. Eighths
# are just three halvings in a row, so they stay accurate. Across its 12.8 mm
# width: only as far as 4, because an eighth of 12.8 mm is 1.6 mm, and nobody
# is judging 1.6 mm by eye.
LONG_DIVISIONS  = [1, 2, 3, 4, 8]
SHORT_DIVISIONS = [1, 2, 3, 4]


def fraction_cut(want, film_mg, full_mm, wide_mm, tolerance_mg):
    # want          the dose you are after, as a fraction of one whole film.
    #               1.0 is the entire film, 0.5 is half of it. It can be a
    #               fraction because the film is an evenly made sheet, so the
    #               share of the AREA you take is the share of the DOSE.
    # film_mg       what a whole film is worth, e.g. 8 mg. Turns any fraction
    #               back into milligrams.
    # full_mm       the long side, 22.0 mm.
    # wide_mm       the short side, 12.8 mm.
    # tolerance_mg  how far off you would be anyway. If you can cut to half a
    #               millimetre, that is 0.18 mg of dose on an 8 mg film, and a
    #               fold that lands inside 0.18 mg is not actually worse than
    #               measuring, so we are allowed to prefer an easier one.

    candidates = []

    # Try every grid, and inside each grid every possible number of parts.
    # 5 long ways x 4 short ways = 20 grids; summed over all of them there are
    # 180 (grid, number of parts) combinations to consider. That is small
    # enough to check the lot, so there is no cleverness here. It is a plain
    # exhaustive search.
    for long_div in LONG_DIVISIONS:
        for short_div in SHORT_DIVISIONS:
            parts_in_all = long_div * short_div

            for parts_taken in range(1, parts_in_all + 1):

                # ── What shape is that? ─────────────────────────────────
                # "Take 5 of the 12 parts" does not say WHICH 5, and five
                # parts dotted around the film is not something anyone can
                # cut. So the shape is decided here, once, and it is always
                # the same kind of shape.
                #
                # divmod(a, b) is Python for "divide, and give me both
                # answers": how many whole times b fits into a, and what is
                # left over. divmod(5, 3) is 1 and 2: three fits into five
                # once, with two spare.
                #
                # Asked of the grid, that reads: 5 parts, 3 to a column, so
                # ONE whole column plus TWO parts left over. Those leftovers
                # sit in the column right next door, never anywhere else, so
                # the piece is always a rectangle or an L, one connected bit
                # of film you can actually pick up.
                columns, tab = divmod(parts_taken, short_div)

                # ── How many cuts is that? ──────────────────────────────
                # Nothing is searched for here. Once you know the shape, the
                # number of blade strokes is fixed. Four cases, that is all:
                if parts_taken == parts_in_all:
                    # The whole film. Swallow it; no blade at all.
                    cuts, pieces = 0, 1

                elif tab == 0:
                    # A whole number of columns and no leftover, so one
                    # straight cut across the film does it.
                    cuts, pieces = 1, 1

                else:
                    # There is a tab, so the column holding it has to be
                    # separated from its neighbours and then split.
                    #
                    # The middle line is the interesting one. If the tab's
                    # column is the LAST column, its outer side is already the
                    # edge of the film, the manufacturer cut it for you, so
                    # that stroke is free. That is the whole reason 5/6 on a
                    # 3x2 grid takes two strokes rather than three, and 5/6 is
                    # the very first fold of a standard taper.
                    cuts = ((1 if columns else 0)              # cut left of the tab
                            + (1 if columns + 1 < long_div else 0)  # and right of it
                            + 1)                              # and split the tab off
                    # A tab plus whole columns is two separate bits of film.
                    # Both go under the tongue; it is still one dose.
                    pieces = 2 if columns else 1

                # ── How hard is it to actually do? ──────────────────────
                # A score, invented for this job, only ever compared against
                # other folds. Lower means easier. The numbers are weights,
                # not units of anything.
                cut_along_the_length = 1 if tab else 0
                cut_across_the_width = cuts - cut_along_the_length

                # Cutting ACROSS the narrow width is a 12.8 mm stroke you can
                # run against the film's own straight edge. Cutting ALONG the
                # length is 22 mm freehand down the middle of the film with
                # nothing to guide you. Those are not the same job, and if you
                # price them the same the search cheerfully suggests slicing a
                # film lengthways down the middle to get one half, which is
                # the harder way to do the easiest cut there is.
                difficulty  = cut_across_the_width * 10
                difficulty += cut_along_the_length * 14
                difficulty += (pieces - 1) * 4        # two bits is fiddlier than one

                # Whatever the arithmetic says, a scrap under 2 mm on its
                # short side is not something fingers and a razor can handle.
                if tab:
                    tab_mm = min(full_mm / long_div, wide_mm * tab / short_div)
                    if tab_mm < 2:
                        difficulty += 20

                # Grid fineness is NOT difficulty. Folding into 3 is a
                # judgement and folding into 2 is not, but that only ranks two
                # folds that already take the same strokes and land equally
                # close. Put it in the score and 0.32 mg on an 8 mg film
                # prefers 1/16 (off by 0.18 mg) over 1/24 (off by 0.01 mg)
                # because thirds look three points dearer than halves: same
                # two cuts, 0.17 mg spent on the coarser-looking grid.
                fineness  = {1: 0, 2: 0, 3: 2, 4: 3, 8: 6}[long_div]
                fineness += {1: 0, 2: 2, 3: 5, 4: 7}[short_div]

                # And how wrong the dose would be, in milligrams. The number
                # that gets shown to the reader, every single time.
                error_mg = abs(parts_taken / parts_in_all - want) * film_mg

                candidates.append((difficulty, error_mg, fineness, long_div,
                                   short_div, parts_taken, columns, tab,
                                   cuts, pieces))

    # ── Pick one ────────────────────────────────────────────────────────
    # Two things matter and they pull against each other: how close the dose
    # is, and how easy the fold is. The trade is settled by the reader's own
    # tolerance. Any fold inside it is no worse than what a ruler would have
    # given them, so among THOSE we are free to take the easiest.
    closest_possible = min(error for _, error, *_ in candidates)

    # The most error we will put up with. Note it is a ceiling on the error
    # ITSELF, not a margin added on top of the closest possible one. Written
    # the other way round, closest_possible + tolerance, the two stack up,
    # and the fold you end up drawing can sit further out than the tolerance
    # the reader gave you. That bug shipped once and put a real cycle 0.22 mg
    # out while claiming to respect 0.18.
    worst_allowed = max(closest_possible, tolerance_mg)

    shortlist = [c for c in candidates if c[1] <= worst_allowed]

    # Easiest wins. Among equal strokes, the closer dose wins;
    # fineness only breaks a remaining tie, so the picture still never
    # changes under a reader who has not changed anything.
    chosen = min(shortlist, key=lambda c: (
        c[0],    # difficulty, the strokes, the real decision
        c[1],    # then whichever is closer
        c[2],    # then the coarser grid
        c[4],    # then the coarser fold across the width
        -c[3],   # then the finer fold along the length
        c[5],    # and finally the fewer parts
    ))
    return chosen


# ── Draw it ─────────────────────────────────────────────────────────────────
# The picture is built from `columns` and `tab`, the same two numbers the cut
# count came from, so the drawing and the caption cannot drift apart. There is
# no second idea of the geometry anywhere.
column_width = film_width / long_div      # on screen, not in mm
row_height   = film_height / short_div

paint(0, 0, columns * column_width, film_height)            # the whole columns
paint(columns * column_width, 0, column_width, tab * row_height)  # the tab

# The blade strokes: the exact same three conditions that counted them.
if columns:                 line_down(columns * column_width)
if columns + 1 < long_div:  line_down((columns + 1) * column_width)
if tab:                     line_across(tab * row_height)
01 The target

One number, as a fraction of the film

The taper schedule hands over a dose in milligrams. First thing the algorithm does is forget the milligrams.

A Suboxone film is one evenly made sheet, so the share of the area you take is the share of the dose you get. Take a third of the film, get a third of the dose. That means the whole problem collapses to a single number between 0 and 1, and the film's size, and its strength, stop mattering until it is time to draw the answer.

That number is the entire input. Everything after this is a search for the closest fraction a person can actually fold to.

The exact cut. One straight line, at a number you would need a steel rule to hit.

02 The search space

Fold the long side into 1, 2, 3, 4 or 8; the short side into 1, 2, 3 or 4

A fold is only worth offering if a person can make it accurately without tools, and the one thing hands are genuinely good at is halving. Halve a 22 mm film and you have eighths in three moves, each one as easy as the last, so the long side goes up to 8.

The short side is only 12.8 mm to begin with, and an eighth of that is 1.6 mm. Nobody judges 1.6 mm by eye, so it stops at 4.

Five ways to fold the length times four ways to fold the width is 20 different grids, and in each of them you can take any whole number of the parts. Counted up that is 180 folds to choose from. Many of them come to the same amount. Half is half whether you got there by folding into 2 or into 8, so between them they can hit 54 different fractions of a film.

Those 54 fractions are what the folding method can reach, and everything in between it cannot. So the question is how wide the gaps are. The widest one anywhere is between 1/8 and 5/32, a hair over 3% of a film. Land in the middle of that and you are half a gap from either neighbour, which on an 8 mg film is 0.125 mg.

Worth holding onto: that worst case is smaller than the ±0.18 mg you would be out by anyway if your ruler slipped half a millimetre. The approximation is not the weak link.

The ladder is live. Click any tick, or take it from the list underneath, and that fold is drawn next to the one the search chose, with both difficulty scores broken out term by term. It is worth doing at least once, because the search does not simply take the closest fraction. Every fold closer than the chosen one was in the running: being closer than the winner puts you inside the tolerance by definition, so nothing closer is ever thrown out on distance. A closer fold that takes more strokes can still lose. A closer fold with the same strokes cannot.

Park the slider at 0.32 mg (the left stop, or the 0.32 button) and compare 1/16 with 1/24. Both are two cuts and one piece. 1/16 is 0.18 mg out; 1/24 is 0.01. The search takes 1/24. Put grid fineness in the difficulty score and it takes 1/16 instead: same strokes, 0.17 mg spent on the coarser-looking grid. That is the whole reason fineness ranks after error, and the whole reason 0.32 mg has a button.

All 54 fractions the folding method can reach. Taller ticks are the easier ones, reachable with fewer strokes. The red line is the dose you asked for; the ring is the fold that gets chosen, and the highlighted tick is the one being compared below.

Where the difficulty comes from chosenpicked

03 The shape rule

Whole columns, plus a tab off the next one

Here is the constraint that makes the rest possible. Saying "take 5 of the 12 parts" does not say which five, and five parts scattered around the film is not an instruction anyone can follow. So the shape is settled once, with a single division:

columns, tab = divmod(parts_taken, short_div)

Five parts, three to a column, gives one whole column and two parts left over. Those leftovers, the tab, always sit in the column immediately next door, never off on their own. So the piece is always a rectangle or an L: one connected bit of film you can pick up.

One line of arithmetic buys three things at once. The piece is always in one connected shape. The number of cuts follows from it without searching for anything. And the drawing has something simple to draw.

Left: what divmod produces. Right: a set of cells adding to the same fraction that the rule refuses to consider.

04 Counting the cuts

Nothing searches for the cut count; it falls out of the shape

Given columns and tab, how many strokes the piece needs is fixed. There are only four cases:

# the whole film: swallow it, no blade at all
cuts = 0

# whole columns and nothing left over: one stroke across
cuts = 1

# anything with a tab: free its column, then split it
cuts = (1 if columns else 0)
     + (1 if columns + 1 < long_div else 0)
     + 1

Reading those three lines: cut to the left of the tab's column, unless there is nothing to its left. Cut to its right, unless it is already the last column. Then one more to split the tab off.

The middle one is worth dwelling on. If the tab's column is the last one, its outer side is already the edge of the film, so the manufacturer cut it for you, so that stroke is free. That single condition is why 5/6 on a 3×2 grid takes two strokes rather than three. And 5/6 is the very first fold of a standard 8 mg taper, so the most common cut anyone makes is also one of the cheapest.

The four shapes, with the strokes you actually make picked out in red. Fold lines are dashed; they cost nothing.

05 Scoring by hand-difficulty

Not every cut is the same cut

Two candidates can both be "one cut" and be completely different jobs. A stroke across the short axis is 12.8 mm guided by the film's own straight edge. A stroke along the long axis is 22 mm freehand down the middle.

# cuts across the narrow width, and cuts along the length
difficulty  = across × 10 + along × 14
# two bits of film to handle instead of one
difficulty += (pieces − 1) × 4
# a scrap under 2 mm is not something fingers do
difficulty += 20 if the tab is that small

The numbers are weights, not units of anything. The score exists only to be compared against other folds. What matters is the ordering they produce. Grid fineness is not in it. Folding into 3 is a harder judgement than folding into 2, but that only ranks two folds that already take the same strokes and land equally close. It is a tie-break on the next stage, not a reason to take a worse dose.

Why 10 against 14: a stroke across the short axis is 12.8 mm guided by the film's own straight edge, and a stroke along the long axis is 22 mm freehand down the middle. Price them the same and a plain half comes out as a lengthwise slice. Why 20: a scrap under 2 mm is not something fingers and a razor handle, whatever the arithmetic says about it.

FractionGridCols+tab CutsPcsScoreError

The folds close enough to still be in the running, ranked by difficulty. The winner is highlighted, and notice it is not always the one with the smallest error.

06 Picking one

Buy simplicity, but only with error you were going to spend anyway

Two things matter and they pull against each other: how close the fold gets to the dose, and how easy it is to do. Something has to settle the trade, and the tool already knows the answer. It asks you how accurately you can cut.

Half a millimetre is realistic with a razor and a steel rule, and on an 8 mg film that is ±0.18 mg of dose. That is a slip you make whether you fold or measure. So any fold inside that band is no worse than measuring, and among those the simplest one is plainly the better instruction:

# the most error we will put up with
worst_allowed = max(closest_possible, tolerance)

# the folds still in the running
shortlist = [f for f in candidates
             if f.error <= worst_allowed]

# of those, the easiest to actually do; among equals, the closer
chosen = min(shortlist, key=(difficulty, error, fineness,
                              short_div, −long_div, parts))

Read max there carefully, because the obvious alternative is wrong. The ceiling is on the error itself, not a margin added on top of the closest fold. Write it the other way round, as closest + tolerance, and the two stack: the fold you draw can end up further out than the tolerance the reader actually gave you. That version shipped once, and put a real cycle 0.22 mg out while claiming to respect 0.18.

The keys after difficulty are not decoration. error is the one that matters among equal strokes. Hit the 0.32 button: 1/16 and 1/24 are both two cuts and one piece, and the first is 0.18 mg out while the second is 0.01. Put grid fineness in the score and 1/16 wins, because thirds look three points dearer than halves. Keep it as a tie-break after the error and 1/24 wins, which is the dose you asked for. fineness only decides when two folds take the same strokes and land equally close, so a plain half is still 2×1 rather than 8×1. The same dose always gives the same fold, so the picture never moves under a reader who has not changed anything.

The tolerance band, every candidate inside it, and the one that wins. Filled is the pick; hollow are the folds it beat.

07 Drawing it

The picture is built from the same two numbers

There is no second implementation of the geometry. The drawing reads columns and tab straight off the result:

# one column and one row, on screen
col = film_width / long_div
row = film_height / short_div

# the piece you swallow, in two rectangles
paint(0, 0, columns × col, film_height)
paint(columns × col, 0, col, tab × row)

# the blade strokes, the very same three
# conditions that counted them earlier
if columns:
    line_down(columns × col)
if columns + 1 < long_div:
    line_down((columns + 1) × col)
if tab:
    line_across(tab × row)

Because the count and the picture are built from the same two numbers, they cannot drift apart. There is no second idea of the geometry to get out of step. The test suite checks it anyway: the orange area has to be the fraction the caption claims, and the number of red lines has to be the number of cuts the words promise.

The finished cutting guide, at true scale for an 8 mg film.


Three things it got wrong first

Two were caught by drawing it. The third shipped.

A plain half came out lengthwise

You can halve a film two ways: cut across it, or cut down its length. Both give you exactly half, and both are "one cut", so nothing in the scoring told them apart and the tie-break picked arbitrarily. It chose slicing 22 mm down the middle of the film over a 12.8 mm stroke run against the film's own straight edge. The harder way to do the easiest cut there is. Fixed by pricing the two kinds of stroke differently, which is the whole reason the ×14 term exists.

The tolerance compounded

The rule is meant to say "any fold inside your stated tolerance counts as good enough". Written as closest + tolerance it says something else: it takes however close the best fold got and allows a whole tolerance on top of that. The two stack. On cycle 2 of the default run it picked a fold 0.22 mg out while the page claimed to respect ±0.18. The ceiling has to sit on the error itself, max(closest, tolerance).

A finer grid beat a closer dose

At 0.32 mg on an 8 mg film, 1/16 and 1/24 are both two cuts and one piece. 1/16 is 0.18 mg out; 1/24 is 0.01. The score used to add points for folding into 3 instead of 2, so 1/16 won: same strokes, 0.17 mg spent on the coarser-looking grid. Grid fineness now ranks after the error, and only when two folds already take the same strokes and land equally close. Hit the 0.32 button and you can watch 1/24 win.

And one thing that fell out for free

The tool offers a linear taper, where the dose drops by the same number of milligrams every cycle rather than the same percentage. Do that from 8 mg and the doses you need are 5/6, 2/3, 1/2, 1/3, 1/6 of a film, every single one a fraction this grid hits exactly, with no approximation at all. A linear taper can be cut from the first day to the last without ever picking up a ruler. Nobody designed that; it is just what happens when a constant step meets a grid of simple fractions.