simple_fraction

Cookbook - Real-World Recipes

Recipe Collection

Ready-to-use code patterns for common fraction scenarios. Each recipe is complete and can be adapted to your needs.

Recipe: Recipe Scaler

Scale recipe ingredients up or down while keeping exact fractional amounts.

class
    RECIPE_SCALER

feature -- Access

    ingredients: ARRAYED_LIST [TUPLE [name: STRING; amount: SIMPLE_FRACTION; unit: STRING]]
        attribute
            create Result.make (10)
        end

feature -- Setup

    add_ingredient (name, amount_str, unit: STRING)
            -- Add ingredient with fractional amount
        local
            amount: SIMPLE_FRACTION
        do
            create amount.make_from_string (amount_str)
            ingredients.extend ([name, amount, unit])
        end

feature -- Scaling

    scale (factor: INTEGER): ARRAYED_LIST [STRING]
            -- Return scaled ingredient list
        local
            scaled: SIMPLE_FRACTION
        do
            create Result.make (ingredients.count)
            across ingredients as ing loop
                scaled := ing.amount.scale (factor)
                Result.extend (scaled.to_mixed_string + " " + ing.unit + " " + ing.name)
            end
        end

    scale_fraction (num, den: INTEGER): ARRAYED_LIST [STRING]
            -- Scale by fraction (e.g., 1/2 for half recipe)
        local
            factor, scaled: SIMPLE_FRACTION
        do
            create factor.make (num, den)
            create Result.make (ingredients.count)
            across ingredients as ing loop
                scaled := ing.amount * factor
                Result.extend (scaled.to_mixed_string + " " + ing.unit + " " + ing.name)
            end
        end

end

-- Usage:
local
    recipe: RECIPE_SCALER
do
    create recipe
    recipe.add_ingredient ("flour", "2 1/2", "cups")
    recipe.add_ingredient ("sugar", "3/4", "cup")
    recipe.add_ingredient ("butter", "1/3", "cup")

    -- Triple the recipe
    across recipe.scale (3) as line loop
        print (line + "%N")
    end
    -- Output:
    -- 7 1/2 cups flour
    -- 2 1/4 cup sugar
    -- 1 cup butter
end

Recipe: Probability Calculator

Calculate probabilities with exact fractions - no floating-point approximation.

class
    PROBABILITY_CALCULATOR

feature -- Basic probabilities

    dice_roll (target: INTEGER): SIMPLE_FRACTION
            -- Probability of rolling target on a 6-sided die
        require
            valid_target: target >= 1 and target <= 6
        do
            create Result.make (1, 6)
        end

    coin_flip (heads: BOOLEAN): SIMPLE_FRACTION
            -- Probability of heads or tails
        do
            create Result.make (1, 2)
        end

    card_draw (favorable, total: INTEGER): SIMPLE_FRACTION
            -- Probability of drawing favorable card from deck
        require
            valid: favorable >= 0 and favorable <= total and total > 0
        do
            create Result.make (favorable, total)
        end

feature -- Combined probabilities

    both (p1, p2: SIMPLE_FRACTION): SIMPLE_FRACTION
            -- Probability of both independent events
        do
            Result := p1 * p2
        end

    either (p1, p2: SIMPLE_FRACTION): SIMPLE_FRACTION
            -- Probability of either event (mutually exclusive)
        do
            Result := p1 + p2
        end

    not_event (p: SIMPLE_FRACTION): SIMPLE_FRACTION
            -- Probability of event NOT happening
        local
            one: SIMPLE_FRACTION
        do
            create one.make_one
            Result := one - p
        end

feature -- Display

    as_percentage (p: SIMPLE_FRACTION): STRING
            -- Display probability as percentage
        do
            Result := (p.to_double * 100).truncated_to_integer.out + "%%"
        end

    as_odds (p: SIMPLE_FRACTION): STRING
            -- Display as "X in Y"
        do
            Result := p.numerator.out + " in " + p.denominator.out
        end

end

-- Usage:
local
    prob: PROBABILITY_CALCULATOR
    p_six, p_ace, p_both: SIMPLE_FRACTION
do
    create prob

    -- Probability of rolling a 6
    p_six := prob.dice_roll (6)
    print ("Roll 6: " + p_six.out + " = " + prob.as_odds (p_six))
    -- Roll 6: 1/6 = 1 in 6

    -- Probability of drawing an Ace from 52 cards
    p_ace := prob.card_draw (4, 52)
    print ("Draw Ace: " + p_ace.out)
    -- Draw Ace: 1/13 (automatically reduced from 4/52)

    -- Probability of BOTH rolling 6 AND drawing Ace
    p_both := prob.both (p_six, p_ace)
    print ("Both: " + p_both.out)
    -- Both: 1/78
end

Recipe: Music Note Duration Calculator

Calculate musical note durations and time signatures using fractions.

class
    MUSIC_CALCULATOR

feature -- Note durations (relative to whole note)

    whole_note: SIMPLE_FRACTION
        do create Result.make_one end

    half_note: SIMPLE_FRACTION
        do create Result.make (1, 2) end

    quarter_note: SIMPLE_FRACTION
        do create Result.make (1, 4) end

    eighth_note: SIMPLE_FRACTION
        do create Result.make (1, 8) end

    sixteenth_note: SIMPLE_FRACTION
        do create Result.make (1, 16) end

feature -- Modifiers

    dotted (note: SIMPLE_FRACTION): SIMPLE_FRACTION
            -- Dotted note = 1.5x duration
        do
            Result := note + note.scale_down (2)
        end

    triplet (note: SIMPLE_FRACTION): SIMPLE_FRACTION
            -- Triplet = 2/3 duration (3 in time of 2)
        local
            two_thirds: SIMPLE_FRACTION
        do
            create two_thirds.make (2, 3)
            Result := note * two_thirds
        end

feature -- Measure calculation

    fits_in_measure (notes: ARRAY [SIMPLE_FRACTION]; time_sig_num, time_sig_den: INTEGER): BOOLEAN
            -- Do notes fit exactly in one measure?
        local
            total, measure: SIMPLE_FRACTION
        do
            create total.make_zero
            across notes as n loop
                total := total + n
            end
            create measure.make (time_sig_num, time_sig_den)
            Result := total.is_equal (measure)
        end

    remaining_in_measure (notes: ARRAY [SIMPLE_FRACTION]; time_sig_num, time_sig_den: INTEGER): SIMPLE_FRACTION
            -- How much duration remaining in measure?
        local
            total, measure: SIMPLE_FRACTION
        do
            create total.make_zero
            across notes as n loop
                total := total + n
            end
            create measure.make (time_sig_num, time_sig_den)
            Result := measure - total
        end

end

-- Usage:
local
    music: MUSIC_CALCULATOR
    notes: ARRAY [SIMPLE_FRACTION]
do
    create music

    -- Dotted quarter = 3/8
    print (music.dotted (music.quarter_note).out)  -- "3/8"

    -- Quarter triplet = 1/6
    print (music.triplet (music.quarter_note).out)  -- "1/6"

    -- Check if notes fill a 4/4 measure
    notes := <<music.quarter_note, music.quarter_note, music.half_note>>
    print (music.fits_in_measure (notes, 4, 4).out)  -- "True"
end

Recipe: Imperial Measurements Converter

Convert between imperial measurement fractions.

class
    IMPERIAL_CONVERTER

feature -- Length conversions

    inches_to_feet (inches: SIMPLE_FRACTION): SIMPLE_FRACTION
            -- Convert inches to feet
        do
            Result := inches.scale_down (12)
        end

    feet_to_inches (feet: SIMPLE_FRACTION): SIMPLE_FRACTION
            -- Convert feet to inches
        do
            Result := feet.scale (12)
        end

    yards_to_feet (yards: SIMPLE_FRACTION): SIMPLE_FRACTION
        do
            Result := yards.scale (3)
        end

feature -- Volume conversions

    cups_to_tablespoons (cups: SIMPLE_FRACTION): SIMPLE_FRACTION
            -- 1 cup = 16 tablespoons
        do
            Result := cups.scale (16)
        end

    tablespoons_to_teaspoons (tbsp: SIMPLE_FRACTION): SIMPLE_FRACTION
            -- 1 tablespoon = 3 teaspoons
        do
            Result := tbsp.scale (3)
        end

    cups_to_quarts (cups: SIMPLE_FRACTION): SIMPLE_FRACTION
            -- 4 cups = 1 quart
        do
            Result := cups.scale_down (4)
        end

feature -- Formatting

    format_feet_inches (total_inches: SIMPLE_FRACTION): STRING
            -- Format as "X ft Y in"
        local
            feet_part: INTEGER_64
            inches_part: SIMPLE_FRACTION
            twelve: SIMPLE_FRACTION
        do
            create twelve.make_integer (12)
            feet_part := (total_inches / twelve).to_integer
            create inches_part.make (total_inches.numerator - feet_part * 12 * total_inches.denominator, total_inches.denominator)

            if feet_part = 0 then
                Result := inches_part.to_mixed_string + " in"
            elseif inches_part.is_zero then
                Result := feet_part.out + " ft"
            else
                Result := feet_part.out + " ft " + inches_part.to_mixed_string + " in"
            end
        end

end

-- Usage:
local
    conv: IMPERIAL_CONVERTER
    half_cup, quarter_inch: SIMPLE_FRACTION
do
    create conv

    -- 1/2 cup to tablespoons
    create half_cup.make (1, 2)
    print (conv.cups_to_tablespoons (half_cup).out + " tbsp")
    -- "8 tbsp"

    -- 3/4 cup to teaspoons
    create half_cup.make (3, 4)
    print (conv.tablespoons_to_teaspoons (conv.cups_to_tablespoons (half_cup)).out + " tsp")
    -- "36 tsp"
end

Recipe: Ratio and Proportion Calculator

Solve ratio and proportion problems.

class
    RATIO_CALCULATOR

feature -- Ratios

    simplify_ratio (a, b: INTEGER): TUPLE [first, second: INTEGER_64]
            -- Simplify ratio a:b to lowest terms
        local
            f: SIMPLE_FRACTION
        do
            create f.make (a, b)
            Result := [f.numerator, f.denominator]
        end

    ratio_to_fraction (a, b: INTEGER): SIMPLE_FRACTION
            -- Convert ratio a:b to fraction a/b
        do
            create Result.make (a, b)
        end

feature -- Proportions

    solve_proportion (a, b, c: SIMPLE_FRACTION): SIMPLE_FRACTION
            -- Solve a/b = c/x for x
            -- x = b*c/a
        require
            a_not_zero: not a.is_zero
        do
            Result := (b * c) / a
        end

    scale_to_total (parts: ARRAY [SIMPLE_FRACTION]; total: SIMPLE_FRACTION): ARRAY [SIMPLE_FRACTION]
            -- Scale parts proportionally to sum to total
        local
            sum, factor: SIMPLE_FRACTION
            i: INTEGER
        do
            -- Calculate current sum
            create sum.make_zero
            across parts as p loop
                sum := sum + p
            end

            -- Calculate scaling factor
            factor := total / sum

            -- Scale each part
            create Result.make_filled (create {SIMPLE_FRACTION}.make_zero, 1, parts.count)
            from i := 1 until i > parts.count loop
                Result[i] := parts[i] * factor
                i := i + 1
            end
        end

feature -- Golden ratio

    golden_ratio_approximate: SIMPLE_FRACTION
            -- Fibonacci approximation of golden ratio: 21/13 ~ 1.618
        do
            create Result.make (21, 13)
        end

    golden_section (total: SIMPLE_FRACTION): TUPLE [larger, smaller: SIMPLE_FRACTION]
            -- Divide total by golden ratio
        local
            phi, larger: SIMPLE_FRACTION
        do
            phi := golden_ratio_approximate
            larger := total / phi
            Result := [larger, total - larger]
        end

end

-- Usage:
local
    ratio: RATIO_CALCULATOR
    parts: ARRAY [SIMPLE_FRACTION]
    scaled: ARRAY [SIMPLE_FRACTION]
    total: SIMPLE_FRACTION
do
    create ratio

    -- Simplify 12:8 to 3:2
    print (ratio.simplify_ratio (12, 8))  -- [3, 2]

    -- Divide 100 in ratio 2:3:5
    parts := <<create {SIMPLE_FRACTION}.make (2, 1),
               create {SIMPLE_FRACTION}.make (3, 1),
               create {SIMPLE_FRACTION}.make (5, 1)>>
    create total.make_integer (100)
    scaled := ratio.scale_to_total (parts, total)
    -- [20, 30, 50]
end

Recipe: Dice Probability Calculator

Calculate exact dice probabilities for games.

class
    DICE_CALCULATOR

feature -- Single die

    probability_single (target, die_sides: INTEGER): SIMPLE_FRACTION
            -- Probability of rolling exactly target
        require
            valid: target >= 1 and target <= die_sides
        do
            create Result.make (1, die_sides)
        end

    probability_at_least (minimum, die_sides: INTEGER): SIMPLE_FRACTION
            -- Probability of rolling minimum or higher
        require
            valid: minimum >= 1 and minimum <= die_sides
        do
            create Result.make (die_sides - minimum + 1, die_sides)
        end

feature -- Multiple dice

    probability_all_same (dice_count, die_sides: INTEGER): SIMPLE_FRACTION
            -- Probability all dice show same value
        local
            single: SIMPLE_FRACTION
        do
            -- First die can be anything, rest must match
            -- = 1 * (1/sides)^(n-1) * sides = (1/sides)^(n-1)
            create single.make (1, die_sides)
            Result := single.power (dice_count - 1)
        end

    probability_sum_7_two_d6: SIMPLE_FRACTION
            -- Probability of rolling 7 on 2d6 (most common roll)
        do
            -- 6 ways to make 7: (1,6),(2,5),(3,4),(4,3),(5,2),(6,1)
            -- out of 36 total combinations
            create Result.make (6, 36)  -- Reduces to 1/6
        end

feature -- Display

    odds_format (prob: SIMPLE_FRACTION): STRING
            -- Format as "X to Y" odds against
        local
            against: SIMPLE_FRACTION
            one: SIMPLE_FRACTION
        do
            create one.make_one
            against := one - prob
            Result := against.numerator.out + " to " + prob.numerator.out
        end

end

-- Usage:
local
    dice: DICE_CALCULATOR
    p: SIMPLE_FRACTION
do
    create dice

    -- Roll 4+ on d6
    p := dice.probability_at_least (4, 6)
    print (p.out)  -- "1/2"

    -- Roll 7 on 2d6
    p := dice.probability_sum_7_two_d6
    print (p.out)  -- "1/6"

    -- Yahtzee (all 5 dice same)
    p := dice.probability_all_same (5, 6)
    print (p.out)  -- "1/1296"
    print (dice.odds_format (p))  -- "1295 to 1"
end

Recipe: Exactness Demonstration

Demonstrate why fractions matter for exact arithmetic.

class
    EXACTNESS_DEMO

feature -- Demonstrations

    thirds_sum: STRING
            -- Show that 1/3 + 1/3 + 1/3 = 1 exactly
        local
            third, sum: SIMPLE_FRACTION
            double_sum: DOUBLE
        do
            create third.make (1, 3)
            sum := third + third + third

            double_sum := 1.0/3.0 + 1.0/3.0 + 1.0/3.0

            Result := "Fraction: 1/3 + 1/3 + 1/3 = " + sum.out + "%N"
            Result := Result + "Double:   1/3 + 1/3 + 1/3 = " + double_sum.out
        end

    sevenths_product: STRING
            -- Show that 1/7 * 7 = 1 exactly
        local
            seventh, product: SIMPLE_FRACTION
            double_product: DOUBLE
        do
            create seventh.make (1, 7)
            product := seventh.scale (7)

            double_product := (1.0/7.0) * 7.0

            Result := "Fraction: 1/7 * 7 = " + product.out + "%N"
            Result := Result + "Double:   1/7 * 7 = " + double_product.out
        end

    complex_calculation: STRING
            -- Complex calculation remains exact
        local
            a, b, c, result: SIMPLE_FRACTION
        do
            -- Calculate: ((1/2 + 1/3) * 6 - 2) / 3
            create a.make (1, 2)
            create b.make (1, 3)
            create c.make_integer (6)

            result := a + b           -- 5/6
            result := result * c      -- 5
            result := result - create {SIMPLE_FRACTION}.make_integer (2)  -- 3
            result := result / create {SIMPLE_FRACTION}.make_integer (3)  -- 1

            Result := "((1/2 + 1/3) * 6 - 2) / 3 = " + result.out + " (exactly 1)"
        end

end

-- Usage:
local
    demo: EXACTNESS_DEMO
do
    create demo
    print (demo.thirds_sum)
    -- Fraction: 1/3 + 1/3 + 1/3 = 1
    -- Double:   1/3 + 1/3 + 1/3 = 1 (may vary)

    print (demo.complex_calculation)
    -- ((1/2 + 1/3) * 6 - 2) / 3 = 1 (exactly 1)
end