Functional coverage tree: Difference between revisions

m
(→‎{{header|Haskell}}: Added a Haskell draft.)
m (→‎{{header|Wren}}: Minor tidy)
 
(46 intermediate revisions by 10 users not shown)
Line 136:
=={{header|Go}}==
{{trans|Kotlin}}
<langsyntaxhighlight lang="go">package main
 
import "fmt"
Line 289:
fmt.Printf("%8.6f to %8.6f\n", diff, topCoverage+diff)
h2Basement[2].setCoverage(0.75) // restore to original value if required
}</langsyntaxhighlight>
 
{{out}}
Line 346:
Using a function from a text outline to an updated text outline.
 
The raw table (supplied in the task description) is read in from a text file, parsed to a tree structure, and updated by two traversals (one bottom-up and one top down) before being serialised back to a completed outline text, with an additional 'Share of Residue' column:
 
The raw table (supplied in the task description) is read in from a text file, parsed to tree structure, and updated by two traversals (one bottom-up and one top down) before being serialised back to a completed outline text, with an additional 'Share of Residue' column:
{{Trans|Python}}
<langsyntaxhighlight lang="haskell">{-# LANGUAGE OverloadedStrings #-}
 
import qualified Data.Text.Read asBifunctor T(first)
import qualified Data.Text.IO asBool T(bool)
import Data.Char (isSpace)
import qualified Data.Text as T
import Controlqualified Data.Text.ArrowIO ((&&&),as first)T
import qualified Data.Text.Read as T
import Data.Tree (Forest, Tree (..), foldTree)
import Numeric (showFFloat)
import DataSystem.BoolDirectory (booldoesFileExist)
 
import Data.Tree
----------------- FUNCTIONAL COVERAGE TREE ---------------
 
data Coverage = Coverage
{ name :: T.Text,
, weight :: Float,
, coverage :: Float,
, share :: Float
}
} deriving (Eq, Show)
deriving (Show)
 
--------------------------- TEST -------------------------
fp = "./coverageOutline.txt"
 
-- TEST ---------------------------------------------------
main :: IO ()
main =
doesFileExist fp
T.readFile "coverageOutline.txt" >>= (T.putStrLn . updatedCoverageOutline)
>>= bool
(print $ "File not found: " <> fp)
(T.readFile fp >>= T.putStrLn . updatedCoverageOutline)
 
-- UPDATED COVERAGE OUTLINE ---------------- UPDATED COVERAGE OUTLINE ---------------
updatedCoverageOutline :: T.Text -> T.Text
updatedCoverageOutline s =
let delimiter = "|"
indentedLines = T.lines s
columnNames =
titles = init $ columnNames delimiter (head indentedLines)
init $
in T.unlines
tokenizeWith
[ titleLine (titles ++ ["SHARE OF RESIDUE"])
, indentedLinesFromTree " " showCoverage $delimiter
withResidueShares 1.0 $ ( head indentedLines
foldTree )
in T.unlines
weightedCoverage
[ tabulation
((partialRecord . fmap T.strip . T.splitOn delimiter) <$>
head (outlineParse (tail indentedLines)))delimiter
(columnNames <> ["SHARE OF RESIDUE"]),
]
indentedLinesFromTree
" "
(showCoverage delimiter)
$ withResidueShares 1.0 $
foldTree
weightedCoverage
(parseTreeFromOutline delimiter indentedLines)
]
 
------ WEIGHTED COVERAGE AND SHARES OF REMAINING WORK ---------
weightedCoverage :: Coverage -> Forest Coverage -> Tree Coverage
Coverage ->
Forest Coverage ->
Tree Coverage
weightedCoverage x xs =
let cws = ((,) . coverage &&&<*> weight) <$>. (rootLabel <$> xs)
totalWeight = foldr ((+) . snd) 0 cws
in Node
( x
{ coverage =
foldr (\(c, w) a -> (c * w) + a) (coverage x) cws /foldr
bool 1 totalWeight (0\(c, w) a -> (c * w) <+ totalWeighta)
} (coverage x)
xs cws
/ bool 1 totalWeight (0 < totalWeight)
}
)
xs
 
withResidueShares :: Float -> Tree Coverage -> Tree Coverage
Line 404 ⟶ 428:
let go fraction node =
let forest = subForest node
cwsweights = (coverage &&& weight) <$>. (rootLabel <$> forest)
weights = snd <$> cws
weightTotal = sum weights
nodeRoot = rootLabel node
in Node
( nodeRoot
{ share = fraction *{ (1share - coverage nodeRoot)=
}) fraction
(zipWith go (((fraction *) . (/ weightTotal)) <$> weights) forest * (1 - coverage nodeRoot)
}
in go shareOfTotal tree
)
( zipWith
go
((fraction *) . (/ weightTotal) <$> weights)
forest
)
in go shareOfTotal tree
 
-- OUTLINE PARSE --------------------- OUTLINE PARSE ---------------------
outlineParseparseTreeFromOutline :: [T.Text] -> [Tree T.Text] -> Tree Coverage
parseTreeFromOutline delimiter indentedLines =
outlineParse = forestFromLineIndents . indentLevelsFromLines
partialRecord . tokenizeWith delimiter
<$> head
( forestFromLineIndents $
indentLevelsFromLines $ tail indentedLines
)
 
forestFromLineIndents :: [(Int, T.Text)] -> [Tree T.Text]
forestFromLineIndents pairs =
let go [] = []
go ((n, s) : xs) =
let (firstTreeLines, rest) = span ((n <) . fst) xs
in Node s (go firstTreeLines) : go rest
in go pairs
 
indentLevelsFromLines :: [T.Text] -> [(Int, T.Text)]
indentLevelsFromLines xs =
let pairs = T.span (' ' ==)isSpace <$> xs
indentUnit =
foldr
( \x a ->
let w = (T.length . fst) x
in bool a w (w < a && 0 < w))
)
(maxBound :: Int)
pairs
in first (flip div indentUnit . T.length) <$> pairs
 
partialRecord :: [T.Text] -> Coverage
partialRecord xs =
let [name, weightText, coverageText] = take 3 (xs ++ repeat "")
in Coverage take
{ name = name 3
(xs <> repeat "")
, weight = defaultOrRead 1.0 weightText
in Coverage
, coverage = defaultOrRead 0.0 coverageText
, share { name = 0.0name,
weight = defaultOrRead 1.0 weightText,
}
coverage = defaultOrRead 0.0 coverageText,
 
share = 0.0
columnNames :: T.Text -> T.Text -> [T.Text]
}
columnNames delimiter = fmap T.strip . T.splitOn delimiter
 
defaultOrRead :: Float -> T.Text -> Float
defaultOrRead n txt = either (const n) fst $ T.rational txt
 
tokenizeWith :: T.Text -> T.Text -> [T.Text]
-- SERIALISATION OF TREE TO TABULATED OUTLINE -------------
tokenizeWith delimiter = fmap T.strip . T.splitOn delimiter
indentedLinesFromTree :: T.Text -> (T.Text -> a -> T.Text) -> Tree a -> T.Text
 
-------- SERIALISATION OF TREE TO TABULATED OUTLINE ------
indentedLinesFromTree ::
T.Text ->
(T.Text -> a -> T.Text) ->
Tree a ->
T.Text
indentedLinesFromTree tab showRoot tree =
let go indent node =
showRoot indent (rootLabel node) :
(subForest node >>= go (T.append tab indent))
in T.unlines $ go "" tree
 
showCoverage :: T.Text -> T.Text -> Coverage -> T.Text
showCoverage delimiter indent x =
tabulation
T.intercalate
"| "delimiter
( [T.justifyLeftappend 31indent '(name 'x), (T.appendpack indent(showN 0 (nameweight x)) :]
T.justifyLeft 9 ' '<> (T.pack (. showN 04 <$> (weight[coverage, share] <*> [x]))) :
)
((T.justifyLeft 9 ' ' . T.pack . showN 4) <$> ([coverage, share] <*> [x])))
 
titleLinetabulation :: T.Text -> [T.Text] -> T.Text
tabulation delimiter =
titleLine = T.intercalate "| " . zipWith (`T.justifyLeft` ' ') [31, 9, 9, 9]
T.intercalate (T.append delimiter " ")
. zipWith (`T.justifyLeft` ' ') [31, 9, 9, 9]
 
justifyRight :: Int -> a -> [a] -> [a]
justifyRight n c = (drop . length) <*> (replicate n c ++<>)
 
showN :: Int -> Float -> String
showN p n = justifyRight 7 ' ' (showFFloat (Just p) n "")</syntaxhighlight>
 
-- GENERIC ------------------------------------------------
foldTree :: (a -> [b] -> b) -> Tree a -> b
foldTree f = go
where
go (Node x ts) = f x (map go ts)</lang>
{{Out}}
<pre>NAME_HIERARCHY | WEIGHT | COVERAGE | SHARE OF RESIDUE
Line 532 ⟶ 571:
=={{header|J}}==
 
Implementation part 1 of 4 (raw data):
 
<langsyntaxhighlight Jlang="j">raw=: 0 :0
NAME_HIERARCHY |WEIGHT |COVERAGE |
cleaning | | |
Line 578 ⟶ 617:
wine_cellar | |1 |
cinema | |0.75 |
)</langsyntaxhighlight>
 
Implementation part 2 of 4 (unpacking raw data):
 
<langsyntaxhighlight Jlang="j">labels=: {.<;._2;._2 raw
'hier wspec cspec'=:|:}.<;._2;._2 raw
level=: (%+./) (0 i.~' '&=)"1 hier
weight=: (+ 0=]) ,".wspec
coverage=: ,".cspec</langsyntaxhighlight>
 
To understand this implementation, it's best to run it and inspect the data.
 
That said:
 
each of the above names is a column variable (with one value for each row in the dataset).
 
level has values 0, 1, 2, 3 or 4 (depending on depth of indent), and the calculation relies on each indent level using the same number of spaces. It might be smarter to use <code>level=: (i.~ ~.) (0 i.~' '&=)"1 hier</code> which only relies on consistent indentation at each level, but ultimately a general case implementation would have to enforce an indentation standard and that sort of mechanism is out of scope for this task.
 
weight fills in the blanks for weights (1 if not otherwise specified). This calculation is simplified because we do not have to worry about any explicitly 0 weights.
 
coverage fills in the blanks for coverage (0 if not otherwise specified).
 
Implementation part 3 of 4 (translation of leaf coverage to functional coverage):
 
<langsyntaxhighlight Jlang="j">merge=: ;@(({.@[,(+}.)~)&.> [: +/\1,_1}.#@>)
unrooted=: ([: merge <@(_1,$:@}.);.1)^:(0<#)
parent=: unrooted level
parent_cover=: (] (1}.~.parent)}~ 1}. * %&(parent +//. ]) [)^:_</langsyntaxhighlight>
 
<code>unrooted</code> translates indentation information to a [[Tree_traversal#J:_Alternate_implementation|parent tree structure]]. However, the limitations of recursion require we distinguish the parent node from its children, so we use _1 to denote the parent node of the recursive intermediate result unrooted trees. (This works well with using arithmetic to adjust sub-tree indices based on the lengths of preceding sub-trees.) <code>merge</code> combines a boxed sequence of these subtrees to form a single tree - we also rely on the first node of each tree being both _1 and the root node.
Line 599 ⟶ 650:
Thus, <code>parent_cover</code> propagates coverage to parent nodes based on the weighted average of coverage at the children.
 
Task example part 4 of 4 (format and show result):
 
<langsyntaxhighlight Jlang="j"> 1 1 }._1 }.":labels,each ":each hier;(,.weight);,.weight parent_cover coverage
NAME_HIERARCHY │WEIGHT │COVERAGE │
cleaning │ 1 │0.409167 │
Line 644 ⟶ 695:
cellars │ 1 │ 1 │
wine_cellar │ 1 │ 1 │
cinema │ 1 │ 0.75 │</langsyntaxhighlight>
 
Extra credit:
 
<langsyntaxhighlight Jlang="j">trace=: (~.@,each (0 >. parent)&{)^:_ i.#parent
power=: */@:{&(parent (] % (i.~ ~.)@[ { +//.) weight)@> trace
 
power*1-weight parent_cover coverage
0.590833 0.2675 0.0375 0.025 0.00833333 0.0166667 0 0.0125 0.045 0.0375 0.0125 0.0125 0.0125 0 0.05 0.05 0.01 0.323333 0.17 0.05 0.0125 0.0125 0.0125 0.0125 0.05 0.05 0.02 0.136667 0.0333333 0.0333333 0.00833333 0.00833333 0.00833333 0.00833333 0.0333333 0.0333333 0.00333333 0 0.0166667 0 0 0.0166667</langsyntaxhighlight>
 
Explanation:
Line 663 ⟶ 714:
 
And, <code>weight parent_cover coverage</code> was the functional coverage for each node.
 
=={{header|Java}}==
<syntaxhighlight lang="java">
import java.util.ArrayList;
import java.util.List;
 
public final class FunctionalCoverageTree {
 
public static void main(String[] aArgs) {
FCNode cleaning = new FCNode("Cleaning", 1, 0.0);
List<FCNode> houses = List.of(
new FCNode("House_1", 40, 0.0),
new FCNode("House_2", 60, 0.0) );
cleaning.addChildren(houses);
List<FCNode> house_1 = List.of(
new FCNode("Bedrooms", 1, 0.25),
new FCNode("Bathrooms", 1, 0.0),
new FCNode("Attic", 1, 0.75),
new FCNode("Kitchen", 1, 0.1),
new FCNode("Living_rooms", 1, 0.0),
new FCNode("Basement", 1, 0.0),
new FCNode("Garage", 1, 0.0),
new FCNode("Garden",1, 0.8) );
houses.get(0).addChildren(house_1);
List<FCNode> bathrooms_house_1 = List.of(
new FCNode("Bathroom_1", 1, 0.5),
new FCNode("Bathroom_2", 1, 0.0),
new FCNode("Outside_lavatory", 1, 1.0) );
house_1.get(1).addChildren(bathrooms_house_1);
List<FCNode> living_rooms_house_1 = List.of(
new FCNode("lounge", 1, 0.0),
new FCNode("Dining_room", 1, 0.0),
new FCNode("Conservatory", 1, 0.0),
new FCNode("Playroom", 1, 1.0) );
house_1.get(4).addChildren(living_rooms_house_1);
List<FCNode> house_2 = List.of(
new FCNode("Upstairs", 1, 0.15),
new FCNode("Ground_floor", 1, 0.316667),
new FCNode("Basement", 1, 0.916667));
houses.get(1).addChildren(house_2);
List<FCNode> upstairs = List.of(
new FCNode("Bedrooms", 1, 0.0),
new FCNode("Bathroom", 1, 0.0),
new FCNode("Toilet", 1, 0.0),
new FCNode("Attics", 1, 0.6) );
house_2.get(0).addChildren(upstairs);
List<FCNode> ground_floor = List.of(
new FCNode("Kitchen", 1, 0.0),
new FCNode("Living_rooms", 1, 0.0),
new FCNode("Wet_room_&_toilet", 1, 0.0),
new FCNode("Garage", 1, 0.0),
new FCNode("Garden", 1, 0.9),
new FCNode("Hot_tub_suite", 1, 1.0) );
house_2.get(1).addChildren(ground_floor);
List<FCNode> basement = List.of(
new FCNode("Cellars", 1, 1.0),
new FCNode("Wine_cellar", 1, 1.0),
new FCNode("Cinema", 1, 0.75) );
house_2.get(2).addChildren(basement);
List<FCNode> bedrooms = List.of(
new FCNode("Suite_1", 1, 0.0),
new FCNode("Suite_2", 1, 0.0),
new FCNode("Bedroom_3",1, 0.0),
new FCNode("Bedroom_4",1, 0.0) );
upstairs.get(0).addChildren(bedrooms);
List<FCNode> living_rooms_house_2 = List.of(
new FCNode("lounge", 1, 0.0),
new FCNode("Dining_room", 1, 0.0),
new FCNode("Conservatory", 1, 0.0),
new FCNode("Playroom", 1, 0.0) );
ground_floor.get(1).addChildren(living_rooms_house_2);
final double overallCoverage = cleaning.getCoverage();
System.out.println("OVERALL COVERAGE = " + String.format("%.6f", overallCoverage) + System.lineSeparator());
System.out.println("NAME HIERARCHY | WEIGHT | COVERAGE |" );
System.out.println("--------------------------------|--------|----------|");
cleaning.display();
System.out.println();
basement.get(2).setCoverage(1.0); // Change House_2 Cinema node coverage to 1.0
final double updatedCoverage = cleaning.getCoverage();
final double difference = updatedCoverage - overallCoverage;
System.out.println("If the coverage of the House_2 Cinema node were increased from 0.75 to 1.0");
System.out.print("the overall coverage would increase by ");
System.out.println(String.format("%.6f%s%.6f", difference, " to ", updatedCoverage));;
basement.get(2).setCoverage(0.75); // Restore to House_2 Cinema node coverage to its original value
}
 
}
 
final class FCNode {
public FCNode(String aName, int aWeight, double aCoverage) {
name = aName;
weight = aWeight;
coverage = aCoverage;
}
public void addChildren(List<FCNode> aNodes) {
for ( FCNode node : aNodes ) {
node.parent = this;
children.add(node);
updateCoverage();
}
}
public double getCoverage() {
return coverage;
}
public void setCoverage(double aCoverage) {
if ( coverage != aCoverage ) {
coverage = aCoverage;
if ( parent != null ) {
parent.updateCoverage();
}
}
}
public void display() {
display(0);
}
private void updateCoverage() {
double value1 = 0.0;
int value2 = 0;
for ( FCNode node : children ) {
value1 += node.weight * node.coverage;
value2 += node.weight;
}
setCoverage(value1 / value2);
}
private void display(int aLevel) {
final String initial = " ".repeat(4 * aLevel) + name;
final String padding = " ".repeat(NAME_FIELD_WIDTH - initial.length());
System.out.print(initial + padding + "|");
System.out.print(" " + String.format("%3d", weight) + " |");
System.out.println(" " + String.format("%.6f", coverage) + " |");
 
for ( FCNode child : children ) {
child.display(aLevel + 1);
}
}
private String name;
private int weight;
private double coverage;
private FCNode parent;
private List<FCNode> children = new ArrayList<FCNode>();
private static final int NAME_FIELD_WIDTH = 32;
}
</syntaxhighlight>
{{ out }}
<pre>
OVERALL COVERAGE = 0.409167
 
NAME HIERARCHY | WEIGHT | COVERAGE |
--------------------------------|--------|----------|
Cleaning | 1 | 0.409167 |
House_1 | 40 | 0.331250 |
Bedrooms | 1 | 0.250000 |
Bathrooms | 1 | 0.500000 |
Bathroom_1 | 1 | 0.500000 |
Bathroom_2 | 1 | 0.000000 |
Outside_lavatory | 1 | 1.000000 |
Attic | 1 | 0.750000 |
Kitchen | 1 | 0.100000 |
Living_rooms | 1 | 0.250000 |
lounge | 1 | 0.000000 |
Dining_room | 1 | 0.000000 |
Conservatory | 1 | 0.000000 |
Playroom | 1 | 1.000000 |
Basement | 1 | 0.000000 |
Garage | 1 | 0.000000 |
Garden | 1 | 0.800000 |
House_2 | 60 | 0.461111 |
Upstairs | 1 | 0.150000 |
Bedrooms | 1 | 0.000000 |
Suite_1 | 1 | 0.000000 |
Suite_2 | 1 | 0.000000 |
Bedroom_3 | 1 | 0.000000 |
Bedroom_4 | 1 | 0.000000 |
Bathroom | 1 | 0.000000 |
Toilet | 1 | 0.000000 |
Attics | 1 | 0.600000 |
Ground_floor | 1 | 0.316667 |
Kitchen | 1 | 0.000000 |
Living_rooms | 1 | 0.000000 |
lounge | 1 | 0.000000 |
Dining_room | 1 | 0.000000 |
Conservatory | 1 | 0.000000 |
Playroom | 1 | 0.000000 |
Wet_room_&_toilet | 1 | 0.000000 |
Garage | 1 | 0.000000 |
Garden | 1 | 0.900000 |
Hot_tub_suite | 1 | 1.000000 |
Basement | 1 | 0.916667 |
Cellars | 1 | 1.000000 |
Wine_cellar | 1 | 1.000000 |
Cinema | 1 | 0.750000 |
 
If the coverage of the House_2 Cinema node were increased from 0.75 to 1.0
the overall coverage would increase by 0.016667 to 0.425833
</pre>
 
=={{header|JavaScript}}==
Parsing the outline text to a tree structure, and traversing this with two computations (one bottom-up, and one top-down), before serialising the updated tree to a new outline text.
 
{{Trans|Haskell}}
{{Trans|Python}}
<syntaxhighlight lang="javascript">(() => {
'use strict';
 
// updatedCoverageOutline :: String -> String
const updatedCoverageOutline = outlineText => {
const
delimiter = '|',
indentedLines = indentLevelsFromLines(lines(outlineText)),
columns = init(tokenizeWith(delimiter)(snd(indentedLines[0])));
 
// SERIALISATION OF UPDATED PARSE TREE (TO NEW OUTLINE TEXT)
return tabulation(delimiter)(
columns.concat('SHARE OF RESIDUE\n')
) + unlines(
indentedLinesFromTree(
showCoverage(delimiter))(' ')(
 
// TWO TRAVERSAL COMPUTATIONS
 
withResidueShares(1.0)(
foldTree(weightedCoverage)(
 
// PARSE TREE (FROM OUTLINE TEXT)
fmapTree(compose(
partialRecord, tokenizeWith(delimiter)
))(fst(
forestFromLineIndents(tail(indentedLines))
))
)
))
);
};
 
// TEST -----------------------------------------------
// main :: IO ()
const main = () =>
console.log(
// strOutline is included as literal text
// at the foot of this code listing.
updatedCoverageOutline(strOutline)
);
 
// COVERAGE AND SHARES OF RESIDUE ---------------------
 
// weightedCoverage :: Dict -> Forest Dict -> Tree Dict
const weightedCoverage = x => xs => {
const
cws = map(compose(
fanArrow(x => x.coverage)(x => x.weight),
root
))(xs),
totalWeight = cws.reduce((a, tpl) => a + snd(tpl), 0);
return Node(
insertDict('coverage')(
cws.reduce((a, tpl) => {
const [c, w] = Array.from(tpl);
return a + (c * w);
}, x.coverage) / (
0 < totalWeight ? totalWeight : 1
)
)(x)
)(xs);
};
 
 
// withResidueShares :: Float -> Tree Dict -> Tree Dict
const withResidueShares = shareOfTotal => tree => {
const go = fraction => node => {
const
nodeRoot = node.root,
forest = node.nest,
weights = forest.map(x => x.root.weight),
weightTotal = sum(weights);
return Node(
insertDict('share')(
fraction * (1 - nodeRoot.coverage)
)(nodeRoot)
)(
zipWith(go)(
weights.map(w => fraction * (w / weightTotal))
)(forest)
);
};
return go(shareOfTotal)(tree);
};
 
 
// OUTLINE PARSED TO TREE -----------------------------
 
// forestFromLineIndents :: [(Int, String)] -> [Tree String]
const forestFromLineIndents = tuples => {
const go = xs =>
0 < xs.length ? (() => {
const [n, s] = Array.from(xs[0]);
// Lines indented under this line,
// tupled with all the rest.
const [firstTreeLines, rest] = Array.from(
span(x => n < x[0])(xs.slice(1))
);
// This first tree, and then the rest.
return [Node(s)(go(firstTreeLines))]
.concat(go(rest));
})() : [];
return go(tuples);
};
 
// indentLevelsFromLines :: [String] -> [(Int, String)]
const indentLevelsFromLines = xs => {
const
indentTextPairs = xs.map(compose(
firstArrow(length), span(isSpace)
)),
indentUnit = minimum(indentTextPairs.flatMap(pair => {
const w = fst(pair);
return 0 < w ? [w] : [];
}));
return indentTextPairs.map(
firstArrow(flip(div)(indentUnit))
);
};
 
// partialRecord :: [String] -> Dict
const partialRecord = xs => {
const [name, weightText, coverageText] = take(3)(
xs.concat(['', '', ''])
);
return {
name: name || '?',
weight: parseFloat(weightText) || 1.0,
coverage: parseFloat(coverageText) || 0.0,
share: 0.0
};
};
 
// tokenizeWith :: String -> String -> [String]
const tokenizeWith = delimiter =>
// A sequence of trimmed tokens obtained by
// splitting s on the supplied delimiter.
s => s.split(delimiter).map(x => x.trim());
 
 
// TREE SERIALIZED TO OUTLINE -------------------------
 
// indentedLinesFromTree :: (String -> a -> String) ->
// String -> Tree a -> [String]
const indentedLinesFromTree = showRoot =>
strTab => tree => {
const go = indent =>
node => [showRoot(indent)(node.root)]
.concat(node.nest.flatMap(go(strTab + indent)));
return go('')(tree);
};
 
// showN :: Int -> Float -> String
const showN = p =>
n => justifyRight(7)(' ')(n.toFixed(p));
 
// showCoverage :: String -> String -> Dict -> String
const showCoverage = delimiter =>
indent => x => tabulation(delimiter)(
[indent + x.name, showN(0)(x.weight)]
.concat([x.coverage, x.share].map(showN(4)))
);
 
// tabulation :: String -> [String] -> String
const tabulation = delimiter =>
// Up to 4 tokens drawn from the argument list,
// as a single string with fixed left-justified
// white-space widths, between delimiters.
compose(
intercalate(delimiter + ' '),
zipWith(flip(justifyLeft)(' '))([31, 9, 9, 9])
);
 
 
// GENERIC AND REUSABLE FUNCTIONS ---------------------
 
// Node :: a -> [Tree a] -> Tree a
const Node = v => xs => ({
type: 'Node',
root: v, // any type of value (consistent across tree)
nest: xs || []
});
 
// Tuple (,) :: a -> b -> (a, b)
const Tuple = a => b => ({
type: 'Tuple',
'0': a,
'1': b,
length: 2
});
 
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
const compose = (...fs) =>
x => fs.reduceRight((a, f) => f(a), x);
 
// concat :: [[a]] -> [a]
// concat :: [String] -> String
const concat = xs =>
0 < xs.length ? (() => {
const unit = 'string' !== typeof xs[0] ? (
[]
) : '';
return unit.concat.apply(unit, xs);
})() : [];
 
// div :: Int -> Int -> Int
const div = x => y => Math.floor(x / y);
 
// either :: (a -> c) -> (b -> c) -> Either a b -> c
const either = fl => fr => e =>
'Either' === e.type ? (
undefined !== e.Left ? (
fl(e.Left)
) : fr(e.Right)
) : undefined;
 
// Compose a function from a simple value to a tuple of
// the separate outputs of two different functions
 
// fanArrow (&&&) :: (a -> b) -> (a -> c) -> (a -> (b, c))
const fanArrow = f => g => x => Tuple(f(x))(
g(x)
);
 
// Lift a simple function to one which applies to a tuple,
// transforming only the first item of the tuple
 
// firstArrow :: (a -> b) -> ((a, c) -> (b, c))
const firstArrow = f => xy => Tuple(f(xy[0]))(
xy[1]
);
 
// flip :: (a -> b -> c) -> b -> a -> c
const flip = f =>
1 < f.length ? (
(a, b) => f(b, a)
) : (x => y => f(y)(x));
 
// fmapTree :: (a -> b) -> Tree a -> Tree b
const fmapTree = f => tree => {
const go = node => Node(f(node.root))(
node.nest.map(go)
);
return go(tree);
};
 
// foldTree :: (a -> [b] -> b) -> Tree a -> b
const foldTree = f => tree => {
const go = node => f(node.root)(
node.nest.map(go)
);
return go(tree);
};
 
// foldl1 :: (a -> a -> a) -> [a] -> a
const foldl1 = f => xs =>
1 < xs.length ? xs.slice(1)
.reduce(uncurry(f), xs[0]) : xs[0];
 
// fst :: (a, b) -> a
const fst = tpl => tpl[0];
 
// init :: [a] -> [a]
const init = xs =>
0 < xs.length ? (
xs.slice(0, -1)
) : undefined;
 
// insertDict :: String -> a -> Dict -> Dict
const insertDict = k => v => dct =>
Object.assign({}, dct, {
[k]: v
});
 
// intercalate :: [a] -> [[a]] -> [a]
// intercalate :: String -> [String] -> String
const intercalate = sep =>
xs => xs.join(sep);
 
// isSpace :: Char -> Bool
const isSpace = c => /\s/.test(c);
 
// justifyLeft :: Int -> Char -> String -> String
const justifyLeft = n => cFiller => s =>
n > s.length ? (
s.padEnd(n, cFiller)
) : s;
 
// justifyRight :: Int -> Char -> String -> String
const justifyRight = n => cFiller => s =>
n > s.length ? (
s.padStart(n, cFiller)
) : s;
 
// length :: [a] -> Int
const length = xs =>
(Array.isArray(xs) || 'string' === typeof xs) ? (
xs.length
) : Infinity;
 
// lines :: String -> [String]
const lines = s => s.split(/[\r\n]/);
 
// map :: (a -> b) -> [a] -> [b]
const map = f => xs =>
(Array.isArray(xs) ? (
xs
) : xs.split('')).map(f);
 
// minimum :: Ord a => [a] -> a
const minimum = xs =>
0 < xs.length ? (
foldl1(a => x => x < a ? x : a)(xs)
) : undefined;
 
// root :: Tree a -> a
const root = tree => tree.root;
 
// showLog :: a -> IO ()
const showLog = (...args) =>
console.log(
args
.map(JSON.stringify)
.join(' -> ')
);
 
// snd :: (a, b) -> b
const snd = tpl => tpl[1];
 
// span :: (a -> Bool) -> [a] -> ([a], [a])
const span = p => xs => {
const iLast = xs.length - 1;
return splitAt(
until(i => iLast < i || !p(xs[i]))(
succ
)(0)
)(xs);
};
 
// splitAt :: Int -> [a] -> ([a], [a])
const splitAt = n => xs =>
Tuple(xs.slice(0, n))(
xs.slice(n)
);
 
// succ :: Enum a => a -> a
const succ = x =>
1 + x;
 
// sum :: [Num] -> Num
const sum = xs =>
xs.reduce((a, x) => a + x, 0);
 
// tail :: [a] -> [a]
const tail = xs =>
0 < xs.length ? xs.slice(1) : [];
 
// take :: Int -> [a] -> [a]
// take :: Int -> String -> String
const take = n => xs =>
'GeneratorFunction' !== xs.constructor.constructor.name ? (
xs.slice(0, n)
) : [].concat.apply([], Array.from({
length: n
}, () => {
const x = xs.next();
return x.done ? [] : [x.value];
}));
 
// uncurry :: (a -> b -> c) -> ((a, b) -> c)
const uncurry = f =>
(x, y) => f(x)(y);
 
// unlines :: [String] -> String
const unlines = xs => xs.join('\n');
 
// until :: (a -> Bool) -> (a -> a) -> a -> a
const until = p => f => x => {
let v = x;
while (!p(v)) v = f(v);
return v;
};
 
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith = f => xs => ys =>
xs.slice(
0, Math.min(xs.length, ys.length)
).map((x, i) => f(x)(ys[i]));
 
// SOURCE OUTLINE -----------------------------------------
 
const strOutline = `NAME_HIERARCHY |WEIGHT |COVERAGE |
cleaning | | |
house1 |40 | |
bedrooms | |0.25 |
bathrooms | | |
bathroom1 | |0.5 |
bathroom2 | | |
outside_lavatory | |1 |
attic | |0.75 |
kitchen | |0.1 |
living_rooms | | |
lounge | | |
dining_room | | |
conservatory | | |
playroom | |1 |
basement | | |
garage | | |
garden | |0.8 |
house2 |60 | |
upstairs | | |
bedrooms | | |
suite_1 | | |
suite_2 | | |
bedroom_3 | | |
bedroom_4 | | |
bathroom | | |
toilet | | |
attics | |0.6 |
groundfloor | | |
kitchen | | |
living_rooms | | |
lounge | | |
dining_room | | |
conservatory | | |
playroom | | |
wet_room_&_toilet | | |
garage | | |
garden | |0.9 |
hot_tub_suite | |1 |
basement | | |
cellars | |1 |
wine_cellar | |1 |
cinema | |0.75 |`;
 
// MAIN ---
return main();
})();</syntaxhighlight>
{{Out}}
<pre>NAME_HIERARCHY | WEIGHT | COVERAGE | SHARE OF RESIDUE
cleaning | 1 | 0.4092 | 0.5908
house1 | 40 | 0.3313 | 0.2675
bedrooms | 1 | 0.2500 | 0.0375
bathrooms | 1 | 0.5000 | 0.0250
bathroom1 | 1 | 0.5000 | 0.0083
bathroom2 | 1 | 0.0000 | 0.0167
outside_lavatory | 1 | 1.0000 | 0.0000
attic | 1 | 0.7500 | 0.0125
kitchen | 1 | 0.1000 | 0.0450
living_rooms | 1 | 0.2500 | 0.0375
lounge | 1 | 0.0000 | 0.0125
dining_room | 1 | 0.0000 | 0.0125
conservatory | 1 | 0.0000 | 0.0125
playroom | 1 | 1.0000 | 0.0000
basement | 1 | 0.0000 | 0.0500
garage | 1 | 0.0000 | 0.0500
garden | 1 | 0.8000 | 0.0100
house2 | 60 | 0.4611 | 0.3233
upstairs | 1 | 0.1500 | 0.1700
bedrooms | 1 | 0.0000 | 0.0500
suite_1 | 1 | 0.0000 | 0.0125
suite_2 | 1 | 0.0000 | 0.0125
bedroom_3 | 1 | 0.0000 | 0.0125
bedroom_4 | 1 | 0.0000 | 0.0125
bathroom | 1 | 0.0000 | 0.0500
toilet | 1 | 0.0000 | 0.0500
attics | 1 | 0.6000 | 0.0200
groundfloor | 1 | 0.3167 | 0.1367
kitchen | 1 | 0.0000 | 0.0333
living_rooms | 1 | 0.0000 | 0.0333
lounge | 1 | 0.0000 | 0.0083
dining_room | 1 | 0.0000 | 0.0083
conservatory | 1 | 0.0000 | 0.0083
playroom | 1 | 0.0000 | 0.0083
wet_room_&_toilet | 1 | 0.0000 | 0.0333
garage | 1 | 0.0000 | 0.0333
garden | 1 | 0.9000 | 0.0033
hot_tub_suite | 1 | 1.0000 | 0.0000
basement | 1 | 0.9167 | 0.0167
cellars | 1 | 1.0000 | 0.0000
wine_cellar | 1 | 1.0000 | 0.0000
cinema | 1 | 0.7500 | 0.0167 </pre>
 
=={{header|Julia}}==
Most implementations of functional coverage are going to store values in a database. The implementation
stores the tree in a CSV file with an index to the parent of each entry to allow reconstitution of the tree.
<langsyntaxhighlight Julialang="julia">using CSV, DataFrames, Formatting
 
function updatecoverage(dfname, outputname)
Line 729 ⟶ 1,487:
println("\nUpdated data:")
displaycoveragedb(newdbname)
</langsyntaxhighlight>{{out}}
<pre>
Input data:
Line 869 ⟶ 1,627:
 
=={{header|Kotlin}}==
<langsyntaxhighlight lang="scala">// version 1.2.10
 
class FCNode(val name: String, val weight: Int = 1, coverage: Double = 0.0) {
Line 1,006 ⟶ 1,764:
println("${"%8.6f".format(diff)} to ${"%8.6f".format(topCoverage + diff)}")
h2Basement[2].coverage = 0.75 // restore to original value if required
}</langsyntaxhighlight>
 
{{out}}
Line 1,059 ⟶ 1,817:
the top level coverage would increase by 0.016667 to 0.425833
</pre>
=={{header|Lua}}==
<syntaxhighlight lang="lua">-- DATA:
local function node(name, weight, coverage, children)
return { name=name, weight=weight or 1.0, coverage=coverage or 0.0, sumofweights=0, delta=0, children=children }
end
 
local root =
=={{header|Perl}}==
node("cleaning", nil, nil, {
<lang perl>#!/usr/bin/perl
node("house1", 40, nil, {
node("bedrooms", nil, 0.25),
node("bathrooms", nil, nil, {
node("bathroom1", nil, 0.5),
node("bathroom2"),
node("outside_lavatory", nil, 1)
}),
node("attic", nil, 0.75),
node("kitchen", nil, 0.1),
node("living_rooms", nil, nil, {
node("lounge"),
node("dining_room"),
node("conservatory"),
node("playroom",nil,1)
}),
node("basement"),
node("garage"),
node("garden", nil, 0.8)
}),
node("house2", 60, nil, {
node("upstairs", nil, nil, {
node("bedrooms", nil, nil, {
node("suite_1"),
node("suite_2"),
node("bedroom_3"),
node("bedroom_4")
}),
node("bathroom"),
node("toilet"),
node("attics", nil, 0.6)
}),
node("groundfloor", nil, nil, {
node("kitchen"),
node("living_rooms", nil, nil, {
node("lounge"),
node("dining_room"),
node("conservatory"),
node("playroom")
}),
node("wet_room_&_toilet"),
node("garage"),
node("garden", nil, 0.9),
node("hot_tub_suite", nil, 1)
}),
node("basement", nil, nil, {
node("cellars", nil, 1),
node("wine_cellar", nil, 1),
node("cinema", nil, 0.75)
})
})
})
 
-- TASK:
use strict;
local function calccover(node)
if (node.children) then
local cnt, sum = 0, 0
for _,child in ipairs(node.children) do
local ccnt, csum = calccover(child)
cnt, sum = cnt+ccnt, sum+csum
end
node.coverage = sum/cnt
node.sumofweights = cnt -- just as prep for extra credit
end
return node.weight, node.coverage * node.weight
end
calccover(root)
 
-- EXTRA CREDIT:
local function calcdelta(node, power)
node.delta = (1.0 - node.coverage) * power
if (node.children) then
for _,child in ipairs(node.children) do
calcdelta(child, power * child.weight / node.sumofweights)
end
end
end
calcdelta(root,1)
 
-- OUTPUT:
local function printnode(node, space)
print(string.format("%-32s| %3.f | %8.6f | %8.6f |", string.rep(" ",space)..node.name, node.weight, node.coverage, node.delta))
if node.children then
for _,child in ipairs(node.children) do printnode(child,space+4) end
end
end
print("NAME_HIERARCHY |WEIGHT |COVERAGE |DELTA |")
printnode(root,0)</syntaxhighlight>
{{out}}
<pre>NAME_HIERARCHY |WEIGHT |COVERAGE |DELTA |
cleaning | 1 | 0.409167 | 0.590833 |
house1 | 40 | 0.331250 | 0.267500 |
bedrooms | 1 | 0.250000 | 0.037500 |
bathrooms | 1 | 0.500000 | 0.025000 |
bathroom1 | 1 | 0.500000 | 0.008333 |
bathroom2 | 1 | 0.000000 | 0.016667 |
outside_lavatory | 1 | 1.000000 | 0.000000 |
attic | 1 | 0.750000 | 0.012500 |
kitchen | 1 | 0.100000 | 0.045000 |
living_rooms | 1 | 0.250000 | 0.037500 |
lounge | 1 | 0.000000 | 0.012500 |
dining_room | 1 | 0.000000 | 0.012500 |
conservatory | 1 | 0.000000 | 0.012500 |
playroom | 1 | 1.000000 | 0.000000 |
basement | 1 | 0.000000 | 0.050000 |
garage | 1 | 0.000000 | 0.050000 |
garden | 1 | 0.800000 | 0.010000 |
house2 | 60 | 0.461111 | 0.323333 |
upstairs | 1 | 0.150000 | 0.170000 |
bedrooms | 1 | 0.000000 | 0.050000 |
suite_1 | 1 | 0.000000 | 0.012500 |
suite_2 | 1 | 0.000000 | 0.012500 |
bedroom_3 | 1 | 0.000000 | 0.012500 |
bedroom_4 | 1 | 0.000000 | 0.012500 |
bathroom | 1 | 0.000000 | 0.050000 |
toilet | 1 | 0.000000 | 0.050000 |
attics | 1 | 0.600000 | 0.020000 |
groundfloor | 1 | 0.316667 | 0.136667 |
kitchen | 1 | 0.000000 | 0.033333 |
living_rooms | 1 | 0.000000 | 0.033333 |
lounge | 1 | 0.000000 | 0.008333 |
dining_room | 1 | 0.000000 | 0.008333 |
conservatory | 1 | 0.000000 | 0.008333 |
playroom | 1 | 0.000000 | 0.008333 |
wet_room_&_toilet | 1 | 0.000000 | 0.033333 |
garage | 1 | 0.000000 | 0.033333 |
garden | 1 | 0.900000 | 0.003333 |
hot_tub_suite | 1 | 1.000000 | 0.000000 |
basement | 1 | 0.916667 | 0.016667 |
cellars | 1 | 1.000000 | 0.000000 |
wine_cellar | 1 | 1.000000 | 0.000000 |
cinema | 1 | 0.750000 | 0.016667 |</pre>
 
=={{header|Nim}}==
{{trans|Go}}
<syntaxhighlight lang="nim">import strformat, strutils
 
type
 
FCNode = ref object
name: string
weight: int
coverage: float
children: seq[FCNode]
parent: FCNode
 
func newFCNode(name: string; weight: int; coverage: float): FCNode =
FCNode(name: name, weight: weight, coverage: coverage)
 
# Forward reference.
func updateCoverage(n: FCNode)
 
func addChildren(n: FCNode; nodes: openArray[FCNode]) =
for node in nodes:
node.parent = n
n.children = @nodes
n.updateCoverage()
 
func setCoverage(n: FCNode; value: float) =
if n.coverage != value:
n.coverage = value
# Update any parent's coverage.
if not n.parent.isNil:
n.parent.updateCoverage()
 
func updateCoverage(n: FCNode) =
var v1 = 0.0
var v2 = 0
for node in n.children:
v1 += node.weight.toFloat * node.coverage
v2 += node.weight
n.setCoverage(v1 / v2.toFloat)
 
proc show(n: FCNode; level: int) =
let indent = level * 4
let nl = n.name.len + indent
const Sep = "|"
echo &"{n.name.align(nl)}{Sep.align(32-nl)} {n.weight:>3d} | {n.coverage:8.6f} |"
for child in n.children:
child.show(level + 1)
 
#———————————————————————————————————————————————————————————————————————————————————————————————————
 
let houses = [newFCNode("house1", 40, 0), newFCNode("house2", 60, 0)]
 
let house1 = [
newFCNode("bedrooms", 1, 0.25),
newFCNode("bathrooms", 1, 0),
newFCNode("attic", 1, 0.75),
newFCNode("kitchen", 1, 0.1),
newFCNode("living_rooms", 1, 0),
newFCNode("basement", 1, 0),
newFCNode("garage", 1, 0),
newFCNode("garden", 1, 0.8)]
 
let house2 = [
newFCNode("upstairs", 1, 0),
newFCNode("groundfloor", 1, 0),
newFCNode("basement", 1, 0)]
 
let h1Bathrooms = [
newFCNode("bathroom1", 1, 0.5),
newFCNode("bathroom2", 1, 0),
newFCNode("outside_lavatory", 1, 1)]
 
let h1LivingRooms = [
newFCNode("lounge", 1, 0),
newFCNode("dining_room", 1, 0),
newFCNode("conservatory", 1, 0),
newFCNode("playroom", 1, 1)]
 
let h2Upstairs = [
newFCNode("bedrooms", 1, 0),
newFCNode("bathroom", 1, 0),
newFCNode("toilet", 1, 0),
newFCNode("attics", 1, 0.6)]
 
let h2Groundfloor = [
newFCNode("kitchen", 1, 0),
newFCNode("living_rooms", 1, 0),
newFCNode("wet_room_&_toilet", 1, 0),
newFCNode("garage", 1, 0),
newFCNode("garden", 1, 0.9),
newFCNode("hot_tub_suite", 1, 1)]
 
let h2Basement = [
newFCNode("cellars", 1, 1),
newFCNode("wine_cellar", 1, 1),
newFCNode("cinema", 1, 0.75)]
 
let h2UpstairsBedrooms = [
newFCNode("suite_1", 1, 0),
newFCNode("suite_2", 1, 0),
newFCNode("bedroom_3", 1, 0),
newFCNode("bedroom_4", 1, 0)]
 
let h2GroundfloorLivingRooms = [
newFCNode("lounge", 1, 0),
newFCNode("dining_room", 1, 0),
newFCNode("conservatory", 1, 0),
newFCNode("playroom", 1, 0)]
 
let cleaning = newFCNode("cleaning", 1, 0)
 
house1[1].addChildren(h1Bathrooms)
house1[4].addChildren(h1LivingRooms)
houses[0].addChildren(house1)
 
h2Upstairs[0].addChildren(h2UpstairsBedrooms)
house2[0].addChildren(h2Upstairs)
h2Groundfloor[1].addChildren(h2GroundfloorLivingRooms)
house2[1].addChildren(h2Groundfloor)
house2[2].addChildren(h2Basement)
houses[1].addChildren(house2)
 
cleaning.addChildren(houses)
let topCoverage = cleaning.coverage
echo &"TOP COVERAGE = {topCoverage:8.6f}\n"
echo "NAME HIERARCHY | WEIGHT | COVERAGE |"
cleaning.show(0)
 
h2Basement[2].setCoverage(1) # Change Cinema node coverage to 1.
let diff = cleaning.coverage - topCoverage
echo "\nIf the coverage of the Cinema node were increased from 0.75 to 1"
echo &"the top level coverage would increase by {diff:8.6f} to {topCoverage + diff:8.6f}"
h2Basement[2].setCoverage(0.75) # Restore to original value if required.</syntaxhighlight>
 
{{out}}
<pre>TOP COVERAGE = 0.409167
 
NAME HIERARCHY | WEIGHT | COVERAGE |
cleaning | 1 | 0.409167 |
house1 | 40 | 0.331250 |
bedrooms | 1 | 0.250000 |
bathrooms | 1 | 0.500000 |
bathroom1 | 1 | 0.500000 |
bathroom2 | 1 | 0.000000 |
outside_lavatory | 1 | 1.000000 |
attic | 1 | 0.750000 |
kitchen | 1 | 0.100000 |
living_rooms | 1 | 0.250000 |
lounge | 1 | 0.000000 |
dining_room | 1 | 0.000000 |
conservatory | 1 | 0.000000 |
playroom | 1 | 1.000000 |
basement | 1 | 0.000000 |
garage | 1 | 0.000000 |
garden | 1 | 0.800000 |
house2 | 60 | 0.461111 |
upstairs | 1 | 0.150000 |
bedrooms | 1 | 0.000000 |
suite_1 | 1 | 0.000000 |
suite_2 | 1 | 0.000000 |
bedroom_3 | 1 | 0.000000 |
bedroom_4 | 1 | 0.000000 |
bathroom | 1 | 0.000000 |
toilet | 1 | 0.000000 |
attics | 1 | 0.600000 |
groundfloor | 1 | 0.316667 |
kitchen | 1 | 0.000000 |
living_rooms | 1 | 0.000000 |
lounge | 1 | 0.000000 |
dining_room | 1 | 0.000000 |
conservatory | 1 | 0.000000 |
playroom | 1 | 0.000000 |
wet_room_&_toilet | 1 | 0.000000 |
garage | 1 | 0.000000 |
garden | 1 | 0.900000 |
hot_tub_suite | 1 | 1.000000 |
basement | 1 | 0.916667 |
cellars | 1 | 1.000000 |
wine_cellar | 1 | 1.000000 |
cinema | 1 | 0.750000 |
 
If the coverage of the Cinema node were increased from 0.75 to 1
the top level coverage would increase by 0.016667 to 0.425833</pre>
 
=={{header|Perl}}==
<syntaxhighlight lang="perl">use strict;
use warnings;
 
print $_->[0] forsub walktree( do { local $/; <DATA> } );
my @parts;
while( $_[0] =~ /(?<head> (\s*) \N+\n ) # split off one level as 'head' (or terminal 'leaf')
(?<body> (?:\2 \s\N+\n)*)/gx ) { # next sub-level is 'body' (defined by extra depth of indentation)
 
my($head, $body) = ($+{head}, $+{body});
sub walktree
$head =~ /^.*? \| # ignore name
{
(\S*) \s* \| # save weight
my @parts;
(\S*) /x; # save coverage
while( $_[0] =~ /(( *)\S.*\n)((?:\2 .*\n)*)/g )
my $weight = sprintf '%-8s', $1 || 1;
{
my ($head, $body, $w, my $wsum)coverage = ($1sprintf '%-10s', $3,2 0,|| 0);
$head =~ /^.*?\| my(\S*$w, $wsum) *\|= (\S*)0, *\|/0);
 
my $weight = sprintf '%-8s', $1 || 1;
my $coverage = sprintf '%-10s', $2head ||.= $_->[0;],
$w += $_->[1], $wsum += $_->[1] * $_->[2], $head .= $_->[0]
for walktree( $bodywsum );+= $_->[1] * $_->[2]
for walktree( $body );
$w and $coverage = sprintf '%-10.8g', $wsum / $w;
 
push @parts, [ $head =~ s/\|.*/|$weight|$coverage|/r, $weight, $coverage ];
$coverage = sprintf '%-10.2g', $wsum/$w unless $w == 0;
push @parts, [ $head =~ s/\|.*/|$weight|$coverage|/r, $weight, $coverage ];
}
return @parts;
}
 
print $_->[0] for walktree( join '', <DATA> );
 
__DATA__
Line 1,129 ⟶ 2,215:
wine_cellar | |1 |
cinema | |0.75 |
</syntaxhighlight>
</lang>
{{out}}
<pre style="height:20ex" style="font-size:80%;">NAME_HIERARCHY |WEIGHT |COVERAGE |
<pre>
NAME_HIERARCHYcleaning |WEIGHT1 |COVERAGE0.41 |
cleaning house1 |1 |40 |0.4091666733 |
house1 |40 |0.33125 |
bedrooms |1 |0.25 |
bathrooms |1 |0.5 |
Line 1,150 ⟶ 2,235:
garage |1 |0 |
garden |1 |0.8 |
house2 |60 |0.4611111146 |
upstairs |1 |0.15 |
bedrooms |1 |0 |
Line 1,160 ⟶ 2,245:
toilet |1 |0 |
attics |1 |0.6 |
groundfloor |1 |0.3166666732 |
kitchen |1 |0 |
living_rooms |1 |0 |
Line 1,171 ⟶ 2,256:
garden |1 |0.9 |
hot_tub_suite |1 |1 |
basement |1 |0.9166666792 |
cellars |1 |1 |
wine_cellar |1 |1 |
cinema |1 |0.75 |</pre>
 
=={{header|Phix}}==
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #000080;font-style:italic;">--
-- demo\rosetta\Functional_coverage_tree.exw
-- =========================================
--</span>
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">data</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"""
NAME_HIERARCHY | WEIGHT | COVERAGE |
cleaning | | |
house1 |40 | |
bedrooms | |0.25 |
bathrooms | | |
bathroom1 | |0.5 |
bathroom2 | | |
outside_lavatory | |1 |
attic | |0.75 |
kitchen | |0.1 |
living_rooms | | |
lounge | | |
dining_room | | |
conservatory | | |
playroom | |1 |
basement | | |
garage | | |
garden | |0.8 |
house2 |60 | |
upstairs | | |
bedrooms | | |
suite_1 | | |
suite_2 | | |
bedroom_3 | | |
bedroom_4 | | |
bathroom | | |
toilet | | |
attics | |0.6 |
groundfloor | | |
kitchen | | |
living_rooms | | |
lounge | | |
dining_room | | |
conservatory | | |
playroom | | |
wet_room_&_toilet | | |
garage | | |
garden | |0.9 |
hot_tub_suite | |1 |
basement | | |
cellars | |1 |
wine_cellar | |1 |
cinema | |0.75 |
"""</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">lines</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">split</span><span style="color: #0000FF;">(</span><span style="color: #000000;">data</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">),</span>
<span style="color: #000000;">pi</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{},</span> <span style="color: #000080;font-style:italic;">-- indents (to locate parents)</span>
<span style="color: #000000;">pdx</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{},</span> <span style="color: #000080;font-style:italic;">-- indexes for ""</span>
<span style="color: #000000;">children</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">desc</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">weights</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">covers</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">parent</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">child</span>
<span style="color: #004080;">atom</span> <span style="color: #000000;">weight</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">coverage</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">childw</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
<span style="color: #008080;">enum</span> <span style="color: #000000;">DESC</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">WEIGHT</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">COVERAGE</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">PARENT</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">CHILDREN</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">CHILDW</span>
<span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">&=</span> <span style="color: #008000;">" SHARE OF RESIDUE"</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">2</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #000080;font-style:italic;">-- decode text to useable data, link up parents & children</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">desc</span><span style="color: #0000FF;">,</span><span style="color: #000000;">weights</span><span style="color: #0000FF;">,</span><span style="color: #000000;">covers</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">split</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #008000;">"|"</span><span style="color: #0000FF;">)</span>
<span style="color: #000080;font-style:italic;">-- (nb this assumes /totally/ consistent indenting)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">indent</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">desc</span><span style="color: #0000FF;">)-</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">trim_head</span><span style="color: #0000FF;">(</span><span style="color: #000000;">desc</span><span style="color: #0000FF;">)),</span>
<span style="color: #000000;">k</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">indent</span><span style="color: #0000FF;">,</span><span style="color: #000000;">pi</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">k</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">pi</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">pi</span><span style="color: #0000FF;">,</span><span style="color: #000000;">indent</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">pdx</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">pdx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">k</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">pi</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">pdx</span><span style="color: #0000FF;">[</span><span style="color: #000000;">k</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">i</span>
<span style="color: #000000;">parent</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">k</span><span style="color: #0000FF;">></span><span style="color: #000000;">1</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">parent</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">pdx</span><span style="color: #0000FF;">[</span><span style="color: #000000;">k</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span>
<span style="color: #000080;font-style:italic;">-- lines[parent][CHILDREN] &= i</span>
<span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">parent</span><span style="color: #0000FF;">][</span><span style="color: #000000;">CHILDREN</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">deep_copy</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">parent</span><span style="color: #0000FF;">][</span><span style="color: #000000;">CHILDREN</span><span style="color: #0000FF;">])</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">i</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">children</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
<span style="color: #000000;">weight</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">to_number</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">trim</span><span style="color: #0000FF;">(</span><span style="color: #000000;">weights</span><span style="color: #0000FF;">),</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">coverage</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">to_number</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">trim</span><span style="color: #0000FF;">(</span><span style="color: #000000;">covers</span><span style="color: #0000FF;">),</span><span style="color: #000000;">0</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">desc</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">weight</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">coverage</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">parent</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">children</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">childw</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">to</span> <span style="color: #000000;">2</span> <span style="color: #008080;">by</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">1</span> <span style="color: #008080;">do</span>
<span style="color: #000080;font-style:italic;">-- calculate the parent coverages, and save child weight sums</span>
<span style="color: #000000;">children</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">][</span><span style="color: #000000;">CHILDREN</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">children</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">coverage</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
<span style="color: #000000;">childw</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">children</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">child</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">children</span><span style="color: #0000FF;">[</span><span style="color: #000000;">c</span><span style="color: #0000FF;">]</span>
<span style="color: #004080;">atom</span> <span style="color: #000000;">w</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">child</span><span style="color: #0000FF;">][</span><span style="color: #000000;">WEIGHT</span><span style="color: #0000FF;">]</span>
<span style="color: #000000;">coverage</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">child</span><span style="color: #0000FF;">][</span><span style="color: #000000;">COVERAGE</span><span style="color: #0000FF;">]*</span><span style="color: #000000;">w</span>
<span style="color: #000000;">childw</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">w</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">][</span><span style="color: #000000;">COVERAGE</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">coverage</span><span style="color: #0000FF;">/</span><span style="color: #000000;">childw</span>
<span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">][</span><span style="color: #000000;">CHILDW</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">childw</span> <span style="color: #000080;font-style:italic;">-- (save for next loop)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">to</span> <span style="color: #000000;">2</span> <span style="color: #008080;">by</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">1</span> <span style="color: #008080;">do</span>
<span style="color: #000080;font-style:italic;">-- calculate the share of residue, and format lines</span>
<span style="color: #000000;">child</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">i</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">desc</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">weight</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">coverage</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">parent</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
<span style="color: #004080;">atom</span> <span style="color: #000000;">residue</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span><span style="color: #0000FF;">-</span><span style="color: #000000;">coverage</span>
<span style="color: #008080;">while</span> <span style="color: #000000;">parent</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">residue</span> <span style="color: #0000FF;">*=</span> <span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">child</span><span style="color: #0000FF;">][</span><span style="color: #000000;">WEIGHT</span><span style="color: #0000FF;">]/</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">parent</span><span style="color: #0000FF;">][</span><span style="color: #000000;">CHILDW</span><span style="color: #0000FF;">]</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">child</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">parent</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">parent</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">parent</span><span style="color: #0000FF;">][</span><span style="color: #000000;">PARENT</span><span style="color: #0000FF;">]}</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"%-32s| %6d | %-8g | %g"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">desc</span><span style="color: #0000FF;">,</span><span style="color: #000000;">weight</span><span style="color: #0000FF;">,</span><span style="color: #000000;">coverage</span><span style="color: #0000FF;">,</span><span style="color: #000000;">residue</span><span style="color: #0000FF;">})</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">)&</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">)</span>
<span style="color: #0000FF;">{}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">wait_key</span><span style="color: #0000FF;">()</span>
<!--</syntaxhighlight>-->
{{out}}
<pre>
NAME_HIERARCHY | WEIGHT | COVERAGE | SHARE OF RESIDUE
cleaning | 1 | 0.409167 | 0.590833
house1 | 40 | 0.33125 | 0.2675
bedrooms | 1 | 0.25 | 0.0375
bathrooms | 1 | 0.5 | 0.025
bathroom1 | 1 | 0.5 | 0.00833333
bathroom2 | 1 | 0 | 0.0166667
outside_lavatory | 1 | 1 | 0
attic | 1 | 0.75 | 0.0125
kitchen | 1 | 0.1 | 0.045
living_rooms | 1 | 0.25 | 0.0375
lounge | 1 | 0 | 0.0125
dining_room | 1 | 0 | 0.0125
conservatory | 1 | 0 | 0.0125
playroom | 1 | 1 | 0
basement | 1 | 0 | 0.05
garage | 1 | 0 | 0.05
garden | 1 | 0.8 | 0.01
house2 | 60 | 0.461111 | 0.323333
upstairs | 1 | 0.15 | 0.17
bedrooms | 1 | 0 | 0.05
suite_1 | 1 | 0 | 0.0125
suite_2 | 1 | 0 | 0.0125
bedroom_3 | 1 | 0 | 0.0125
bedroom_4 | 1 | 0 | 0.0125
bathroom | 1 | 0 | 0.05
toilet | 1 | 0 | 0.05
attics | 1 | 0.6 | 0.02
groundfloor | 1 | 0.316667 | 0.136667
kitchen | 1 | 0 | 0.0333333
living_rooms | 1 | 0 | 0.0333333
lounge | 1 | 0 | 0.00833333
dining_room | 1 | 0 | 0.00833333
conservatory | 1 | 0 | 0.00833333
playroom | 1 | 0 | 0.00833333
wet_room_&_toilet | 1 | 0 | 0.0333333
garage | 1 | 0 | 0.0333333
garden | 1 | 0.9 | 0.00333333
hot_tub_suite | 1 | 1 | 0
basement | 1 | 0.916667 | 0.0166667
cellars | 1 | 1 | 0
wine_cellar | 1 | 1 | 0
cinema | 1 | 0.75 | 0.0166667
</pre>
 
=={{header|Python}}==
===Python: Using lists and tuples===
It's actually some of the raw code used when researching this task.
 
<langsyntaxhighlight lang="python">from itertools import zip_longest
 
 
Line 1,332 ⟶ 2,578:
print('\n\nTOP COVERAGE = %f\n' % covercalc(lstc))
depth_first(lstc)
pptreefields(['NAME_HIERARCHY WEIGHT COVERAGE'.split()] + lstc)</langsyntaxhighlight>
 
{{out}}
Line 1,384 ⟶ 2,630:
A cleaner implementation that uses the class static variable path2node as in the previous example so you don't have to traverse the tree to work out the position to add new nodes. This relies on parent nodes appearing before their children which is the case in the order of the add_node calls.
 
<langsyntaxhighlight lang="python"># -*- coding: utf-8 -*-
 
SPACES = 4
Line 1,510 ⟶ 2,756:
assert isclose((delta + cover), 1.0), "Top level delta + coverage should " \
"equal 1 instead of (%f + %f)" % (delta, cover)
</syntaxhighlight>
</lang>
 
{{out}}
Line 1,577 ⟶ 2,823:
Mainly uses pre-existing generic functions, including '''forestFromLineIndents''', '''foldTree''' and '''fmapTree''':
{{Works with|Python|3.7}}
<langsyntaxhighlight lang="python">'''Functional coverage tree'''
 
from itertools import chain, product
from functools import reduce
import re
 
 
Line 1,595 ⟶ 2,840:
delimiter = '|'
 
reportLines = lines(REPORT.splitlines()
columnTitles = init(columnNames(delimiter)(reportLines[0]))
 
# ------ SERIALISATION OF DECORATED PARSE TREE -------
print(titleLine(delimiter)(columnWidths)(
columnTitles + ['share of residue']
Line 1,604 ⟶ 2,849:
print(indentedLinesFromTree(' ', tabulation(columnWidths))(
 
# -------- TWO COMPUTATIONS BY TRAVERSAL ---------
withResidueShares(1.0)(
foldTree(weightedTreeAverageweightedCoverage)(
 
# --- TREE FROM PARSE OF OUTLINE TEXT ----
fmapTree(
recordFromKeysDefaultsDelimiterAndLine(columnTitles)(
[str, float, float])(['?', 1.0, 0.0])(delimiter)columnTitles
)(
[str, float, float])([
'?', 1.0, 0.0
])(delimiter)
)(
forestFromLineIndents(indentLevelsFromLinesforestFromIndentLevels(
reportLines[1:]indentLevelsFromLines(
)) reportLines[01:]
)
)[0]
)
)
Line 1,622 ⟶ 2,873:
 
 
# ---- WEIGHTED COVERAGE, AND SHARE OF TOTAL RESIDUE -----------
 
# weightedCoverage :: Tree Dict -> Float
def weightedCoverage(node):
'''The weighted coverage of a
node in a coverage tree.
'''
nodeRoot = root(node)
return nodeRoot['coverage'] * nodeRoot['weight']
 
 
# weightedTreeAverage :: Tree Dict ->
# [Tree Dict] -> Tree Dict
def weightedTreeAverageweightedCoverage(x):
'''The weighted averagecoverage of a tree node,
as a function of the weighted averages
of its children.
'''
def go(xs):
cws = [
(r['coverage'], r['weight']) for r
in [root(x) for x in xs]
]
totalWeight = reduce(lambda a, x: a + x[1], cws, 0)
return Node(dict(
x, **{
'coverage': round(x['coverage'] + reduce(
reducelambda a, cw: a + (cw[0] * cw[1]),
lambda acws, node: weightedCoverage(node) + a,x['coverage']
) / (totalWeight if 0 < totalWeight else xs1), 05)
) / (
reduce(
lambda a, node: root(node)['weight'] + a,
xs, 0
) or 1
)
), 5)
}
))(xs)
return lambda xs: go(xs)
 
 
Line 1,665 ⟶ 2,905:
'''
def go(fraction, node):
[nodeRoot, nodeNest] = apListap([root, nest])([node])
weights = [root(x)['weight'] for x in nodeNest]
siblingsTotal = sum(weights)
Line 1,682 ⟶ 2,922:
 
 
# OUTLINE TABULATION -------------------- OUTLINE TABULATION ------------------
 
# tabulation :: [Int] -> String -> Dict -> String
Line 1,712 ⟶ 2,952:
 
 
# GENERIC AND REUSABLE FUNCTIONS -------------- GENERIC AND REUSABLE FUNCTIONS ------------
 
# Node :: a -> [Tree a] -> Tree a
Line 1,723 ⟶ 2,963:
 
 
# Tupleap (,<*>) :: [(a -> b)] -> ([a,] -> [b)]
def Tupleap(xfs):
'''Constructor for a pair of values,
possibly of two different types.
'''
return lambda y: (
x + (y,)
) if isinstance(x, tuple) else (x, y)
 
 
# apList (<*>) :: [(a -> b)] -> [a] -> [b]
def apList(fs):
'''The application of each of a list of functions,
to each of a list of values.
'''
def go(xs):
return liftA2List(identity)(fs)
return [
f(x) for (f, x)
in product(fs, xs)
]
return go
 
 
Line 1,765 ⟶ 3,000:
# concatMap :: (a -> [b]) -> [a] -> [b]
def concatMap(f):
'''A concatenated list or string over which a function fhas been mapped.
The list monad can be derived by using a function f which
has been mapped.
Thewraps listits monadoutput canin be derived by using an (a -> [b])list,
(using an empty list to represent computational failure).
function which wraps its output in a list (using an
empty list to represent computational failure).
'''
def go(xs):
return lambda xs: (''.join if isinstance(xs, str) else list)(
return chain.from_iterable(map(f, xs))
)return go
 
 
Line 1,782 ⟶ 3,016:
 
 
# first :: (a -> b) -> ((a, c) -> (b, c))
def firstArrow(f):
def first(f):
'''A simple function lifted to one which applies
'''A simple function lifted to a function over a tuple,
to a tuple, transforming only its first item.
with f applied only the first of two values.
'''
return lambda xy: Tuple(f(xy[0]), xy[1])(
xy[1]
)
 
 
Line 1,810 ⟶ 3,043:
def fmapTree(f):
'''A new tree holding the results of
applyingan application of f to each root in
the existing tree.
'''
def go(x):
return Node(f(x['root']))(
[gof(v) for v in x['nestroot']])
)([go(v) for v in x['nest']])
return lambda tree: go(tree)
 
 
Line 1,823 ⟶ 3,056:
def foldTree(f):
'''The catamorphism on trees. A summary
value obtaineddefined by a depth-first fold.
'''
def go(node):
return f(node['root'](node))([
go(x) for x in node['nest'](node)
])
return lambda tree: go(tree)
 
 
# forestFromLineIndentsforestFromIndentLevels :: [(Int, Stringa)] -> [Tree Stringa]
def forestFromLineIndentsforestFromIndentLevels(tuples):
'''A list of trees derived from a list of linesvalues paired
with integers giving their levels of indentation.
'''
def go(xs):
if xs:
(intIndent, txt)v = xs[0]
(firstTreeLines, rest) = span(
compose(lt(lambda x: intIndent), fst)< x[0]
)(xs[1:])
return [Node(txtv)(go(firstTreeLines))] + go(rest)
else:
return []
Line 1,853 ⟶ 3,086:
'''First member of a pair.'''
return tpl[0]
 
 
# identity :: a -> a
def identity(x):
'''The identity function.'''
return x
 
 
Line 1,867 ⟶ 3,094:
giving its level of indentation from 0 upwards.
'''
rgxSpace = re.compile(r'\s+')
indentTextPairs = list(map(
compose(firstArrowfirst(len), span(rgxSpace.matchisSpace)),
xs
))
Line 1,876 ⟶ 3,102:
)(indentTextPairs))
return list(map(
firstArrowfirst(flip(div)(indentUnit)),
indentTextPairs
))
Line 1,888 ⟶ 3,114:
'''
def go(indent):
return lambda node: [f(indent, node['root'])] + concatMaplist(
goconcatMap(strTab + indent)
) go(node['nest']strTab + indent)
return lambda tree: unlines(go )(node['nest')(tree)])
)
return lambda tree: '\n'.join(go('')(tree))
 
 
Line 1,910 ⟶ 3,138:
 
 
# isSpace :: Char -> Bool
# liftA2List :: (a -> b -> c) -> [a] -> [b] -> [c]
# isSpace :: String -> Bool
def liftA2List(f):
def isSpace(s):
'''The binary operator f lifted to a function over two
'''True if s is not empty, and
lists. f applied to each pair of arguments in the
cartesiancontains productonly ofwhite xs and ysspace.
'''
return lambda xs: lambda ys: [s.isspace()
f(x)(y) for x, y in product(xs, ys)
]
 
 
# lines :: String -> [String]
def lines(s):
'''A list of strings,
(containing no newline characters)
derived from a single new-line delimited string.
'''
return s.splitlines()
 
 
Line 1,979 ⟶ 3,196:
span p xs is equivalent to (takeWhile p xs, dropWhile p xs).
'''
def match(ab):
b = ab[1]
return not b or not p(b[0])
 
def f(ab):
a, b = ab
return a + [b[0]], b[1:]
 
def go(xs):
lngreturn = lenuntil(match)(f)(([], xs))
return splitAt(go
until(lambda i: (lng == i) or not p(xs[i]))(succ)(0)
)(xs)
return lambda xs: go(xs)
 
 
# Unimplemented <- splitOn for lists (Eq a => [a] -> [a] -> [[a]])
# splitOn :: String -> String -> [String]
def splitOn(pat):
Line 1,996 ⟶ 3,217:
xs.split(pat) if isinstance(xs, str) else None
)
 
 
# succ :: Enum a => a -> a
def succ(x):
'''The successor of a value.
For numeric types, (1 +).
'''
return 1 + x if isinstance(x, int) else (
chr(1 + ord(x))
)
 
 
# splitAt :: Int -> [a] -> ([a], [a])
def splitAt(n):
'''A tuple pairing the prefix of length n
with the rest of xs.
'''
return lambda xs: (xs[0:n], xs[n:])
 
 
Line 2,020 ⟶ 3,223:
'''String in lower case.'''
return s.lower()
 
 
# unlines :: [String] -> String
def unlines(xs):
'''A single string formed by the intercalation
of a list of strings with the newline character.
'''
return '\n'.join(xs)
 
 
Line 2,035 ⟶ 3,230:
The initial seed value is x.
'''
def go(f, x):
v =def g(x):
while not p( v): = x
vwhile =not fp(v):
return v = f(v)
return lambda f: lambda x: go(f, x) return v
return g
return go
 
 
Line 2,088 ⟶ 3,285:
wine_cellar | |1 |
cinema | |0.75 |'''
main()</langsyntaxhighlight>
{{Out}}
<pre>NAME_HIERARCHY | WEIGHT | COVERAGE | SHARE OF RESIDUE
Line 2,139 ⟶ 3,336:
(in this case <code>data/functional-coverage.txt</code>).
 
<langsyntaxhighlight lang="racket">#lang racket/base
(require racket/list racket/string racket/match racket/format racket/file)
 
Line 2,210 ⟶ 3,407:
(for-each
(compose print-coverage-tree find-wght-cvrg)
(build-hierarchies (report->indent.c/e-list (file->string "data/functional-coverage.txt")))))</langsyntaxhighlight>
 
{{out}}
Line 2,257 ⟶ 3,454:
| wine_cellar | 1 | 1.00 | 1.00000 |
| cinema | 1 | 0.75 | 0.75000 |</pre>
 
=={{header|Raku}}==
(formerly Perl 6)
{{trans|Perl}}
<syntaxhighlight lang="raku" line>sub walktree ($data) {
my (@parts, $cnt);
 
while ($data ~~ m:nth(++$cnt)/$<head>=[(\s*) \N+\n ] # split off one level as 'head' (or terminal 'leaf')
$<body>=[[$0 \s+ \N+\n]*]/ ) { # next sub-level is 'body' (defined by extra depth of indentation)
 
my ($head, $body) = ($<head>, $<body>);
$head ~~ /'|' $<weight>=[\S*] \s* '|' $<coverage>=[\S*]/; # save values of weight and coverage (if any) for later
 
my ($w, $wsum) = (0, 0);
$head ~= .[0],
$w += .[1],
$wsum += .[1] * .[2]
for walktree $body;
 
my $weight = (~$<weight> or 1).fmt('%-8s');
my $coverage = $w == 0
?? (~$<coverage> or 0).fmt('%-10s')
!! ($wsum/$w) .fmt('%-10.2g');
@parts.push: [$head.subst(/'|' \N+/, "|$weight|$coverage|"), $weight, $coverage ];
}
return @parts;
}
 
(say .[0] for walktree $_) given
 
q:to/END/
NAME_HIERARCHY |WEIGHT |COVERAGE |
cleaning | | |
house1 |40 | |
bedrooms | |0.25 |
bathrooms | | |
bathroom1 | |0.5 |
bathroom2 | | |
outside_lavatory | |1 |
attic | |0.75 |
kitchen | |0.1 |
living_rooms | | |
lounge | | |
dining_room | | |
conservatory | | |
playroom | |1 |
basement | | |
garage | | |
garden | |0.8 |
house2 |60 | |
upstairs | | |
bedrooms | | |
suite_1 | | |
suite_2 | | |
bedroom_3 | | |
bedroom_4 | | |
bathroom | | |
toilet | | |
attics | |0.6 |
groundfloor | | |
kitchen | | |
living_rooms | | |
lounge | | |
dining_room | | |
conservatory | | |
playroom | | |
wet_room_&_toilet | | |
garage | | |
garden | |0.9 |
hot_tub_suite | |1 |
basement | | |
cellars | |1 |
wine_cellar | |1 |
cinema | |0.75 |
END
</syntaxhighlight>
{{out}}
<pre style="font-size:70%;">NAME_HIERARCHY |WEIGHT |COVERAGE |
 
cleaning |1 |0.41 |
house1 |40 |0.33 |
bedrooms |1 |0.25 |
bathrooms |1 |0.5 |
bathroom1 |1 |0.5 |
bathroom2 |1 |0 |
outside_lavatory |1 |1 |
attic |1 |0.75 |
kitchen |1 |0.1 |
living_rooms |1 |0.25 |
lounge |1 |0 |
dining_room |1 |0 |
conservatory |1 |0 |
playroom |1 |1 |
basement |1 |0 |
garage |1 |0 |
garden |1 |0.8 |
house2 |60 |0.46 |
upstairs |1 |0.15 |
bedrooms |1 |0 |
suite_1 |1 |0 |
suite_2 |1 |0 |
bedroom_3 |1 |0 |
bedroom_4 |1 |0 |
bathroom |1 |0 |
toilet |1 |0 |
attics |1 |0.6 |
groundfloor |1 |0.32 |
kitchen |1 |0 |
living_rooms |1 |0 |
lounge |1 |0 |
dining_room |1 |0 |
conservatory |1 |0 |
playroom |1 |0 |
wet_room_&_toilet |1 |0 |
garage |1 |0 |
garden |1 |0.9 |
hot_tub_suite |1 |1 |
basement |1 |0.92 |
cellars |1 |1 |
wine_cellar |1 |1 |
cinema |1 |0.75 |</pre>
 
=={{header|Swift}}==
 
{{trans|Kotlin}}
 
<syntaxhighlight lang="swift">import Foundation
 
extension String {
func paddedLeft(totalLen: Int) -> String {
let needed = totalLen - count
 
guard needed > 0 else {
return self
}
 
return String(repeating: " ", count: needed) + self
}
}
 
class FCNode {
let name: String
let weight: Int
 
var coverage: Double {
didSet {
if oldValue != coverage {
parent?.updateCoverage()
}
}
}
 
weak var parent: FCNode?
var children = [FCNode]()
 
init(name: String, weight: Int = 1, coverage: Double = 0) {
self.name = name
self.weight = weight
self.coverage = coverage
}
 
func addChildren(_ children: [FCNode]) {
for child in children {
child.parent = self
}
 
self.children += children
 
updateCoverage()
}
 
func show(level: Int = 0) {
let indent = level * 4
let nameLen = name.count + indent
 
print(name.paddedLeft(totalLen: nameLen), terminator: "")
print("|".paddedLeft(totalLen: 32 - nameLen), terminator: "")
print(String(format: " %3d |", weight), terminator: "")
print(String(format: " %8.6f |", coverage))
for child in children {
child.show(level: level + 1)
}
}
 
func updateCoverage() {
let v1 = children.reduce(0.0, { $0 + $1.coverage * Double($1.weight) })
let v2 = children.reduce(0.0, { $0 + Double($1.weight) })
 
coverage = v1 / v2
}
}
 
let houses = [
FCNode(name: "house1", weight: 40),
FCNode(name: "house2", weight: 60)
]
 
let house1 = [
FCNode(name: "bedrooms", weight: 1, coverage: 0.25),
FCNode(name: "bathrooms"),
FCNode(name: "attic", weight: 1, coverage: 0.75),
FCNode(name: "kitchen", weight: 1, coverage: 0.1),
FCNode(name: "living_rooms"),
FCNode(name: "basement"),
FCNode(name: "garage"),
FCNode(name: "garden", weight: 1, coverage: 0.8)
]
 
let house2 = [
FCNode(name: "upstairs"),
FCNode(name: "groundfloor"),
FCNode(name: "basement")
]
 
let h1Bathrooms = [
FCNode(name: "bathroom1", weight: 1, coverage: 0.5),
FCNode(name: "bathroom2"),
FCNode(name: "outside_lavatory", weight: 1, coverage: 1.0)
]
 
let h1LivingRooms = [
FCNode(name: "lounge"),
FCNode(name: "dining_room"),
FCNode(name: "conservatory"),
FCNode(name: "playroom", weight: 1, coverage: 1.0)
]
 
let h2Upstairs = [
FCNode(name: "bedrooms"),
FCNode(name: "bathroom"),
FCNode(name: "toilet"),
FCNode(name: "attics", weight: 1, coverage: 0.6)
]
 
let h2Groundfloor = [
FCNode(name: "kitchen"),
FCNode(name: "living_rooms"),
FCNode(name: "wet_room_&_toilet"),
FCNode(name: "garage"),
FCNode(name: "garden", weight: 1, coverage: 0.9),
FCNode(name: "hot_tub_suite", weight: 1, coverage: 1.0)
]
 
let h2Basement = [
FCNode(name: "cellars", weight: 1, coverage: 1.0),
FCNode(name: "wine_cellar", weight: 1, coverage: 1.0),
FCNode(name: "cinema", weight: 1, coverage: 0.75)
]
 
let h2UpstairsBedrooms = [
FCNode(name: "suite_1"),
FCNode(name: "suite_2"),
FCNode(name: "bedroom_3"),
FCNode(name: "bedroom_4")
]
 
let h2GroundfloorLivingRooms = [
FCNode(name: "lounge"),
FCNode(name: "dining_room"),
FCNode(name: "conservatory"),
FCNode(name: "playroom")
]
 
let cleaning = FCNode(name: "cleaning")
 
house1[1].addChildren(h1Bathrooms)
house1[4].addChildren(h1LivingRooms)
houses[0].addChildren(house1)
 
h2Upstairs[0].addChildren(h2UpstairsBedrooms)
house2[0].addChildren(h2Upstairs)
h2Groundfloor[1].addChildren(h2GroundfloorLivingRooms)
house2[1].addChildren(h2Groundfloor)
house2[2].addChildren(h2Basement)
houses[1].addChildren(house2)
 
cleaning.addChildren(houses)
 
let top = cleaning.coverage
 
print("Top Coverage: \(String(format: "%8.6f", top))")
print("Name Hierarchy | Weight | Coverage |")
 
cleaning.show()
 
h2Basement[2].coverage = 1.0
 
let diff = cleaning.coverage - top
 
print("\nIf the coverage of the Cinema node were increased from 0.75 to 1.0")
print("the top level coverage would increase by ")
print("\(String(format: "%8.6f", diff)) to \(String(format: "%8.6f", top))")</syntaxhighlight>
 
{{out}}
 
<pre>Top Coverage: 0.409167
Name Hierarchy | Weight | Coverage |
cleaning | 1 | 0.409167 |
house1 | 40 | 0.331250 |
bedrooms | 1 | 0.250000 |
bathrooms | 1 | 0.500000 |
bathroom1 | 1 | 0.500000 |
bathroom2 | 1 | 0.000000 |
outside_lavatory | 1 | 1.000000 |
attic | 1 | 0.750000 |
kitchen | 1 | 0.100000 |
living_rooms | 1 | 0.250000 |
lounge | 1 | 0.000000 |
dining_room | 1 | 0.000000 |
conservatory | 1 | 0.000000 |
playroom | 1 | 1.000000 |
basement | 1 | 0.000000 |
garage | 1 | 0.000000 |
garden | 1 | 0.800000 |
house2 | 60 | 0.461111 |
upstairs | 1 | 0.150000 |
bedrooms | 1 | 0.000000 |
suite_1 | 1 | 0.000000 |
suite_2 | 1 | 0.000000 |
bedroom_3 | 1 | 0.000000 |
bedroom_4 | 1 | 0.000000 |
bathroom | 1 | 0.000000 |
toilet | 1 | 0.000000 |
attics | 1 | 0.600000 |
groundfloor | 1 | 0.316667 |
kitchen | 1 | 0.000000 |
living_rooms | 1 | 0.000000 |
lounge | 1 | 0.000000 |
dining_room | 1 | 0.000000 |
conservatory | 1 | 0.000000 |
playroom | 1 | 0.000000 |
wet_room_&_toilet | 1 | 0.000000 |
garage | 1 | 0.000000 |
garden | 1 | 0.900000 |
hot_tub_suite | 1 | 1.000000 |
basement | 1 | 0.916667 |
cellars | 1 | 1.000000 |
wine_cellar | 1 | 1.000000 |
cinema | 1 | 0.750000 |
 
If the coverage of the Cinema node were increased from 0.75 to 1.0
the top level coverage would increase by
0.016667 to 0.409167</pre>
 
=={{header|Wren}}==
{{trans|Kotlin}}
{{libheader|Wren-fmt}}
<syntaxhighlight lang="wren">import "./fmt" for Fmt
 
class FCNode {
construct new(name, weight, coverage) {
_name = name
_weight = weight
_coverage = coverage
_children = []
_parent = null
}
 
static new(name, weight) { new(name, weight, 0) }
static new(name) { new(name, 1, 0) }
 
name { _name }
weight { _weight }
coverage { _coverage }
 
coverage=(value) {
if (_coverage != value) {
_coverage = value
if (_parent) {
_parent.updateCoverage_() // update any parent's coverage
}
}
}
 
parent { _parent }
parent=(p) { _parent = p }
 
addChildren(nodes) {
_children.addAll(nodes)
for (node in nodes) node.parent = this
updateCoverage_()
}
 
updateCoverage_() {
var v1 = _children.reduce(0) { |acc, n| acc + n.weight * n.coverage }
var v2 = _children.reduce(0) { |acc, n| acc + n.weight }
coverage = v1 / v2
}
 
show(level) {
var indent = level * 4
var nl = _name.count + indent
Fmt.lprint("$*s$*s $3d | $8.6f |", [nl, _name, 32-nl, "|", _weight, _coverage])
if (_children.isEmpty) return
for (child in _children) child.show(level+1)
}
}
 
var houses = [
FCNode.new("house1", 40),
FCNode.new("house2", 60)
]
 
var house1 = [
FCNode.new("bedrooms", 1, 0.25),
FCNode.new("bathrooms"),
FCNode.new("attic", 1, 0.75),
FCNode.new("kitchen", 1, 0.1),
FCNode.new("living_rooms"),
FCNode.new("basement"),
FCNode.new("garage"),
FCNode.new("garden", 1, 0.8)
]
 
var house2 = [
FCNode.new("upstairs"),
FCNode.new("groundfloor"),
FCNode.new("basement")
]
 
var h1Bathrooms = [
FCNode.new("bathroom1", 1, 0.5),
FCNode.new("bathroom2"),
FCNode.new("outside_lavatory", 1, 1)
]
 
var h1LivingRooms = [
FCNode.new("lounge"),
FCNode.new("dining_room"),
FCNode.new("conservatory"),
FCNode.new("playroom", 1, 1)
]
 
var h2Upstairs = [
FCNode.new("bedrooms"),
FCNode.new("bathroom"),
FCNode.new("toilet"),
FCNode.new("attics", 1, 0.6)
]
 
var h2Groundfloor = [
FCNode.new("kitchen"),
FCNode.new("living_rooms"),
FCNode.new("wet_room_&_toilet"),
FCNode.new("garage"),
FCNode.new("garden", 1, 0.9),
FCNode.new("hot_tub_suite", 1, 1)
]
 
var h2Basement = [
FCNode.new("cellars", 1, 1),
FCNode.new("wine_cellar", 1, 1),
FCNode.new("cinema", 1, 0.75)
]
 
var h2UpstairsBedrooms = [
FCNode.new("suite_1"),
FCNode.new("suite_2"),
FCNode.new("bedroom_3"),
FCNode.new("bedroom_4")
]
 
var h2GroundfloorLivingRooms = [
FCNode.new("lounge"),
FCNode.new("dining_room"),
FCNode.new("conservatory"),
FCNode.new("playroom")
]
 
var cleaning = FCNode.new("cleaning")
 
house1[1].addChildren(h1Bathrooms)
house1[4].addChildren(h1LivingRooms)
houses[0].addChildren(house1)
 
h2Upstairs[0].addChildren(h2UpstairsBedrooms)
house2[0].addChildren(h2Upstairs)
h2Groundfloor[1].addChildren(h2GroundfloorLivingRooms)
house2[1].addChildren(h2Groundfloor)
house2[2].addChildren(h2Basement)
houses[1].addChildren(house2)
 
cleaning.addChildren(houses)
var topCoverage = cleaning.coverage
Fmt.print("TOP COVERAGE = $8.6f\n", topCoverage)
System.print("NAME HIERARCHY | WEIGHT | COVERAGE |")
cleaning.show(0)
 
h2Basement[2].coverage = 1 // change Cinema node coverage to 1
var diff = cleaning.coverage - topCoverage
System.print("\nIf the coverage of the Cinema node were increased from 0.75 to 1")
System.write("the top level coverage would increase by ")
Fmt.print("$8.6f to $8.6f", diff, topCoverage + diff)
h2Basement[2].coverage = 0.75 // restore to original value if required</syntaxhighlight>
 
{{out}}
<pre>
TOP COVERAGE = 0.409167
 
NAME HIERARCHY | WEIGHT | COVERAGE |
cleaning | 1 | 0.409167 |
house1 | 40 | 0.331250 |
bedrooms | 1 | 0.250000 |
bathrooms | 1 | 0.500000 |
bathroom1 | 1 | 0.500000 |
bathroom2 | 1 | 0.000000 |
outside_lavatory | 1 | 1.000000 |
attic | 1 | 0.750000 |
kitchen | 1 | 0.100000 |
living_rooms | 1 | 0.250000 |
lounge | 1 | 0.000000 |
dining_room | 1 | 0.000000 |
conservatory | 1 | 0.000000 |
playroom | 1 | 1.000000 |
basement | 1 | 0.000000 |
garage | 1 | 0.000000 |
garden | 1 | 0.800000 |
house2 | 60 | 0.461111 |
upstairs | 1 | 0.150000 |
bedrooms | 1 | 0.000000 |
suite_1 | 1 | 0.000000 |
suite_2 | 1 | 0.000000 |
bedroom_3 | 1 | 0.000000 |
bedroom_4 | 1 | 0.000000 |
bathroom | 1 | 0.000000 |
toilet | 1 | 0.000000 |
attics | 1 | 0.600000 |
groundfloor | 1 | 0.316667 |
kitchen | 1 | 0.000000 |
living_rooms | 1 | 0.000000 |
lounge | 1 | 0.000000 |
dining_room | 1 | 0.000000 |
conservatory | 1 | 0.000000 |
playroom | 1 | 0.000000 |
wet_room_&_toilet | 1 | 0.000000 |
garage | 1 | 0.000000 |
garden | 1 | 0.900000 |
hot_tub_suite | 1 | 1.000000 |
basement | 1 | 0.916667 |
cellars | 1 | 1.000000 |
wine_cellar | 1 | 1.000000 |
cinema | 1 | 0.750000 |
 
If the coverage of the Cinema node were increased from 0.75 to 1
the top level coverage would increase by 0.016667 to 0.425833
</pre>
9,476

edits