Check-in [857ec64989]
Not logged in
Overview
Comment:CPUs
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | trunk
Files: files | file ages | folders
SHA1: 857ec649894e9cb944c33d8175f903f6b1416c75
User & Date: vhost7825ssh on 2018-06-07 02:00:16
Other Links: manifest | tags
Context
2018-06-07 02:00
CPUs Leaf check-in: 857ec64989 user: vhost7825ssh tags: trunk
2018-06-06 23:35
ExaHyPE check-in: 076420858a user: vhost7825ssh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/COMMENTS.txt version [1ff7eb951e].





























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Home page:

    https://github.com/tommythorn/Reduceron

    http://thorn.ws/reduceron/Reduceron/Practical_Reduceron.html

    https://www.cs.york.ac.uk/fp/reduceron/


Some related pages:

    https://hackage.haskell.org/package/york-lava

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_http_thorn_ws_reduceron_Reduceron_Practical_Reduceron_html/bonnet/thorn.ws/reduceron/Reduceron/Practical_Reduceron.html version [06b28216b8].





































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="Generator" content="iWeb 3.0.4" />
    <meta name="iWeb-Build" content="local-build-20130226" />
    <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
    <meta name="viewport" content="width=700" />
    <title>PRACTICAL REDUCERON</title>
    <link rel="stylesheet" type="text/css" media="screen,print" href="Practical_Reduceron_files/Practical_Reduceron.css" />
    <!--[if lt IE 8]><link rel='stylesheet' type='text/css' media='screen,print' href='Practical_Reduceron_files/Practical_ReduceronIE.css'/><![endif]-->
    <!--[if gte IE 8]><link rel='stylesheet' type='text/css' media='screen,print' href='Media/IE8.css'/><![endif]-->
    <script type="text/javascript" src="Scripts/iWebSite.js"></script>
    <script type="text/javascript" src="Scripts/Widgets/SharedResources/WidgetCommon.js"></script>
    <script type="text/javascript" src="Scripts/Widgets/Navbar/navbar.js"></script>
    <script type="text/javascript" src="Scripts/iWebImage.js"></script>
    <script type="text/javascript" src="Practical_Reduceron_files/Practical_Reduceron.js"></script>
  </head>
  <body style="background: rgb(255, 255, 255) url(Practical_Reduceron_files/PhotoGray_browser_bg-2.jpg) repeat scroll top left; margin: 0pt; " onload="onPageLoad();" onunload="onPageUnload();">
    <div style="text-align: center; ">
      <div style="margin-bottom: 0px; margin-left: auto; margin-right: auto; margin-top: 0px; overflow: hidden; position: relative; word-wrap: break-word;  text-align: left; width: 700px; " id="body_content">
        <div style="background: transparent url(Practical_Reduceron_files/PhotoGray_bg_c-1.jpg) repeat scroll top left; width: 700px; ">
          <div style="margin-left: 0px; position: relative; width: 700px; z-index: 0; " id="nav_layer">
            <div style="height: 0px; line-height: 0px; " class="bumper"> </div>
            <div style="height: 700px; width: 700px;  height: 700px; left: 0px; position: absolute; top: -80px; width: 700px; z-index: 1; " class="tinyText style_SkipStroke">
              <img src="Practical_Reduceron_files/Darkroom_spotlight.jpg" alt="" style="border: none; height: 700px; width: 700px; " />
            </div>
            <div style="height: 1px; line-height: 1px; " class="tinyText"> </div>
            <div class="com-apple-iweb-widget-navbar flowDefining" id="widget0" style="margin-left: 0px; margin-top: 19px; opacity: 1.00; position: relative; width: 700px; z-index: 1; ">
    
              <div id="widget0-navbar" class="navbar">

      
                <div id="widget0-bg" class="navbar-bg">

        
                  <ul id="widget0-navbar-list" class="navbar-list">
 <li></li> 
</ul>
                  
      
</div>
                
    
</div>
            </div>
            <script type="text/javascript"><!--//--><![CDATA[//><!--
new NavBar('widget0', 'Scripts/Widgets/Navbar', 'Scripts/Widgets/SharedResources', '.', {"path-to-root": "", "navbar-css": ".navbar {\n\tfont-family: Futura, 'Trebuchet MS', sans-serif;\n\ttext-transform: uppercase;\n\tfont-size: .7em;\n\tcolor: #C1C1C1;\n\tline-height: 25px;\n\ttext-align: center;\n\tpadding: 0px;\n\tbackground-image: url(Practical_Reduceron_files\/PhotoGray_nav_bg.png);\n\t_background-image: url(Practical_Reduceron_files\/PhotoGray_nav_bg.jpg);\n\tbackground-repeat: repeat-y;\n\tborder-top: 1px solid #999;\n\tborder-bottom: 1px solid #999;\n}\n\n.navbar-bg {\n\tpadding: 6px 0px 4px 0px;\n}\n\n.navbar ul {\n\tlist-style: none;\n\tmargin: 0px;\n\tpadding: 0px 10px 0px 10px;\n}\n\n.navbar ul li {\n\tdisplay: inline; \n\tlist-style-type: none;\n\tmargin: 0px;\n\tpadding: 0px 10px 0px 10px;\n}\n\n\n.noncurrent-page  a:visited {\n\ttext-decoration: none;\n\tcolor: #C1C1C1;\n\ttext-shadow: 0px -1px 2px #333;\n}\n\n.noncurrent-page  a {\n\ttext-decoration: none;\n\tcolor: #C1C1C1;\n\ttext-shadow: 0px -1px 2px #333;\n}\n\n\n.noncurrent-page a:hover {\n\ttext-decoration: none;\n\tcolor: #EC001E;\n\ttext-shadow: 0px -1px 2px #333;\n}\n\n.current-page a\n{\n\ttext-decoration: none;\n\tcolor: #000;\n\ttext-shadow: 0px -1px 2px #7E7E7E;\n}\n\n.current-page a:visited\n{\n\ttext-decoration: none;\n\tcolor: #000;\n\ttext-shadow: 0px -1px 2px #7E7E7E;\n}\n\n", "current-page-GUID": "B1FBFB8F-24BE-4157-868E-47665F78D548", "isCollectionPage": "NO"});
//--><!]]></script>

            <div style="clear: both; height: 0px; line-height: 0px; " class="spacer"> </div>
          </div>
          <div style="height: 52px; margin-left: 0px; position: relative; width: 700px; z-index: 10; " id="header_layer">
            <div style="height: 0px; line-height: 0px; " class="bumper"> </div>
            <div id="id1" style="height: 39px; left: 30px; position: absolute; top: 13px; width: 640px; z-index: 1; " class="style_SkipStroke_1 shape-with-text">
              <div class="text-content style_External_640_39" style="padding: 0px; ">
                <div class="style">
                  <p style="padding-bottom: 0pt; padding-top: 0pt; " class="Header">PRACTICAL REDUCERON</p>
                </div>
              </div>
            </div>
          </div>
          <div style="margin-left: 0px; position: relative; width: 700px; z-index: 5; " id="body_layer">
            <div style="height: 0px; line-height: 0px; " class="bumper"> </div>
            <div style="height: 1px; line-height: 1px; " class="tinyText"> </div>
            <div style="margin-left: 37px; margin-top: 307px; position: relative; width: 620px; z-index: 1; " class="style_SkipStroke_2 shape-with-text flowDefining">
              <div class="text-content style_External_620_179" style="padding: 0px; ">
                <div class="style">
                  <p style="padding-top: 0pt; " class="Body">Reduceron is a high performance FPGA soft-core for running lazy functional programs, complete with hardware garbage collection.  Reduceron has been implemented on various FPGAs with clock frequency ranging from 60 to 150 MHz depending on the FPGA.  A high degree of parallelism allows Reduceron to implement graph evaluation very efficiently.<br /></p>
                  <p class="Body"><br /></p>
                  <p style="padding-bottom: 0pt; " class="Body">Reduceron is the work of Matthew Naylor, Colin Runciman and Jason Reich, who have kindly made their work available for others to use.  Please see <a title="../../../../../2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/index.html" href="../../../../../2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/index.html">http://www.cs.york.ac.uk/fp/reduceron</a> for supporting articles, memos, and original distribution.</p>
                </div>
                <div style="height: 3px; line-height: 3px; " class="tinyText"> </div>
              </div>
            </div>
            


            <div style="height: 221px; width: 390px;  height: 221px; left: 34px; position: absolute; top: 19px; width: 390px; z-index: 1; " class="tinyText stroke_0">
              <div style="position: relative; width: 390px; ">
                <img src="Practical_Reduceron_files/shapeimage_1.png" alt="" style="height: 221px; left: 0px; position: absolute; top: 0px; width: 390px; " />
              </div>
            </div>
            


            <div id="id2" style="height: 29px; left: 30px; position: absolute; top: 275px; width: 485px; z-index: 1; " class="style_SkipStroke_3 shape-with-text">
              <div class="text-content graphic_shape_layout_style_default_External_485_29" style="padding: 0px; ">
                <div class="graphic_shape_layout_style_default">
                  <p style="padding-bottom: 0pt; padding-top: 0pt; " class="Title">WHAT IS REDUCERON?</p>
                </div>
              </div>
            </div>
            


            <div id="id3" style="height: 29px; left: 30px; position: absolute; top: 500px; width: 485px; z-index: 1; " class="style_SkipStroke_3 shape-with-text">
              <div class="text-content graphic_shape_layout_style_default_External_485_29" style="padding: 0px; ">
                <div class="graphic_shape_layout_style_default">
                  <p style="padding-bottom: 0pt; padding-top: 0pt; " class="Title"><span style="line-height: 19px; " class="style_1">HOW OK, WHAT’S THIS THEN?</span></p>
                </div>
              </div>
            </div>
            


            <div id="id4" style="height: 739px; left: 39px; position: absolute; top: 543px; width: 640px; z-index: 1; " class="style_SkipStroke_3 shape-with-text">
              <div class="text-content Normal_External_640_739" style="padding: 0px; ">
                <div class="Normal">
                  <p style="padding-top: 0pt; " class="Body">The present is a fork of the original distribution which intends to take the (York) Reduceron from the research prototype to the point where it can be useful for embedded projects.  One could imagine an Arduino-like ecosystem, except replacing C++ for Haskell and the inexpensive Atmel AVR for a less inexpensive FPGA.<br /></p>
                  <p class="Body"><br /></p>
                  <p class="Body">The York Reduceron needs the following enhancements to meet our needs:<br /></p>
                  <p class="Body"><br /></p>
                  <p class="Body"> 0. The heap and program must (for the most parts) be kept in external<br /></p>
                  <p class="Body">    memory, with FPGA block memory used for the stacks and heap and<br /></p>
                  <p class="Body">    program caches.<br /></p>
                  <p class="Body"><br /></p>
                  <p class="Body">    This simultaneously enables smaller and less expensive FPGAs to be<br /></p>
                  <p class="Body">    used as well as allows for a much larger heap and larger programs.<br /></p>
                  <p class="Body"><br /></p>
                  <p class="Body"> 1. Access to memory mapped IO devices (and optionally, RAM).<br /></p>
                  <p class="Body"><br /></p>
                  <p class="Body"> 2. Richer set of primitives, including multiplication, shifts, logical<br /></p>
                  <p class="Body">    and, or, ...<br /></p>
                  <p class="Body"><br /></p>
                  <p class="Body"> 3. Support for 32-bit integers - this greatly simplifies interfacing to<br /></p>
                  <p class="Body">    existing IO devices and simplifies various numerical computations.<br /></p>
                  <p class="Body"><br /></p>
                  <p class="Body">[[Work is progressing on all four fronts, but we are not there yet.]]<br /></p>
                  <p class="Body"><br /></p>
                  <p class="Body"><br /></p>
                  <p class="Body">While Reduceron technically refers to the FPGA implementation, it is supported by<br /></p>
                  <p class="Body"><br /></p>
                  <p class="Body"> - Flite: the F-lite to Red translator.<br /></p>
                  <p class="Body"> - A Red emulator in C<br /></p>
                  <p class="Body"> - York Lava: Reduceron is a York Lava program, which generate HDL<br /></p>
                  <p class="Body"> - Support for Verilog simulation and synthesis for various FPGA dev kits.<br /></p>
                  <p class="Body"><br /></p>
                  <p class="Body"><br /></p>
                  <p style="padding-bottom: 0pt; " class="Body">As much of the history as was available has been gathered and reduceron, york-lava, and the Flite distribution have been merged into one repository.</p>
                  <script data-gittip-username="tommythorn" src="https://www.gittip.com/assets/widgets/0002.js"></script>
                </div>
              </div>
            </div>
            <div style="height: 795px; line-height: 795px; " class="spacer"> </div>
          </div>
          <div style="height: 150px; margin-left: 0px; position: relative; width: 700px; z-index: 15; " id="footer_layer">
            <div style="height: 0px; line-height: 0px; " class="bumper"> </div>
            <a href="http://apple.com/mac" title="http://apple.com/mac"><img src="Practical_Reduceron_files/mwmac_white.png" alt="Made on a Mac" style="border: none; height: 50px; left: 280px; opacity: 0.25; position: absolute; top: 75px; width: 139px; z-index: 1; " id="id5" />

</a>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>


Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_http_thorn_ws_reduceron_Reduceron_Practical_Reduceron_html/bonnet/thorn.ws/reduceron/Reduceron/Practical_Reduceron_files/Darkroom_spotlight.jpg version [55965fed7a].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_http_thorn_ws_reduceron_Reduceron_Practical_Reduceron_html/bonnet/thorn.ws/reduceron/Reduceron/Practical_Reduceron_files/PhotoGray_bg_c-1.jpg version [9354b7b595].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_http_thorn_ws_reduceron_Reduceron_Practical_Reduceron_html/bonnet/thorn.ws/reduceron/Reduceron/Practical_Reduceron_files/PhotoGray_browser_bg-2.jpg version [2b1cc80598].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_http_thorn_ws_reduceron_Reduceron_Practical_Reduceron_html/bonnet/thorn.ws/reduceron/Reduceron/Practical_Reduceron_files/Practical_Reduceron.css version [08165b6aec].











































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
.style {
    padding: 4px;
}
.style_1 {
    color: rgb(255, 255, 255);
    font-family: 'Futura-Medium', 'Futura', 'Trebuchet MS', sans-serif;
    font-size: 14px;
    font-stretch: normal;
    font-style: normal;
    font-variant: normal;
    font-weight: 500;
    line-height: 19px;
    opacity: 1.00;
    text-transform: none;
}
.style_SkipStroke_2 {
    background: transparent;
    opacity: 1.00;
}
.style_SkipStroke_3 {
    background: transparent;
    opacity: 1.00;
}
.style_External_620_179 {
    position: relative;
}
.style_SkipStroke {
    background: transparent;
    opacity: 1.00;
}
.style_External_640_39 {
    position: relative;
}
.style_SkipStroke_1 {
    background: transparent;
    opacity: 1.00;
}
.Body {
    color: rgb(255, 255, 255);
    font-family: 'Futura-Medium', 'Futura', 'Trebuchet MS', sans-serif;
    font-size: 14px;
    font-stretch: normal;
    font-style: normal;
    font-variant: normal;
    font-weight: 500;
    letter-spacing: 0;
    line-height: 21px;
    margin-bottom: 0px;
    margin-left: 0px;
    margin-right: 0px;
    margin-top: 0px;
    opacity: 1.00;
    padding-bottom: 0px;
    padding-top: 0px;
    text-align: left;
    text-decoration: none;
    text-indent: 0px;
    text-transform: none;
}
.Header {
    color: rgb(255, 255, 255);
    font-family: 'Futura-Medium', 'Futura', 'Trebuchet MS', sans-serif;
    font-size: 24px;
    font-stretch: normal;
    font-style: normal;
    font-variant: normal;
    font-weight: 500;
    letter-spacing: 0;
    line-height: 32px;
    margin-bottom: 0px;
    margin-left: 0px;
    margin-right: 0px;
    margin-top: 0px;
    opacity: 1.00;
    padding-bottom: 0px;
    padding-top: 0px;
    text-align: left;
    text-decoration: none;
    text-indent: 0px;
    text-transform: uppercase;
}
.Normal {
    padding: 4px;
}
.Normal_External_640_739 {
    position: relative;
}
.Normal_External_640_39 {
    position: relative;
}
.Normal_External_620_179 {
    position: relative;
}
.Title {
    color: rgb(203, 203, 203);
    font-family: 'Futura-Medium', 'Futura', 'Trebuchet MS', sans-serif;
    font-size: 16px;
    font-stretch: normal;
    font-style: normal;
    font-variant: normal;
    font-weight: 500;
    letter-spacing: 0;
    line-height: 21px;
    margin-bottom: 0px;
    margin-left: 0px;
    margin-right: 0px;
    margin-top: 0px;
    opacity: 1.00;
    padding-bottom: 0px;
    padding-top: 0px;
    text-align: left;
    text-decoration: none;
    text-indent: 0px;
    text-transform: uppercase;
}
.graphic_generic_body_textbox_style_default_SkipStroke {
    background: transparent;
    opacity: 1.00;
}
.graphic_generic_header_textbox_style_default_SkipStroke {
    background: transparent;
    opacity: 1.00;
}
.graphic_generic_title_textbox_style_default_SkipStroke {
    background: transparent;
    opacity: 1.00;
}
.graphic_image_style_default_SkipStroke {
    background: transparent;
    opacity: 1.00;
}
.graphic_image_style_default_2_SkipStroke {
    background: transparent;
    opacity: 1.00;
}
.graphic_shape_layout_style_default {
    padding: 4px;
}
.graphic_shape_layout_style_default_External_485_29 {
    position: relative;
}
a {
    color: rgb(203, 203, 203);
    text-decoration: none;
}
a:visited {
    color: rgb(121, 121, 124);
    text-decoration: none;
}
a:hover {
    color: rgb(189, 37, 44);
    text-decoration: none;
}
.bumper {
    font-size: 1px;
    line-height: 1px;
}
.tinyText {
    font-size: 1px;
    line-height: 1px;
}
#widget0 a:hover {
    color: rgb(189, 37, 44);
    text-decoration: none;
}
#widget0 a:visited {
    color: rgb(121, 121, 124);
    text-decoration: none;
}
#widget0 a {
    color: rgb(203, 203, 203);
    text-decoration: none;
}
.spacer {
    font-size: 1px;
    line-height: 1px;
}
body { 
    -webkit-text-size-adjust: none;
}
div { 
    overflow: visible; 
}
img { 
    border: none; 
}
.InlineBlock { 
    display: inline; 
}
.InlineBlock { 
    display: inline-block; 
}
.inline-block {
    display: inline-block;
    vertical-align: baseline;
    margin-bottom:0.3em;
}
.inline-block.shape-with-text {
    vertical-align: bottom;
}
.vertical-align-middle-middlebox {
    display: table;
}
.vertical-align-middle-innerbox {
    display: table-cell;
    vertical-align: middle;
}
div.paragraph {
    position: relative;
}
li.full-width {
    width: 100;
}

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_http_thorn_ws_reduceron_Reduceron_Practical_Reduceron_html/bonnet/thorn.ws/reduceron/Reduceron/Practical_Reduceron_files/Practical_Reduceron.js version [226d2915b5].























>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
// Created by iWeb 3.0.4 local-build-20130226

setTransparentGifURL('Media/transparent.gif');function applyEffects()
{var registry=IWCreateEffectRegistry();registry.registerEffects({stroke_0:new IWStrokeParts([{rect:new IWRect(-2,2,4,217),url:'Practical_Reduceron_files/stroke.png'},{rect:new IWRect(-2,-2,4,4),url:'Practical_Reduceron_files/stroke_1.png'},{rect:new IWRect(2,-2,386,4),url:'Practical_Reduceron_files/stroke_2.png'},{rect:new IWRect(388,-2,4,4),url:'Practical_Reduceron_files/stroke_3.png'},{rect:new IWRect(388,2,4,217),url:'Practical_Reduceron_files/stroke_4.png'},{rect:new IWRect(388,219,4,4),url:'Practical_Reduceron_files/stroke_5.png'},{rect:new IWRect(2,219,386,4),url:'Practical_Reduceron_files/stroke_6.png'},{rect:new IWRect(-2,219,4,4),url:'Practical_Reduceron_files/stroke_7.png'}],new IWSize(390,221))});registry.applyEffects();}
function hostedOnDM()
{return false;}
function onPageLoad()
{loadMozillaCSS('Practical_Reduceron_files/Practical_ReduceronMoz.css')
adjustLineHeightIfTooBig('id1');adjustFontSizeIfTooBig('id1');adjustLineHeightIfTooBig('id2');adjustFontSizeIfTooBig('id2');adjustLineHeightIfTooBig('id3');adjustFontSizeIfTooBig('id3');adjustLineHeightIfTooBig('id4');adjustFontSizeIfTooBig('id4');Widget.onload();fixupAllIEPNGBGs();fixAllIEPNGs('Media/transparent.gif');fixupIECSS3Opacity('id5');applyEffects()}
function onPageUnload()
{Widget.onunload();}

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_http_thorn_ws_reduceron_Reduceron_Practical_Reduceron_html/bonnet/thorn.ws/reduceron/Reduceron/Practical_Reduceron_files/mwmac_white.png version [9a84b9ab72].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_http_thorn_ws_reduceron_Reduceron_Practical_Reduceron_html/bonnet/thorn.ws/reduceron/Reduceron/Practical_Reduceron_files/shapeimage_1.png version [44c3999e34].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_http_thorn_ws_reduceron_Reduceron_Practical_Reduceron_html/bonnet/thorn.ws/reduceron/Reduceron/Scripts/Widgets/Navbar/navbar.js version [267026c091].







































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//
//  iWeb - navbar.js
//  Copyright (c) 2007-2008 Apple Inc. All rights reserved.
//

var NavBar=Class.create(Widget,{widgetIdentifier:"com-apple-iweb-widget-NavBar",initialize:function($super,instanceID,widgetPath,sharedPath,sitePath,preferences,runningInApp)
{if(instanceID)
{$super(instanceID,widgetPath,sharedPath,sitePath,preferences,runningInApp);if(!this.preferenceForKey("useStaticFeed")&&this.preferenceForKey("dotMacAccount"))
{var depthPrefix=this.preferenceForKey("path-to-root");if(!depthPrefix||depthPrefix=="")
depthPrefix="./";this.xml_feed=depthPrefix+"?webdav-method=truthget&depth=infinity&ns=iweb&filterby=in-navbar";}
else
{this.xml_feed="feed.xml";if(this.sitePath)
{this.xml_feed=this.sitePath+"/"+this.xml_feed;}}
this.changedPreferenceForKey("navbar-css");this.regenerate();}},regenerate:function()
{new Ajax.Request(this.xml_feed,{method:'get',onSuccess:this.populateNavItems.bind(this)});return true;},getStyleElement:function(key)
{if(!this.styleElement)
{var head=document.getElementsByTagName("head")[0];if(head)
{var newElement=document.createElement("style");newElement.type="text/css";head.appendChild(newElement);this.styleElement=newElement;}}
return this.styleElement;},substWidgetPath:function(text)
{var result=text.replace(/\$WIDGET_PATH/gm,this.widgetPath);return result;},addCSSSelectorPrefix:function(text)
{var prefix="div#"+this.instanceID+" ";text=text.replace(/\/\*[^*]*\*+([^/][^*]*\*+)*\//gm,"");text=text.replace(/(^\s*|\}\s*)([^{]+)({[^}]*})/gm,function(match,beforeSelectorList,selectorList,propertyList){var result=beforeSelectorList;var selectors=selectorList.split(",");for(var i=0;i<selectors.length;i++){result+=prefix+selectors[i];if(i+1<selectors.length)result+=",";}
result+=propertyList;return result;});return text;},changedPreferenceForKey:function(key)
{if(key=="navbar-css")
{var text=this.preferenceForKey(key);if(!text)
{text="";}
text=this.substWidgetPath(text);text=this.addCSSSelectorPrefix(text);var styleElement=this.getStyleElement();if(styleElement)
{if(!windowsInternetExplorer)
{var node=document.createTextNode(text);if(node)
{while(styleElement.hasChildNodes())
{styleElement.removeChild(styleElement.firstChild);}
styleElement.appendChild(node);}}
else
{styleElement.styleSheet.cssText=text;}}}},populateNavItems:function(req)
{var items;var feedRoot=ajaxGetDocumentElement(req);if(feedRoot){var parsedFeed=this.getAtomFeedItems(feedRoot);var items=parsedFeed.resultArray;var currentPageGUID=null;var isCollectionPage="NO";var curPagePat=null;if(this.runningInApp)
curPagePat=/\.#current#.$/;else
{currentPageGUID=this.preferenceForKey("current-page-GUID");isCollectionPage=this.preferenceForKey("isCollectionPage");}
var navDiv=this.div("navbar-list");var navBgDiv=navDiv.parentNode;$(navBgDiv).ensureHasLayoutForIE();while(navDiv.firstChild){navDiv.removeChild(navDiv.firstChild);}
var depthPrefix=this.preferenceForKey("path-to-root");if(!depthPrefix||depthPrefix=="")
depthPrefix="./";for(var x=0;x<items.length;x++){var navItem=document.createElement("li");var anchor=document.createElement("a");var title=items[x].title;var pageGUID=items[x].GUID;title=title.replace(/ /g,"\u00a0")+" ";var url=items[x].url;if(!this.runningInApp&&!url.match(/^http:/i))
url=depthPrefix+url;var inAppCurPage=this.runningInApp&&curPagePat.exec(unescape(new String(url)));if(inAppCurPage)
{url=url.replace(curPagePat,"");}
if(pageGUID==currentPageGUID||inAppCurPage){navItem.className='current-page';if(!this.runningInApp&&isCollectionPage!="YES"){url="";}}
else
navItem.className='noncurrent-page';anchor.setAttribute("href",url);anchor.appendChild(document.createTextNode(title));navItem.appendChild(anchor);navDiv.appendChild(navItem);}
if(this.preferences&&this.preferences.postNotification){this.preferences.postNotification("BLWidgetIsSafeToDrawNotification",1);}}},getAtomFeedItems:function(feedNode)
{var results=new Array;var pageOrder=new Array;if(feedNode)
{var generator="";var generatorElt=getFirstElementByTagName(feedNode,"generator");if(generatorElt&&generatorElt.firstChild){generator=allData(generatorElt);}
var pageGUIDs,pageGUIDsElt;for(var entryElt=feedNode.firstChild;entryElt;entryElt=entryElt.nextSibling){var isInNavbarElt=null;if(!pageGUIDs&&(pageGUIDsElt=findChild(entryElt,"site-navbar","urn:iweb:"))){pageGUIDs=allData(pageGUIDsElt).split(",");for(var x=0;x<pageGUIDs.length;x++){var pageGUID=pageGUIDs[x];pageOrder[""+pageGUID]=x;}}
if(entryElt.nodeName=="entry"&&(isInNavbarElt=findChild(entryElt,"in-navbar","urn:iweb:"))){if(!isInNavbarElt)
continue;var pageGUID="";if(isInNavbarElt.firstChild){pageGUID=""+allData(isInNavbarElt);}else{iWLog("no navBarElt child");}
if(pageGUID=="navbar-sort")
continue;var title="";var titleElt=findChild(entryElt,"title","urn:iweb:");if(!titleElt){iWLog("No iWeb title");titleElt=findChild(entryElt,"title");}
if(titleElt&&titleElt.firstChild){title=allData(titleElt);}
var linkElt=getFirstElementByTagName(entryElt,'link');url=linkElt.getAttribute("href");if(!url&&linkElement.firstChild){url=allData(linkElement);}
results[results.length]={title:title,url:url,GUID:pageGUID};}}}
if(pageGUIDs){results=$(results).reject(function(result){return(pageOrder[result.GUID]===undefined);});results.sort(function(lhs,rhs){return pageOrder[lhs.GUID]-pageOrder[rhs.GUID];});}
return{resultArray:results};},onload:function()
{},onunload:function()
{}});function findChild(element,nodeName,namespace)
{var child;for(child=element.firstChild;child;child=child.nextSibling){if(child.localName==nodeName||child.baseName==nodeName){if(!namespace){return child;}
var childNameSpace=child.namespaceURI;if(childNameSpace==namespace){return child;}}}
return null;}
function getFirstElementByTagName(node,tag_name){var elements=node.getElementsByTagName(tag_name);if(elements.length){return elements[0];}
else{return findChild(node,tag_name);}}
function allData(node)
{node=node.firstChild;var data=node.data;while((node=node.nextSibling)){data+=node.data;}
return data;}

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_http_thorn_ws_reduceron_Reduceron_Practical_Reduceron_html/bonnet/thorn.ws/reduceron/Reduceron/Scripts/Widgets/SharedResources/WidgetCommon.js version [717ecc5c1f].















































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
//
//  iWeb - WidgetCommon.js
//  Copyright (c) 2007-2008 Apple Inc. All rights reserved.
//

var widgets=[];var identifiersToStringLocalizations=[];var Widget=Class.create({initialize:function(instanceID,widgetPath,sharedPath,sitePath,preferences,runningInApp)
{if(instanceID)
{this.instanceID=instanceID;this.widgetPath=widgetPath;this.sharedPath=sharedPath;this.sitePath=sitePath;this.preferences=preferences;this.runningInApp=(runningInApp===undefined)?false:runningInApp;this.onloadReceived=false;if(this.preferences&&this.runningInApp==true)
{this.preferences.widget=this;setTransparentGifURL(this.sharedPath.stringByAppendingPathComponent("None.gif"));}
this.div().widget=this;window[instanceID]=this;widgets.push(this);widgets[instanceID]=this;if(!this.constructor.instances)
{this.constructor.instances=new Array();}
this.constructor.instances.push(this);}},div:function()
{var divID=this.instanceID;if(arguments.length==1)
{divID=this.instanceID+"-"+arguments[0];}
return $(divID);},onload:function()
{this.onloadReceived=true;},onunload:function()
{},didBecomeSelected:function()
{},didBecomeDeselected:function()
{},didBeginEditing:function()
{},didEndEditing:function()
{},setNeedsDisplay:function()
{},preferenceForKey:function(key)
{var value;if(this.preferences)
value=this.preferences[key];return value;},initializeDefaultPreferences:function(prefs)
{var self=this;$H(prefs).each(function(pair)
{if(self.preferenceForKey(pair.key)===undefined)
{self.setPreferenceForKey(pair.value,pair.key,false);}});},setPreferenceForKey:function(preference,key,registerUndo)
{if(this.runningInApp)
{if(registerUndo===undefined)
registerUndo=true;if((registerUndo==false)&&this.preferences.disableUndoRegistration)
this.preferences.disableUndoRegistration();this.preferences[key]=preference;if((registerUndo==false)&&this.preferences.enableUndoRegistration)
this.preferences.enableUndoRegistration();}
else
{this.preferences[key]=preference;this.changedPreferenceForKey(key);}},changedPreferenceForKey:function(key)
{},postNotificationWithNameAndUserInfo:function(name,userInfo)
{if(window.NotificationCenter!==undefined)
{NotificationCenter.postNotification(new IWNotification(name,null,userInfo));}},sizeWillChange:function()
{},sizeDidChange:function()
{},widgetWidth:function()
{var enclosingDiv=this.div();if(enclosingDiv)
return enclosingDiv.offsetWidth;else
return null;},widgetHeight:function()
{var enclosingDiv=this.div();if(enclosingDiv)
return enclosingDiv.offsetHeight;else
return null;},getInstanceId:function(id)
{var fullId=this.instanceID+"-"+id;if(arguments.length==2)
{fullId+=("$"+arguments[1]);}
return fullId;},getElementById:function(id)
{var fullId=this.getInstanceId.apply(this,arguments);return $(fullId);},localizedString:function(string)
{return LocalizedString(this.widgetIdentifier,string);},showView:function(viewName)
{var futureView=this.m_views[viewName];if((futureView!=this.m_currentView)&&(futureView!=this.m_futureView))
{this.m_futureView=futureView;if(this.m_fadeAnimation)
{this.m_fadeAnimation.stop();}
var previousView=this.m_currentView;this.m_currentView=futureView;var currentView=this.m_currentView;this.m_futureView=null;this.m_fadeAnimation=new SimpleAnimation(function(){delete this.m_fadeAnimation;}.bind(this));this.m_fadeAnimation.pre=function()
{if(previousView)
{previousView.ensureDiv().setStyle({zIndex:0,opacity:1});}
if(currentView)
{currentView.ensureDiv().setStyle({zIndex:1,opacity:0});currentView.show();currentView.render();}}
this.m_fadeAnimation.post=function()
{!previousView||previousView.hide();!currentView||currentView.ensureDiv().setStyle({zIndex:'',opacity:1});!currentView||!currentView.doneFadingIn||currentView.doneFadingIn();}
this.m_fadeAnimation.update=function(now)
{!currentView||currentView.ensureDiv().setOpacity(now);!previousView||previousView.ensureDiv().setOpacity(1-now);}.bind(this);this.m_fadeAnimation.start();}}});Widget.onload=function()
{for(var i=0;i<widgets.length;i++)
{widgets[i].onload();}}
Widget.onunload=function()
{for(var i=0;i<widgets.length;i++)
{widgets[i].onunload();}}
function RegisterWidgetStrings(identifier,strings)
{identifiersToStringLocalizations[identifier]=strings;}
function LocalizedString(identifier,string)
{var localized=undefined;var localizations=identifiersToStringLocalizations[identifier];if(localizations===undefined)
{iWLog("warning: no localizations for widget "+identifier+", (key:"+string+")");}
else
{localized=localizations[string];}
if(localized===undefined)
{iWLog("warning: couldn't find a localization for '"+string+"' for widget "+identifier);localized=string;}
return localized;}
function WriteLocalizedString(identifier,string)
{document.write(LocalizedString(identifier,string));}
var JSONFeedRendererWidget=Class.create(Widget,{initialize:function($super,instanceID,widgetPath,sharedPath,sitePath,preferences,runningInApp)
{if(instanceID)
{$super(instanceID,widgetPath,sharedPath,sitePath,preferences,runningInApp);}},changedPreferenceForKey:function(key)
{try
{var value=this.preferenceForKey(key);if(key=="sfr-shadow")
{if(value!=null)
{this.sfrShadow=eval(value);}
else
{this.sfrShadow=null;}
this.renderFeedItems("sfr-shadow");}
if(key=="sfr-stroke")
{if(value!==null)
this.sfrStroke=eval(value);else
this.sfrStroke=null;this.invalidateFeedItems("sfr-stroke");}
if(key=="sfr-reflection")
{if(value!==null)
{this.sfrReflection=eval(value);}
else
{this.sfrReflection=null;}
this.invalidateFeedItems("sfr-reflection");}}
catch(e)
{iWLog("JSONFeedRendererWidget: exception");debugPrintException(e);}},invalidateFeedItems:function(reason)
{trace('invalidateFeedItems(%s)',reason);if(this.pendingRender!==null)
{clearTimeout(this.pendingRender);}
this.pendingRender=setTimeout(function()
{this.pendingRender=null;this.renderFeedItems(reason);}.bind(this),50);},rerenderImage:function(imgGroupDiv,imgDiv,imageUrlString,entryHasImage,photoProportions,imageWidth,positioningHandler,onloadHandler)
{imgGroupDiv.update();if(entryHasImage)
{imgGroupDiv.strokeApplied=false;imgGroupDiv.reflectionApplied=false;imgGroupDiv.shadowApplied=false;imgGroupDiv.setStyle({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});imgGroupDiv.style.position='relative';imgDiv.style.position='relative';var imageUrl=imageUrlString||transparentGifURL();var image=IWCreateImage(imageUrl);image.load(function(image,imgDiv,imgGroupDiv,positioningHandler,onloadHandler)
{var cropDiv=this.croppingDivForImage(image,photoProportions,imageWidth);imgGroupDiv.appendChild(cropDiv);if(positioningHandler)
{positioningHandler();}
if(image.sourceURL()!==transparentGifURL())
{this.applyEffects(imgGroupDiv);}
if(onloadHandler)
{onloadHandler();}}.bind(this,image,imgDiv,imgGroupDiv,positioningHandler,onloadHandler));}},croppingDivForImage:function(image,kind,width)
{var croppedSize=function(originalSize,cropKind,width)
{if(cropKind=="Square")
{return new IWSize(width,width);}
else if(cropKind=="Landscape")
{return new IWSize(width,width*(3/4));}
else if(cropKind=="Portrait")
{return new IWSize(width,width*(4/3));}
else
{var scaleFactor=width/originalSize.width;return originalSize.scale(scaleFactor,scaleFactor,true);}};var cropDiv=null;if(image.loaded())
{var img=$(document.createElement('img'));img.src=image.sourceURL();var natural=image.naturalSize();cropDiv=$(document.createElement("div"));cropDiv.appendChild(img);var croppingDivForImage_helper=function(loadedImage)
{if(loadedImage)
{natural=new IWSize(loadedImage.width,loadedImage.height);}
var cropped=croppedSize(natural,kind,width);var scaleFactor=cropped.width/natural.width;if(natural.aspectRatio()>cropped.aspectRatio())
{scaleFactor=cropped.height/natural.height;}
var scaled=natural.scale(scaleFactor);var offset=new IWPoint(Math.abs(scaled.width-cropped.width)/2,Math.abs(scaled.height-cropped.height)/2);img.setStyle({width:px(scaled.width),height:px(scaled.height),marginLeft:px(-offset.x),marginTop:px(-offset.y),position:'relative'});cropDiv.setStyle({width:px(cropped.width),height:px(cropped.height),overflow:"hidden",position:'relative'});cropDiv.className="crop";}
if(windowsInternetExplorer&&effectiveBrowserVersion<7&&img.src.indexOf(transparentGifURL())!=-1)
{var originalImage=new Image();originalImage.src=img.originalSrc;if(originalImage.complete)
{croppingDivForImage_helper(originalImage);}
else
{originalImage.onload=croppingDivForImage_helper.bind(null,originalImage);}}
else
{croppingDivForImage_helper(null);}}
return cropDiv;},applyEffects:function(div)
{if(this.sfrShadow||this.sfrReflection||this.sfrStroke)
{if((div.offsetWidth===undefined)||(div.offsetHeight===undefined)||(div.offsetWidth===0)||(div.offsetHeight===0))
{setTimeout(JSONFeedRendererWidget.prototype.applyEffects.bind(this,div),0)
return;}
if(this.sfrStroke&&(div.strokeApplied==false))
{this.sfrStroke.applyToElement(div);div.strokeApplied=true;}
if(this.sfrReflection&&(div.reflectionApplied==false))
{this.sfrReflection.applyToElement(div);div.reflectionApplied=true;}
if(this.sfrShadow&&(!this.disableShadows)&&(div.shadowApplied==false))
{this.sfrShadow.applyToElement(div);div.shadowApplied=true;}
if(this.runningInApp&&(window.webKitVersion<=419)&&this.preferences.setNeedsDisplay)
{this.preferences.setNeedsDisplay();}}
if(windowsInternetExplorer)
{var cropDivs=div.select(".crop");var cropDiv=cropDivs[cropDivs.length-1];if(cropDiv)
{cropDiv.onclick=function()
{var anchorNode=div.parentNode;var targetHref=locationHRef();while(anchorNode&&(anchorNode.tagName!="A"))
{anchorNode=anchorNode.parentNode}
if(anchorNode)
{targetHref=anchorNode.href;}
window.location=targetHref;};cropDiv.onmouseover=function()
{this.style.cursor='pointer';}}}},summaryExcerpt:function(descriptionHTML,maxSummaryLength)
{var div=document.createElement("div");div.innerHTML=descriptionHTML;if(maxSummaryLength>0)
{var model=new HTMLTextModel(div);model.truncateAroundPosition(maxSummaryLength,"...");}
else if(maxSummaryLength===0)
{div.innerHTML="";}
return div.innerHTML;}});var PrefMarkupWidget=Class.create(Widget,{initialize:function($super,instanceID,widgetPath,sharedPath,sitePath,preferences,runningInApp)
{if(instanceID)
{$super(instanceID,widgetPath,sharedPath,sitePath,preferences,runningInApp);}},onload:function()
{if(!this.runningInApp)
{this.setUpSubDocumentOnLoad();}},setUpSubDocumentOnLoad:function()
{var self=this;var oIFrame=this.getElementById("frame");if(oIFrame)
{setTimeout(function(){self.loadedSubDocument()},250);}},loadedSubDocument:function()
{var oIFrame=this.getElementById("frame");var oSubDocument=oIFrame.contentWindow||oIFrame.contentDocument;if(oSubDocument.document)
{oSubDocument=oSubDocument.document;}
if(oSubDocument.body)
{this.fixTargetOnElements(oSubDocument,"a");this.fixTargetOnElements(oSubDocument,"form");}
else
{var self=this;setTimeout(function(){self.loadedSubDocument()},250);}},fixTargetOnElements:function(doc,tagName)
{var elements=doc.getElementsByTagName(tagName);for(var i=0;i<elements.length;i++)
{var target=elements[i].target;if(target===undefined||target=="")
elements[i].target="_top";}}});function IWScrollbar(scrollbar)
{}
IWScrollbar.prototype._init=function()
{var style=null;var element=null;this._track=$(document.createElement("div"));style=this._track.style;style.height="100%";style.width="100%";this.scrollbar.appendChild(this._track);element=$(document.createElement("div"));element.style.position="absolute";this._setObjectStart(element,0);this._track.appendChild(element);element=$(document.createElement("div"));element.style.position="absolute";this._track.appendChild(element);element=$(document.createElement("div"));element.style.position="absolute";windowsInternetExplorer||this._setObjectEnd(element,0);this._track.appendChild(element);this._thumb=$(document.createElement("div"));style=this._thumb.style;style.position="absolute";this._setObjectSize(this._thumb,this.minThumbSize);this._track.appendChild(this._thumb);element=$(document.createElement("div"));element.style.position="absolute";this._setObjectStart(element,0);this._thumb.appendChild(element);element=$(document.createElement("div"));element.style.position="absolute";this._thumb.appendChild(element);element=$(document.createElement("div"));element.style.position="absolute";windowsInternetExplorer||this._setObjectEnd(element,0);this._thumb.appendChild(element);this.setSize(this.size);this.setTrackStart(this.trackStartPath,this.trackStartLength);this.setTrackMiddle(this.trackMiddlePath);this.setTrackEnd(this.trackEndPath,this.trackEndLength);this.setThumbStart(this.thumbStartPath,this.thumbStartLength);this.setThumbMiddle(this.thumbMiddlePath);this.setThumbEnd(this.thumbEndPath,this.thumbEndLength);this._thumb.style.display="none";Event.observe(this._track,"mousedown",this._mousedownTrackHandler,false);Event.observe(this._thumb,"mousedown",this._mousedownThumbHandler,false);}
IWScrollbar.prototype.remove=function()
{this.scrollbar.removeChild(this._track);}
IWScrollbar.prototype._captureEvent=function(event)
{event.stopPropagation();event.preventDefault();}
IWScrollbar.prototype._mousedownThumb=function(event)
{Event.observe(document,"mousemove",this._mousemoveThumbHandler,true);Event.observe(document,"mouseup",this._mouseupThumbHandler,true);Event.observe(document,"mouseover",this._captureEventHandler,true);Event.observe(document,"mouseout",this._captureEventHandler,true);this._thumbStart_temp=this._getMousePosition(event);this._scroll_thumbStartPos=this._getThumbStartPos();event.stopPropagation();event.preventDefault();}
IWScrollbar.prototype._mousemoveThumb=function(event)
{var delta=this._getMousePosition(event)-this._thumbStart_temp;var new_pos=this._scroll_thumbStartPos+delta;this.scrollTo(this._contentPositionForThumbPosition(new_pos));event.stopPropagation();event.preventDefault();}
IWScrollbar.prototype._mouseupThumb=function(event)
{Event.stopObserving(document,"mousemove",this._mousemoveThumbHandler,true);Event.stopObserving(document,"mouseup",this._mouseupThumbHandler,true);Event.stopObserving(document,"mouseover",this._captureEventHandler,true);Event.stopObserving(document,"mouseout",this._captureEventHandler,true);delete this._thumbStart_temp;delete this._scroll_thumbStartPos;event.stopPropagation();event.preventDefault();}
IWScrollbar.prototype._mousedownTrack=function(event)
{this._track_mouse_temp=this._getMousePosition(event)-this._trackOffset;if(event.altKey)
{this.scrollTo(this._contentPositionForThumbPosition(this._track_mouse_temp-(this._thumbLength/2)));delete this._track_mouse_temp;}
else
{this._track_scrolling=true;Event.observe(this._track,"mousemove",this._mousemoveTrackHandler,true);Event.observe(this._track,"mouseover",this._mouseoverTrackHandler,true);Event.observe(this._track,"mouseout",this._mouseoutTrackHandler,true);Event.observe(document,"mouseup",this._mouseupTrackHandler,true);this._trackScrollOnePage(this);this._track_timer=setInterval(this._trackScrollDelay,500,this);}
event.stopPropagation();event.preventDefault();}
IWScrollbar.prototype._trackScrollDelay=function(self)
{if(!self._track_scrolling)return;clearInterval(self._track_timer);self._trackScrollOnePage(self);self._track_timer=setInterval(self._trackScrollOnePage,150,self);}
IWScrollbar.prototype._mousemoveTrack=function(event)
{this._track_mouse_temp=this._getMousePosition(event)-this._trackOffset;event.stopPropagation();event.preventDefault();}
IWScrollbar.prototype._mouseoverTrack=function(event)
{this._track_mouse_temp=this._getMousePosition(event)-this._trackOffset;this._track_scrolling=true;event.stopPropagation();event.preventDefault();}
IWScrollbar.prototype._mouseoutTrack=function(event)
{this._track_scrolling=false;event.stopPropagation();event.preventDefault();}
IWScrollbar.prototype._mouseupTrack=function(event)
{clearInterval(this._track_timer);Event.stopObserving(this._track,"mousemove",this._mousemoveTrackHandler,true);Event.stopObserving(this._track,"mouseover",this._mouseoverTrackHandler,true);Event.stopObserving(this._track,"mouseout",this._mouseoutTrackHandler,true);Event.stopObserving(document,"mouseup",this._mouseupTrackHandler,true);delete this._track_mouse_temp;delete this._track_scrolling;delete this._track_timer;event.stopPropagation();event.preventDefault();}
IWScrollbar.prototype._trackScrollOnePage=function(self)
{if(!self._track_scrolling)return;var deltaScroll=Math.round(self._trackLength*self._getViewToContentRatio());if(self._track_mouse_temp<self._thumbStart)
self.scrollByThumbDelta(-deltaScroll);else if(self._track_mouse_temp>(self._thumbStart+self._thumbLength))
self.scrollByThumbDelta(deltaScroll);}
IWScrollbar.prototype.setScrollArea=function(scrollarea)
{if(this.scrollarea)
{Event.stopObserving(this.scrollbar,"mousewheel",this.scrollarea._mousewheelScrollHandler,true);Event.stopObserving(this.scrollbar,"DOMMouseScroll",this.scrollarea._mousewheelScrollHandler,true);}
this.scrollarea=scrollarea;Event.observe(this.scrollbar,"mousewheel",this.scrollarea._mousewheelScrollHandler,true);Event.observe(this.scrollbar,"DOMMouseScroll",this.scrollarea._mousewheelScrollHandler,true);}
IWScrollbar.prototype.refresh=function()
{this._trackOffset=this._computeTrackOffset();this._trackLength=this._computeTrackLength();var ratio=this._getViewToContentRatio();if(ratio>=1.0||!this._canScroll())
{if(this.autohide)
{this.hide();}
this._thumb.style.display="none";this.scrollbar.style.appleDashboardRegion="none";}
else
{this._thumbLength=Math.max(Math.round(this._trackLength*ratio),this.minThumbSize);this._numScrollablePixels=this._trackLength-this._thumbLength-(2*this.padding);this._setObjectLength(this._thumb,this._thumbLength);if(windowsInternetExplorer)
{this._setObjectStart(this._thumb.down().next(),this.thumbStartLength);this._setObjectLength(this._thumb.down().next(),this._thumbLength
-this.thumbStartLength-this.thumbEndLength);this._setObjectStart(this._thumb.down().next(1),this._thumbLength-this.thumbEndLength);this._setObjectLength(this._thumb.down().next(1),this.thumbEndLength);if(!this.fixedUpIEPNGBGs)
{fixupIEPNGBGsInTree(this._track);Event.stopObserving(this._track,"mousedown",this._mousedownTrackHandler);Event.stopObserving(this._thumb,"mousedown",this._mousedownThumbHandler);Event.observe(this._track,"mousedown",this._mousedownTrackHandler);Event.observe(this._thumb,"mousedown",this._mousedownThumbHandler);this.fixedUpIEPNGBGs=true;}}
this._thumb.style.display="block";this.scrollbar.style.appleDashboardRegion="dashboard-region(control rectangle)";this.show();}
this.verticalHasScrolled();this.horizontalHasScrolled();}
IWScrollbar.prototype.setAutohide=function(autohide)
{this.autohide=autohide;if(this._getViewToContentRatio()>=1.0&&autohide)
{this.hide();}
else
{this.show();}}
IWScrollbar.prototype.hide=function()
{this._track.style.display="none";this.hidden=true;}
IWScrollbar.prototype.show=function()
{this._track.style.display="block";this.hidden=false;}
IWScrollbar.prototype.setSize=function(size)
{this.size=size;this._setObjectSize(this.scrollbar,size);this._setObjectSize(this._track.down().next(),size);this._setObjectSize(this._thumb.down().next(),size);}
IWScrollbar.prototype.setTrackStart=function(imgpath,length)
{this.trackStartPath=imgpath;this.trackStartLength=length;var element=this._track.down();element.style.background="url("+imgpath+") no-repeat top left";this._setObjectLength(element,length);this._setObjectSize(element,this.size);this._setObjectStart(this._track.down().next(),length);}
IWScrollbar.prototype.setTrackMiddle=function(imgpath)
{this.trackMiddlePath=imgpath;this._track.down().next().style.background="url("+imgpath+") "+this._repeatType+" top left";}
IWScrollbar.prototype.setTrackEnd=function(imgpath,length)
{this.trackEndPath=imgpath;this.trackEndLength=length;var element=this._track.down().next(1);element.style.background="url("+imgpath+") no-repeat top left";this._setObjectLength(element,length);this._setObjectSize(element,this.size);windowsInternetExplorer||this._setObjectEnd(this._track.down().next(),length);}
IWScrollbar.prototype.setThumbStart=function(imgpath,length)
{this.thumbStartPath=imgpath;this.thumbStartLength=length;var element=this._thumb.down();element.style.background="url("+imgpath+") no-repeat top left";this._setObjectLength(element,length);this._setObjectSize(element,this.size);this._setObjectStart(this._thumb.down().next(),length);}
IWScrollbar.prototype.setThumbMiddle=function(imgpath)
{this.thumbMiddlePath=imgpath;this._thumb.down().next().style.background="url("+imgpath+") "+this._repeatType+" top left";}
IWScrollbar.prototype.setThumbEnd=function(imgpath,length)
{this.thumbEndPath=imgpath;this.thumbEndLength=length;var element=this._thumb.down().next(1);element.style.background="url("+imgpath+") no-repeat top left";this._setObjectLength(element,length);this._setObjectSize(element,this.size);windowsInternetExplorer||this._setObjectEnd(this._thumb.down().next(),length);}
IWScrollbar.prototype._contentPositionForThumbPosition=function(thumb_pos)
{if(this._getViewToContentRatio()>=1.0)
{return 0;}
else
{return(thumb_pos-this.padding)*((this._getContentLength()-this._getViewLength())/this._numScrollablePixels);}}
IWScrollbar.prototype._thumbPositionForContentPosition=function(page_pos)
{if(this._getViewToContentRatio()>=1.0)
{return this.padding;}
else
{var result=this.padding+(page_pos/((this._getContentLength()-this._getViewLength())/this._numScrollablePixels));if(isNaN(result))
result=0;return result;}}
IWScrollbar.prototype.scrollByThumbDelta=function(deltaScroll)
{if(deltaScroll==0)
return;this.scrollTo(this._contentPositionForThumbPosition(this._thumbStart+deltaScroll));}
function IWVerticalScrollbar(scrollbar)
{this.scrollarea=null;this.scrollbar=$(scrollbar);this.minThumbSize=28;this.padding=-1;this.autohide=true;this.hidden=true;this.size=19;this.trackStartPath=transparentGifURL();this.trackStartLength=18;this.trackMiddlePath=transparentGifURL();this.trackEndPath=transparentGifURL();this.trackEndLength=18;this.thumbStartPath=transparentGifURL();this.thumbStartLength=9;this.thumbMiddlePath=transparentGifURL();this.thumbEndPath=transparentGifURL();this.thumbEndLength=9;this._track=null;this._thumb=null;this._trackOffset=0;this._trackLength=0;this._numScrollablePixels=0;this._thumbLength=0;this._repeatType="repeat-y";this._thumbStart=this.padding;var _self=this;this._captureEventHandler=function(event){_self._captureEvent(event);};this._mousedownThumbHandler=function(event){_self._mousedownThumb(event);};this._mousemoveThumbHandler=function(event){_self._mousemoveThumb(event);};this._mouseupThumbHandler=function(event){_self._mouseupThumb(event);};this._mousedownTrackHandler=function(event){_self._mousedownTrack(event);};this._mousemoveTrackHandler=function(event){_self._mousemoveTrack(event);};this._mouseoverTrackHandler=function(event){_self._mouseoverTrack(event);};this._mouseoutTrackHandler=function(event){_self._mouseoutTrack(event);};this._mouseupTrackHandler=function(event){_self._mouseupTrack(event);};this._init();}
IWVerticalScrollbar.prototype=new IWScrollbar(null);IWVerticalScrollbar.prototype.scrollTo=function(pos)
{this.scrollarea.verticalScrollTo(pos);}
IWVerticalScrollbar.prototype._setObjectSize=function(object,size)
{object.style.width=size+"px";}
IWVerticalScrollbar.prototype._setObjectLength=function(object,length)
{object.style.height=length+"px";}
IWVerticalScrollbar.prototype._setObjectStart=function(object,start)
{object.style.top=start+"px";}
IWVerticalScrollbar.prototype._setObjectEnd=function(object,end)
{object.style.bottom=end+"px";}
IWVerticalScrollbar.prototype._getMousePosition=function(event)
{if(event!=undefined)
return Event.pointerY(event);else
return 0;}
IWVerticalScrollbar.prototype._getThumbStartPos=function()
{return this._thumb.offsetTop;}
IWVerticalScrollbar.prototype._computeTrackOffset=function()
{var obj=this.scrollbar;var curtop=0;while(obj.offsetParent)
{curtop+=obj.offsetTop;obj=obj.offsetParent;}
return curtop;}
IWVerticalScrollbar.prototype._computeTrackLength=function()
{return this.scrollbar.offsetHeight;}
IWVerticalScrollbar.prototype._getViewToContentRatio=function()
{return this.scrollarea.viewToContentHeightRatio;}
IWVerticalScrollbar.prototype._getContentLength=function()
{return this.scrollarea.content.scrollHeight;}
IWVerticalScrollbar.prototype._getViewLength=function()
{return this.scrollarea.viewHeight;}
IWVerticalScrollbar.prototype._canScroll=function()
{return this.scrollarea.scrollsVertically;}
IWVerticalScrollbar.prototype.verticalHasScrolled=function()
{var new_thumb_pos=this._thumbPositionForContentPosition(this.scrollarea.content.scrollTop);this._thumbStart=new_thumb_pos;this._thumb.style.top=new_thumb_pos+"px";}
IWVerticalScrollbar.prototype.horizontalHasScrolled=function()
{}
function IWHorizontalScrollbar(scrollbar)
{this.scrollarea=null;this.scrollbar=$(scrollbar);this.minThumbSize=28;this.padding=-1;this.autohide=true;this.hidden=true;this.size=19;this.trackStartPath=transparentGifURL();this.trackStartLength=18;this.trackMiddlePath=transparentGifURL();this.trackEndPath=transparentGifURL();this.trackEndLength=18;this.thumbStartPath=transparentGifURL();this.thumbStartLength=9;this.thumbMiddlePath=transparentGifURL();this.thumbEndPath=transparentGifURL();this.thumbEndLength=9;this._track=null;this._thumb=null;this._trackOffset=0;this._trackLength=0;this._numScrollablePixels=0;this._thumbLength=0;this._repeatType="repeat-x";this._thumbStart=this.padding;var _self=this;this._captureEventHandler=function(event){_self._captureEvent(event);};this._mousedownThumbHandler=function(event){_self._mousedownThumb(event);};this._mousemoveThumbHandler=function(event){_self._mousemoveThumb(event);};this._mouseupThumbHandler=function(event){_self._mouseupThumb(event);};this._mousedownTrackHandler=function(event){_self._mousedownTrack(event);};this._mousemoveTrackHandler=function(event){_self._mousemoveTrack(event);};this._mouseoverTrackHandler=function(event){_self._mouseoverTrack(event);};this._mouseoutTrackHandler=function(event){_self._mouseoutTrack(event);};this._mouseupTrackHandler=function(event){_self._mouseupTrack(event);};this._init();}
IWHorizontalScrollbar.prototype=new IWScrollbar(null);IWHorizontalScrollbar.prototype.scrollTo=function(pos)
{this.scrollarea.horizontalScrollTo(pos);}
IWHorizontalScrollbar.prototype._setObjectSize=function(object,size)
{object.style.height=size+"px";}
IWHorizontalScrollbar.prototype._setObjectLength=function(object,length)
{object.style.width=length+"px";}
IWHorizontalScrollbar.prototype._setObjectStart=function(object,start)
{object.style.left=start+"px";}
IWHorizontalScrollbar.prototype._setObjectEnd=function(object,end)
{object.style.right=end+"px";}
IWHorizontalScrollbar.prototype._getMousePosition=function(event)
{if(event!=undefined)
return Event.pointerX(event);else
return 0;}
IWHorizontalScrollbar.prototype._getThumbStartPos=function()
{return this._thumb.offsetLeft;}
IWHorizontalScrollbar.prototype._computeTrackOffset=function()
{var obj=this.scrollbar;var curtop=0;while(obj.offsetParent)
{curtop+=obj.offsetLeft;obj=obj.offsetParent;}
return curtop;}
IWHorizontalScrollbar.prototype._computeTrackLength=function()
{return this.scrollbar.offsetWidth;}
IWHorizontalScrollbar.prototype._getViewToContentRatio=function()
{return this.scrollarea.viewToContentWidthRatio;}
IWHorizontalScrollbar.prototype._getContentLength=function()
{return this.scrollarea.content.scrollWidth;}
IWHorizontalScrollbar.prototype._getViewLength=function()
{return this.scrollarea.viewWidth;}
IWHorizontalScrollbar.prototype._canScroll=function()
{return this.scrollarea.scrollsHorizontally;}
IWHorizontalScrollbar.prototype.verticalHasScrolled=function()
{}
IWHorizontalScrollbar.prototype.horizontalHasScrolled=function()
{var new_thumb_pos=this._thumbPositionForContentPosition(this.scrollarea.content.scrollLeft);this._thumbStart=new_thumb_pos;this._thumb.style.left=new_thumb_pos+"px";}
function IWScrollArea(content)
{this.content=$(content);this.scrollsVertically=true;this.scrollsHorizontally=true;this.singlepressScrollPixels=10;this.viewHeight=0;this.viewToContentHeightRatio=1.0;this.viewWidth=0;this.viewToContentWidthRatio=1.0;this._scrollbars=new Array();var _self=this;this._refreshHandler=function(){_self.refresh();};this._keyPressedHandler=function(){_self.keyPressed(event);};this._mousewheelScrollHandler=function(event){_self.mousewheelScroll(event);};this.content.style.overflow="hidden";this.content.scrollTop=0;this.content.scrollLeft=0;Event.observe(this.content,"mousewheel",this._mousewheelScrollHandler,true);Event.observe(this.content,"DOMMouseScroll",this._mousewheelScrollHandler,true);this.refresh();var c=arguments.length;for(var i=1;i<c;++i)
{this.addScrollbar(arguments[i]);}}
IWScrollArea.prototype.addScrollbar=function(scrollbar)
{scrollbar.setScrollArea(this);this._scrollbars.push(scrollbar);scrollbar.refresh();}
IWScrollArea.prototype.removeScrollbar=function(scrollbar)
{var scrollbars=this._scrollbars;var c=scrollbars.length;for(var i=0;i<c;++i)
{if(scrollbars[i]==scrollbar)
{scrollbars.splice(i,1);break;}}}
IWScrollArea.prototype.remove=function()
{Event.stopObserving(this.content,"mousewheel",this._mousewheelScrollHandler,true);Event.stopObserving(this.content,"DOMMouseScroll",this._mousewheelScrollHandler,true);var scrollbars=this._scrollbars;var c=scrollbars.length;for(var i=0;i<c;++i)
{scrollbars[i].setScrollArea(null);}}
IWScrollArea.prototype.refresh=function()
{this.viewHeight=this.content.offsetHeight;this.viewWidth=this.content.offsetWidth;if(this.content.scrollHeight>this.viewHeight)
{this.viewToContentHeightRatio=this.viewHeight/this.content.scrollHeight;this.verticalScrollTo(this.content.scrollTop);}
else
{this.viewToContentHeightRatio=1.0;this.verticalScrollTo(0);}
if(this.content.scrollWidth>this.viewWidth)
{this.viewToContentWidthRatio=this.viewWidth/this.content.scrollWidth;this.horizontalScrollTo(this.content.scrollLeft);}
else
{this.viewToContentWidthRatio=1.0;this.horizontalScrollTo(0);}
var scrollbars=this._scrollbars;var c=scrollbars.length;for(var i=0;i<c;++i)
{scrollbars[i].refresh();}}
IWScrollArea.prototype.focus=function()
{Event.observe(document,"keypress",this._keyPressedHandler,true);}
IWScrollArea.prototype.blur=function()
{Event.stopObserving(document,"keypress",this._keyPressedHandler,true);}
IWScrollArea.prototype.reveal=function(element)
{var offsetY=0;var obj=element;do
{offsetY+=obj.offsetTop;obj=obj.offsetParent;}while(obj&&obj!=this.content);var offsetX=0;obj=element;do
{offsetX+=obj.offsetLeft;obj=obj.offsetParent;}while(obj&&obj!=this.content);this.verticalScrollTo(offsetY);this.horizontalScrollTo(offsetX);}
IWScrollArea.prototype.verticalScrollTo=function(new_content_top)
{if(!this.scrollsVertically)
return;var bottom=this.content.scrollHeight-this.viewHeight;if(new_content_top<0)
{new_content_top=0;}
else if(new_content_top>bottom)
{new_content_top=bottom;}
this.content.scrollTop=new_content_top;var scrollbars=this._scrollbars;var c=scrollbars.length;for(var i=0;i<c;++i)
{scrollbars[i].verticalHasScrolled();}}
IWScrollArea.prototype.horizontalScrollTo=function(new_content_left)
{if(!this.scrollsHorizontally)
return;var right=this.content_width-this.viewWidth;if(new_content_left<0)
{new_content_left=0;}
else if(new_content_left>right)
{new_content_left=right;}
this.content.scrollLeft=new_content_left;var scrollbars=this._scrollbars;var c=scrollbars.length;for(var i=0;i<c;++i)
{scrollbars[i].horizontalHasScrolled();}}
IWScrollArea.prototype.keyPressed=function(event)
{var handled=true;if(event.altKey)
return;if(event.shiftKey)
return;switch(event.keyIdentifier)
{case"Home":this.verticalScrollTo(0);break;case"End":this.verticalScrollTo(this.content.scrollHeight-this.viewHeight);break;case"Up":this.verticalScrollTo(this.content.scrollTop-this.singlepressScrollPixels);break;case"Down":this.verticalScrollTo(this.content.scrollTop+this.singlepressScrollPixels);break;case"PageUp":this.verticalScrollTo(this.content.scrollTop-this.viewHeight);break;case"PageDown":this.verticalScrollTo(this.content.scrollTop+this.viewHeight);break;case"Left":this.horizontalScrollTo(this.content.scrollLeft-this.singlepressScrollPixels);break;case"Right":this.horizontalScrollTo(this.content.scrollLeft+this.singlepressScrollPixels);break;default:handled=false;}
if(handled)
{event.stopPropagation();event.preventDefault();}}
IWScrollArea.prototype.mousewheelScroll=function(event)
{var deltaScroll=event.wheelDelta?(event.wheelDelta/120*this.singlepressScrollPixels):(event.detail/-2*this.singlepressScrollPixels);this.verticalScrollTo(this.content.scrollTop-deltaScroll);event.stopPropagation();event.preventDefault();}
var View=Class.create({initialize:function(widget,parentDiv)
{this.m_widget=widget;this.m_parentDiv=parentDiv;this.m_divInstanceId=this.m_divId;this.hide();},ensureDiv:function()
{var div=this.m_widget.div(this.m_divInstanceId);if(!div)
{div=new Element('div',{'id':this.m_widget.getInstanceId(this.m_divInstanceId)});div.addClassName(this.m_divClass);this.m_parentDiv.appendChild(div);}
return $(div);},hide:function()
{this.ensureDiv().hide();},show:function()
{this.ensureDiv().show();},render:function()
{},resize:function()
{}});var StatusView=Class.create(View,{initialize:function($super,widget,parentDiv)
{$super(widget,parentDiv);this.render();this.hide();},render:function()
{var markup="<table class='StatusMessageTable'><tr><td>";if(this.badgeImage)
{markup+=imgMarkup(this.m_widget.widgetPath+"/"+this.badgeImage,"","id='"+this.p_badgeImgId()+"'","");}
markup+="</td></tr></table>";if(this.upperRightBadgeWidth&&this.upperRightBadgeHeight)
{var badgeURL=(this.upperRightBadge)?(this.m_widget.widgetPath+"/"+this.upperRightBadge):transparentGifURL();markup+=imgMarkup(badgeURL,"","class='StatusUpperRightBadge' width='"+this.upperRightBadgeWidth+"' height='"+this.upperRightBadgeHeight+"' ","");}
var overlayPath=this.m_widget.sharedPath.stringByAppendingPathComponent("Translucent-Overlay.png");markup+=imgMarkup(overlayPath,"position: absolute; top: 0; left: 0;","id='"+this.p_overlayImgId()+"' width='700' height='286' ","");if(this.statusMessageKey)
{markup+="<div id='"+this.p_statusMessageBlockId()+"' class='StatusMessageBlock' ><span>"+
this.m_widget.localizedString(this.statusMessageKey)+"</span></div>";}
this.ensureDiv().update(markup);this.resize();},resize:function()
{var widgetWidth=(this.runningInApp)?window.innerWidth:this.m_widget.div().offsetWidth;var widgetHeight=(this.runningInApp)?window.innerHeight:this.m_widget.div().offsetHeight;if(this.badgeImage)
{var badgeImageEl=$(this.p_badgeImgId());var badgeSize=new IWSize(this.badgeImageWidth,this.badgeImageHeight);if((badgeSize.width>widgetWidth)||(badgeSize.height>widgetHeight))
{var widgetSize=new IWSize(widgetWidth,widgetHeight);badgeSize=badgeSize.scaleToFit(widgetSize);}
badgeImageEl.width=badgeSize.width;badgeImageEl.height=badgeSize.height;}
var overlayNativeWidth=700;var overlayNativeHeight=286;var overlayWidth=Math.max(widgetWidth,overlayNativeWidth);var overlayHeight=overlayNativeHeight;var overlayTop=Math.min(((widgetHeight/2)-overlayNativeHeight),0);var overlayLeft=Math.min(((widgetWidth/2)-(overlayNativeWidth/2)),0);var overlayImage=$(this.p_overlayImgId());overlayImage.width=overlayWidth;overlayImage.height=overlayHeight;overlayImage.setStyle({left:px(overlayLeft),top:px(overlayTop)});var statusMessageBlock=$(this.p_statusMessageBlockId());if(statusMessageBlock)
{var leftValue=px(Math.max(((widgetWidth-statusMessageBlock.offsetWidth)/2),0));var positionStyles={left:leftValue};if(this.statusMessageVerticallyCentered)
{var topValue=px(Math.max(((widgetHeight-statusMessageBlock.offsetHeight)/2),0));positionStyles.top=topValue;}
statusMessageBlock.setStyle(positionStyles);}
if(this.footerView)
{this.footerView.resize();}},doneFadingIn:function()
{this.m_widget.setPreferenceForKey(true,"x-viewDoneFadingIn",false);},p_badgeImgId:function()
{return this.m_widget.getInstanceId(this.m_divId+"-badge");},p_overlayImgId:function()
{return this.m_widget.getInstanceId(this.m_divId+"-overlay");},p_statusMessageBlockId:function()
{return this.m_widget.getInstanceId(this.m_divId+"-messageBlock");}});

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_http_thorn_ws_reduceron_Reduceron_Practical_Reduceron_html/bonnet/thorn.ws/reduceron/Reduceron/Scripts/iWebImage.js version [4395f0fba3].







































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
//
// iWeb - iWebImage.js
// Copyright 2007-2008 Apple Inc.
// All rights reserved.
//

var IWAllImages={};var IWAllImageObjects={};function IWCreateImage(url)
{return IWAllImages[url]||new IWImage(url);}
var IWNamedImages={};function IWImageNamed(name)
{var url=IWNamedImages[name];return url?IWCreateImage(url):null}
function IWRegisterNamedImage(name,url)
{IWNamedImages[name]=url;}
var IWImageEnableUnload=isiPhone;var IWImage=Class.create({initialize:function(url)
{if(IWAllImages.hasOwnProperty(url))
{iWLog("warning -- use IWCreateImage rather than new IWImage and you'll get better performance");}
this.mPreventUnloading=0;this.mLoading=false;this.mLoaded=false;this.mURL=url;this.mCallbacks=[];IWAllImages[url]=this;},sourceURL:function()
{return this.mURL;},loaded:function()
{return this.mLoaded;},load:function(callback,delayCallbackIfLoaded)
{if(this.mLoaded&&(callback!=null))
{delayCallbackIfLoaded?setTimeout(callback,0):callback();}
else
{if(callback!=null)
{this.mCallbacks.push(callback);}
if(this.mLoading==false)
{this.mLoading=true;var img=new Image();IWAllImageObjects[this.sourceURL()]=img;img.onload=this.p_onload.bind(this);img.src=this.mURL;}}},unload:function(evenIfNotEnabled)
{if((evenIfNotEnabled||IWImageEnableUnload)&&this.mLoaded)
{if(this.mPreventUnloading<=0)
{this.mLoaded=false;this.mLoading=false;IWAllImageObjects[this.sourceURL()]=null;}
else
{this.mPreventedUnload=true;}}},preventUnloading:function()
{if(this.mPreventUnloading==0)
{this.mPreventedUnload=false;}
++this.mPreventUnloading;},allowUnloading:function()
{--this.mPreventUnloading;if(this.mPreventUnloading<=0&&this.mPreventedUnload)
{this.unload();}},naturalSize:function()
{(function(){return this.mNaturalSize!==undefined}).bind(this).assert();return this.mNaturalSize;},imgObject:function()
{return IWAllImageObjects[this.sourceURL()];},p_onload:function()
{this.preventUnloading();this.mLoaded=true;if(this.mNaturalSize===undefined)
{var imgObject=this.imgObject();(function(){return imgObject!==undefined}).assert();this.mNaturalSize=new IWSize(imgObject.width,imgObject.height);}
for(var i=0;i<this.mCallbacks.length;++i)
{this.mCallbacks[i]();}
this.mCallbacks=[];this.allowUnloading();},toString:function()
{return"IWImage("+this.mNaturalSize+", "+this.mURL+")";}});function IWCreateLoadingArea()
{if(IWSharedLoadingAreaManager==null)
{IWSharedLoadingAreaManager=new IWLoadingAreaManager();}
return IWSharedLoadingAreaManager.createLoadingArea();}
var IWLoadingAreaManager=Class.create({initialize:function()
{var div=$(document.createElement("div"));div.setStyle({visibility:"hidden",position:"absolute",width:0,height:0,overflow:"hidden"});document.body.appendChild(div);this.mCurrentLoadingArea=div;},createLoadingArea:function()
{var loadingArea=document.createElement('div');this.mCurrentLoadingArea.appendChild(loadingArea);return loadingArea;}});var IWSharedLoadingAreaManager=null;var IWSharedEffectRegistry=null;var allStyleSheetsLoaded=false;var timeStyleSheetsAppearedInDOM=null;function IWCreateEffectRegistry()
{if(IWSharedEffectRegistry==null)
{IWSharedEffectRegistry=new IWEffectRegistry();}
return IWSharedEffectRegistry;}
var IWEffectRegistry=Class.create({initialize:function()
{this.mEffects=null;},registerEffects:function(effects)
{this.mEffects=effects;},applyEffects:function()
{var effectQueue=[];effectQueue=effectQueue.concat(this.p_queueForEffectType("crop"));effectQueue=effectQueue.concat(this.p_queueForEffectType("stroke"));effectQueue=effectQueue.concat(this.p_queueForEffectType("reflection"));effectQueue=effectQueue.concat(this.p_queueForEffectType("shadow"));this.p_applyEffectsFromQueue(effectQueue);},p_queueForEffectType:function(effectType)
{var effectQueue=[];var i=0;var effectClass=effectType+"_"+i++;while(effect=this.mEffects[effectClass])
{effectQueue=effectQueue.concat(this.p_queueForEffectClass(effect,effectClass));effectClass=effectType+"_"+i++;}
return effectQueue;},p_queueForEffectClass:function(effect,effectClass,elementList)
{var effectQueue=[];var elements=elementList||$$("."+effectClass);while(elements&&elements.length>0)
{var element=elements.shift();var children=element.select("."+effectClass);if(children.length>0)
{elements=elements.minusArray(children);effectQueue=effectQueue.concat(this.p_queueForEffectClass(effect,effectClass,children));}
effectQueue.push({element:element,effect:effect});}
return effectQueue;},p_allStyleSheetsLoaded:function()
{if(isCamino||isFirefox)
{if(timeStyleSheetsAppearedInDOM!=null)
{duration=(new Date().getTime())-timeStyleSheetsAppearedInDOM;if(duration>100)
{allStyleSheetsLoaded=true;timeStyleSheetsAppearedInDOM=null;}}
else if(!allStyleSheetsLoaded)
{for(var i=0,sheetCount=document.styleSheets.length;i<sheetCount;i++)
{var styleSheet=document.styleSheets[i];if(styleSheet.href&&styleSheet.href.indexOf("Moz.css")!=-1)
{timeStyleSheetsAppearedInDOM=new Date().getTime();}}}}
else
{allStyleSheetsLoaded=true;}
return allStyleSheetsLoaded;},p_applyEffectsFromQueue:function(queue)
{var startTime=new Date().getTime();var duration=0;var readyToApplyEffects=this.p_allStyleSheetsLoaded();while(queue.length>0&&duration<100&&readyToApplyEffects)
{var queueEntry=queue.shift();if(queueEntry&&queueEntry.effect&&queueEntry.element)
{queueEntry.effect.applyToElement(queueEntry.element);}
duration=(new Date().getTime())-startTime;}
if(queue.length>0)
{setTimeout(this.p_applyEffectsFromQueue.bind(this,queue),0);}
else
{performPostEffectsFixups();}}});function IWChildOffset(child,parent,positionedOnly)
{var l=0;var t=0;if(parent)
{var current=child;while(current&&current!=parent)
{if(!positionedOnly||(current.style.position=="absolute")||(current.style.position=="relative"))
{l+=current.offsetLeft;t+=current.offsetTop;}
current=current.parentNode;}}
return new IWPoint(l,t);}
function IWImageExtents(ancestor,images,left,top,right,bottom)
{var unionedBounds=new IWRect(left,top,right-left,bottom-top);for(var e=0;e<images.length;++e)
{var imageClippedBounds=new IWRect(images[e].offsetLeft,images[e].offsetTop,images[e].offsetWidth,images[e].offsetHeight);if(ancestor)
{var current=images[e].parentNode;while(current&&current!=ancestor)
{if((current.style.position=="absolute")||(current.style.position=="relative"))
{imageClippedBounds.origin.x+=current.offsetLeft||0;imageClippedBounds.origin.y+=current.offsetTop||0;}
var testForHidden=function(str)
{return str=='hidden';};var clipX=[current.style.overflow,current.style.overflowX].any(testForHidden);var clipY=[current.style.overflow,current.style.overflowY].any(testForHidden);if(clipX||clipY)
{var currentRect=new IWRect(clipX?current.offsetLeft:imageClippedBounds.origin.x,clipY?current.offsetTop:imageClippedBounds.origin.y,clipX?current.offsetWidth:imageClippedBounds.size.width,clipY?current.offsetHeight:imageClippedBounds.size.height);imageClippedBounds=imageClippedBounds.intersection(currentRect);}
current=current.parentNode;}}
if((imageClippedBounds.size.width>0)&&(imageClippedBounds.size.height>0))
{if((unionedBounds.size.width>0)&&(unionedBounds.size.height>0))
{unionedBounds=unionedBounds.union(imageClippedBounds);}
else
{unionedBounds=imageClippedBounds.clone();}}}
var extents={left:unionedBounds.origin.x,top:unionedBounds.origin.y,right:unionedBounds.origin.x+unionedBounds.size.width,bottom:unionedBounds.origin.y+unionedBounds.size.height};return extents;}
function IWEffectChildren(element,imagesOnly)
{element=$(element);var inlineBlocks=element.select('.inline-block');return element.descendants().findAll(function(child){if((!imagesOnly&&child.match("div.badge-fill"))||child.match("img"))
{var inline=false;for(var index=0,end=inlineBlocks.length;inline==false&&index<end;++index)
{inline=child.descendantOf(inlineBlocks[index]);}
return inline==false;}
else
{return false;}});}
function IWClippingNode(node)
{if(node)
{if(node.style&&(node.style.overflow||node.style.overflowX||node.style.overflowY))
{if([node.style.overflow,node.style.overflowX,node.style.overflowY].include('hidden'))
return node;}
else
{return IWClippingNode(node.parentNode);}}
return null;}
var IWShadow=Class.create({initialize:function(params)
{this.mBlurRadius=params.blurRadius;this.mOffset=params.offset;this.mColor=params.color;this.mOpacity=params.opacity;},applyToElement:function(shadowed)
{var framePos=new IWPoint(shadowed.offsetLeft,shadowed.offsetTop);var frameSize=new IWSize(shadowed.offsetWidth,shadowed.offsetHeight);var opacity=1.0;if(shadowed!=null)
{shadowed=$(shadowed);opacity=shadowed.getStyle('opacity');if(windowsInternetExplorer)
{var newRoot=$(shadowed.cloneNode(false));shadowed.parentNode.insertBefore(newRoot,shadowed);var shadow=$(document.createElement('DIV'));var shadowContents=shadowed.cloneNodeExcludingIDs(true);shadow.appendChild(shadowContents);shadow.select('map').each(function(mapElement){mapElement.parentNode.removeChild(mapElement);});shadow.select(".IWReflection").invoke("remove");newRoot.appendChild(shadow);newRoot.appendChild(shadowed);shadowed.setStyle({top:0,left:0});var blurRadius=this.mBlurRadius*0.5;var xOffset=this.mOffset.x-(this.mBlurRadius*0.6);var yOffset=this.mOffset.y-(this.mBlurRadius*0.6);shadow.setStyle({position:"absolute",left:px(xOffset-500),top:px(yOffset-500),width:px(frameSize.width+1000),height:px(frameSize.height+1000)});shadowContents.setStyle({position:"absolute",left:px(500),top:px(500),padding:0,margin:0});shadow.style.filter="progid:DXImageTransform.Microsoft.MaskFilter()"+" progid:DXImageTransform.Microsoft.MaskFilter(color="+this.mColor+")"+" progid:DXImageTransform.Microsoft.Alpha(opacity="+this.mOpacity*opacity*100+")"+" progid:DXImageTransform.Microsoft.Blur(pixelradius="+blurRadius+")";if(newRoot.hasClassName("inline-block"))
{var rootTop=newRoot.style.top;var rootMarginTop=newRoot.style.marginTop;if(rootTop&&!rootMarginTop)
{rootTop=(toPixelsAtElement(newRoot,rootTop,true));newRoot.style.marginTop=px(-rootTop);}
else if(!rootTop&&rootMarginTop)
{rootMarginTop=(toPixelsAtElement(newRoot,rootMarginTop,true));newRoot.style.rootTop=px(-rootMarginTop);}
else if(rootTop&&rootMarginTop)
{rootTop=(toPixelsAtElement(newRoot,rootTop,true));rootMarginTop=(toPixelsAtElement(newRoot,rootMarginTop,true));if(rootTop!=rootMarginTop)
{newRoot.style.rootTop=px(-rootMarginTop);}}}
if(shadowed.offsetTop!=0)
{var top=shadowed.style.top;top=top?(toPixelsAtElement(shadowed,top,true)):0;top-=shadowed.offsetTop;shadowed.style.top=px(top);}}
else
{var sourceElements=IWEffectChildren(shadowed,false);var extents=IWImageExtents(shadowed,sourceElements,0,0,frameSize.width,frameSize.height);var canvas=undefined;if(shadowed.sandwich&&shadowed.sandwich.canvas)
{canvas=shadowed.sandwich.canvas;}
extents.left-=Math.max(this.mBlurRadius-this.mOffset.x,0);extents.top-=Math.max(this.mBlurRadius-this.mOffset.y,0);extents.right+=Math.max(this.mBlurRadius+this.mOffset.x,0);extents.bottom+=Math.max(this.mBlurRadius+this.mOffset.y,0);extents.left=Math.floor(extents.left);extents.top=Math.floor(extents.top);extents.right=Math.ceil(extents.right);extents.bottom=Math.ceil(extents.bottom);var leftOffset=extents.left;var topOffset=extents.top;extents.right-=extents.left;extents.bottom-=extents.top;extents.left=0;extents.top=0;var width=extents.right-extents.left;var height=extents.bottom-extents.top;if(canvas===undefined)
{canvas=$(document.createElement("canvas"));}
var context=canvas.getContext?canvas.getContext("2d"):null;var canvasCanDrawShadow=context?context.shadowColor:false;if(canvasCanDrawShadow)
{$(canvas).setAttribute("width",width);$(canvas).setAttribute("height",height);$(canvas).setStyle({position:"absolute",top:px(topOffset),left:px(leftOffset)});var workingCanvas=undefined;if(shadowed.sandwich&&shadowed.sandwich.workingCanvas)
{workingCanvas=shadowed.sandwich.workingCanvas;}
if(workingCanvas===undefined)
{workingCanvas=canvas.cloneNode(false);}
var self=this;var sandwich=shadowed.sandwich||{};sandwich.loadedElements=[];sandwich.elementCount=sourceElements.length;sandwich.loadedElementCount=0;sandwich.canvas=canvas;sandwich.workingCanvas=workingCanvas;shadowed.sandwich=sandwich;sandwich.onImageLoad=function(j,img,image)
{var offset=IWChildOffset(img,shadowed,true);this.loadedElements[j]={imgObject:image.imgObject(),left:offset.x-leftOffset,top:offset.y-topOffset,width:img.offsetWidth,height:img.offsetHeight,render:function(context){context.drawImage(this.imgObject,this.left,this.top,this.width,this.height);}};this.loadedElementCount++;if(this.loadedElementCount==this.elementCount)
{this.renderShadow()}}
sandwich.registerDiv=function(j,div)
{var offset=IWChildOffset(div,shadowed,true);this.loadedElements[j]={divElement:div,left:offset.x-leftOffset,top:offset.y-topOffset,width:div.offsetWidth,height:div.offsetHeight,render:function(context){var div=this.divElement;var color=div.getStyle('background-color');var opacity=parseFloat(div.style.opacity||1);context.save();context.globalAlpha*=opacity;context.fillStyle=color;context.fillRect(this.left,this.top,this.width,this.height);context.restore();}};this.loadedElementCount++;if(this.loadedElementCount==this.elementCount)
{this.renderShadow()}}
sandwich.renderShadow=function()
{if(canvas.parentNode===null)
{shadowed.insertBefore(canvas,shadowed.firstChild);}
canvas.parentNode.insertBefore(workingCanvas,canvas);var context=workingCanvas.getContext("2d");new IWRect(0,0,width,height).clear(context);var bgImage=shadowed.getStyle('background-image');var hasBGImage=bgImage&&bgImage.indexOf('url(')==0;var bgColor=shadowed.getStyle('background-color');var alphaComponent=self.p_alphaComponent(bgColor);IWAssert(function(){return alphaComponent==0||alphaComponent==1},"alpha must be 0 or 1 for background color if shadow is applied");var fillBackground=(hasBGImage||alphaComponent>0);var divBounds=new IWRect(-leftOffset,-topOffset,frameSize.width,frameSize.height).round();if(fillBackground)
{context.fillStyle='rgba(0,0,0,1)';divBounds.fill(context);}
for(var k=0;k<this.loadedElements.length;++k)
{var loaded=this.loadedElements[k];var clipper=$(IWClippingNode(sourceElements[k]));if(clipper&&clipper.descendantOf(shadowed))
{var clipToShadow=IWChildOffset(clipper,shadowed,true);context.save();context.rect(clipToShadow.x-leftOffset,clipToShadow.y-topOffset,clipper.offsetWidth,clipper.offsetHeight);context.clip();loaded.render(context);context.restore();}
else
{loaded.render(context);}}
context=canvas.getContext("2d");new IWRect(0,0,width,height).clear(context);var drawImageUnshadowed=true;context.globalAlpha=opacity;if(context.shadowColor)
{var usingShadowAlpha=true;context.save();usingShadowAlpha=!(isWebKit&&isEarlyWebKitVersion);if(usingShadowAlpha)
{var components=self.mColor.toLowerCase().match(/#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/);if(components&&components.length>=4)
{context.shadowColor="rgba("+parseInt(components[1],16)+", "+parseInt(components[2],16)+", "+parseInt(components[3],16)+", "+self.mOpacity+")";}
else
{components=self.mColor.match(/rgb\(([0-9\.]+),[ ]*([0-9\.]+),[ ]*([0-9\.]+)\)/);if(components&&components.length>=4)
{context.shadowColor="rgba("+components[1]+", "+components[2]+", "+components[3]+", "+self.mOpacity+")";}
else
{iWLog("not using shadow alpha, failed to match "+self.mColor);usingShadowAlpha=false;}}}
if(usingShadowAlpha==false)
{context.globalAlpha*=self.mOpacity;context.shadowColor=self.mColor;}
context.shadowBlur=self.mBlurRadius;context.shadowOffsetX=self.mOffset.x;context.shadowOffsetY=self.mOffset.y;context.drawImage(workingCanvas,0,0);context.restore();if(usingShadowAlpha==false)
{drawImageUnshadowed=self.mOpacity<1.0;}
else
{drawImageUnshadowed=false;}}
if(drawImageUnshadowed)
{context.drawImage(workingCanvas,0,0);}
if(fillBackground)
{divBounds.clear(context);context.save();context.globalAlpha=opacity;context.rect(divBounds.origin.x,divBounds.origin.y,divBounds.size.width,divBounds.size.height);context.clip();for(var k=0;k<this.loadedElements.length;++k)
{this.loadedElements[k].render(context);}
context.restore();}
if(workingCanvas.parentNode)
{workingCanvas.parentNode.removeChild(workingCanvas);delete this.workingCanvas;this.workingCanvas=null;}
for(var j=0;j<sourceElements.length;++j)
{sourceElements[j].style.opacity=0.0;}};if(sourceElements.length>0)
{for(var j=0;j<sourceElements.length;++j)
{var element=$(sourceElements[j]);if(element.match('img'))
{var image=IWCreateImage(element.src);image.load(sandwich.onImageLoad.bind(sandwich,j,element,image));}
else if(element.match('div'))
{sandwich.registerDiv(j,element);}}}
else
{sandwich.renderShadow();}
workingCanvas.style.visibility="hidden";}}}},p_alphaComponent:function(color)
{var alpha=1.0;if(color&&color.indexOf('rgba(')!=-1)
{if(color.match(/rgba\((?:\s*\S+\s*,\s*){3}(\S+)\s*\)/))
{alpha=RegExp.$1;}}
return alpha;}});var IWReflection=Class.create({initialize:function(parameters)
{this.mOpacity=parameters.opacity;this.mOffset=Math.max(parameters.offset,1);this.mFadeSustain=0.4;this.mMaxSustain=120;},applyToElement:function(div)
{var bounds=new IWRect(div.offsetLeft,div.offsetTop,div.offsetWidth,div.offsetHeight);var reflectionHeight=Math.min(div.offsetHeight*this.mFadeSustain,this.mMaxSustain)*0.75;if(div!=null)
{var imgs=IWEffectChildren(div,true);var extents=IWImageExtents(div,imgs,0,0,bounds.size.width,bounds.size.height);var totalWidth=extents.right-extents.left;var totalHeight=extents.bottom-extents.top;var leftOffset=extents.left;var topOffset=extents.top;var bottomOffset=totalHeight-bounds.size.height;if(windowsInternetExplorer)
{var reflection=$(document.createElement("div"));reflection.setStyle({position:"absolute",left:px(extents.left),top:px(bounds.size.height),marginTop:px(this.mOffset),width:px(totalWidth),height:px(reflectionHeight),overflow:"hidden",filter:'progid:DXImageTransform.Microsoft.Alpha(opacity='+(this.mOpacity*100)+', style=1, finishOpacity=0, startx=0, starty=0, finishx=0, finishy=100)'});reflection.addClassName("IWReflection");var flipped=$(document.createElement("div"));flipped.setStyle({position:"relative",width:px(totalWidth),height:px(totalHeight),filter:'flipv'});var cloned=$(div.cloneNode(true));cloned.setStyle({left:px(-extents.left),top:px(-extents.top),position:"absolute"});cloned.className=cloned.className.replace(/(shadow_\d+)/g,'');reflection.appendChild(flipped);flipped.appendChild(cloned);div.insertBefore(reflection,div.firstChild);}
else
{var reflection=$(document.createElement("canvas"));extents.right-=extents.left;extents.bottom-=extents.top;extents.left=0;extents.top=0;reflection.setAttribute("width",extents.right-extents.left);reflection.setAttribute("height",reflectionHeight+this.mOffset/2);reflection.setStyle({position:"absolute",top:px(bounds.size.height),marginTop:px(this.mOffset),left:px(leftOffset)});div.insertBefore(reflection,div.firstChild);var context=reflection.getContext("2d");context.clearRect(0,0,reflection.width,reflection.height);var sandwich={};sandwich.loadedImgs=[];sandwich.imgCount=imgs.length;var self=this;sandwich.onImageLoad=function(j,img,image){var offset=IWChildOffset(img,div,true);this.loadedImgs[j]={imgObject:image.imgObject(),left:offset.x-leftOffset,top:offset.y-topOffset-bottomOffset,width:img.offsetWidth,height:img.offsetHeight};var allImagesLoaded=false;if(this.loadedImgs.length>=this.imgCount)
{allImagesLoaded=true;for(var k=0;allImagesLoaded&&k<this.loadedImgs.length;++k)
{if(this.loadedImgs[k]===undefined)
{allImagesLoaded=false;}}}
if(allImagesLoaded)
{context.save();context.translate(0,bounds.size.height-1);context.scale(1,-1);for(var k=0;k<this.loadedImgs.length;++k)
{var loadedImg=this.loadedImgs[k];var clipper=$(IWClippingNode(imgs[k]));if(clipper&&clipper.descendantOf(div))
{var clipOffset=IWChildOffset(clipper,div,true);context.save();context.rect(clipOffset.x-leftOffset,clipOffset.y-topOffset-bottomOffset,clipper.offsetWidth,clipper.offsetHeight);context.clip();context.drawImage(loadedImg.imgObject,loadedImg.left,loadedImg.top,loadedImg.width,loadedImg.height);context.restore();}
else
{context.drawImage(loadedImg.imgObject,loadedImg.left,loadedImg.top,loadedImg.width,loadedImg.height);}}
context.restore();context.save();context.globalCompositeOperation="destination-out";var gradient=context.createLinearGradient(0,0,0,reflection.height);gradient.addColorStop(1,"rgba(255, 255, 255, 1.0)");gradient.addColorStop(0,"rgba(255, 255, 255, "+(1-self.mOpacity)+")");context.fillStyle=gradient;if(navigator.appVersion.indexOf('WebKit')!=-1)
{context.rect(0,0,reflection.width,reflection.height*2);context.fill();}
else
{context.fillRect(0,0,reflection.width,reflection.height*2);}
context.restore();}};for(var j=0;j<imgs.length;++j)
{var img=imgs[j];var image=IWCreateImage(img.src);image.load(sandwich.onImageLoad.bind(sandwich,j,img,image));}}}}});var kLeft=0,kTopLeft=1,kTop=2,kTopRight=3,kRight=4,kBottomRight=5,kBottom=6,kBottomLeft=7,kPartCount=8;var IWStrokeParts=Class.create({initialize:function(strokeParts,maxImageSize,shouldClip,strokeWidth)
{this.mStrokeParts=strokeParts;this.mMaxImageSize=maxImageSize;this.mShouldClip=shouldClip;if(shouldClip)
{this.mStrokeWidth=strokeWidth;}},p_imageLayout:function(imageSize)
{var strokeParts=this.mStrokeParts;var hDelta=this.mMaxImageSize.width-imageSize.width;var vDelta=this.mMaxImageSize.height-imageSize.height;var topLeft=strokeParts[kTopLeft].rect;var topRight=strokeParts[kTopRight].rect.offset(-hDelta,0);var bottomRight=strokeParts[kBottomRight].rect.offset(-hDelta,-vDelta);var bottomLeft=strokeParts[kBottomLeft].rect.offset(0,-vDelta);var top=strokeParts[kTop].rect;top.size.width=topRight.origin.x-top.origin.x;var right=strokeParts[kRight].rect.offset(-hDelta,0);right.size.height=bottomRight.origin.y-right.origin.y;var bottom=strokeParts[kBottom].rect.offset(0,-vDelta);bottom.size.width=bottomRight.origin.x-bottom.origin.x;var left=strokeParts[kLeft].rect;left.size.height=bottomLeft.origin.y-left.origin.y;return[left,topLeft,top,topRight,right,bottomRight,bottom,bottomLeft];},p_imageMarkup:function(imageSize,zIndex)
{var markup='';var layoutRects=this.p_imageLayout(imageSize);for(var index=kLeft;index<kPartCount;++index)
{var style=layoutRects[index].position();if(zIndex)
{style+='z-index: '+zIndex+';';}
markup+=imgMarkup(this.mStrokeParts[index].url,style);}
return markup;},markupForImageStreamEntry:function(imageStreamEntry,imageSize)
{var rect=new IWRect(0,0,imageSize.width,imageSize.height);var clippingDivPre='';var clippingDivPost='';var thumbRect=rect.clone();if(this.mShouldClip)
{var left=(this.mStrokeWidth/2+1);var top=(this.mStrokeWidth/2+1);var clippingRect=new IWRect(left,top,(imageSize.width-this.mStrokeWidth-2),(imageSize.height-this.mStrokeWidth-2));clippingDivPre='<div style="overflow: hidden; '+clippingRect.position()+'">';clippingDivPost='</div>';thumbRect.origin.x-=left;thumbRect.origin.y-=top;}
var markup='<div class="framedImage" style="'+rect.position()+'">';markup+=clippingDivPre;markup+=imageStreamEntry.thumbnailMarkupForRect(thumbRect);markup+=clippingDivPost;markup+=this.p_imageMarkup(imageSize,2);markup+='</div>';return markup;},applyToElement:function(div)
{div=$(div);if(div!=null)
{if(div.parentNode)
{$(div.parentNode).ensureHasLayoutForIE();}
var size=new IWSize(div.offsetWidth,div.offsetHeight);div.insert(this.p_imageMarkup(size,(div.hasClassName("aboveStrokesAndFrames")?-1:"auto")));if(!div.hasClassName("flowDefining"))
{if(div.style.position!='absolute')
{var divRect=new IWRect(0,0,div.offsetWidth,div.offsetHeight);var unionRect=IWZeroRect();var layoutRects=this.p_imageLayout(size);layoutRects.each(function(r)
{unionRect=unionRect.union(r);});var padding=divRect.paddingToRect(unionRect);var marginLeft=Element.getStyle(div,"marginLeft");marginLeft=marginLeft?(toPixelsAtElement(div,marginLeft,false)):0;var marginTop=Element.getStyle(div,"marginTop");marginTop=marginTop?(toPixelsAtElement(div,marginTop,true)):0;var marginRight=Element.getStyle(div,"marginRight");marginRight=marginRight?(toPixelsAtElement(div,marginRight,false)):0;var marginBottom=Element.getStyle(div,"marginBottom");marginBottom=marginBottom?(toPixelsAtElement(div,marginBottom,true)):0;if(windowsInternetExplorer)
{div.setStyle({marginLeft:px(Math.max(0,padding.left-1)+marginLeft),marginTop:px(Math.max(0,padding.top-1)+marginTop),marginRight:px(Math.max(0,padding.right-1)+marginRight),marginBottom:px(Math.max(0,padding.bottom-1)+marginBottom)});if(effectiveBrowserVersion==7)
{updateListOfIE7FloatsFix(div);}}
else
{div.setStyle({marginLeft:px(padding.left+marginLeft),marginTop:px(padding.top+marginTop),marginRight:px(padding.right+marginRight),marginBottom:px(padding.bottom+marginBottom)});}}}}},strokeExtra:function(imageSize)
{if(!imageSize)
{imageSize=this.mMaxImageSize;}
rect=new IWRect(IWZeroPoint(),imageSize);var layout=this.p_imageLayout(rect.size);var unionRect=IWZeroRect();layout.each(function(r)
{unionRect=unionRect.union(r);});return rect.paddingToRect(unionRect);}});var IWStroke=Class.create({initialize:function(strokeURL,strokeRect,maxImageSize)
{this.mStrokeURL=strokeURL;this.mStrokeRect=strokeRect;this.mMaxImageSize=maxImageSize;},p_strokeRect:function(imageSize)
{var hScale=imageSize.width/this.mMaxImageSize.width;var vScale=imageSize.height/this.mMaxImageSize.height;var strokeRect=this.mStrokeRect.scale(hScale,vScale,true);return strokeRect;},p_imageMarkup:function(imageSize,zIndex)
{var style=this.p_strokeRect(imageSize).position();if(zIndex)
{style+='z-index: '+zIndex+';';}
return imgMarkup(this.mStrokeURL,style);},markupForImageStreamEntry:function(imageStreamEntry,imageSize)
{var rect=new IWRect(0,0,imageSize.width,imageSize.height);var markup='<div class="framedImage" style="'+rect.position()+'">';markup+=imageStreamEntry.thumbnailMarkupForRect(rect);markup+=this.p_imageMarkup(imageSize,2);markup+='</div>';return markup;},applyToElement:function(div)
{div=$(div);if(div!=null)
{if(div.parentNode)
{$(div.parentNode).ensureHasLayoutForIE();}
var size=new IWSize(div.offsetWidth,div.offsetHeight);div.insert(this.p_imageMarkup(size,(div.hasClassName("aboveStrokesAndFrames")?-1:"auto")));if(!div.hasClassName("flowDefining"))
{if(div.style.position!='absolute')
{var divRect=new IWRect(0,0,div.offsetWidth,div.offsetHeight);var padding=divRect.paddingToRect(this.mStrokeRect);var marginLeft=Element.getStyle(div,"marginLeft");marginLeft=marginLeft?(toPixelsAtElement(div,marginLeft,false)):0;var marginTop=Element.getStyle(div,"marginTop");marginTop=marginTop?(toPixelsAtElement(div,marginTop,true)):0;var marginRight=Element.getStyle(div,"marginRight");marginRight=marginRight?(toPixelsAtElement(div,marginRight,false)):0;var marginBottom=Element.getStyle(div,"marginBottom");marginBottom=marginBottom?(toPixelsAtElement(div,marginBottom,true)):0;div.setStyle({marginLeft:px(padding.left+marginLeft),marginTop:px(padding.top+marginTop),marginRight:px(padding.right+marginRight),marginBottom:px(padding.bottom+marginBottom)});if(windowsInternetExplorer&&effectiveBrowserVersion==7)
{updateListOfIE7FloatsFix(div);}}}}},strokeExtra:function(imageSize)
{if(imageSize===undefined)
{imageSize=this.mMaxImageSize;}
var imageRect=new IWRect(IWZeroPoint(),imageSize);return imageRect.paddingToRect(this.p_strokeRect(imageSize));}});var IWEmptyStroke=Class.create({initialize:function()
{},markupForImageStreamEntry:function(imageStreamEntry,imageSize)
{var rect=new IWRect(0,0,imageSize.width,imageSize.height);var markup='<div class="framedImage" style="'+rect.position()+'">';markup+=imageStreamEntry.thumbnailMarkupForRect(rect);markup+='</div>';return markup;},applyToElement:function(div)
{},strokeExtra:function()
{return new IWPadding(0,0,0,0);}});var kSFRFrameTopLeft=0;var kSFRFrameTop=1;var kSFRFrameTopRight=2;var kSFRFrameRight=3;var kSFRFrameBottomRight=4;var kSFRFrameBottom=5;var kSFRFrameBottomLeft=6;var kSFRFrameLeft=7;var kSFRFrameClip=0;var kSFRFrameStretchEvenly=1;var kSFRFrameStretchToFit=2;var IWPhotoFrame=Class.create({initialize:function(images,maskImages,tilingMode,assetScale,leftInset,topInset,rightInset,bottomInset,unscaledLeftWidth,unscaledTopHeight,unscaledRightWidth,unscaledBottomHeight,leftTileHeight,topTileWidth,rightTileHeight,bottomTileWidth,adornmentURL,adornmentPosition,adornmentSize,minimumAssetScale)
{this.mImages=images;this.mMaskImages=maskImages;this.mTilingMode=tilingMode;this.mLeftInset=leftInset;this.mTopInset=topInset;this.mRightInset=rightInset;this.mBottomInset=bottomInset;this.mUnscaledLeftWidth=unscaledLeftWidth;this.mUnscaledTopHeight=unscaledTopHeight;this.mUnscaledRightWidth=unscaledRightWidth;this.mUnscaledBottomHeight=unscaledBottomHeight;this.mLeftTileHeight=leftTileHeight;this.mTopTileWidth=topTileWidth;this.mRightTileHeight=rightTileHeight;this.mBottomTileWidth=bottomTileWidth;this.mAdornmentURL=adornmentURL;this.mAdornmentPosition=adornmentPosition;this.mAdornmentSize=adornmentSize;this.mMinimumAssetScale=minimumAssetScale;this.setAssetScale(assetScale);},setAssetScale:function(assetScale)
{assetScale=Math.min(assetScale,1.0);assetScale=Math.max(this.mMinimumAssetScale,assetScale);this.mAssetScale=assetScale;this.mLeftWidth=this.scaledValue(this.mUnscaledLeftWidth);this.mTopHeight=this.scaledValue(this.mUnscaledTopHeight);this.mRightWidth=this.scaledValue(this.mUnscaledRightWidth);this.mBottomHeight=this.scaledValue(this.mUnscaledBottomHeight);},scaledValue:function(valueToScale)
{return Math.ceil(valueToScale*this.mAssetScale);},markupForImageStreamEntry:function(imageStreamEntry,size)
{var oldAssetScale=this.mAssetScale;var maximumScale=this.maximumAssetScaleForImageSize(size);if((maximumScale<oldAssetScale)&&(maximumScale>=this.mMinimumAssetScale))
{this.setAssetScale(maximumScale);}
var coverageRect=this.coverageRect(new IWRect(0,0,size.width,size.height));var imageRect=new IWRect(-coverageRect.origin.x,-coverageRect.origin.y,size.width,size.height);coverageRect=coverageRect.offsetToOrigin();var markup='<div class="framedImage" style="'+coverageRect.position()+'">';markup+=imageStreamEntry.thumbnailMarkupForRect(imageRect);if(maximumScale>=this.mMinimumAssetScale)
{if(this.mImages!=null)
{markup+=this.p_buildFrame(this.mImages,coverageRect.size,2);}
if(this.mAdornmentURL!=null)
{markup+=this.p_adornmentMarkupForRect(imageRect,2);}
if(this.mMaskImages)
{}}
markup+='</div>';if(oldAssetScale!=this.mAssetScale)this.setAssetScale(oldAssetScale);return markup;},strokeExtra:function()
{var adornmentExtraTopMargin=0;if(this.mAdornmentURL)
{adornmentExtraTopMargin=Math.max(0,(this.scaledValue(this.mAdornmentSize.height)-this.mTopHeight)/2.0-this.mAdornmentPosition.y);}
return new IWPadding(this.mLeftWidth-this.scaledValue(this.mLeftInset),this.mTopHeight-this.scaledValue(this.mTopInset)+adornmentExtraTopMargin,this.mRightWidth-this.scaledValue(this.mRightInset),this.mBottomHeight-this.scaledValue(this.mBottomInset));},applyToElement:function(div)
{div=$(div);if(div!=null)
{if(div.parentNode)
{$(div.parentNode).ensureHasLayoutForIE();}
var markup='';var divRect=new IWRect(0,0,div.offsetWidth,div.offsetHeight);if((divRect.size.width>=(this.scaledValue(this.mLeftInset)+this.scaledValue(this.mRightInset)))&&(divRect.size.height>=(this.scaledValue(this.mTopInset)+this.scaledValue(this.mTopInset))))
{if(this.mImages!=null)
{var coverageRect=this.coverageRect(divRect);var containerRect=new IWRect(coverageRect.origin.x,coverageRect.origin.y,0,0);markup+='<div style="'+containerRect.position()+'">';markup+=this.p_buildFrame(this.mImages,coverageRect.size,(div.hasClassName("aboveStrokesAndFrames")?-1:"auto"));markup+='</div>';}
if(this.mAdornmentURL!=null)
{markup+=this.p_adornmentMarkupForRect(divRect);}}
div.insert(markup);if(!div.hasClassName("flowDefining"))
{if(div.style.position!='absolute')
{var frameExtra=this.strokeExtra();var marginLeft=Element.getStyle(div,"marginLeft");marginLeft=marginLeft?(toPixelsAtElement(div,marginLeft,false)):0;var marginTop=Element.getStyle(div,"marginTop");marginTop=marginTop?(toPixelsAtElement(div,marginTop,true)):0;var marginRight=Element.getStyle(div,"marginRight");marginRight=marginRight?(toPixelsAtElement(div,marginRight,false)):0;var marginBottom=Element.getStyle(div,"marginBottom");marginBottom=marginBottom?(toPixelsAtElement(div,marginBottom,true)):0;div.setStyle({marginLeft:px(frameExtra.left+marginLeft),marginTop:px(frameExtra.top+marginTop),marginRight:px(frameExtra.right+marginRight),marginBottom:px(frameExtra.bottom+marginBottom)});if(windowsInternetExplorer&&effectiveBrowserVersion==7)
{updateListOfIE7FloatsFix(div);}}}}},maximumAssetScaleForImageSize:function(in_imgSize)
{var maxScale=1;if((in_imgSize.width>this.mLeftInset+this.mRightInset)&&(in_imgSize.height>this.mTopInset+this.mBottomInset))
{maxScale=1;}
else if((in_imgSize.width<Math.ceil(this.mLeftInset*this.mMinimumAssetScale)+Math.ceil(this.mRightInset*this.mMinimumAssetScale))||(in_imgSize.height<Math.ceil(this.mTopInset*this.mMinimumAssetScale)+Math.ceil(this.mBottomInset*this.mMinimumAssetScale)))
{maxScale=0;}
else
{var maxWidthScale=1;var floatEpsilon=0.0000001;if(((this.mLeftInset+this.mRightInset)>=in_imgSize.width)&&((this.mLeftInset+this.mRightInset)>0))
{var leftChunkRatio=Math.floor(this.mLeftInset/(this.mLeftInset+this.mRightInset)*in_imgSize.width)/this.mLeftInset;var rightChunkRatio=Math.floor(this.mRightInset/(this.mLeftInset+this.mRightInset)*in_imgSize.width)/this.mRightInset;leftChunkRatio-=floatEpsilon;rightChunkRatio-=floatEpsilon;maxWidthScale=Math.max(leftChunkRatio,rightChunkRatio);if(in_imgSize.width<(Math.ceil(this.mLeftInset*maxWidthScale)+Math.ceil(this.mRightInset*maxWidthScale)))
{maxWidthScale=Math.min(leftChunkRatio,rightChunkRatio);}
if((maxWidthScale<this.mMinimumAssetScale)||in_imgSize.width<(Math.ceil(this.mLeftInset*maxWidthScale)+Math.ceil(this.mRightInset*maxWidthScale)))
{maxWidthScale=this.mMinimumAssetScale;}}
var maxHeightScale=1;if(((this.mTopInset+this.mBottomInset)>=in_imgSize.height)&&((this.mTopInset+this.mBottomInset)>0))
{var topChunkRatio=Math.floor(this.mTopInset/(this.mTopInset+this.mBottomInset)*in_imgSize.height)/this.mTopInset;var bottomChunkRatio=Math.floor(this.mBottomInset/(this.mTopInset+this.mBottomInset)*in_imgSize.height)/this.mBottomInset;topChunkRatio-=floatEpsilon;bottomChunkRatio-=floatEpsilon;maxHeightScale=Math.max(topChunkRatio,bottomChunkRatio);if(in_imgSize.height<(Math.ceil(this.mTopInset*maxHeightScale)+Math.ceil(this.mBottomInset*maxHeightScale)))
{maxHeightScale=Math.min(topChunkRatio,bottomChunkRatio);}
if((maxHeightScale<this.mMinimumAssetScale)||in_imgSize.height<(Math.ceil(this.mTopInset*maxHeightScale)+Math.ceil(this.mBottomInset*maxHeightScale)))
{maxHeightScale=this.mMinimumAssetScale;}}
maxScale=Math.min(maxWidthScale,maxHeightScale);}
return maxScale;},coverageRect:function(rect)
{var left=rect.origin.x+this.scaledValue(this.mLeftInset);var top=rect.origin.y+this.scaledValue(this.mTopInset);var right=rect.maxX()-this.scaledValue(this.mRightInset);var bottom=rect.maxY()-this.scaledValue(this.mBottomInset);left-=this.mLeftWidth;right+=this.mRightWidth;top-=this.mTopHeight;bottom+=this.mBottomHeight;return(new IWRect(left,top,right-left,bottom-top)).round();},p_buildFrame:function(images,size,zIndex)
{var width=size.width;var height=size.height;var startX=this.mLeftWidth;var endX=width-this.mRightWidth;var startY=this.mTopHeight;var endY=height-this.mBottomHeight;var markup="";var zIndexStyle=zIndex?('z-index: '+zIndex+';'):'';if((startX<=endX+1)&&(startY<=endY+1))
{var imageRect=new IWRect(0.0,0.0,this.mLeftWidth,this.mTopHeight);markup=imgMarkup(images[kSFRFrameTopLeft].sourceURL(),imageRect.position()+zIndexStyle);imageRect=new IWRect(0.0,(height-this.mBottomHeight),this.mLeftWidth,this.mBottomHeight);markup+=imgMarkup(images[kSFRFrameBottomLeft].sourceURL(),imageRect.position()+zIndexStyle);imageRect=new IWRect((width-this.mRightWidth),0.0,this.mRightWidth,this.mTopHeight);markup+=imgMarkup(images[kSFRFrameTopRight].sourceURL(),imageRect.position()+zIndexStyle);imageRect=new IWRect((width-this.mRightWidth),(height-this.mBottomHeight),this.mRightWidth,this.mBottomHeight);markup+=imgMarkup(images[kSFRFrameBottomRight].sourceURL(),imageRect.position()+zIndexStyle);var naturalSize=new IWSize(this.mLeftWidth,this.scaledValue(this.mLeftTileHeight));imageRect=new IWRect(0.0,startY,naturalSize.width,naturalSize.height);markup+=this.p_tiles(images[kSFRFrameLeft].sourceURL(),imageRect,startY,endY,true,zIndex);naturalSize=new IWSize(this.mRightWidth,this.scaledValue(this.mRightTileHeight));imageRect=new IWRect(width-this.mRightWidth,startY,naturalSize.width,naturalSize.height);markup+=this.p_tiles(images[kSFRFrameRight].sourceURL(),imageRect,startY,endY,true,zIndex);naturalSize=new IWSize(this.scaledValue(this.mTopTileWidth),this.mTopHeight);imageRect=new IWRect(startX,0.0,naturalSize.width,naturalSize.height);markup+=this.p_tiles(images[kSFRFrameTop].sourceURL(),imageRect,startX,endX,false,zIndex);naturalSize=new IWSize(this.scaledValue(this.mBottomTileWidth),this.mBottomHeight);imageRect=new IWRect(startX,height-this.mBottomHeight,naturalSize.width,naturalSize.height);markup+=this.p_tiles(images[kSFRFrameBottom].sourceURL(),imageRect,startX,endX,false,zIndex);}
return markup;},p_adornmentRectForRect:function(rect)
{var adornmentCenter=new IWPoint();rect=this.coverageRect(rect);adornmentCenter.x=(rect.size.width-(this.mLeftWidth+this.mRightWidth))*this.mAdornmentPosition.x;adornmentCenter.x+=rect.origin.x+this.mLeftWidth;adornmentCenter.y=this.mTopHeight/2.0+(rect.origin.y+this.mAdornmentPosition.y);var scaledAdornmentSize=new IWSize(this.scaledValue(this.mAdornmentSize.width),this.scaledValue(this.mAdornmentSize.height));var adornmentOrigin=new IWPoint(adornmentCenter.x-(scaledAdornmentSize.width/2.0),adornmentCenter.y-(scaledAdornmentSize.height/2.0));var adornmentRect=new IWRect(adornmentOrigin,scaledAdornmentSize);return adornmentRect;},p_adornmentMarkupForRect:function(rect,zIndex)
{var zIndexStyle=zIndex?('z-index: '+zIndex+';'):'';return imgMarkup(this.mAdornmentURL,this.p_adornmentRectForRect(rect).position()+zIndexStyle);},p_tiles:function(imageURL,imageRect,start,end,vertical,zIndex)
{var markup="";if(start<end)
{var zIndexStyle=zIndex?('z-index: '+zIndex+';'):'';var tileRect=imageRect.clone();var tilingMode=this.mTilingMode;if(vertical)
{tileRect.size.height=Math.ceil(end-start);if(imageRect.size.height==1)
{tilingMode=kSFRFrameStretchToFit;}}
else
{tileRect.size.width=Math.ceil(end-start);if(imageRect.size.width==1)
{tilingMode=kSFRFrameStretchToFit;}}
if(tilingMode==kSFRFrameStretchToFit)
{markup+=imgMarkup(imageURL,tileRect.position()+zIndexStyle);}
else
{var naturalSize=imageRect.size;var offset=(vertical?naturalSize.height:naturalSize.width);var maxTiles=Math.ceil((end-start)/offset);if(offset<5||maxTiles>20)
{IWAssert(function(){return true},"Please remove this assert and the surrouding block.");iWLog("Too many frame image tiles are getting generated.  Performance may be affected.");}
if(tilingMode==kSFRFrameStretchEvenly)
{offset=(end-start)/maxTiles;if(vertical)
{imageRect.size.height=offset;}
else
{imageRect.size.width=offset;}}
else if(tilingMode==kSFRFrameClip)
{markup+='<div style="'+tileRect.position()+'overflow: hidden; ">';imageRect.origin.x=0;imageRect.origin.y=0;}
for(var i=0;i<maxTiles;++i)
{var left=Math.round(imageRect.origin.x);var right=Math.round(imageRect.origin.x+imageRect.size.width);var top=Math.round(imageRect.origin.y);var bottom=Math.round(imageRect.origin.y+imageRect.size.height);var roundedRect=new IWRect(left,top,(right-left),(bottom-top));markup+=imgMarkup(imageURL,roundedRect.position()+zIndexStyle);imageRect=vertical?imageRect.offset(0.0,offset):imageRect.offset(offset,0.0);}
if(tilingMode==kSFRFrameClip)
{markup+="</div>";}}}
return markup;}});

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_http_thorn_ws_reduceron_Reduceron_Practical_Reduceron_html/bonnet/thorn.ws/reduceron/Reduceron/Scripts/iWebSite.js version [cb2eef7efb].



































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
//
//  iWeb - iWebSite.js
//  Copyright (c) 2007-2008 Apple Inc. All rights reserved.
//
//
//  This file includes a copy of the Prototype JavaScript framework:
//

var Prototype={Version:'1.6.0',Browser:{IE:!!(window.attachEvent&&!window.opera),Opera:!!window.opera,WebKit:navigator.userAgent.indexOf('AppleWebKit/')>-1,Gecko:navigator.userAgent.indexOf('Gecko')>-1&&navigator.userAgent.indexOf('KHTML')==-1,MobileSafari:!!navigator.userAgent.match(/Apple.*Mobile.*Safari/)},BrowserFeatures:{XPath:!!document.evaluate,ElementExtensions:!!window.HTMLElement,SpecificElementExtensions:document.createElement('div').__proto__&&document.createElement('div').__proto__!==document.createElement('form').__proto__},ScriptFragment:'<script[^>]*>([\\S\\s]*?)<\/script>',JSONFilter:/^\/\*-secure-([\s\S]*)\*\/\s*$/,emptyFunction:function(){},K:function(x){return x}};if(Prototype.Browser.MobileSafari)
Prototype.BrowserFeatures.SpecificElementExtensions=false;if(Prototype.Browser.WebKit)
Prototype.BrowserFeatures.XPath=false;var Class={create:function(){var parent=null,properties=$A(arguments);if(Object.isFunction(properties[0]))
parent=properties.shift();function klass(){this.initialize.apply(this,arguments);}
Object.extend(klass,Class.Methods);klass.superclass=parent;klass.subclasses=[];if(parent){var subclass=function(){};subclass.prototype=parent.prototype;klass.prototype=new subclass;parent.subclasses.push(klass);}
for(var i=0;i<properties.length;i++)
klass.addMethods(properties[i]);if(!klass.prototype.initialize)
klass.prototype.initialize=Prototype.emptyFunction;klass.prototype.constructor=klass;return klass;}};Class.Methods={addMethods:function(source){var ancestor=this.superclass&&this.superclass.prototype;var properties=Object.keys(source);if(!Object.keys({toString:true}).length)
properties.push("toString","valueOf");for(var i=0,length=properties.length;i<length;i++){var property=properties[i],value=source[property];if(ancestor&&Object.isFunction(value)&&value.argumentNames().first()=="$super"){var method=value,value=Object.extend((function(m){return function(){return ancestor[m].apply(this,arguments)};})(property).wrap(method),{valueOf:function(){return method},toString:function(){return method.toString()}});}
this.prototype[property]=value;}
return this;}};var Abstract={};Object.extend=function(destination,source){for(var property in source)
destination[property]=source[property];return destination;};Object.extend(Object,{inspect:function(object){try{if(object===undefined)return'undefined';if(object===null)return'null';return object.inspect?object.inspect():object.toString();}catch(e){if(e instanceof RangeError)return'...';throw e;}},toJSON:function(object){var type=typeof object;switch(type){case'undefined':case'function':case'unknown':return;case'boolean':return object.toString();}
if(object===null)return'null';if(object.toJSON)return object.toJSON();if(Object.isElement(object))return;var results=[];for(var property in object){var value=Object.toJSON(object[property]);if(value!==undefined)
results.push(property.toJSON()+': '+value);}
return'{'+results.join(', ')+'}';},toQueryString:function(object){return $H(object).toQueryString();},toHTML:function(object){return object&&object.toHTML?object.toHTML():String.interpret(object);},keys:function(object){var keys=[];for(var property in object)
keys.push(property);return keys;},values:function(object){var values=[];for(var property in object)
values.push(object[property]);return values;},clone:function(object){return Object.extend({},object);},isElement:function(object){return object&&object.nodeType==1;},isArray:function(object){return object&&object.constructor===Array;},isHash:function(object){return object instanceof Hash;},isFunction:function(object){return typeof object=="function";},isString:function(object){return typeof object=="string";},isNumber:function(object){return typeof object=="number";},isUndefined:function(object){return typeof object=="undefined";}});Object.extend(Function.prototype,{argumentNames:function(){var names=this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");return names.length==1&&!names[0]?[]:names;},bind:function(){if(arguments.length<2&&arguments[0]===undefined)return this;var __method=this,args=$A(arguments),object=args.shift();return function(){return __method.apply(object,args.concat($A(arguments)));}},bindAsEventListener:function(){var __method=this,args=$A(arguments),object=args.shift();return function(event){return __method.apply(object,[event||window.event].concat(args));}},curry:function(){if(!arguments.length)return this;var __method=this,args=$A(arguments);return function(){return __method.apply(this,args.concat($A(arguments)));}},delay:function(){var __method=this,args=$A(arguments),timeout=args.shift()*1000;return window.setTimeout(function(){return __method.apply(__method,args);},timeout);},wrap:function(wrapper){var __method=this;return function(){return wrapper.apply(this,[__method.bind(this)].concat($A(arguments)));}},methodize:function(){if(this._methodized)return this._methodized;var __method=this;return this._methodized=function(){return __method.apply(null,[this].concat($A(arguments)));};}});Function.prototype.defer=Function.prototype.delay.curry(0.01);Date.prototype.toJSON=function(){return'"'+this.getUTCFullYear()+'-'+
(this.getUTCMonth()+1).toPaddedString(2)+'-'+
this.getUTCDate().toPaddedString(2)+'T'+
this.getUTCHours().toPaddedString(2)+':'+
this.getUTCMinutes().toPaddedString(2)+':'+
this.getUTCSeconds().toPaddedString(2)+'Z"';};var Try={these:function(){var returnValue;for(var i=0,length=arguments.length;i<length;i++){var lambda=arguments[i];try{returnValue=lambda();break;}catch(e){}}
return returnValue;}};RegExp.prototype.match=RegExp.prototype.test;RegExp.escape=function(str){return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g,'\\$1');};var PeriodicalExecuter=Class.create({initialize:function(callback,frequency){this.callback=callback;this.frequency=frequency;this.currentlyExecuting=false;this.registerCallback();},registerCallback:function(){this.timer=setInterval(this.onTimerEvent.bind(this),this.frequency*1000);},execute:function(){this.callback(this);},stop:function(){if(!this.timer)return;clearInterval(this.timer);this.timer=null;},onTimerEvent:function(){if(!this.currentlyExecuting){try{this.currentlyExecuting=true;this.execute();}finally{this.currentlyExecuting=false;}}}});Object.extend(String,{interpret:function(value){return value==null?'':String(value);},specialChar:{'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','\\':'\\\\'}});Object.extend(String.prototype,{gsub:function(pattern,replacement){var result='',source=this,match;replacement=arguments.callee.prepareReplacement(replacement);while(source.length>0){if(match=source.match(pattern)){result+=source.slice(0,match.index);result+=String.interpret(replacement(match));source=source.slice(match.index+match[0].length);}else{result+=source,source='';}}
return result;},sub:function(pattern,replacement,count){replacement=this.gsub.prepareReplacement(replacement);count=count===undefined?1:count;return this.gsub(pattern,function(match){if(--count<0)return match[0];return replacement(match);});},scan:function(pattern,iterator){this.gsub(pattern,iterator);return String(this);},truncate:function(length,truncation){length=length||30;truncation=truncation===undefined?'...':truncation;return this.length>length?this.slice(0,length-truncation.length)+truncation:String(this);},strip:function(){return this.replace(/^\s+/,'').replace(/\s+$/,'');},stripTags:function(){return this.replace(/<\/?[^>]+>/gi,'');},stripScripts:function(){return this.replace(new RegExp(Prototype.ScriptFragment,'img'),'');},extractScripts:function(){var matchAll=new RegExp(Prototype.ScriptFragment,'img');var matchOne=new RegExp(Prototype.ScriptFragment,'im');return(this.match(matchAll)||[]).map(function(scriptTag){return(scriptTag.match(matchOne)||['',''])[1];});},evalScripts:function(){return this.extractScripts().map(function(script){return eval(script)});},escapeHTML:function(){var self=arguments.callee;self.text.data=this;return self.div.innerHTML;},unescapeHTML:function(){var div=new Element('div');div.innerHTML=this.stripTags();return div.childNodes[0]?(div.childNodes.length>1?$A(div.childNodes).inject('',function(memo,node){return memo+node.nodeValue}):div.childNodes[0].nodeValue):'';},toQueryParams:function(separator){var match=this.strip().match(/([^?#]*)(#.*)?$/);if(!match)return{};return match[1].split(separator||'&').inject({},function(hash,pair){if((pair=pair.split('='))[0]){var key=decodeURIComponent(pair.shift());var value=pair.length>1?pair.join('='):pair[0];if(value!=undefined)value=decodeURIComponent(value);if(key in hash){if(!Object.isArray(hash[key]))hash[key]=[hash[key]];hash[key].push(value);}
else hash[key]=value;}
return hash;});},toArray:function(){return this.split('');},succ:function(){return this.slice(0,this.length-1)+
String.fromCharCode(this.charCodeAt(this.length-1)+1);},times:function(count){return count<1?'':new Array(count+1).join(this);},camelize:function(){var parts=this.split('-'),len=parts.length;if(len==1)return parts[0];var camelized=this.charAt(0)=='-'?parts[0].charAt(0).toUpperCase()+parts[0].substring(1):parts[0];for(var i=1;i<len;i++)
camelized+=parts[i].charAt(0).toUpperCase()+parts[i].substring(1);return camelized;},capitalize:function(){return this.charAt(0).toUpperCase()+this.substring(1).toLowerCase();},underscore:function(){return this.gsub(/::/,'/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();},dasherize:function(){return this.gsub(/_/,'-');},inspect:function(useDoubleQuotes){var escapedString=this.gsub(/[\x00-\x1f\\]/,function(match){var character=String.specialChar[match[0]];return character?character:'\\u00'+match[0].charCodeAt().toPaddedString(2,16);});if(useDoubleQuotes)return'"'+escapedString.replace(/"/g,'\\"')+'"';return"'"+escapedString.replace(/'/g,'\\\'')+"'";},toJSON:function(){return this.inspect(true);},unfilterJSON:function(filter){return this.sub(filter||Prototype.JSONFilter,'#{1}');},isJSON:function(){var str=this.replace(/\\./g,'@').replace(/"[^"\\\n\r]*"/g,'');return(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);},evalJSON:function(sanitize){var json=this.unfilterJSON();try{if(!sanitize||json.isJSON())return eval('('+json+')');}catch(e){}
throw new SyntaxError('Badly formed JSON string: '+this.inspect());},include:function(pattern){return this.indexOf(pattern)>-1;},startsWith:function(pattern){return this.indexOf(pattern)===0;},endsWith:function(pattern){var d=this.length-pattern.length;return d>=0&&this.lastIndexOf(pattern)===d;},empty:function(){return this=='';},blank:function(){return/^\s*$/.test(this);},interpolate:function(object,pattern){return new Template(this,pattern).evaluate(object);}});if(Prototype.Browser.WebKit||Prototype.Browser.IE)Object.extend(String.prototype,{escapeHTML:function(){return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');},unescapeHTML:function(){return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');}});String.prototype.gsub.prepareReplacement=function(replacement){if(Object.isFunction(replacement))return replacement;var template=new Template(replacement);return function(match){return template.evaluate(match)};};String.prototype.parseQuery=String.prototype.toQueryParams;Object.extend(String.prototype.escapeHTML,{div:document.createElement('div'),text:document.createTextNode('')});with(String.prototype.escapeHTML)div.appendChild(text);var Template=Class.create({initialize:function(template,pattern){this.template=template.toString();this.pattern=pattern||Template.Pattern;},evaluate:function(object){if(Object.isFunction(object.toTemplateReplacements))
object=object.toTemplateReplacements();return this.template.gsub(this.pattern,function(match){if(object==null)return'';var before=match[1]||'';if(before=='\\')return match[2];var ctx=object,expr=match[3];var pattern=/^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/,match=pattern.exec(expr);if(match==null)return before;while(match!=null){var comp=match[1].startsWith('[')?match[2].gsub('\\\\]',']'):match[1];ctx=ctx[comp];if(null==ctx||''==match[3])break;expr=expr.substring('['==match[3]?match[1].length:match[0].length);match=pattern.exec(expr);}
return before+String.interpret(ctx);}.bind(this));}});Template.Pattern=/(^|.|\r|\n)(#\{(.*?)\})/;var $break={};var Enumerable={each:function(iterator,context){var index=0;iterator=iterator.bind(context);try{this._each(function(value){iterator(value,index++);});}catch(e){if(e!=$break)throw e;}
return this;},eachSlice:function(number,iterator,context){iterator=iterator?iterator.bind(context):Prototype.K;var index=-number,slices=[],array=this.toArray();while((index+=number)<array.length)
slices.push(array.slice(index,index+number));return slices.collect(iterator,context);},all:function(iterator,context){iterator=iterator?iterator.bind(context):Prototype.K;var result=true;this.each(function(value,index){result=result&&!!iterator(value,index);if(!result)throw $break;});return result;},any:function(iterator,context){iterator=iterator?iterator.bind(context):Prototype.K;var result=false;this.each(function(value,index){if(result=!!iterator(value,index))
throw $break;});return result;},collect:function(iterator,context){iterator=iterator?iterator.bind(context):Prototype.K;var results=[];this.each(function(value,index){results.push(iterator(value,index));});return results;},detect:function(iterator,context){iterator=iterator.bind(context);var result;this.each(function(value,index){if(iterator(value,index)){result=value;throw $break;}});return result;},findAll:function(iterator,context){iterator=iterator.bind(context);var results=[];this.each(function(value,index){if(iterator(value,index))
results.push(value);});return results;},grep:function(filter,iterator,context){iterator=iterator?iterator.bind(context):Prototype.K;var results=[];if(Object.isString(filter))
filter=new RegExp(filter);this.each(function(value,index){if(filter.match(value))
results.push(iterator(value,index));});return results;},include:function(object){if(Object.isFunction(this.indexOf))
if(this.indexOf(object)!=-1)return true;var found=false;this.each(function(value){if(value==object){found=true;throw $break;}});return found;},inGroupsOf:function(number,fillWith){fillWith=fillWith===undefined?null:fillWith;return this.eachSlice(number,function(slice){while(slice.length<number)slice.push(fillWith);return slice;});},inject:function(memo,iterator,context){iterator=iterator.bind(context);this.each(function(value,index){memo=iterator(memo,value,index);});return memo;},invoke:function(method){var args=$A(arguments).slice(1);return this.map(function(value){return value[method].apply(value,args);});},max:function(iterator,context){iterator=iterator?iterator.bind(context):Prototype.K;var result;this.each(function(value,index){value=iterator(value,index);if(result==undefined||value>=result)
result=value;});return result;},min:function(iterator,context){iterator=iterator?iterator.bind(context):Prototype.K;var result;this.each(function(value,index){value=iterator(value,index);if(result==undefined||value<result)
result=value;});return result;},partition:function(iterator,context){iterator=iterator?iterator.bind(context):Prototype.K;var trues=[],falses=[];this.each(function(value,index){(iterator(value,index)?trues:falses).push(value);});return[trues,falses];},pluck:function(property){var results=[];this.each(function(value){results.push(value[property]);});return results;},reject:function(iterator,context){iterator=iterator.bind(context);var results=[];this.each(function(value,index){if(!iterator(value,index))
results.push(value);});return results;},sortBy:function(iterator,context){iterator=iterator.bind(context);return this.map(function(value,index){return{value:value,criteria:iterator(value,index)};}).sort(function(left,right){var a=left.criteria,b=right.criteria;return a<b?-1:a>b?1:0;}).pluck('value');},toArray:function(){return this.map();},zip:function(){var iterator=Prototype.K,args=$A(arguments);if(Object.isFunction(args.last()))
iterator=args.pop();var collections=[this].concat(args).map($A);return this.map(function(value,index){return iterator(collections.pluck(index));});},size:function(){return this.toArray().length;},inspect:function(){return'#<Enumerable:'+this.toArray().inspect()+'>';}};Object.extend(Enumerable,{map:Enumerable.collect,find:Enumerable.detect,select:Enumerable.findAll,filter:Enumerable.findAll,member:Enumerable.include,entries:Enumerable.toArray,every:Enumerable.all,some:Enumerable.any});function $A(iterable){if(!iterable)return[];if(iterable.toArray)return iterable.toArray();var length=iterable.length,results=new Array(length);while(length--)results[length]=iterable[length];return results;}
if(Prototype.Browser.WebKit){function $A(iterable){if(!iterable)return[];if(!(Object.isFunction(iterable)&&iterable=='[object NodeList]')&&iterable.toArray)return iterable.toArray();var length=iterable.length,results=new Array(length);while(length--)results[length]=iterable[length];return results;}}
Array.from=$A;Object.extend(Array.prototype,Enumerable);if(!Array.prototype._reverse)Array.prototype._reverse=Array.prototype.reverse;Object.extend(Array.prototype,{_each:function(iterator){for(var i=0,length=this.length;i<length;i++)
iterator(this[i]);},clear:function(){this.length=0;return this;},first:function(){return this[0];},last:function(){return this[this.length-1];},compact:function(){return this.select(function(value){return value!=null;});},flatten:function(){return this.inject([],function(array,value){return array.concat(Object.isArray(value)?value.flatten():[value]);});},without:function(){var values=$A(arguments);return this.select(function(value){return!values.include(value);});},reverse:function(inline){return(inline!==false?this:this.toArray())._reverse();},reduce:function(){return this.length>1?this:this[0];},uniq:function(sorted){return this.inject([],function(array,value,index){if(0==index||(sorted?array.last()!=value:!array.include(value)))
array.push(value);return array;});},intersect:function(array){return this.uniq().findAll(function(item){return array.detect(function(value){return item===value});});},clone:function(){return[].concat(this);},size:function(){return this.length;},inspect:function(){return'['+this.map(Object.inspect).join(', ')+']';},toJSON:function(){var results=[];this.each(function(object){var value=Object.toJSON(object);if(value!==undefined)results.push(value);});return'['+results.join(', ')+']';}});if(Object.isFunction(Array.prototype.forEach))
Array.prototype._each=Array.prototype.forEach;if(!Array.prototype.indexOf)Array.prototype.indexOf=function(item,i){i||(i=0);var length=this.length;if(i<0)i=length+i;for(;i<length;i++)
if(this[i]===item)return i;return-1;};if(!Array.prototype.lastIndexOf)Array.prototype.lastIndexOf=function(item,i){i=isNaN(i)?this.length:(i<0?this.length+i:i)+1;var n=this.slice(0,i).reverse().indexOf(item);return(n<0)?n:i-n-1;};Array.prototype.toArray=Array.prototype.clone;function $w(string){if(!Object.isString(string))return[];string=string.strip();return string?string.split(/\s+/):[];}
if(Prototype.Browser.Opera){Array.prototype.concat=function(){var array=[];for(var i=0,length=this.length;i<length;i++)array.push(this[i]);for(var i=0,length=arguments.length;i<length;i++){if(Object.isArray(arguments[i])){for(var j=0,arrayLength=arguments[i].length;j<arrayLength;j++)
array.push(arguments[i][j]);}else{array.push(arguments[i]);}}
return array;};}
Object.extend(Number.prototype,{toColorPart:function(){return this.toPaddedString(2,16);},succ:function(){return this+1;},times:function(iterator){$R(0,this,true).each(iterator);return this;},toPaddedString:function(length,radix){var string=this.toString(radix||10);return'0'.times(length-string.length)+string;},toJSON:function(){return isFinite(this)?this.toString():'null';}});$w('abs round ceil floor').each(function(method){Number.prototype[method]=Math[method].methodize();});function $H(object){return new Hash(object);};var Hash=Class.create(Enumerable,(function(){if(function(){var i=0,Test=function(value){this.key=value};Test.prototype.key='foo';for(var property in new Test('bar'))i++;return i>1;}()){function each(iterator){var cache=[];for(var key in this._object){var value=this._object[key];if(cache.include(key))continue;cache.push(key);var pair=[key,value];pair.key=key;pair.value=value;iterator(pair);}}}else{function each(iterator){for(var key in this._object){var value=this._object[key],pair=[key,value];pair.key=key;pair.value=value;iterator(pair);}}}
function toQueryPair(key,value){if(Object.isUndefined(value))return key;return key+'='+encodeURIComponent(String.interpret(value));}
return{initialize:function(object){this._object=Object.isHash(object)?object.toObject():Object.clone(object);},_each:each,set:function(key,value){return this._object[key]=value;},get:function(key){return this._object[key];},unset:function(key){var value=this._object[key];delete this._object[key];return value;},toObject:function(){return Object.clone(this._object);},keys:function(){return this.pluck('key');},values:function(){return this.pluck('value');},index:function(value){var match=this.detect(function(pair){return pair.value===value;});return match&&match.key;},merge:function(object){return this.clone().update(object);},update:function(object){return new Hash(object).inject(this,function(result,pair){result.set(pair.key,pair.value);return result;});},toQueryString:function(){return this.map(function(pair){var key=encodeURIComponent(pair.key),values=pair.value;if(values&&typeof values=='object'){if(Object.isArray(values))
return values.map(toQueryPair.curry(key)).join('&');}
return toQueryPair(key,values);}).join('&');},inspect:function(){return'#<Hash:{'+this.map(function(pair){return pair.map(Object.inspect).join(': ');}).join(', ')+'}>';},toJSON:function(){return Object.toJSON(this.toObject());},clone:function(){return new Hash(this);}}})());Hash.prototype.toTemplateReplacements=Hash.prototype.toObject;Hash.from=$H;var ObjectRange=Class.create(Enumerable,{initialize:function(start,end,exclusive){this.start=start;this.end=end;this.exclusive=exclusive;},_each:function(iterator){var value=this.start;while(this.include(value)){iterator(value);value=value.succ();}},include:function(value){if(value<this.start)
return false;if(this.exclusive)
return value<this.end;return value<=this.end;}});var $R=function(start,end,exclusive){return new ObjectRange(start,end,exclusive);};var Ajax={getTransport:function(){return Try.these(function(){return new XMLHttpRequest()},function(){return new ActiveXObject('Msxml2.XMLHTTP')},function(){return new ActiveXObject('Microsoft.XMLHTTP')})||false;},activeRequestCount:0};Ajax.Responders={responders:[],_each:function(iterator){this.responders._each(iterator);},register:function(responder){if(!this.include(responder))
this.responders.push(responder);},unregister:function(responder){this.responders=this.responders.without(responder);},dispatch:function(callback,request,transport,json){this.each(function(responder){if(Object.isFunction(responder[callback])){try{responder[callback].apply(responder,[request,transport,json]);}catch(e){}}});}};Object.extend(Ajax.Responders,Enumerable);Ajax.Responders.register({onCreate:function(){Ajax.activeRequestCount++},onComplete:function(){Ajax.activeRequestCount--}});Ajax.Base=Class.create({initialize:function(options){this.options={method:'post',asynchronous:true,contentType:'application/x-www-form-urlencoded',encoding:'UTF-8',parameters:'',evalJSON:true,evalJS:true};Object.extend(this.options,options||{});this.options.method=this.options.method.toLowerCase();if(Object.isString(this.options.parameters))
this.options.parameters=this.options.parameters.toQueryParams();}});Ajax.Request=Class.create(Ajax.Base,{_complete:false,initialize:function($super,url,options){$super(options);this.transport=Ajax.getTransport();this.request(url);},request:function(url){this.url=url;this.method=this.options.method;var params=Object.clone(this.options.parameters);if(!['get','post'].include(this.method)){params['_method']=this.method;this.method='post';}
this.parameters=params;if(params=Object.toQueryString(params)){if(this.method=='get')
this.url+=(this.url.include('?')?'&':'?')+params;else if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
params+='&_=';}
try{var response=new Ajax.Response(this);if(this.options.onCreate)this.options.onCreate(response);Ajax.Responders.dispatch('onCreate',this,response);this.transport.open(this.method.toUpperCase(),this.url,this.options.asynchronous);if(this.options.asynchronous)this.respondToReadyState.bind(this).defer(1);this.transport.onreadystatechange=this.onStateChange.bind(this);this.setRequestHeaders();this.body=this.method=='post'?(this.options.postBody||params):null;this.transport.send(this.body);if(!this.options.asynchronous&&this.transport.overrideMimeType)
this.onStateChange();}
catch(e){this.dispatchException(e);}},onStateChange:function(){var readyState=this.transport.readyState;if(readyState>1&&!((readyState==4)&&this._complete))
this.respondToReadyState(this.transport.readyState);},setRequestHeaders:function(){var headers={'X-Requested-With':'XMLHttpRequest','X-Prototype-Version':Prototype.Version,'Accept':'text/javascript, text/html, application/xml, text/xml, */*'};if(this.method=='post'){headers['Content-type']=this.options.contentType+
(this.options.encoding?'; charset='+this.options.encoding:'');if(this.transport.overrideMimeType&&(navigator.userAgent.match(/Gecko\/(\d{4})/)||[0,2005])[1]<2005)
headers['Connection']='close';}
if(typeof this.options.requestHeaders=='object'){var extras=this.options.requestHeaders;if(Object.isFunction(extras.push))
for(var i=0,length=extras.length;i<length;i+=2)
headers[extras[i]]=extras[i+1];else
$H(extras).each(function(pair){headers[pair.key]=pair.value});}
for(var name in headers)
this.transport.setRequestHeader(name,headers[name]);},success:function(){var status=this.getStatus();return!status||(status>=200&&status<300);},getStatus:function(){try{return this.transport.status||0;}catch(e){return 0}},respondToReadyState:function(readyState){var state=Ajax.Request.Events[readyState],response=new Ajax.Response(this);if(state=='Complete'){try{this._complete=true;(this.options['on'+response.status]||this.options['on'+(this.success()?'Success':'Failure')]||Prototype.emptyFunction)(response,response.headerJSON);}catch(e){this.dispatchException(e);}
var contentType=response.getHeader('Content-type');if(this.options.evalJS=='force'||(this.options.evalJS&&contentType&&contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
this.evalResponse();}
try{(this.options['on'+state]||Prototype.emptyFunction)(response,response.headerJSON);Ajax.Responders.dispatch('on'+state,this,response,response.headerJSON);}catch(e){this.dispatchException(e);}
if(state=='Complete'){this.transport.onreadystatechange=Prototype.emptyFunction;}},getHeader:function(name){try{return this.transport.getResponseHeader(name);}catch(e){return null}},evalResponse:function(){try{return eval((this.transport.responseText||'').unfilterJSON());}catch(e){this.dispatchException(e);}},dispatchException:function(exception){(this.options.onException||Prototype.emptyFunction)(this,exception);Ajax.Responders.dispatch('onException',this,exception);}});Ajax.Request.Events=['Uninitialized','Loading','Loaded','Interactive','Complete'];Ajax.Response=Class.create({initialize:function(request){this.request=request;var transport=this.transport=request.transport,readyState=this.readyState=transport.readyState;if((readyState>2&&!Prototype.Browser.IE)||readyState==4){this.status=this.getStatus();this.statusText=this.getStatusText();this.responseText=String.interpret(transport.responseText);this.headerJSON=this._getHeaderJSON();}
if(readyState==4){var xml=transport.responseXML;this.responseXML=xml===undefined?null:xml;this.responseJSON=this._getResponseJSON();}},status:0,statusText:'',getStatus:Ajax.Request.prototype.getStatus,getStatusText:function(){try{return this.transport.statusText||'';}catch(e){return''}},getHeader:Ajax.Request.prototype.getHeader,getAllHeaders:function(){try{return this.getAllResponseHeaders();}catch(e){return null}},getResponseHeader:function(name){return this.transport.getResponseHeader(name);},getAllResponseHeaders:function(){return this.transport.getAllResponseHeaders();},_getHeaderJSON:function(){var json=this.getHeader('X-JSON');if(!json)return null;json=decodeURIComponent(escape(json));try{return json.evalJSON(this.request.options.sanitizeJSON);}catch(e){this.request.dispatchException(e);}},_getResponseJSON:function(){var options=this.request.options;if(!options.evalJSON||(options.evalJSON!='force'&&!(this.getHeader('Content-type')||'').include('application/json')))
return null;try{return this.transport.responseText.evalJSON(options.sanitizeJSON);}catch(e){this.request.dispatchException(e);}}});Ajax.Updater=Class.create(Ajax.Request,{initialize:function($super,container,url,options){this.container={success:(container.success||container),failure:(container.failure||(container.success?null:container))};options=options||{};var onComplete=options.onComplete;options.onComplete=(function(response,param){this.updateContent(response.responseText);if(Object.isFunction(onComplete))onComplete(response,param);}).bind(this);$super(url,options);},updateContent:function(responseText){var receiver=this.container[this.success()?'success':'failure'],options=this.options;if(!options.evalScripts)responseText=responseText.stripScripts();if(receiver=$(receiver)){if(options.insertion){if(Object.isString(options.insertion)){var insertion={};insertion[options.insertion]=responseText;receiver.insert(insertion);}
else options.insertion(receiver,responseText);}
else receiver.update(responseText);}
if(this.success()){if(this.onComplete)this.onComplete.bind(this).defer();}}});Ajax.PeriodicalUpdater=Class.create(Ajax.Base,{initialize:function($super,container,url,options){$super(options);this.onComplete=this.options.onComplete;this.frequency=(this.options.frequency||2);this.decay=(this.options.decay||1);this.updater={};this.container=container;this.url=url;this.start();},start:function(){this.options.onComplete=this.updateComplete.bind(this);this.onTimerEvent();},stop:function(){this.updater.options.onComplete=undefined;clearTimeout(this.timer);(this.onComplete||Prototype.emptyFunction).apply(this,arguments);},updateComplete:function(response){if(this.options.decay){this.decay=(response.responseText==this.lastText?this.decay*this.options.decay:1);this.lastText=response.responseText;}
this.timer=this.onTimerEvent.bind(this).delay(this.decay*this.frequency);},onTimerEvent:function(){this.updater=new Ajax.Updater(this.container,this.url,this.options);}});function $(element){if(arguments.length>1){for(var i=0,elements=[],length=arguments.length;i<length;i++)
elements.push($(arguments[i]));return elements;}
if(Object.isString(element))
element=document.getElementById(element);return Element.extend(element);}
if(Prototype.BrowserFeatures.XPath){document._getElementsByXPath=function(expression,parentElement){var results=[];var query=document.evaluate(expression,$(parentElement)||document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);for(var i=0,length=query.snapshotLength;i<length;i++)
results.push(Element.extend(query.snapshotItem(i)));return results;};}
if(!window.Node)var Node={};if(!Node.ELEMENT_NODE){Object.extend(Node,{ELEMENT_NODE:1,ATTRIBUTE_NODE:2,TEXT_NODE:3,CDATA_SECTION_NODE:4,ENTITY_REFERENCE_NODE:5,ENTITY_NODE:6,PROCESSING_INSTRUCTION_NODE:7,COMMENT_NODE:8,DOCUMENT_NODE:9,DOCUMENT_TYPE_NODE:10,DOCUMENT_FRAGMENT_NODE:11,NOTATION_NODE:12});}
(function(){var element=this.Element;this.Element=function(tagName,attributes){attributes=attributes||{};tagName=tagName.toLowerCase();var cache=Element.cache;if(Prototype.Browser.IE&&attributes.name){tagName='<'+tagName+' name="'+attributes.name+'">';delete attributes.name;return Element.writeAttribute(document.createElement(tagName),attributes);}
if(!cache[tagName])cache[tagName]=Element.extend(document.createElement(tagName));return Element.writeAttribute(cache[tagName].cloneNode(false),attributes);};Object.extend(this.Element,element||{});}).call(window);Element.cache={};Element.Methods={visible:function(element){return $(element).style.display!='none';},toggle:function(element){element=$(element);Element[Element.visible(element)?'hide':'show'](element);return element;},hide:function(element){$(element).style.display='none';return element;},show:function(element){$(element).style.display='';return element;},remove:function(element){element=$(element);element.parentNode.removeChild(element);return element;},update:function(element,content){element=$(element);if(content&&content.toElement)content=content.toElement();if(Object.isElement(content))return element.update().insert(content);content=Object.toHTML(content);element.innerHTML=content.stripScripts();content.evalScripts.bind(content).defer();return element;},replace:function(element,content){element=$(element);if(content&&content.toElement)content=content.toElement();else if(!Object.isElement(content)){content=Object.toHTML(content);var range=element.ownerDocument.createRange();range.selectNode(element);content.evalScripts.bind(content).defer();content=range.createContextualFragment(content.stripScripts());}
element.parentNode.replaceChild(content,element);return element;},insert:function(element,insertions){element=$(element);if(Object.isString(insertions)||Object.isNumber(insertions)||Object.isElement(insertions)||(insertions&&(insertions.toElement||insertions.toHTML)))
insertions={bottom:insertions};var content,t,range;for(position in insertions){content=insertions[position];position=position.toLowerCase();t=Element._insertionTranslations[position];if(content&&content.toElement)content=content.toElement();if(Object.isElement(content)){t.insert(element,content);continue;}
content=Object.toHTML(content);range=element.ownerDocument.createRange();t.initializeRange(element,range);t.insert(element,range.createContextualFragment(content.stripScripts()));content.evalScripts.bind(content).defer();}
return element;},wrap:function(element,wrapper,attributes){element=$(element);if(Object.isElement(wrapper))
$(wrapper).writeAttribute(attributes||{});else if(Object.isString(wrapper))wrapper=new Element(wrapper,attributes);else wrapper=new Element('div',wrapper);if(element.parentNode)
element.parentNode.replaceChild(wrapper,element);wrapper.appendChild(element);return wrapper;},inspect:function(element){element=$(element);var result='<'+element.tagName.toLowerCase();$H({'id':'id','className':'class'}).each(function(pair){var property=pair.first(),attribute=pair.last();var value=(element[property]||'').toString();if(value)result+=' '+attribute+'='+value.inspect(true);});return result+'>';},recursivelyCollect:function(element,property){element=$(element);var elements=[];while(element=element[property])
if(element.nodeType==1)
elements.push(Element.extend(element));return elements;},ancestors:function(element){return $(element).recursivelyCollect('parentNode');},descendants:function(element){return $A($(element).getElementsByTagName('*')).each(Element.extend);},firstDescendant:function(element){element=$(element).firstChild;while(element&&element.nodeType!=1)element=element.nextSibling;return $(element);},immediateDescendants:function(element){if(!(element=$(element).firstChild))return[];while(element&&element.nodeType!=1)element=element.nextSibling;if(element)return[element].concat($(element).nextSiblings());return[];},previousSiblings:function(element){return $(element).recursivelyCollect('previousSibling');},nextSiblings:function(element){return $(element).recursivelyCollect('nextSibling');},siblings:function(element){element=$(element);return element.previousSiblings().reverse().concat(element.nextSiblings());},match:function(element,selector){if(Object.isString(selector))
selector=new Selector(selector);return selector.match($(element));},up:function(element,expression,index){element=$(element);if(arguments.length==1)return $(element.parentNode);var ancestors=element.ancestors();return expression?Selector.findElement(ancestors,expression,index):ancestors[index||0];},down:function(element,expression,index){element=$(element);if(arguments.length==1)return element.firstDescendant();var descendants=element.descendants();return expression?Selector.findElement(descendants,expression,index):descendants[index||0];},previous:function(element,expression,index){element=$(element);if(arguments.length==1)return $(Selector.handlers.previousElementSibling(element));var previousSiblings=element.previousSiblings();return expression?Selector.findElement(previousSiblings,expression,index):previousSiblings[index||0];},next:function(element,expression,index){element=$(element);if(arguments.length==1)return $(Selector.handlers.nextElementSibling(element));var nextSiblings=element.nextSiblings();return expression?Selector.findElement(nextSiblings,expression,index):nextSiblings[index||0];},select:function(){var args=$A(arguments),element=$(args.shift());return Selector.findChildElements(element,args);},adjacent:function(){var args=$A(arguments),element=$(args.shift());return Selector.findChildElements(element.parentNode,args).without(element);},identify:function(element){element=$(element);var id=element.readAttribute('id'),self=arguments.callee;if(id)return id;do{id='anonymous_element_'+self.counter++}while($(id));element.writeAttribute('id',id);return id;},readAttribute:function(element,name){element=$(element);if(Prototype.Browser.IE){var t=Element._attributeTranslations.read;if(t.values[name])return t.values[name](element,name);if(t.names[name])name=t.names[name];if(name.include(':')){return(!element.attributes||!element.attributes[name])?null:element.attributes[name].value;}}
return element.getAttribute(name);},writeAttribute:function(element,name,value){element=$(element);var attributes={},t=Element._attributeTranslations.write;if(typeof name=='object')attributes=name;else attributes[name]=value===undefined?true:value;for(var attr in attributes){var name=t.names[attr]||attr,value=attributes[attr];if(t.values[attr])name=t.values[attr](element,value);if(value===false||value===null)
element.removeAttribute(name);else if(value===true)
element.setAttribute(name,name);else element.setAttribute(name,value);}
return element;},getHeight:function(element){return $(element).getDimensions().height;},getWidth:function(element){return $(element).getDimensions().width;},classNames:function(element){return new Element.ClassNames(element);},hasClassName:function(element,className){if(!(element=$(element)))return;var elementClassName=element.className;return(elementClassName.length>0&&(elementClassName==className||new RegExp("(^|\\s)"+className+"(\\s|$)").test(elementClassName)));},addClassName:function(element,className){if(!(element=$(element)))return;if(!element.hasClassName(className))
element.className+=(element.className?' ':'')+className;return element;},removeClassName:function(element,className){if(!(element=$(element)))return;element.className=element.className.replace(new RegExp("(^|\\s+)"+className+"(\\s+|$)"),' ').strip();return element;},toggleClassName:function(element,className){if(!(element=$(element)))return;return element[element.hasClassName(className)?'removeClassName':'addClassName'](className);},cleanWhitespace:function(element){element=$(element);var node=element.firstChild;while(node){var nextNode=node.nextSibling;if(node.nodeType==3&&!/\S/.test(node.nodeValue))
element.removeChild(node);node=nextNode;}
return element;},empty:function(element){return $(element).innerHTML.blank();},descendantOf:function(element,ancestor){element=$(element),ancestor=$(ancestor);if(element.compareDocumentPosition)
return(element.compareDocumentPosition(ancestor)&8)===8;if(element.sourceIndex&&!Prototype.Browser.Opera){var e=element.sourceIndex,a=ancestor.sourceIndex,nextAncestor=ancestor.nextSibling;if(!nextAncestor){do{ancestor=ancestor.parentNode;}
while(!(nextAncestor=ancestor.nextSibling)&&ancestor.parentNode);}
if(nextAncestor)return(e>a&&e<nextAncestor.sourceIndex);}
while(element=element.parentNode)
if(element==ancestor)return true;return false;},scrollTo:function(element){element=$(element);var pos=element.cumulativeOffset();window.scrollTo(pos[0],pos[1]);return element;},getStyle:function(element,style){element=$(element);style=style=='float'?'cssFloat':style.camelize();var value=element.style[style];if(!value){var css=document.defaultView.getComputedStyle(element,null);value=css?css[style]:null;}
if(style=='opacity')return value?parseFloat(value):1.0;return value=='auto'?null:value;},getOpacity:function(element){return $(element).getStyle('opacity');},setStyle:function(element,styles){element=$(element);var elementStyle=element.style,match;if(Object.isString(styles)){element.style.cssText+=';'+styles;return styles.include('opacity')?element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]):element;}
for(var property in styles)
if(property=='opacity')element.setOpacity(styles[property]);else
elementStyle[(property=='float'||property=='cssFloat')?(elementStyle.styleFloat===undefined?'cssFloat':'styleFloat'):property]=styles[property];return element;},setOpacity:function(element,value){element=$(element);element.style.opacity=(value==1||value==='')?'':(value<0.00001)?0:value;return element;},getDimensions:function(element){element=$(element);var display=$(element).getStyle('display');if(display!='none'&&display!=null)
return{width:element.offsetWidth,height:element.offsetHeight};var els=element.style;var originalVisibility=els.visibility;var originalPosition=els.position;var originalDisplay=els.display;els.visibility='hidden';els.position='absolute';els.display='block';var originalWidth=element.clientWidth;var originalHeight=element.clientHeight;els.display=originalDisplay;els.position=originalPosition;els.visibility=originalVisibility;return{width:originalWidth,height:originalHeight};},makePositioned:function(element){element=$(element);var pos=Element.getStyle(element,'position');if(pos=='static'||!pos){element._madePositioned=true;element.style.position='relative';if(window.opera){element.style.top=0;element.style.left=0;}}
return element;},undoPositioned:function(element){element=$(element);if(element._madePositioned){element._madePositioned=undefined;element.style.position=element.style.top=element.style.left=element.style.bottom=element.style.right='';}
return element;},makeClipping:function(element){element=$(element);if(element._overflow)return element;element._overflow=Element.getStyle(element,'overflow')||'auto';if(element._overflow!=='hidden')
element.style.overflow='hidden';return element;},undoClipping:function(element){element=$(element);if(!element._overflow)return element;element.style.overflow=element._overflow=='auto'?'':element._overflow;element._overflow=null;return element;},cumulativeOffset:function(element){var valueT=0,valueL=0;do{valueT+=element.offsetTop||0;valueL+=element.offsetLeft||0;element=element.offsetParent;}while(element);return Element._returnOffset(valueL,valueT);},positionedOffset:function(element){var valueT=0,valueL=0;do{valueT+=element.offsetTop||0;valueL+=element.offsetLeft||0;element=element.offsetParent;if(element){if(element.tagName=='BODY')break;var p=Element.getStyle(element,'position');if(p=='relative'||p=='absolute')break;}}while(element);return Element._returnOffset(valueL,valueT);},absolutize:function(element){element=$(element);if(element.getStyle('position')=='absolute')return;var offsets=element.positionedOffset();var top=offsets[1];var left=offsets[0];var width=element.clientWidth;var height=element.clientHeight;element._originalLeft=left-parseFloat(element.style.left||0);element._originalTop=top-parseFloat(element.style.top||0);element._originalWidth=element.style.width;element._originalHeight=element.style.height;element.style.position='absolute';element.style.top=top+'px';element.style.left=left+'px';element.style.width=width+'px';element.style.height=height+'px';return element;},relativize:function(element){element=$(element);if(element.getStyle('position')=='relative')return;element.style.position='relative';var top=parseFloat(element.style.top||0)-(element._originalTop||0);var left=parseFloat(element.style.left||0)-(element._originalLeft||0);element.style.top=top+'px';element.style.left=left+'px';element.style.height=element._originalHeight;element.style.width=element._originalWidth;return element;},cumulativeScrollOffset:function(element){var valueT=0,valueL=0;do{valueT+=element.scrollTop||0;valueL+=element.scrollLeft||0;element=element.parentNode;}while(element);return Element._returnOffset(valueL,valueT);},getOffsetParent:function(element){if(element.offsetParent)return $(element.offsetParent);if(element==document.body)return $(element);while((element=element.parentNode)&&element!=document.body)
if(Element.getStyle(element,'position')!='static')
return $(element);return $(document.body);},viewportOffset:function(forElement){var valueT=0,valueL=0;var element=forElement;do{valueT+=element.offsetTop||0;valueL+=element.offsetLeft||0;if(element.offsetParent==document.body&&Element.getStyle(element,'position')=='absolute')break;}while(element=element.offsetParent);element=forElement;do{if(!Prototype.Browser.Opera||element.tagName=='BODY'){valueT-=element.scrollTop||0;valueL-=element.scrollLeft||0;}}while(element=element.parentNode);return Element._returnOffset(valueL,valueT);},clonePosition:function(element,source){var options=Object.extend({setLeft:true,setTop:true,setWidth:true,setHeight:true,offsetTop:0,offsetLeft:0},arguments[2]||{});source=$(source);var p=source.viewportOffset();element=$(element);var delta=[0,0];var parent=null;if(Element.getStyle(element,'position')=='absolute'){parent=element.getOffsetParent();delta=parent.viewportOffset();}
if(parent==document.body){delta[0]-=document.body.offsetLeft;delta[1]-=document.body.offsetTop;}
if(options.setLeft)element.style.left=(p[0]-delta[0]+options.offsetLeft)+'px';if(options.setTop)element.style.top=(p[1]-delta[1]+options.offsetTop)+'px';if(options.setWidth)element.style.width=source.offsetWidth+'px';if(options.setHeight)element.style.height=source.offsetHeight+'px';return element;}};Element.Methods.identify.counter=1;Object.extend(Element.Methods,{getElementsBySelector:Element.Methods.select,childElements:Element.Methods.immediateDescendants});Element._attributeTranslations={write:{names:{className:'class',htmlFor:'for'},values:{}}};if(!document.createRange||Prototype.Browser.Opera){Element.Methods.insert=function(element,insertions){element=$(element);if(Object.isString(insertions)||Object.isNumber(insertions)||Object.isElement(insertions)||(insertions&&(insertions.toElement||insertions.toHTML)))
insertions={bottom:insertions};var t=Element._insertionTranslations,content,position,pos,tagName;for(position in insertions){content=insertions[position];position=position.toLowerCase();pos=t[position];if(content&&content.toElement)content=content.toElement();if(Object.isElement(content)){pos.insert(element,content);continue;}
content=Object.toHTML(content);tagName=((position=='before'||position=='after')?element.parentNode:element).tagName.toUpperCase();if(t.tags[tagName]){var fragments=Element._getContentFromAnonymousElement(tagName,content.stripScripts());if(position=='top'||position=='after')fragments.reverse();fragments.each(pos.insert.curry(element));}
else element.insertAdjacentHTML(pos.adjacency,content.stripScripts());content.evalScripts.bind(content).defer();}
return element;};}
if(Prototype.Browser.Opera){Element.Methods._getStyle=Element.Methods.getStyle;Element.Methods.getStyle=function(element,style){switch(style){case'left':case'top':case'right':case'bottom':if(Element._getStyle(element,'position')=='static')return null;default:return Element._getStyle(element,style);}};Element.Methods._readAttribute=Element.Methods.readAttribute;Element.Methods.readAttribute=function(element,attribute){if(attribute=='title')return element.title;return Element._readAttribute(element,attribute);};}
else if(Prototype.Browser.IE){$w('positionedOffset getOffsetParent viewportOffset').each(function(method){Element.Methods[method]=Element.Methods[method].wrap(function(proceed,element){element=$(element);var position=element.getStyle('position');if(position!='static')return proceed(element);element.setStyle({position:'relative'});var value=proceed(element);element.setStyle({position:position});return value;});});Element.Methods.getStyle=function(element,style){element=$(element);style=(style=='float'||style=='cssFloat')?'styleFloat':style.camelize();var value=element.style[style];if(!value&&element.currentStyle)value=element.currentStyle[style];if(style=='opacity'){if(value=(element.getStyle('filter')||'').match(/alpha\(opacity=(.*)\)/))
if(value[1])return parseFloat(value[1])/100;return 1.0;}
if(value=='auto'){if((style=='width'||style=='height')&&(element.getStyle('display')!='none'))
return element['offset'+style.capitalize()]+'px';return null;}
return value;};Element.Methods.setOpacity=function(element,value){function stripAlpha(filter){return filter.replace(/alpha\([^\)]*\)/gi,'');}
element=$(element);var currentStyle=element.currentStyle;if((currentStyle&&!currentStyle.hasLayout)||(!currentStyle&&element.style.zoom=='normal'))
element.style.zoom=1;var filter=element.getStyle('filter'),style=element.style;if(value==1||value===''){(filter=stripAlpha(filter))?style.filter=filter:style.removeAttribute('filter');return element;}else if(value<0.00001)value=0;style.filter=stripAlpha(filter)+'alpha(opacity='+(value*100)+')';return element;};Element._attributeTranslations={read:{names:{'class':'className','for':'htmlFor'},values:{_getAttr:function(element,attribute){return element.getAttribute(attribute,2);},_getAttrNode:function(element,attribute){var node=element.getAttributeNode(attribute);return node?node.value:"";},_getEv:function(element,attribute){var attribute=element.getAttribute(attribute);return attribute?attribute.toString().slice(23,-2):null;},_flag:function(element,attribute){return $(element).hasAttribute(attribute)?attribute:null;},style:function(element){return element.style.cssText.toLowerCase();},title:function(element){return element.title;}}}};Element._attributeTranslations.write={names:Object.clone(Element._attributeTranslations.read.names),values:{checked:function(element,value){element.checked=!!value;},style:function(element,value){element.style.cssText=value?value:'';}}};Element._attributeTranslations.has={};$w('colSpan rowSpan vAlign dateTime accessKey tabIndex '+'encType maxLength readOnly longDesc').each(function(attr){Element._attributeTranslations.write.names[attr.toLowerCase()]=attr;Element._attributeTranslations.has[attr.toLowerCase()]=attr;});(function(v){Object.extend(v,{href:v._getAttr,src:v._getAttr,type:v._getAttr,action:v._getAttrNode,disabled:v._flag,checked:v._flag,readonly:v._flag,multiple:v._flag,onload:v._getEv,onunload:v._getEv,onclick:v._getEv,ondblclick:v._getEv,onmousedown:v._getEv,onmouseup:v._getEv,onmouseover:v._getEv,onmousemove:v._getEv,onmouseout:v._getEv,onfocus:v._getEv,onblur:v._getEv,onkeypress:v._getEv,onkeydown:v._getEv,onkeyup:v._getEv,onsubmit:v._getEv,onreset:v._getEv,onselect:v._getEv,onchange:v._getEv});})(Element._attributeTranslations.read.values);}
else if(Prototype.Browser.Gecko&&/rv:1\.8\.0/.test(navigator.userAgent)){Element.Methods.setOpacity=function(element,value){element=$(element);element.style.opacity=(value==1)?0.999999:(value==='')?'':(value<0.00001)?0:value;return element;};}
else if(Prototype.Browser.WebKit){Element.Methods.setOpacity=function(element,value){element=$(element);element.style.opacity=(value==1||value==='')?'':(value<0.00001)?0:value;if(value==1)
if(element.tagName=='IMG'&&element.width){element.width++;element.width--;}else try{var n=document.createTextNode(' ');element.appendChild(n);element.removeChild(n);}catch(e){}
return element;};Element.Methods.cumulativeOffset=function(element){var valueT=0,valueL=0;do{valueT+=element.offsetTop||0;valueL+=element.offsetLeft||0;if(element.offsetParent==document.body)
if(Element.getStyle(element,'position')=='absolute')break;element=element.offsetParent;}while(element);return Element._returnOffset(valueL,valueT);};}
if(Prototype.Browser.IE||Prototype.Browser.Opera){Element.Methods.update=function(element,content){element=$(element);if(content&&content.toElement)content=content.toElement();if(Object.isElement(content))return element.update().insert(content);content=Object.toHTML(content);var tagName=element.tagName.toUpperCase();if(tagName in Element._insertionTranslations.tags){$A(element.childNodes).each(function(node){element.removeChild(node)});Element._getContentFromAnonymousElement(tagName,content.stripScripts()).each(function(node){element.appendChild(node)});}
else element.innerHTML=content.stripScripts();content.evalScripts.bind(content).defer();return element;};}
if(document.createElement('div').outerHTML){Element.Methods.replace=function(element,content){element=$(element);if(content&&content.toElement)content=content.toElement();if(Object.isElement(content)){element.parentNode.replaceChild(content,element);return element;}
content=Object.toHTML(content);var parent=element.parentNode,tagName=parent.tagName.toUpperCase();if(Element._insertionTranslations.tags[tagName]){var nextSibling=element.next();var fragments=Element._getContentFromAnonymousElement(tagName,content.stripScripts());parent.removeChild(element);if(nextSibling)
fragments.each(function(node){parent.insertBefore(node,nextSibling)});else
fragments.each(function(node){parent.appendChild(node)});}
else element.outerHTML=content.stripScripts();content.evalScripts.bind(content).defer();return element;};}
Element._returnOffset=function(l,t){var result=[l,t];result.left=l;result.top=t;return result;};Element._getContentFromAnonymousElement=function(tagName,html){var div=new Element('div'),t=Element._insertionTranslations.tags[tagName];div.innerHTML=t[0]+html+t[1];t[2].times(function(){div=div.firstChild});return $A(div.childNodes);};Element._insertionTranslations={before:{adjacency:'beforeBegin',insert:function(element,node){element.parentNode.insertBefore(node,element);},initializeRange:function(element,range){range.setStartBefore(element);}},top:{adjacency:'afterBegin',insert:function(element,node){element.insertBefore(node,element.firstChild);},initializeRange:function(element,range){range.selectNodeContents(element);range.collapse(true);}},bottom:{adjacency:'beforeEnd',insert:function(element,node){element.appendChild(node);}},after:{adjacency:'afterEnd',insert:function(element,node){element.parentNode.insertBefore(node,element.nextSibling);},initializeRange:function(element,range){range.setStartAfter(element);}},tags:{TABLE:['<table>','</table>',1],TBODY:['<table><tbody>','</tbody></table>',2],TR:['<table><tbody><tr>','</tr></tbody></table>',3],TD:['<table><tbody><tr><td>','</td></tr></tbody></table>',4],SELECT:['<select>','</select>',1]}};(function(){this.bottom.initializeRange=this.top.initializeRange;Object.extend(this.tags,{THEAD:this.tags.TBODY,TFOOT:this.tags.TBODY,TH:this.tags.TD});}).call(Element._insertionTranslations);Element.Methods.Simulated={hasAttribute:function(element,attribute){attribute=Element._attributeTranslations.has[attribute]||attribute;var node=$(element).getAttributeNode(attribute);return node&&node.specified;}};Element.Methods.ByTag={};Object.extend(Element,Element.Methods);if(!Prototype.BrowserFeatures.ElementExtensions&&document.createElement('div').__proto__){window.HTMLElement={};window.HTMLElement.prototype=document.createElement('div').__proto__;Prototype.BrowserFeatures.ElementExtensions=true;}
Element.extend=(function(){if(Prototype.BrowserFeatures.SpecificElementExtensions)
return Prototype.K;var Methods={},ByTag=Element.Methods.ByTag;var extend=Object.extend(function(element){if(!element||element._extendedByPrototype||element.nodeType!=1||element==window)return element;var methods=Object.clone(Methods),tagName=element.tagName,property,value;if(ByTag[tagName])Object.extend(methods,ByTag[tagName]);for(property in methods){value=methods[property];if(Object.isFunction(value)&&!(property in element))
element[property]=value.methodize();}
element._extendedByPrototype=Prototype.emptyFunction;return element;},{refresh:function(){if(!Prototype.BrowserFeatures.ElementExtensions){Object.extend(Methods,Element.Methods);Object.extend(Methods,Element.Methods.Simulated);}}});extend.refresh();return extend;})();Element.hasAttribute=function(element,attribute){if(element.hasAttribute)return element.hasAttribute(attribute);return Element.Methods.Simulated.hasAttribute(element,attribute);};Element.addMethods=function(methods){var F=Prototype.BrowserFeatures,T=Element.Methods.ByTag;if(!methods){Object.extend(Form,Form.Methods);Object.extend(Form.Element,Form.Element.Methods);Object.extend(Element.Methods.ByTag,{"FORM":Object.clone(Form.Methods),"INPUT":Object.clone(Form.Element.Methods),"SELECT":Object.clone(Form.Element.Methods),"TEXTAREA":Object.clone(Form.Element.Methods)});}
if(arguments.length==2){var tagName=methods;methods=arguments[1];}
if(!tagName)Object.extend(Element.Methods,methods||{});else{if(Object.isArray(tagName))tagName.each(extend);else extend(tagName);}
function extend(tagName){tagName=tagName.toUpperCase();if(!Element.Methods.ByTag[tagName])
Element.Methods.ByTag[tagName]={};Object.extend(Element.Methods.ByTag[tagName],methods);}
function copy(methods,destination,onlyIfAbsent){onlyIfAbsent=onlyIfAbsent||false;for(var property in methods){var value=methods[property];if(!Object.isFunction(value))continue;if(!onlyIfAbsent||!(property in destination))
destination[property]=value.methodize();}}
function findDOMClass(tagName){var klass;var trans={"OPTGROUP":"OptGroup","TEXTAREA":"TextArea","P":"Paragraph","FIELDSET":"FieldSet","UL":"UList","OL":"OList","DL":"DList","DIR":"Directory","H1":"Heading","H2":"Heading","H3":"Heading","H4":"Heading","H5":"Heading","H6":"Heading","Q":"Quote","INS":"Mod","DEL":"Mod","A":"Anchor","IMG":"Image","CAPTION":"TableCaption","COL":"TableCol","COLGROUP":"TableCol","THEAD":"TableSection","TFOOT":"TableSection","TBODY":"TableSection","TR":"TableRow","TH":"TableCell","TD":"TableCell","FRAMESET":"FrameSet","IFRAME":"IFrame"};if(trans[tagName])klass='HTML'+trans[tagName]+'Element';if(window[klass])return window[klass];klass='HTML'+tagName+'Element';if(window[klass])return window[klass];klass='HTML'+tagName.capitalize()+'Element';if(window[klass])return window[klass];window[klass]={};window[klass].prototype=document.createElement(tagName).__proto__;return window[klass];}
if(F.ElementExtensions){copy(Element.Methods,HTMLElement.prototype);copy(Element.Methods.Simulated,HTMLElement.prototype,true);}
if(F.SpecificElementExtensions){for(var tag in Element.Methods.ByTag){var klass=findDOMClass(tag);if(Object.isUndefined(klass))continue;copy(T[tag],klass.prototype);}}
Object.extend(Element,Element.Methods);delete Element.ByTag;if(Element.extend.refresh)Element.extend.refresh();Element.cache={};};document.viewport={getDimensions:function(){var dimensions={};$w('width height').each(function(d){var D=d.capitalize();dimensions[d]=self['inner'+D]||(document.documentElement['client'+D]||document.body['client'+D]);});return dimensions;},getWidth:function(){return this.getDimensions().width;},getHeight:function(){return this.getDimensions().height;},getScrollOffsets:function(){return Element._returnOffset(window.pageXOffset||document.documentElement.scrollLeft||document.body.scrollLeft,window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop);}};var Selector=Class.create({initialize:function(expression){this.expression=expression.strip();this.compileMatcher();},compileMatcher:function(){if(Prototype.BrowserFeatures.XPath&&!(/(\[[\w-]*?:|:checked)/).test(this.expression))
return this.compileXPathMatcher();var e=this.expression,ps=Selector.patterns,h=Selector.handlers,c=Selector.criteria,le,p,m;if(Selector._cache[e]){this.matcher=Selector._cache[e];return;}
this.matcher=["this.matcher = function(root) {","var r = root, h = Selector.handlers, c = false, n;"];while(e&&le!=e&&(/\S/).test(e)){le=e;for(var i in ps){p=ps[i];if(m=e.match(p)){this.matcher.push(Object.isFunction(c[i])?c[i](m):new Template(c[i]).evaluate(m));e=e.replace(m[0],'');break;}}}
this.matcher.push("return h.unique(n);\n}");eval(this.matcher.join('\n'));Selector._cache[this.expression]=this.matcher;},compileXPathMatcher:function(){var e=this.expression,ps=Selector.patterns,x=Selector.xpath,le,m;if(Selector._cache[e]){this.xpath=Selector._cache[e];return;}
this.matcher=['.//*'];while(e&&le!=e&&(/\S/).test(e)){le=e;for(var i in ps){if(m=e.match(ps[i])){this.matcher.push(Object.isFunction(x[i])?x[i](m):new Template(x[i]).evaluate(m));e=e.replace(m[0],'');break;}}}
this.xpath=this.matcher.join('');Selector._cache[this.expression]=this.xpath;},findElements:function(root){root=root||document;if(this.xpath)return document._getElementsByXPath(this.xpath,root);return this.matcher(root);},match:function(element){this.tokens=[];var e=this.expression,ps=Selector.patterns,as=Selector.assertions;var le,p,m;while(e&&le!==e&&(/\S/).test(e)){le=e;for(var i in ps){p=ps[i];if(m=e.match(p)){if(as[i]){this.tokens.push([i,Object.clone(m)]);e=e.replace(m[0],'');}else{return this.findElements(document).include(element);}}}}
var match=true,name,matches;for(var i=0,token;token=this.tokens[i];i++){name=token[0],matches=token[1];if(!Selector.assertions[name](element,matches)){match=false;break;}}
return match;},toString:function(){return this.expression;},inspect:function(){return"#<Selector:"+this.expression.inspect()+">";}});Object.extend(Selector,{_cache:{},xpath:{descendant:"//*",child:"/*",adjacent:"/following-sibling::*[1]",laterSibling:'/following-sibling::*',tagName:function(m){if(m[1]=='*')return'';return"[local-name()='"+m[1].toLowerCase()+"' or local-name()='"+m[1].toUpperCase()+"']";},className:"[contains(concat(' ', @class, ' '), ' #{1} ')]",id:"[@id='#{1}']",attrPresence:"[@#{1}]",attr:function(m){m[3]=m[5]||m[6];return new Template(Selector.xpath.operators[m[2]]).evaluate(m);},pseudo:function(m){var h=Selector.xpath.pseudos[m[1]];if(!h)return'';if(Object.isFunction(h))return h(m);return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);},operators:{'=':"[@#{1}='#{3}']",'!=':"[@#{1}!='#{3}']",'^=':"[starts-with(@#{1}, '#{3}')]",'$=':"[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",'*=':"[contains(@#{1}, '#{3}')]",'~=':"[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",'|=':"[contains(concat('-', @#{1}, '-'), '-#{3}-')]"},pseudos:{'first-child':'[not(preceding-sibling::*)]','last-child':'[not(following-sibling::*)]','only-child':'[not(preceding-sibling::* or following-sibling::*)]','empty':"[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",'checked':"[@checked]",'disabled':"[@disabled]",'enabled':"[not(@disabled)]",'not':function(m){var e=m[6],p=Selector.patterns,x=Selector.xpath,le,m,v;var exclusion=[];while(e&&le!=e&&(/\S/).test(e)){le=e;for(var i in p){if(m=e.match(p[i])){v=Object.isFunction(x[i])?x[i](m):new Template(x[i]).evaluate(m);exclusion.push("("+v.substring(1,v.length-1)+")");e=e.replace(m[0],'');break;}}}
return"[not("+exclusion.join(" and ")+")]";},'nth-child':function(m){return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ",m);},'nth-last-child':function(m){return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ",m);},'nth-of-type':function(m){return Selector.xpath.pseudos.nth("position() ",m);},'nth-last-of-type':function(m){return Selector.xpath.pseudos.nth("(last() + 1 - position()) ",m);},'first-of-type':function(m){m[6]="1";return Selector.xpath.pseudos['nth-of-type'](m);},'last-of-type':function(m){m[6]="1";return Selector.xpath.pseudos['nth-last-of-type'](m);},'only-of-type':function(m){var p=Selector.xpath.pseudos;return p['first-of-type'](m)+p['last-of-type'](m);},nth:function(fragment,m){var mm,formula=m[6],predicate;if(formula=='even')formula='2n+0';if(formula=='odd')formula='2n+1';if(mm=formula.match(/^(\d+)$/))
return'['+fragment+"= "+mm[1]+']';if(mm=formula.match(/^(-?\d*)?n(([+-])(\d+))?/)){if(mm[1]=="-")mm[1]=-1;var a=mm[1]?Number(mm[1]):1;var b=mm[2]?Number(mm[2]):0;predicate="[((#{fragment} - #{b}) mod #{a} = 0) and "+"((#{fragment} - #{b}) div #{a} >= 0)]";return new Template(predicate).evaluate({fragment:fragment,a:a,b:b});}}}},criteria:{tagName:'n = h.tagName(n, r, "#{1}", c);   c = false;',className:'n = h.className(n, r, "#{1}", c); c = false;',id:'n = h.id(n, r, "#{1}", c);        c = false;',attrPresence:'n = h.attrPresence(n, r, "#{1}"); c = false;',attr:function(m){m[3]=(m[5]||m[6]);return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);},pseudo:function(m){if(m[6])m[6]=m[6].replace(/"/g,'\\"');return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);},descendant:'c = "descendant";',child:'c = "child";',adjacent:'c = "adjacent";',laterSibling:'c = "laterSibling";'},patterns:{laterSibling:/^\s*~\s*/,child:/^\s*>\s*/,adjacent:/^\s*\+\s*/,descendant:/^\s/,tagName:/^\s*(\*|[\w\-]+)(\b|$)?/,id:/^#([\w\-\*]+)(\b|$)/,className:/^\.([\w\-\*]+)(\b|$)/,pseudo:/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/,attrPresence:/^\[([\w]+)\]/,attr:/\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/},assertions:{tagName:function(element,matches){return matches[1].toUpperCase()==element.tagName.toUpperCase();},className:function(element,matches){return Element.hasClassName(element,matches[1]);},id:function(element,matches){return element.id===matches[1];},attrPresence:function(element,matches){return Element.hasAttribute(element,matches[1]);},attr:function(element,matches){var nodeValue=Element.readAttribute(element,matches[1]);return Selector.operators[matches[2]](nodeValue,matches[3]);}},handlers:{concat:function(a,b){for(var i=0,node;node=b[i];i++)
a.push(node);return a;},mark:function(nodes){for(var i=0,node;node=nodes[i];i++)
node._counted=true;return nodes;},unmark:function(nodes){for(var i=0,node;node=nodes[i];i++)
node._counted=undefined;return nodes;},index:function(parentNode,reverse,ofType){parentNode._counted=true;if(reverse){for(var nodes=parentNode.childNodes,i=nodes.length-1,j=1;i>=0;i--){var node=nodes[i];if(node.nodeType==1&&(!ofType||node._counted))node.nodeIndex=j++;}}else{for(var i=0,j=1,nodes=parentNode.childNodes;node=nodes[i];i++)
if(node.nodeType==1&&(!ofType||node._counted))node.nodeIndex=j++;}},unique:function(nodes){if(nodes.length==0)return nodes;var results=[],n;for(var i=0,l=nodes.length;i<l;i++)
if(!(n=nodes[i])._counted){n._counted=true;results.push(Element.extend(n));}
return Selector.handlers.unmark(results);},descendant:function(nodes){var h=Selector.handlers;for(var i=0,results=[],node;node=nodes[i];i++)
h.concat(results,node.getElementsByTagName('*'));return results;},child:function(nodes){var h=Selector.handlers;for(var i=0,results=[],node;node=nodes[i];i++){for(var j=0,children=[],child;child=node.childNodes[j];j++)
if(child.nodeType==1&&child.tagName!='!')results.push(child);}
return results;},adjacent:function(nodes){for(var i=0,results=[],node;node=nodes[i];i++){var next=this.nextElementSibling(node);if(next)results.push(next);}
return results;},laterSibling:function(nodes){var h=Selector.handlers;for(var i=0,results=[],node;node=nodes[i];i++)
h.concat(results,Element.nextSiblings(node));return results;},nextElementSibling:function(node){while(node=node.nextSibling)
if(node.nodeType==1)return node;return null;},previousElementSibling:function(node){while(node=node.previousSibling)
if(node.nodeType==1)return node;return null;},tagName:function(nodes,root,tagName,combinator){tagName=tagName.toUpperCase();var results=[],h=Selector.handlers;if(nodes){if(combinator){if(combinator=="descendant"){for(var i=0,node;node=nodes[i];i++)
h.concat(results,node.getElementsByTagName(tagName));return results;}else nodes=this[combinator](nodes);if(tagName=="*")return nodes;}
for(var i=0,node;node=nodes[i];i++)
if(node.tagName.toUpperCase()==tagName)results.push(node);return results;}else return root.getElementsByTagName(tagName);},id:function(nodes,root,id,combinator){var targetNode=$(id),h=Selector.handlers;if(!targetNode)return[];if(!nodes&&root==document)return[targetNode];if(nodes){if(combinator){if(combinator=='child'){for(var i=0,node;node=nodes[i];i++)
if(targetNode.parentNode==node)return[targetNode];}else if(combinator=='descendant'){for(var i=0,node;node=nodes[i];i++)
if(Element.descendantOf(targetNode,node))return[targetNode];}else if(combinator=='adjacent'){for(var i=0,node;node=nodes[i];i++)
if(Selector.handlers.previousElementSibling(targetNode)==node)
return[targetNode];}else nodes=h[combinator](nodes);}
for(var i=0,node;node=nodes[i];i++)
if(node==targetNode)return[targetNode];return[];}
return(targetNode&&Element.descendantOf(targetNode,root))?[targetNode]:[];},className:function(nodes,root,className,combinator){if(nodes&&combinator)nodes=this[combinator](nodes);return Selector.handlers.byClassName(nodes,root,className);},byClassName:function(nodes,root,className){if(!nodes)nodes=Selector.handlers.descendant([root]);var needle=' '+className+' ';for(var i=0,results=[],node,nodeClassName;node=nodes[i];i++){nodeClassName=node.className;if(nodeClassName.length==0)continue;if(nodeClassName==className||(' '+nodeClassName+' ').include(needle))
results.push(node);}
return results;},attrPresence:function(nodes,root,attr){if(!nodes)nodes=root.getElementsByTagName("*");var results=[];for(var i=0,node;node=nodes[i];i++)
if(Element.hasAttribute(node,attr))results.push(node);return results;},attr:function(nodes,root,attr,value,operator){if(!nodes)nodes=root.getElementsByTagName("*");var handler=Selector.operators[operator],results=[];for(var i=0,node;node=nodes[i];i++){var nodeValue=Element.readAttribute(node,attr);if(nodeValue===null)continue;if(handler(nodeValue,value))results.push(node);}
return results;},pseudo:function(nodes,name,value,root,combinator){if(nodes&&combinator)nodes=this[combinator](nodes);if(!nodes)nodes=root.getElementsByTagName("*");return Selector.pseudos[name](nodes,value,root);}},pseudos:{'first-child':function(nodes,value,root){for(var i=0,results=[],node;node=nodes[i];i++){if(Selector.handlers.previousElementSibling(node))continue;results.push(node);}
return results;},'last-child':function(nodes,value,root){for(var i=0,results=[],node;node=nodes[i];i++){if(Selector.handlers.nextElementSibling(node))continue;results.push(node);}
return results;},'only-child':function(nodes,value,root){var h=Selector.handlers;for(var i=0,results=[],node;node=nodes[i];i++)
if(!h.previousElementSibling(node)&&!h.nextElementSibling(node))
results.push(node);return results;},'nth-child':function(nodes,formula,root){return Selector.pseudos.nth(nodes,formula,root);},'nth-last-child':function(nodes,formula,root){return Selector.pseudos.nth(nodes,formula,root,true);},'nth-of-type':function(nodes,formula,root){return Selector.pseudos.nth(nodes,formula,root,false,true);},'nth-last-of-type':function(nodes,formula,root){return Selector.pseudos.nth(nodes,formula,root,true,true);},'first-of-type':function(nodes,formula,root){return Selector.pseudos.nth(nodes,"1",root,false,true);},'last-of-type':function(nodes,formula,root){return Selector.pseudos.nth(nodes,"1",root,true,true);},'only-of-type':function(nodes,formula,root){var p=Selector.pseudos;return p['last-of-type'](p['first-of-type'](nodes,formula,root),formula,root);},getIndices:function(a,b,total){if(a==0)return b>0?[b]:[];return $R(1,total).inject([],function(memo,i){if(0==(i-b)%a&&(i-b)/a>=0)memo.push(i);return memo;});},nth:function(nodes,formula,root,reverse,ofType){if(nodes.length==0)return[];if(formula=='even')formula='2n+0';if(formula=='odd')formula='2n+1';var h=Selector.handlers,results=[],indexed=[],m;h.mark(nodes);for(var i=0,node;node=nodes[i];i++){if(!node.parentNode._counted){h.index(node.parentNode,reverse,ofType);indexed.push(node.parentNode);}}
if(formula.match(/^\d+$/)){formula=Number(formula);for(var i=0,node;node=nodes[i];i++)
if(node.nodeIndex==formula)results.push(node);}else if(m=formula.match(/^(-?\d*)?n(([+-])(\d+))?/)){if(m[1]=="-")m[1]=-1;var a=m[1]?Number(m[1]):1;var b=m[2]?Number(m[2]):0;var indices=Selector.pseudos.getIndices(a,b,nodes.length);for(var i=0,node,l=indices.length;node=nodes[i];i++){for(var j=0;j<l;j++)
if(node.nodeIndex==indices[j])results.push(node);}}
h.unmark(nodes);h.unmark(indexed);return results;},'empty':function(nodes,value,root){for(var i=0,results=[],node;node=nodes[i];i++){if(node.tagName=='!'||(node.firstChild&&!node.innerHTML.match(/^\s*$/)))continue;results.push(node);}
return results;},'not':function(nodes,selector,root){var h=Selector.handlers,selectorType,m;var exclusions=new Selector(selector).findElements(root);h.mark(exclusions);for(var i=0,results=[],node;node=nodes[i];i++)
if(!node._counted)results.push(node);h.unmark(exclusions);return results;},'enabled':function(nodes,value,root){for(var i=0,results=[],node;node=nodes[i];i++)
if(!node.disabled)results.push(node);return results;},'disabled':function(nodes,value,root){for(var i=0,results=[],node;node=nodes[i];i++)
if(node.disabled)results.push(node);return results;},'checked':function(nodes,value,root){for(var i=0,results=[],node;node=nodes[i];i++)
if(node.checked)results.push(node);return results;}},operators:{'=':function(nv,v){return nv==v;},'!=':function(nv,v){return nv!=v;},'^=':function(nv,v){return nv.startsWith(v);},'$=':function(nv,v){return nv.endsWith(v);},'*=':function(nv,v){return nv.include(v);},'~=':function(nv,v){return(' '+nv+' ').include(' '+v+' ');},'|=':function(nv,v){return('-'+nv.toUpperCase()+'-').include('-'+v.toUpperCase()+'-');}},matchElements:function(elements,expression){var matches=new Selector(expression).findElements(),h=Selector.handlers;h.mark(matches);for(var i=0,results=[],element;element=elements[i];i++)
if(element._counted)results.push(element);h.unmark(matches);return results;},findElement:function(elements,expression,index){if(Object.isNumber(expression)){index=expression;expression=false;}
return Selector.matchElements(elements,expression||'*')[index||0];},findChildElements:function(element,expressions){var exprs=expressions.join(','),expressions=[];exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/,function(m){expressions.push(m[1].strip());});var results=[],h=Selector.handlers;for(var i=0,l=expressions.length,selector;i<l;i++){selector=new Selector(expressions[i].strip());h.concat(results,selector.findElements(element));}
return(l>1)?h.unique(results):results;}});function $$(){return Selector.findChildElements(document,$A(arguments));}
var Form={reset:function(form){$(form).reset();return form;},serializeElements:function(elements,options){if(typeof options!='object')options={hash:!!options};else if(options.hash===undefined)options.hash=true;var key,value,submitted=false,submit=options.submit;var data=elements.inject({},function(result,element){if(!element.disabled&&element.name){key=element.name;value=$(element).getValue();if(value!=null&&(element.type!='submit'||(!submitted&&submit!==false&&(!submit||key==submit)&&(submitted=true)))){if(key in result){if(!Object.isArray(result[key]))result[key]=[result[key]];result[key].push(value);}
else result[key]=value;}}
return result;});return options.hash?data:Object.toQueryString(data);}};Form.Methods={serialize:function(form,options){return Form.serializeElements(Form.getElements(form),options);},getElements:function(form){return $A($(form).getElementsByTagName('*')).inject([],function(elements,child){if(Form.Element.Serializers[child.tagName.toLowerCase()])
elements.push(Element.extend(child));return elements;});},getInputs:function(form,typeName,name){form=$(form);var inputs=form.getElementsByTagName('input');if(!typeName&&!name)return $A(inputs).map(Element.extend);for(var i=0,matchingInputs=[],length=inputs.length;i<length;i++){var input=inputs[i];if((typeName&&input.type!=typeName)||(name&&input.name!=name))
continue;matchingInputs.push(Element.extend(input));}
return matchingInputs;},disable:function(form){form=$(form);Form.getElements(form).invoke('disable');return form;},enable:function(form){form=$(form);Form.getElements(form).invoke('enable');return form;},findFirstElement:function(form){var elements=$(form).getElements().findAll(function(element){return'hidden'!=element.type&&!element.disabled;});var firstByIndex=elements.findAll(function(element){return element.hasAttribute('tabIndex')&&element.tabIndex>=0;}).sortBy(function(element){return element.tabIndex}).first();return firstByIndex?firstByIndex:elements.find(function(element){return['input','select','textarea'].include(element.tagName.toLowerCase());});},focusFirstElement:function(form){form=$(form);form.findFirstElement().activate();return form;},request:function(form,options){form=$(form),options=Object.clone(options||{});var params=options.parameters,action=form.readAttribute('action')||'';if(action.blank())action=window.location.href;options.parameters=form.serialize(true);if(params){if(Object.isString(params))params=params.toQueryParams();Object.extend(options.parameters,params);}
if(form.hasAttribute('method')&&!options.method)
options.method=form.method;return new Ajax.Request(action,options);}};Form.Element={focus:function(element){$(element).focus();return element;},select:function(element){$(element).select();return element;}};Form.Element.Methods={serialize:function(element){element=$(element);if(!element.disabled&&element.name){var value=element.getValue();if(value!=undefined){var pair={};pair[element.name]=value;return Object.toQueryString(pair);}}
return'';},getValue:function(element){element=$(element);var method=element.tagName.toLowerCase();return Form.Element.Serializers[method](element);},setValue:function(element,value){element=$(element);var method=element.tagName.toLowerCase();Form.Element.Serializers[method](element,value);return element;},clear:function(element){$(element).value='';return element;},present:function(element){return $(element).value!='';},activate:function(element){element=$(element);try{element.focus();if(element.select&&(element.tagName.toLowerCase()!='input'||!['button','reset','submit'].include(element.type)))
element.select();}catch(e){}
return element;},disable:function(element){element=$(element);element.blur();element.disabled=true;return element;},enable:function(element){element=$(element);element.disabled=false;return element;}};var Field=Form.Element;var $F=Form.Element.Methods.getValue;Form.Element.Serializers={input:function(element,value){switch(element.type.toLowerCase()){case'checkbox':case'radio':return Form.Element.Serializers.inputSelector(element,value);default:return Form.Element.Serializers.textarea(element,value);}},inputSelector:function(element,value){if(value===undefined)return element.checked?element.value:null;else element.checked=!!value;},textarea:function(element,value){if(value===undefined)return element.value;else element.value=value;},select:function(element,index){if(index===undefined)
return this[element.type=='select-one'?'selectOne':'selectMany'](element);else{var opt,value,single=!Object.isArray(index);for(var i=0,length=element.length;i<length;i++){opt=element.options[i];value=this.optionValue(opt);if(single){if(value==index){opt.selected=true;return;}}
else opt.selected=index.include(value);}}},selectOne:function(element){var index=element.selectedIndex;return index>=0?this.optionValue(element.options[index]):null;},selectMany:function(element){var values,length=element.length;if(!length)return null;for(var i=0,values=[];i<length;i++){var opt=element.options[i];if(opt.selected)values.push(this.optionValue(opt));}
return values;},optionValue:function(opt){return Element.extend(opt).hasAttribute('value')?opt.value:opt.text;}};Abstract.TimedObserver=Class.create(PeriodicalExecuter,{initialize:function($super,element,frequency,callback){$super(callback,frequency);this.element=$(element);this.lastValue=this.getValue();},execute:function(){var value=this.getValue();if(Object.isString(this.lastValue)&&Object.isString(value)?this.lastValue!=value:String(this.lastValue)!=String(value)){this.callback(this.element,value);this.lastValue=value;}}});Form.Element.Observer=Class.create(Abstract.TimedObserver,{getValue:function(){return Form.Element.getValue(this.element);}});Form.Observer=Class.create(Abstract.TimedObserver,{getValue:function(){return Form.serialize(this.element);}});Abstract.EventObserver=Class.create({initialize:function(element,callback){this.element=$(element);this.callback=callback;this.lastValue=this.getValue();if(this.element.tagName.toLowerCase()=='form')
this.registerFormCallbacks();else
this.registerCallback(this.element);},onElementEvent:function(){var value=this.getValue();if(this.lastValue!=value){this.callback(this.element,value);this.lastValue=value;}},registerFormCallbacks:function(){Form.getElements(this.element).each(this.registerCallback,this);},registerCallback:function(element){if(element.type){switch(element.type.toLowerCase()){case'checkbox':case'radio':Event.observe(element,'click',this.onElementEvent.bind(this));break;default:Event.observe(element,'change',this.onElementEvent.bind(this));break;}}}});Form.Element.EventObserver=Class.create(Abstract.EventObserver,{getValue:function(){return Form.Element.getValue(this.element);}});Form.EventObserver=Class.create(Abstract.EventObserver,{getValue:function(){return Form.serialize(this.element);}});if(!window.Event)var Event={};Object.extend(Event,{KEY_BACKSPACE:8,KEY_TAB:9,KEY_RETURN:13,KEY_ESC:27,KEY_LEFT:37,KEY_UP:38,KEY_RIGHT:39,KEY_DOWN:40,KEY_DELETE:46,KEY_HOME:36,KEY_END:35,KEY_PAGEUP:33,KEY_PAGEDOWN:34,KEY_INSERT:45,cache:{},relatedTarget:function(event){var element;switch(event.type){case'mouseover':element=event.fromElement;break;case'mouseout':element=event.toElement;break;default:return null;}
return Element.extend(element);}});Event.Methods=(function(){var isButton;if(Prototype.Browser.IE){var buttonMap={0:1,1:4,2:2};isButton=function(event,code){return event.button==buttonMap[code];};}else if(Prototype.Browser.WebKit){isButton=function(event,code){switch(code){case 0:return event.which==1&&!event.metaKey;case 1:return event.which==1&&event.metaKey;default:return false;}};}else{isButton=function(event,code){return event.which?(event.which===code+1):(event.button===code);};}
return{isLeftClick:function(event){return isButton(event,0)},isMiddleClick:function(event){return isButton(event,1)},isRightClick:function(event){return isButton(event,2)},element:function(event){var node=Event.extend(event).target;return Element.extend(node.nodeType==Node.TEXT_NODE?node.parentNode:node);},findElement:function(event,expression){var element=Event.element(event);return element.match(expression)?element:element.up(expression);},pointer:function(event){return{x:event.pageX||(event.clientX+
(document.documentElement.scrollLeft||document.body.scrollLeft)),y:event.pageY||(event.clientY+
(document.documentElement.scrollTop||document.body.scrollTop))};},pointerX:function(event){return Event.pointer(event).x},pointerY:function(event){return Event.pointer(event).y},stop:function(event){Event.extend(event);event.preventDefault();event.stopPropagation();event.stopped=true;}};})();Event.extend=(function(){var methods=Object.keys(Event.Methods).inject({},function(m,name){m[name]=Event.Methods[name].methodize();return m;});if(Prototype.Browser.IE){Object.extend(methods,{stopPropagation:function(){this.cancelBubble=true},preventDefault:function(){this.returnValue=false},inspect:function(){return"[object Event]"}});return function(event){if(!event)return false;if(event._extendedByPrototype)return event;event._extendedByPrototype=Prototype.emptyFunction;var pointer=Event.pointer(event);Object.extend(event,{target:event.srcElement,relatedTarget:Event.relatedTarget(event),pageX:pointer.x,pageY:pointer.y});return Object.extend(event,methods);};}else{Event.prototype=Event.prototype||document.createEvent("HTMLEvents").__proto__;Object.extend(Event.prototype,methods);return Prototype.K;}})();Object.extend(Event,(function(){var cache=Event.cache;function getEventID(element){if(element._eventID)return element._eventID;arguments.callee.id=arguments.callee.id||1;return element._eventID=++arguments.callee.id;}
function getDOMEventName(eventName){if(eventName&&eventName.include(':'))return"dataavailable";return eventName;}
function getCacheForID(id){return cache[id]=cache[id]||{};}
function getWrappersForEventName(id,eventName){var c=getCacheForID(id);return c[eventName]=c[eventName]||[];}
function createWrapper(element,eventName,handler){var id=getEventID(element);var c=getWrappersForEventName(id,eventName);if(c.pluck("handler").include(handler))return false;var wrapper=function(event){if(!Event||!Event.extend||(event.eventName&&event.eventName!=eventName))
return false;Event.extend(event);handler.call(element,event)};wrapper.handler=handler;c.push(wrapper);return wrapper;}
function findWrapper(id,eventName,handler){var c=getWrappersForEventName(id,eventName);return c.find(function(wrapper){return wrapper.handler==handler});}
function destroyWrapper(id,eventName,handler){var c=getCacheForID(id);if(!c[eventName])return false;c[eventName]=c[eventName].without(findWrapper(id,eventName,handler));}
function destroyCache(){for(var id in cache)
for(var eventName in cache[id])
cache[id][eventName]=null;}
if(window.attachEvent){window.attachEvent("onunload",destroyCache);}
return{observe:function(element,eventName,handler){element=$(element);var name=getDOMEventName(eventName);var wrapper=createWrapper(element,eventName,handler);if(!wrapper)return element;if(element.addEventListener){element.addEventListener(name,wrapper,false);}else{element.attachEvent("on"+name,wrapper);}
return element;},stopObserving:function(element,eventName,handler){element=$(element);var id=getEventID(element),name=getDOMEventName(eventName);if(!handler&&eventName){getWrappersForEventName(id,eventName).each(function(wrapper){element.stopObserving(eventName,wrapper.handler);});return element;}else if(!eventName){Object.keys(getCacheForID(id)).each(function(eventName){element.stopObserving(eventName);});return element;}
var wrapper=findWrapper(id,eventName,handler);if(!wrapper)return element;if(element.removeEventListener){element.removeEventListener(name,wrapper,false);}else{element.detachEvent("on"+name,wrapper);}
destroyWrapper(id,eventName,handler);return element;},fire:function(element,eventName,memo){element=$(element);if(element==document&&document.createEvent&&!element.dispatchEvent)
element=document.documentElement;if(document.createEvent){var event=document.createEvent("HTMLEvents");event.initEvent("dataavailable",true,true);}else{var event=document.createEventObject();event.eventType="ondataavailable";}
event.eventName=eventName;event.memo=memo||{};if(document.createEvent){element.dispatchEvent(event);}else{element.fireEvent(event.eventType,event);}
return event;}};})());Object.extend(Event,Event.Methods);Element.addMethods({fire:Event.fire,observe:Event.observe,stopObserving:Event.stopObserving});Object.extend(document,{fire:Element.Methods.fire.methodize(),observe:Element.Methods.observe.methodize(),stopObserving:Element.Methods.stopObserving.methodize()});(function(){var timer,fired=false;function fireContentLoadedEvent(){if(fired)return;if(timer)window.clearInterval(timer);document.fire("dom:loaded");fired=true;}
if(document.addEventListener){if(Prototype.Browser.WebKit){timer=window.setInterval(function(){if(/loaded|complete/.test(document.readyState))
fireContentLoadedEvent();},0);Event.observe(window,"load",fireContentLoadedEvent);}else{document.addEventListener("DOMContentLoaded",fireContentLoadedEvent,false);}}else{document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");$("__onDOMContentLoaded").onreadystatechange=function(){if(this.readyState=="complete"){this.onreadystatechange=null;fireContentLoadedEvent();}};}})();Hash.toQueryString=Object.toQueryString;var Toggle={display:Element.toggle};Element.Methods.childOf=Element.Methods.descendantOf;var Insertion={Before:function(element,content){return Element.insert(element,{before:content});},Top:function(element,content){return Element.insert(element,{top:content});},Bottom:function(element,content){return Element.insert(element,{bottom:content});},After:function(element,content){return Element.insert(element,{after:content});}};var $continue=new Error('"throw $continue" is deprecated, use "return" instead');var Position={includeScrollOffsets:false,prepare:function(){this.deltaX=window.pageXOffset||document.documentElement.scrollLeft||document.body.scrollLeft||0;this.deltaY=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;},within:function(element,x,y){if(this.includeScrollOffsets)
return this.withinIncludingScrolloffsets(element,x,y);this.xcomp=x;this.ycomp=y;this.offset=Element.cumulativeOffset(element);return(y>=this.offset[1]&&y<this.offset[1]+element.offsetHeight&&x>=this.offset[0]&&x<this.offset[0]+element.offsetWidth);},withinIncludingScrolloffsets:function(element,x,y){var offsetcache=Element.cumulativeScrollOffset(element);this.xcomp=x+offsetcache[0]-this.deltaX;this.ycomp=y+offsetcache[1]-this.deltaY;this.offset=Element.cumulativeOffset(element);return(this.ycomp>=this.offset[1]&&this.ycomp<this.offset[1]+element.offsetHeight&&this.xcomp>=this.offset[0]&&this.xcomp<this.offset[0]+element.offsetWidth);},overlap:function(mode,element){if(!mode)return 0;if(mode=='vertical')
return((this.offset[1]+element.offsetHeight)-this.ycomp)/element.offsetHeight;if(mode=='horizontal')
return((this.offset[0]+element.offsetWidth)-this.xcomp)/element.offsetWidth;},cumulativeOffset:Element.Methods.cumulativeOffset,positionedOffset:Element.Methods.positionedOffset,absolutize:function(element){Position.prepare();return Element.absolutize(element);},relativize:function(element){Position.prepare();return Element.relativize(element);},realOffset:Element.Methods.cumulativeScrollOffset,offsetParent:Element.Methods.getOffsetParent,page:Element.Methods.viewportOffset,clone:function(source,target,options){options=options||{};return Element.clonePosition(target,source,options);}};if(!document.getElementsByClassName)document.getElementsByClassName=function(instanceMethods){function iter(name){return name.blank()?null:"[contains(concat(' ', @class, ' '), ' "+name+" ')]";}
instanceMethods.getElementsByClassName=Prototype.BrowserFeatures.XPath?function(element,className){className=className.toString().strip();var cond=/\s/.test(className)?$w(className).map(iter).join(''):iter(className);return cond?document._getElementsByXPath('.//*'+cond,element):[];}:function(element,className){className=className.toString().strip();var elements=[],classNames=(/\s/.test(className)?$w(className):null);if(!classNames&&!className)return elements;var nodes=$(element).getElementsByTagName('*');className=' '+className+' ';for(var i=0,child,cn;child=nodes[i];i++){if(child.className&&(cn=' '+child.className+' ')&&(cn.include(className)||(classNames&&classNames.all(function(name){return!name.toString().blank()&&cn.include(' '+name+' ');}))))
elements.push(Element.extend(child));}
return elements;};return function(className,parentElement){return $(parentElement||document.body).getElementsByClassName(className);};}(Element.Methods);Element.ClassNames=Class.create();Element.ClassNames.prototype={initialize:function(element){this.element=$(element);},_each:function(iterator){this.element.className.split(/\s+/).select(function(name){return name.length>0;})._each(iterator);},set:function(className){this.element.className=className;},add:function(classNameToAdd){if(this.include(classNameToAdd))return;this.set($A(this).concat(classNameToAdd).join(' '));},remove:function(classNameToRemove){if(!this.include(classNameToRemove))return;this.set($A(this).without(classNameToRemove).join(' '));},toString:function(){return $A(this).join(' ');}};Object.extend(Element.ClassNames.prototype,Enumerable);Element.addMethods();try
{if(NodeList&&NodeList.prototype&&!NodeList.prototype._each)
{Object.extend(NodeList.prototype,{_each:function(iterator){for(var i=0,length=this.length;i<length;++i)
iterator(this[i]);}});Object.extend(NodeList.prototype,Enumerable);}}
catch(exception)
{}
var windowsInternetExplorer=false;var isGecko=false;var isMozilla=false;var isFirefox=false;var isCamino=false;var isSafari=false;var isNS=false;var isWebKit=false;var isOpera=false;var isiPhone=false;var isEarlyWebKitVersion=false;var browserDetected=false;var listOfIE7FloatsFix=[];function detectBrowser()
{if(browserDetected===false)
{windowsInternetExplorer=false;var appVersion=navigator.appVersion;if((appVersion.indexOf("MSIE")!=-1)&&(appVersion.indexOf("Macintosh")==-1))
{var temp=appVersion.split("MSIE");actualBrowserVersion=(document.documentMode?8:parseFloat(temp[1]));effectiveBrowserVersion=document.documentMode?document.documentMode:parseFloat(temp[1]);windowsInternetExplorer=true;}
else
{var ua=navigator.userAgent.toLowerCase();isGecko=(ua.indexOf('gecko')!=-1);isMozilla=(this.isGecko&&ua.indexOf("gecko/")+14==ua.length);isFirefox=(this.isGecko&&ua.indexOf("firefox")!=-1);isCamino=(this.isGecko&&ua.indexOf("camino")!=-1);isSafari=(this.isGecko&&ua.indexOf("safari")!=-1);isNS=((this.isGecko)?(ua.indexOf('netscape')!=-1):((ua.indexOf('mozilla')!=-1)&&(ua.indexOf('spoofer')==-1)&&(ua.indexOf('compatible')==-1)&&(ua.indexOf('opera')==-1)&&(ua.indexOf('webtv')==-1)&&(ua.indexOf('hotjava')==-1)));isOpera=!!window.opera;var matchResult=ua.match(/applewebkit\/(\d+)/);if(matchResult)
{isiPhone=(ua.indexOf("mobile/")!=-1);isWebKit=true;webKitVersion=parseInt(matchResult[1]);isEarlyWebKitVersion=(webKitVersion<522);}}
browserDetected=true;}}
detectBrowser();function shouldApplyCSSBackgroundPNGFix()
{return(windowsInternetExplorer&&(effectiveBrowserVersion<7));}
function photocastHelper(url)
{var feed=new IWURL(url);var iPhotoVersionMin=600;var iPhotoMimeTypePlugin="application/photo";if(navigator.mimeTypes&&navigator.mimeTypes.length>0)
{var iPhoto=navigator.mimeTypes[iPhotoMimeTypePlugin];if(iPhoto)
{var description=iPhoto.description;try
{var components=description.split(" ");if(components&&components.length>1)
{var pluginVersion=components[1];if(pluginVersion>=iPhotoVersionMin)
{feed.mProtocol="photo";}}}
catch(exception)
{}}}
window.location=feed.toURLString();}
function loadCSS(file)
{var cssNode=document.createElement('link');cssNode.setAttribute('rel','stylesheet');cssNode.setAttribute('type','text/css');cssNode.setAttribute('href',file);document.getElementsByTagName('head')[0].appendChild(cssNode);}
function loadMozillaCSS(file)
{if(isMozilla||isFirefox||isCamino)
{loadCSS(file);}}
function utf8sequence(c)
{if(c<=0x0000007f)return[c];if(c<=0x000007ff)return[(0xc0|(c>>>6)),(0x80|(c&0x3f))];if(c<=0x0000ffff)return[(0xe0|(c>>>12)),(0x80|((c>>>6)&0x3f)),(0x80|(c&0x3f))];if(c<=0x001fffff)return[(0xf0|(c>>>18)),(0x80|((c>>>12)&0x3f)),(0x80|((c>>>6)&0x3f)),(0x80|(c&0x3f))];if(c<=0x03ffffff)return[(0xf8|(c>>>24)),(0x80|((c>>>18)&0x3f)),(0x80|((c>>>12)&0x3f)),(0x80|((c>>>6)&0x3f)),(0x80|(c&0x3f))];if(c<=0x7fffffff)return[(0xfc|(c>>>30)),(0x80|((c>>>24)&0x3f)),(0x80|((c>>>18)&0x3f)),(0x80|((c>>>12)&0x3f)),(0x80|((c>>>6)&0x3f)),(0x80|(c&0x3f))];return[];}
function utf8encode(s)
{var result=[];var firstSurrogate=0;for(var i=0;i<s.length;++i)
{var code=s.charCodeAt(i);if(firstSurrogate!=0)
{if((code>=0xDC00)&&(code<=0xDFFF))
{code=(firstSurrogate-0xD800)*0x400+(code-0xDC00)+0x10000;firstSurrogate=0;}}
else
{if((code<0xD800)||(code>0xDFFF))
{}
else if((code>=0xD800)&&(code<0xDC00))
{firstSurrogate=code;continue;}
else
{continue;}}
result=result.concat(utf8sequence(code));}
var resultString="";for(i=0;i<result.length;++i)
{resultString+=String.fromCharCode(result[i]);}
return resultString;}
function IELatin1Munge(UTF8String)
{var munged="";for(var i=0;i<UTF8String.length;i++)
{var c=UTF8String.charCodeAt(i);switch(c){case 0x0080:c=0x20AC;break;case 0x0081:break;case 0x0082:c=0x201A;break;case 0x0083:c=0x0192;break;case 0x0084:c=0x201E;break;case 0x0085:c=0x2026;break;case 0x0086:c=0x2020;break;case 0x0087:c=0x2021;break;case 0x0088:c=0x02C6;break;case 0x0089:c=0x2030;break;case 0x008A:c=0x0160;break;case 0x008B:c=0x2039;break;case 0x008C:c=0x0152;break;case 0x008D:break;case 0x008E:c=0x017D;break;case 0x008F:break;case 0x0090:break;case 0x0091:c=0x2018;break;case 0x0092:c=0x2019;break;case 0x0093:c=0x201C;break;case 0x0094:c=0x201D;break;case 0x0095:c=0x2022;break;case 0x0096:c=0x2013;break;case 0x0097:c=0x2014;break;case 0x0098:c=0x02DC;break;case 0x0099:c=0x2122;break;case 0x009A:c=0x0161;break;case 0x009B:c=0x203A;break;case 0x009C:c=0x0153;break;case 0x009D:break;case 0x009E:c=0x017E;break;case 0x009F:c=0x0178;break;}
munged+=String.fromCharCode(c);}
return munged;}
function IEConvertURLForPNGFix(urlString)
{var result=urlString;if(windowsInternetExplorer)
{var decoded=decodeURI(urlString);if(decoded.match(/[^\x00-\x7f]/))
{result=IELatin1Munge(utf8encode(decodeURI(urlString)));}}
return result;}
function fixAllIEPNGs(transparentGif)
{if(windowsInternetExplorer&&effectiveBrowserVersion<8)
{for(var i=0;i<document.images.length;++i)
{if(document.images[i].src.slice(-4).toLowerCase()==".png")
{var img=$(document.images[i]);var fixPng=function(img)
{if(!img.originalSrc&&!img.hasClassName("noAutoPNGFix")&&!img.hasClassName("noAutoPNGFixInTree")&&img.up(".noAutoPNGFixInTree")==undefined)
{if((img.style.width=="")&&(img.style.height==""))
{var width=img.width;var height=img.height;img.style.width=px(width);img.style.height=px(height);}
var filterName='progid:DXImageTransform.Microsoft.AlphaImageLoader';var filterParams='src="'+IEConvertURLForPNGFix(img.src)+'", sizingMethod="scale"';img.setFilter(filterName,filterParams);img.originalSrc=img.src;img.src=transparentGif;}};img.complete?fixPng(img):img.onload=fixPng.bind(null,img);}}}}
function toPixels(value)
{var converted=0;var px_per_pt=window.screen.logicalXDPI?(window.screen.logicalXDPI/72.0):1.3333;if(value.indexOf("px")>0)
{converted=parseFloat(value);}
else if(value.indexOf("pt")>0)
{converted=px_per_pt*parseFloat(value);}
else if(value.indexOf("in")>0)
{converted=72*px_per_pt*parseFloat(value);}
else if(value.indexOf("pc")>0)
{converted=12*px_per_pt*parseFloat(value);}
else if(value.indexOf("mm")>0)
{converted=2.83465*px_per_pt*parseFloat(value);}
else if(value.indexOf("cm")>0)
{converted=28.3465*px_per_pt*parseFloat(value);}
return converted;}
function toPixelsAtElement(element,value,vertical)
{var converted=0;if(value.indexOf("%")>0)
{var containerSize=0;if(vertical)
{containerSize=$(element.parentNode).getHeight();}
else
{containerSize=$(element.parentNode).getWidth();}
converted=containerSize*parseFloat(value)/100.0;}
else if(value.indexOf("em")>0)
{converted=parseFloat(value)*toPixels(Element.getStyle(element,'fontSize'));}
else
{converted=toPixels(value);}
return converted;}
function backgroundPositionDimension(oBlock,currentBGPosition,blockDimension,imageDimension)
{var position=0;if(currentBGPosition==='center')
{position=(blockDimension/2)-(imageDimension/2);}
else if((currentBGPosition==='right')||(currentBGPosition==='bottom'))
{position=blockDimension-imageDimension;}
else if((currentBGPosition==='left')||(currentBGPosition==='top'))
{position=0;}
else if(currentBGPosition.indexOf("px")>0)
{position=parseFloat(currentBGPosition);}
else if(currentBGPosition.indexOf("em")>0)
{position=parseFloat(currentBGPosition)*toPixels(oBlock.currentStyle.fontSize);}
else if(currentBGPosition.indexOf("%")>0)
{position=parseFloat(currentBGPosition)*blockDimension/100.0;}
else if((currentBGPosition.indexOf("pt")>0)||(currentBGPosition.indexOf("in")>0)||(currentBGPosition.indexOf("pc")>0)||(currentBGPosition.indexOf("cm")>0)||(currentBGPosition.indexOf("mm")>0))
{position=toPixels(currentBGPosition);}
return position;}
function elementHasCSSBGPNG(element)
{return(element.currentStyle&&element.currentStyle.backgroundImage&&(element.currentStyle.backgroundImage.indexOf('url(')!=-1)&&(element.currentStyle.backgroundImage.indexOf('.png")')!=-1));}
function fixupIEPNGBG(oBlock)
{if(oBlock)
{if(elementHasCSSBGPNG(oBlock))
{var currentBGImage=oBlock.currentStyle.backgroundImage;var currentBGRepeat=oBlock.currentStyle.backgroundRepeat;var currentBGPositionX=oBlock.currentStyle.backgroundPositionX;var currentBGPositionY=oBlock.currentStyle.backgroundPositionY;var urlStart=currentBGImage.indexOf('url(');var urlEnd=currentBGImage.indexOf(')',urlStart);var imageURL=currentBGImage.substring(urlStart+4,urlEnd);if(imageURL.charAt(0)=='"')
{imageURL=imageURL.substring(1);}
if(imageURL.charAt(imageURL.length-1)=='"')
{imageURL=imageURL.substring(0,imageURL.length-1);}
imageURL=IEConvertURLForPNGFix(imageURL);var overrideRepeat=false;var filterStyle="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+
imageURL+"', sizingMethod='crop');";var fixupIEPNGBG_helper=function(img)
{var tileWidth=img.width;var tileHeight=img.height;var blockWidth=0;var blockHeight=0;if(oBlock.style.width)
{blockWidth=parseInt(oBlock.style.width,10);}
else
{blockWidth=oBlock.offsetWidth;}
if(oBlock.style.height)
{blockHeight=parseInt(oBlock.style.height,10);}
else
{blockHeight=oBlock.offsetHeight;}
var blockPaddingLeft=parseInt(oBlock.style.paddingLeft||0,10);if((blockWidth===0)||(blockHeight===0))
{return;}
var wholeRows=1;var wholeCols=1;var extraHeight=0;var extraWidth=0;if(((currentBGRepeat.indexOf("repeat-x")!=-1)&&(tileWidth==1)&&(tileHeight==blockHeight))||((currentBGRepeat.indexOf("repeat-y")!=-1)&&(tileWidth==blockWidth)&&(tileHeight==1))||((currentBGRepeat=="repeat")&&(tileWidth==1)&&(tileHeight==1)))
{tileWidth=blockWidth;tileHeight=blockHeight;filterStyle="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+
imageURL+"', sizingMethod='scale');";}
else if((currentBGRepeat.indexOf("no-repeat")!=-1)||((tileWidth===0)&&(tileHeight===0)))
{tileWidth=blockWidth;tileHeight=blockHeight;}
else if((currentBGRepeat.indexOf("repeat-x")!=-1)||(tileHeight===0))
{wholeCols=Math.floor(blockWidth/tileWidth);extraWidth=blockWidth-(tileWidth*wholeCols);tileHeight=blockHeight;}
else if(currentBGRepeat.indexOf("repeat-y")!=-1)
{wholeRows=Math.floor(blockHeight/tileHeight);extraHeight=blockHeight-(tileHeight*wholeRows);tileWidth=blockWidth;}
else
{wholeCols=Math.floor(blockWidth/tileWidth);wholeRows=Math.floor(blockHeight/tileHeight);extraWidth=blockWidth-(tileWidth*wholeCols);extraHeight=blockHeight-(tileHeight*wholeRows);}
var wrappedContent=$(document.createElement("div"));var pngBGFixIsWrappedContentEmpty=true;wrappedContent.setStyle({position:"relative",zIndex:"1",left:0,top:0,background:"transparent"});if(!isNaN(parseInt(oBlock.style.width,10)))
{wrappedContent.style.width=px(blockWidth);}
if(!isNaN(parseInt(oBlock.style.height,10)))
{wrappedContent.style.height=px(blockHeight);}
while(oBlock.hasChildNodes())
{if(oBlock.firstChild.nodeType==3)
{if(RegExp("^ *$").exec(oBlock.firstChild.data)===null)
{pngBGFixIsWrappedContentEmpty=false;}}
else
{pngBGFixIsWrappedContentEmpty=false;}
wrappedContent.appendChild(oBlock.firstChild);}
if(pngBGFixIsWrappedContentEmpty)
{wrappedContent.style.lineHeight=0;}
var bgPositionX=backgroundPositionDimension(oBlock,currentBGPositionX,blockWidth,img.width);var bgPositionY=backgroundPositionDimension(oBlock,currentBGPositionY,blockHeight,img.height);bgPositionX-=blockPaddingLeft;var newMarkup="";for(var currentRow=0;currentRow<wholeRows;currentRow++)
{for(currentCol=0;currentCol<wholeCols;currentCol++)
{newMarkup+="<div class='pngtile' style="+"\"position: absolute; line-height: 0px; "+"width: "+((tileWidth==blockWidth)?"100%":px(tileWidth))+"; "+"height: "+((tileHeight==blockHeight)?"100%":px(tileHeight))+"; "+"left:"+(bgPositionX+(currentCol*tileWidth))+"px; "+"top:"+(bgPositionY+(currentRow*tileHeight))+"px; "+"filter:"+filterStyle+"\" > </div>";}
if(extraWidth!==0)
{newMarkup+="<div class='pngtile' style="+"\"position: absolute; line-height: 0px; "+"width: "+extraWidth+"px; "+"height: "+tileHeight+"px; "+"left:"+(bgPositionX+(currentCol*tileWidth))+"px; "+"top:"+(bgPositionY+(currentRow*tileHeight))+"px; "+"filter:"+filterStyle+"\" > </div>";}}
if(extraHeight!==0)
{for(currentCol=0;currentCol<wholeCols;currentCol++)
{newMarkup+="<div class='pngtile' style="+"\"position: absolute; line-height: 0px; "+"width: "+tileWidth+"px; "+"height: "+extraHeight+"px; "+"left:"+(bgPositionX+(currentCol*tileWidth))+"px; "+"top:"+(bgPositionY+(currentRow*tileHeight))+"px; "+"filter:"+filterStyle+"\" > </div>";}
if(extraWidth!==0)
{newMarkup+="<div class='pngtile' style="+"\"position: absolute; line-height: 0px; "+"width: "+extraWidth+"px; "+"height: "+extraHeight+"px; "+"left:"+(bgPositionX+(currentCol*tileWidth))+"px; "+"top:"+(bgPositionY+(currentRow*tileHeight))+"px; "+"filter:"+filterStyle+"\" > </div>";}}
oBlock.innerHTML=newMarkup;if(!pngBGFixIsWrappedContentEmpty)
{oBlock.appendChild(wrappedContent);}
oBlock.style.background="";}
var backgroundImage=new Image();backgroundImage.src=imageURL;if(backgroundImage.complete)
{fixupIEPNGBG_helper(backgroundImage);}
else
{backgroundImage.onload=fixupIEPNGBG_helper.bind(null,backgroundImage);}}}}
function fixupIEPNGBGsInTree(oAncestor,forceAutoFixup)
{if(shouldApplyCSSBackgroundPNGFix())
{try
{var allDivs=$(oAncestor).select('div');if(isDiv(oAncestor))
{allDivs.push(oAncestor);}
allDivs.each(function(oNode)
{if((!($(oNode).hasClassName("noAutoPNGFix"))&&!($(oNode).hasClassName("noAutoPNGFixInTree"))&&($(oNode.up(".noAutoPNGFixInTree")==undefined)))||forceAutoFixup)
{fixupIEPNGBG(oNode);}});}
catch(e)
{}}}
function fixupAllIEPNGBGs()
{setTimeout(fixupIEPNGBGsInTree.bind(null,document),1);}
function optOutOfCSSBackgroundPNGFix(element)
{if(shouldApplyCSSBackgroundPNGFix())
{$(element).select('div').each(function(div)
{if(elementHasCSSBGPNG(div))
{$(div).addClassName("noAutoPNGFix");}});}}
function fixupIECSS3Opacity(strElementID)
{if(windowsInternetExplorer)
{var oNode=$(strElementID);if(oNode&&(parseFloat(oNode.currentStyle.getAttribute('opacity'))<1))
{var opacity=parseFloat(oNode.currentStyle.getAttribute('opacity'));oNode.style.height=px(oNode.offsetHeight);var targetNode=oNode;if(oNode.tagName.toLowerCase()=='img')
{targetNode=$(document.createElement('div'));targetNode.setStyle({position:oNode.style.position,top:oNode.style.top,left:oNode.style.left,width:oNode.style.width,height:oNode.style.height,opacity:oNode.style.opacity,zIndex:oNode.style.zIndex});oNode.setStyle({left:0,top:0,opacity:''});if(oNode.parentNode.tagName.toLowerCase()=='a')
{var anchor=oNode.parentNode;anchor.parentNode.insertBefore(targetNode,anchor);targetNode.appendChild(anchor);}
else
{oNode.parentNode.insertBefore(targetNode,oNode);targetNode.appendChild(oNode);}}
else if(oNode.tagName.toLowerCase()=='div')
{var bufferWidth=100;var oNodeWidth=oNode.offsetWidth;var oNodeHeight=oNode.offsetHeight;extents=new IWExtents(-bufferWidth,-bufferWidth,oNodeWidth+bufferWidth,oNodeHeight*2+bufferWidth);var positionStyleVal=oNode.getStyle("position");var floatStyleVal=oNode.getStyle("float");var positioned=((positionStyleVal=="relative")||(positionStyleVal=="absolute"));var absolutelyPositioned=(positionStyleVal=="absolute"&&(floatStyleVal=="none"));targetNode=$(document.createElement('div'));var classString=oNode.className;classString=classString.replace(/(shadow_\d+)/g,'');classString=classString.replace(/(stroke_\d+)/g,'');classString=classString.replace(/(reflection_\d+)/g,'');targetNode.className=classString;targetNode.setStyle({position:positioned?positionStyleVal:"relative",styleFloat:floatStyleVal,clear:oNode.getStyle("clear"),width:px(extents.right-extents.left),height:px(extents.bottom-extents.top),opacity:oNode.style.opacity,zIndex:oNode.style.zIndex});if(absolutelyPositioned)
{targetNode.setStyle({top:px((parseFloat(oNode.getStyle("top"))||0)+extents.top),left:px((parseFloat(oNode.getStyle("left"))||0)+extents.left)});}
else
{targetNode.setStyle({marginTop:px((parseFloat(oNode.getStyle("marginTop"))||0)+extents.top),marginLeft:px((parseFloat(oNode.getStyle("marginLeft"))||0)+extents.left),marginBottom:px((parseFloat(oNode.getStyle("marginBottom"))||0)-
(extents.bottom-oNodeHeight)),marginRight:px((parseFloat(oNode.getStyle("marginRight"))||0)-
(extents.right-oNodeWidth))});}
oNode.setStyle({position:"absolute",styleFloat:"none",clear:"none",left:px(-extents.left),top:px(-extents.top),margin:0,verticalAlign:'baseline',display:'block',opacity:''});if(effectiveBrowserVersion<7||actualBrowserVersion>=8)
{oNode.className=oNode.className.replace(/(shadow_\d+)/g,'');}
oNode.parentNode.insertBefore(targetNode,oNode);targetNode.appendChild(oNode);}
$(targetNode).setFilter('progid:DXImageTransform.Microsoft.BasicImage','opacity='+opacity);}}}
function IWSetDivOpacity(div,fraction,suppressFilterRemoval)
{if(windowsInternetExplorer)
{if(fraction<.99||(suppressFilterRemoval==true))
{$(div).setFilter('alpha','opacity='+fraction*100);}
else
{$(div).killFilter('alpha');}}
else
{$(div).setOpacity(fraction);}}
function IMpreload(path,name,areaIndex)
{var rolloverName=name+'_rollover_'+areaIndex;var rolloverPath=path+'/'+rolloverName+'.png';self[rolloverName]=new Image();self[rolloverName].src=rolloverPath;var linkName=name+'_link_'+areaIndex;var linkPath=path+'/'+linkName+'.png';self[linkName]=new Image();self[linkName].src=linkPath;return true;}
function swapAlphaImageLoaderFilterSrc(img,src)
{var filterName='progid:DXImageTransform.Microsoft.AlphaImageLoader';var filterParams='src="'+IEConvertURLForPNGFix(src)+'", sizingMethod="scale"';img.setFilter(filterName,filterParams);img.originalSrc=img.src;}
function IMmouseover(name,areaIndex)
{var rolloverName=name+'_rollover_'+areaIndex;var linkName=name+'_link_'+areaIndex;var img=$(linkName);if(img)
{if(windowsInternetExplorer&&img.originalSrc)
{swapAlphaImageLoaderFilterSrc(img,self[rolloverName].src);}
else
{img.src=self[rolloverName].src;}}
return true;}
function IMmouseout(name,areaIndex)
{var linkName=name+'_link_'+areaIndex;var img=$(linkName);if(img)
{if(windowsInternetExplorer&&img.originalSrc)
{swapAlphaImageLoaderFilterSrc(img,self[linkName].src);}
else
{img.src=self[linkName].src;}}
return true;}
var quicktimeAvailable=false;var quicktimeVersion702=false;var isQuicktimeDetectionInitialized=false;var minVersionNum=0x7028000;var minVersionArray=['7','0','2'];function initializeQuicktimeDetection()
{if((navigator.plugins!==null)&&(navigator.plugins.length>0))
{for(i=0;i<navigator.plugins.length;i++)
{var plugin=navigator.plugins[i];if(plugin.name.toLowerCase().indexOf('quicktime plug-in ')!=-1)
{quicktimeAvailable=true;quicktimeVersionString=plugin.name.substring(18);var qtVersionArray=quicktimeVersionString.split('.');for(j=0;j<minVersionArray.length&&j<qtVersionArray.length;j++)
{var qtVersionComponent=qtVersionArray[j];var minVersionComponent=minVersionArray[j];if((qtVersionComponent>minVersionComponent)||((qtVersionComponent==minVersionComponent)&&(j==minVersionArray.length-1)))
{quicktimeVersion702=true;break;}
else if(qtVersionComponent<minVersionComponent)
{break;}}
break;}}}
else if(window.ActiveXObject)
{try
{quicktimeObj=new ActiveXObject('QuickTimeCheckObject.QuickTimeCheck.1');if(quicktimeObj!==null)
{quicktimeAvailable=true;quicktimeVersionNum=quicktimeObj.QuickTimeVersion;if(quicktimeVersionNum>=minVersionNum)
{quicktimeVersion702=true;}}}
catch(e)
{}}
isQuicktimeDetectionInitialized=true;}
function fixupPodcast(mediaId,anchorId)
{if(!isQuicktimeDetectionInitialized)
{initializeQuicktimeDetection();}
if(!quicktimeVersion702)
{var oMediaElem=$(mediaId);var oAnchorElem=$(anchorId);if(oMediaElem&&oAnchorElem)
{oAnchorElem.style.display='inline';oMediaElem.parentNode.removeChild(oMediaElem);}}}
function allListBulletImagesContainedBy(node)
{var result=[];for(var i=0;i<node.childNodes.length;++i)
{var child=node.childNodes[i];if((child.nodeName=="IMG")&&((node.nodeName=="SPAN")||(node.nodeName=="A"))&&(node.parentNode!=null)&&(node.parentNode.nodeName=="P")&&(node.parentNode.parentNode!=null)&&(node.parentNode.parentNode.nodeName=="LI"))
{result=result.concat([child]);}
result=result.concat(allListBulletImagesContainedBy(child));}
return result;}
function hideAllListBulletImagesContainedBy(node)
{$A(allListBulletImagesContainedBy(node)).invoke('hide');}
function showAllListBulletImagesContainedBy(node)
{$A(allListBulletImagesContainedBy(node)).invoke('show');}
function getChildOfType(oParent,sNodeName,requestedIndex)
{var childrenOfType=$(oParent).select(sNodeName);return(requestedIndex<childrenOfType.length)?childrenOfType.item(requestedIndex):null;}
function containsFixedHeightIntermediate(oDescendant,oAncestor)
{if(oDescendant===oAncestor||oDescendant==null)
{return false;}
else if(parseFloat(oDescendant.style.height)>0)
{return true;}
else
{return containsFixedHeightIntermediate(oDescendant.parentNode,oAncestor);}}
function getShrinkableParaDescendants(oAncestor)
{return $(oAncestor).select('div.paragraph, p').findAll(function(paragraph){return!containsFixedHeightIntermediate(paragraph,oAncestor);});}
var MINIMUM_FONT="10";var UNITS="";function elementFontSize(element)
{var fontSize=MINIMUM_FONT;if(document.defaultView)
{var computedStyle=document.defaultView.getComputedStyle(element,null);if(computedStyle)
{fontSize=computedStyle.getPropertyValue("font-size");}}
else if(element.currentStyle)
{fontSize=element.currentStyle.fontSize;}
if((UNITS.length===0)&&(fontSize!=MINIMUM_FONT))
{UNITS=fontSize.substring(fontSize.length-2,fontSize.length);}
return parseFloat(fontSize);}
function isExceptionToOneLineRule(element)
{return $(element).hasClassName("Header");}
var HEIGHT_ERROR_MARGIN=2;function adjustFontSizeIfTooBig(idOfElement)
{var oParagraphDiv;var oSpan;var oTextBoxInnerDiv;var oTextBoxOuterDiv=$(idOfElement);if(oTextBoxOuterDiv)
{oTextBoxInnerDiv=oTextBoxOuterDiv.selectFirst("div.text-content");if(oTextBoxInnerDiv)
{hideAllListBulletImagesContainedBy(oTextBoxInnerDiv);var offsetHeight=oTextBoxInnerDiv.offsetHeight;var specifiedHeight=offsetHeight;if(oTextBoxOuterDiv.style.height!=="")
{specifiedHeight=parseFloat(oTextBoxOuterDiv.style.height);}
if(offsetHeight>(specifiedHeight+HEIGHT_ERROR_MARGIN))
{var smallestFontSize=200;var aParaChildren=getShrinkableParaDescendants(oTextBoxInnerDiv);var oneLine=false;var exceptionToOneLineRule=false;for(i=0;i<aParaChildren.length;i++)
{oParagraphDiv=aParaChildren[i];var lineHeight=elementLineHeight(oParagraphDiv);if(!isNaN(lineHeight))
{oneLine=oneLine||(lineHeight*1.5>=specifiedHeight);exceptionToOneLineRule=oneLine&&isExceptionToOneLineRule(oParagraphDiv);}
var fontSize=elementFontSize(oParagraphDiv);if(!isNaN(fontSize))
{smallestFontSize=Math.min(smallestFontSize,fontSize);}
for(j=0;j<oParagraphDiv.childNodes.length;j++)
{oSpan=oParagraphDiv.childNodes[j];if((oSpan.nodeName=="SPAN")||(oSpan.nodeName=="A"))
{fontSize=elementFontSize(oSpan);if(!isNaN(fontSize))
{smallestFontSize=Math.min(smallestFontSize,fontSize);}}}}
var minimum=parseFloat(MINIMUM_FONT);var count=0;while((smallestFontSize>minimum)&&(offsetHeight>(specifiedHeight+HEIGHT_ERROR_MARGIN))&&(count<10))
{++count;if(oneLine&&!exceptionToOneLineRule)
{var oldWidth=parseInt(oTextBoxOuterDiv.style.width,10);oTextBoxInnerDiv.style.width=px(oldWidth*Math.pow(1.05,count));}
else
{var scale=Math.max(0.95,minimum/smallestFontSize);for(i=0;i<aParaChildren.length;i++)
{oParagraphDiv=aParaChildren[i];var paraFontSize=elementFontSize(oParagraphDiv)*scale;var paraLineHeight=elementLineHeight(oParagraphDiv)*scale;for(j=0;j<oParagraphDiv.childNodes.length;j++)
{oSpan=oParagraphDiv.childNodes[j];if((oSpan.nodeName=="SPAN")||(oSpan.nodeName=="A"))
{var spanLineHeight=elementLineHeight(oSpan)*scale;if(!isNaN(spanLineHeight))
{oSpan.style.lineHeight=spanLineHeight+UNITS;}
var spanFontSize=elementFontSize(oSpan)*scale;if(!isNaN(spanFontSize))
{oSpan.style.fontSize=spanFontSize+UNITS;smallestFontSize=Math.min(smallestFontSize,spanFontSize);}}}
if(!isNaN(paraLineHeight))
{oParagraphDiv.style.lineHeight=paraLineHeight+UNITS;}
if(!isNaN(paraFontSize))
{oParagraphDiv.style.fontSize=paraFontSize+UNITS;smallestFontSize=Math.min(smallestFontSize,paraFontSize);}}}
offsetHeight=oTextBoxInnerDiv.offsetHeight;}}
showAllListBulletImagesContainedBy(oTextBoxInnerDiv);}}}
function elementLineHeight(element)
{var lineHeight=MINIMUM_FONT;if(document.defaultView)
{var computedStyle=document.defaultView.getComputedStyle(element,null);if(computedStyle)
{lineHeight=computedStyle.getPropertyValue("line-height");}}
else if(element.currentStyle)
{lineHeight=element.currentStyle.lineHeight;}
if((UNITS.length===0)&&(lineHeight!=MINIMUM_FONT))
{UNITS=lineHeight.substring(lineHeight.length-2,lineHeight.length);}
return parseFloat(lineHeight);}
function adjustLineHeightIfTooBig(idOfElement)
{var oTextBoxInnerDiv;var oTextBoxOuterDiv=$(idOfElement);if(oTextBoxOuterDiv)
{oTextBoxInnerDiv=oTextBoxOuterDiv.selectFirst("div.text-content");if(oTextBoxInnerDiv)
{hideAllListBulletImagesContainedBy(oTextBoxInnerDiv);var offsetHeight=oTextBoxInnerDiv.offsetHeight;var specifiedHeight=offsetHeight;if(oTextBoxOuterDiv.style.height!=="")
{specifiedHeight=parseFloat(oTextBoxOuterDiv.style.height);}
if(offsetHeight>(specifiedHeight+HEIGHT_ERROR_MARGIN))
{var adjusted=true;var count=0;while((adjusted)&&(offsetHeight>(specifiedHeight+HEIGHT_ERROR_MARGIN))&&(count<10))
{adjusted=false;++count;var aParaChildren=getShrinkableParaDescendants(oTextBoxInnerDiv);for(i=0;i<aParaChildren.length;i++)
{var fontSize;var lineHeight;var oParagraphDiv=aParaChildren[i];fontSize=elementFontSize(oParagraphDiv);lineHeight=elementLineHeight(oParagraphDiv)*0.95;if(!isNaN(lineHeight)&&lineHeight>=(fontSize*1.1))
{oParagraphDiv.style.lineHeight=lineHeight+UNITS;adjusted=true;}
for(j=0;j<oParagraphDiv.childNodes.length;j++)
{var oSpan=oParagraphDiv.childNodes[j];if((oSpan.nodeName=="SPAN")||(oSpan.nodeName=="A"))
{fontSize=elementFontSize(oSpan);lineHeight=elementLineHeight(oSpan)*0.95;if(!isNaN(lineHeight)&&lineHeight>=(fontSize*1.1))
{oSpan.style.lineHeight=lineHeight+UNITS;adjusted=true;}}}}
offsetHeight=oTextBoxInnerDiv.offsetHeight;}}
showAllListBulletImagesContainedBy(oTextBoxInnerDiv);}}}
function isDiv(node)
{return(node.nodeType==Node.ELEMENT_NODE)&&(node.tagName=="DIV");}
function fixupAllMozInlineBlocks()
{if(isFirefox||isCamino)
{var oInlineBlocks=$$("div.inline-block");for(var i=0,inlineBlocksLength=oInlineBlocks.length;i<inlineBlocksLength;++i)
{var oInlineBlock=oInlineBlocks[i];var oInterposingDiv=document.createElement("div");oInterposingDiv.style.position="relative";oInterposingDiv.style.overflow="visible";for(var j=0,childNodesLength=oInlineBlock.childNodes.length;j<childNodesLength;++j)
{var oChildNode=oInlineBlock.childNodes[0];oInlineBlock.removeChild(oChildNode);oInterposingDiv.appendChild(oChildNode);}
oInlineBlock.appendChild(oInterposingDiv);}}}
function getWidthDefiningAncestor(elem)
{return elem.up('[style~="width:"]')||document.body;}
function updateListOfIE7FloatsFix(div)
{var div=$(div);var floatValue=div.getStyle("float");if(floatValue=="left"||floatValue=="right")
{var commonAncestor=getWidthDefiningAncestor(div);var floatDescendants=commonAncestor.select('[style~="float:"]');while(floatDescendants.length>0)
{var floatElem=floatDescendants.shift();floatValue=floatElem.getStyle("float");if(floatValue=="left"||floatValue=="right")
{var floatAncestor=getWidthDefiningAncestor(floatElem);if(floatAncestor===commonAncestor)
{if(!listOfIE7FloatsFix.include(floatElem))
{listOfIE7FloatsFix.push(floatElem);}}}}}}
function fixupFloatsIfIE7()
{if(windowsInternetExplorer&&effectiveBrowserVersion==7)
{if(listOfIE7FloatsFix.length>0)
{var floatsToRestore=[];var floatElem;var displayStyle;while(listOfIE7FloatsFix.length>0)
{floatElem=listOfIE7FloatsFix.shift();displayStyle=floatElem.getStyle("display");$(floatElem).hide();floatsToRestore.push({element:floatElem,displayStyle:displayStyle});}
while(floatsToRestore.length>0)
{var queueEntry=floatsToRestore.shift();floatElem=queueEntry.element;displayStyle=queueEntry.displayStyle;$(floatElem).setStyle({"display":displayStyle});}}}}
function joltLater(element)
{setTimeout(function(element){$(element).hide();}.bind(null,element),100);setTimeout(function(element){$(element).show();}.bind(null,element),200);}
function performPostEffectsFixups()
{fixupAllMozInlineBlocks();fixupFloatsIfIE7();}
function reduceLeftMarginIfIE6(element)
{if(windowsInternetExplorer&&effectiveBrowserVersion<7)
{$(element).style.marginLeft=px(parseFloat($(element).style.marginLeft||0)-1);}}
function reduceRightMarginIfIE6(element)
{if(windowsInternetExplorer&&effectiveBrowserVersion<7)
{$(element).style.marginRight=px(parseFloat($(element).style.marginRight||0)-1);}}
Object.objectType=function(obj)
{var result=typeof obj;if(result=="object")
{if(obj.constructor==Array)
result="Array";}
return result;}
var trace=function(){};function ajaxGetDocumentElement(req)
{var dom=null;if(req)
{if(req.responseXML&&req.responseXML.documentElement)
{dom=req.responseXML;}
else if(req.responseText)
{if(window.DOMParser)
{dom=(new DOMParser()).parseFromString(req.responseText,"text/xml");}
else if(window.ActiveXObject)
{dom=new ActiveXObject("MSXML.DOMDocument");if(dom)
{dom.async=false;dom.loadXML(req.responseText);}}}}
return dom?dom.documentElement:null;}
function iWLog(str)
{if(window.console)
{window.console.log(str);}
else if(window.dump)
{window.dump(str+"\n");}}
function iWPosition(abs,left,top,width,height)
{var pos="";if(abs)
pos="position: absolute; ";var size="";if(width&&height)
size=' width: '+width+'px; height: '+height+'px;';return pos+'left: '+left+'px; top: '+top+'px;'+size;}
var gIWUtilsTransparentGifURL="";function setTransparentGifURL(url)
{if(gIWUtilsTransparentGifURL=="")
{gIWUtilsTransparentGifURL=url;}}
function transparentGifURL()
{(function(){return gIWUtilsTransparentGifURL!=""}).assert("Transparent image URL not set");return gIWUtilsTransparentGifURL;}
function imgMarkup(src,style,attributes,alt,forceFixupIE7)
{var markup="";if(src)
{style=style||"";attributes=attributes||"";alt=alt||"";if(windowsInternetExplorer&&((effectiveBrowserVersion<7)||(effectiveBrowserVersion<8&&forceFixupIE7!==false)))
{style+=" filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+IEConvertURLForPNGFix(src)+"', sizingMethod='scale');";src=gIWUtilsTransparentGifURL;}
if(style.length>0)
{style=' style="'+style+'"';}
if(attributes.length>0)
{attributes=' '+attributes;}
if(alt.length>0)
{alt=' alt="'+alt.stringByEscapingXML(true)+'"';}
markup='<img src="'+src+'"'+style+attributes+alt+' />';}
return markup;}
function setImgSrc(imgElement,src,forceFixupIE7)
{if(windowsInternetExplorer&&((effectiveBrowserVersion<7)||(effectiveBrowserVersion<8&&forceFixupIE7!==false))&&src.slice(-4).toLowerCase()==".png")
{$(imgElement).setFilter('progid:DXImageTransform.Microsoft.AlphaImageLoader','src="'+IEConvertURLForPNGFix(src)+'", sizingMethod="scale"');imgElement.src=gIWUtilsTransparentGifURL;}
else
{imgElement.src=src;}}
function iWOpacity(opacity)
{var style="";if(windowsInternetExplorer)
{style=" progid:DXImageTransform.Microsoft.Alpha(opacity="+opacity*100+"); ";}
else
{style=" opacity: "+opacity+"; ";}
return style;}
var IWRange=Class.create({initialize:function(location,length)
{this.setLocation(location);this.setLength(length);},length:function()
{return this.p_length;},setLength:function(length)
{this.p_length=parseFloat(length);},location:function()
{return this.p_location;},setLocation:function(location)
{this.p_location=parseFloat(location);},max:function()
{return this.location()+this.length();},min:function()
{return this.location();},shift:function(amount)
{this.setLocation(this.location()+amount);},containsLocation:function(location)
{return((location>=this.min())&&(location<this.max()));}});var IWPageRange=Class.create(IWRange,{initialize:function($super,location,length)
{$super(location,length);},setMax:function(newMax)
{var maxLength=this.p_lengthForMax(newMax);this.setLocation(Math.max(newMax-maxLength,0))
this.setLength(newMax-this.location());},shift:function($super,amount)
{$super(amount);this.setMax(this.max());},p_lengthForMax:function(max)
{return(max<=9)?5:3;}});function px(s)
{return s.toString()+"px";}
function depx(s)
{return parseInt(s||0);}
function locationHRef()
{var result=window.location.href;if(result.match(/file:\/[^\/]/))
{result="file://"+result.substr(5);}
return result;}
function IWZeroSize()
{return new IWSize(0,0);}
var IWSize=Class.create({initialize:function(width,height)
{this.width=width;this.height=height;},scale:function(hscale,vscale,round)
{if(round===undefined)round=false;if(vscale===undefined)vscale=hscale;var scaled=new IWSize(this.width*hscale,this.height*vscale);if(round)
{scaled.width=Math.round(scaled.width);scaled.height=Math.round(scaled.height);}
return scaled;},scaleToTargetSize:function(targetSize,fitTarget)
{var scaledSize=new IWSize(this.width,this.height);if(scaledSize.width>0&&scaledSize.height>0)
{var wScale=targetSize.width/scaledSize.width;var hScale=targetSize.height/scaledSize.height;var scale=fitTarget?Math.min(wScale,hScale):Math.max(wScale,hScale);scaledSize.width*=scale;scaledSize.height*=scale;}
return scaledSize;},scaleToFit:function(sizeToFit)
{return this.scaleToTargetSize(sizeToFit,true);},round:function()
{return this.scale(1,1,true);},toString:function()
{return"Size("+this.width+", "+this.height+")";},aspectRatio:function()
{return this.width/this.height;},subtractSize:function(s)
{return new IWSize(this.width-s.width,this.height-s.height);}});function IWZeroPoint()
{return new IWPoint(0,0);}
var IWPoint=Class.create({initialize:function(x,y)
{this.x=x;this.y=y;},scale:function(hscale,vscale,round)
{if(round===undefined)round=false;if(vscale===undefined)vscale=hscale;var scaled=new IWPoint(this.x*hscale,this.y*vscale);if(round)
{scaled.x=Math.round(scaled.x);scaled.y=Math.round(scaled.y);}
return scaled;},round:function()
{return this.scale(1,1,true);},offset:function(deltaX,deltaY)
{return new IWPoint(this.x+deltaX,this.y+deltaY);},toString:function()
{return"Point("+this.x+", "+this.y+")";}});function IWZeroRect()
{return new IWRect(0,0,0,0);}
var IWRect=Class.create({initialize:function()
{if(arguments.length==1)
{this.origin=arguments[0].origin;this.size=arguments[0].size;}
else if(arguments.length==2)
{this.origin=arguments[0];this.size=arguments[1];}
else if(arguments.length==4)
{this.origin=new IWPoint(arguments[0],arguments[1]);this.size=new IWSize(arguments[2],arguments[3]);}},clone:function()
{return new IWRect(this.origin.x,this.origin.y,this.size.width,this.size.height);},toString:function()
{return"Rect("+this.origin.toString()+", "+this.size.toString()+")";},maxX:function()
{return this.origin.x+this.size.width;},maxY:function()
{return this.origin.y+this.size.height;},union:function(that)
{var minX=Math.min(this.origin.x,that.origin.x);var minY=Math.min(this.origin.y,that.origin.y);var maxX=Math.max(this.maxX(),that.maxX());var maxY=Math.max(this.maxY(),that.maxY());return new IWRect(minX,minY,maxX-minX,maxY-minY);},intersection:function(that)
{var intersectionRect;var minX=Math.max(this.origin.x,that.origin.x);var minY=Math.max(this.origin.y,that.origin.y);var maxX=Math.min(this.maxX(),that.maxX());var maxY=Math.min(this.maxY(),that.maxY());if((minX<maxX)&&(minY<maxY))
{intersectionRect=new IWRect(minX,minY,maxX-minX,maxY-minY);}
else
{intersectionRect=new IWRect(0,0,0,0);}
return intersectionRect;},scale:function(hscale,vscale,round)
{if(round===undefined)round=false;if(vscale===undefined)vscale=hscale;var scaledOrigin=this.origin.scale(hscale,vscale,round);var scaledSize=this.size.scale(hscale,vscale,round);return new IWRect(scaledOrigin.x,scaledOrigin.y,scaledSize.width,scaledSize.height);},scaleSize:function(hscale,vscale,round)
{var scaledSize=this.size.scale(hscale,vscale,round);return new IWRect(this.origin.x,this.origin.y,scaledSize.width,scaledSize.height);},round:function()
{return this.scale(1,1,true);},offset:function(deltaX,deltaY)
{var offsetOrigin=this.origin.offset(deltaX,deltaY);return new IWRect(offsetOrigin.x,offsetOrigin.y,this.size.width,this.size.height);},offsetToOrigin:function()
{return this.offset(-this.origin.x,-this.origin.y)},centerPoint:function()
{return this.offset(this.size.width/2,this.size.height/2);},position:function()
{return iWPosition(true,this.origin.x,this.origin.y,this.size.width,this.size.height);},clip:function()
{return"clip: rect("+this.origin.y+"px, "+this.maxX()+"px, "+this.maxY()+"px, "+this.origin.x+"px);";},toExtents:function()
{return new IWExtents(this.origin.x,this.origin.y,this.origin.x+this.size.width,this.origin.y+this.size.height);},paddingToRect:function(padded)
{return new IWPadding(this.origin.x-padded.origin.x,this.origin.y-padded.origin.y,padded.maxX()-this.maxX(),padded.maxY()-this.maxY());},fill:function(context)
{context.fillRect(this.origin.x,this.origin.y,this.size.width,this.size.height);},clear:function(context)
{context.clearRect(this.origin.x,this.origin.y,this.size.width,this.size.height);}});var IWExtents=Class.create({initialize:function(left,top,right,bottom)
{this.left=left;this.top=top;this.right=right;this.bottom=bottom;},clone:function()
{return new IWExtents(this.left,this.top,this.right,this.bottom);},toRect:function()
{return new IWRect(this.left,this.top,this.right-this.left,this.bottom-this.top);}});var IWPadding=Class.create({initialize:function(left,top,right,bottom)
{this.left=left;this.top=top;this.right=right;this.bottom=bottom;}});var IWNotificationCenter=Class.create({initialize:function()
{this.mDispatchTable=new Array();},addObserver:function(observer,method,name,object)
{this.p_observersForName(name).push(new Array(observer,method,object));},removeObserver:function(observer)
{},postNotification:function(notification)
{if(notification.name()!=null)
{var observersForName=this.mDispatchTable[notification.name()];this.p_postNotificationToObservers(notification,observersForName);}
var observersForNullName=this.mDispatchTable[null];this.p_postNotificationToObservers(notification,observersForNullName);},postNotificationWithInfo:function(name,object,userInfo)
{this.postNotification(new IWNotification(name,object,userInfo));},p_postNotificationToObservers:function(notification,observers)
{if(notification!=null&&observers!=null)
{for(var i=0;i<observers.length;i++)
{var observer=observers[i][0];var method=observers[i][1];var obj=observers[i][2];if(obj==null||obj===notification.object())
{method.call(observer,notification);}}}},p_observersForName:function(name)
{if(this.mDispatchTable[name]===undefined)
{this.mDispatchTable[name]=new Array();}
return this.mDispatchTable[name];}});var NotificationCenter=new IWNotificationCenter();var IWNotification=Class.create({initialize:function(name,object,userInfo)
{this.mName=name;this.mObject=object;this.mUserInfo=userInfo;},name:function()
{return this.mName;},object:function()
{return this.mObject;},userInfo:function()
{return this.mUserInfo;}});var IWAssertionsEnabled=true;function IWAssert(func,description)
{if(IWAssertionsEnabled)
{function IWAssertionFailed(func,description)
{var formatter=new RegExp("return[\t\r ]*([^};\r]*)");var assertionText=func.toString().match(formatter)[1];var message='Assertion failed: "'+assertionText+'"';if(description!=null)
message+='.  '+description;iWLog(message);}
function IWCoreAssert(func,description)
{if(func()==false)
{IWAssertionFailed(func,description);}}
IWCoreAssert(function(){return typeof(func)=='function'},"IWAssert requires its first argument to be a function.  "+"Try wrapping your assertion in function(){return ... }");var result=func();IWCoreAssert(function(){return result!=null},"The result of your assertion function is null; "+"did you remember your return statement?");IWCoreAssert(function(){return result==true||result==false},"The result of your assertion function is neither true nor false");if(result==false)
{IWAssertionFailed(func,description);}}}
Function.prototype.assert=function(description)
{IWAssert(this,description);}
function getTextFromNode(node)
{if(node.textContent)
return node.textContent;if(node.innerText)
return node.innerText;var result="";if(node.nodeType==Node.ELEMENT_NODE)
{var children=node.childNodes;for(var i=0;i<children.length;++i)
{result=result+getTextFromNode(children[i]);}}
else if(node.nodeType==Node.TEXT_NODE)
{return node.nodeValue;}
return result;}
function getChildElementsByTagNameNS(node,ns,nsPrefix,localName)
{var result=[];for(var i=0;i<node.childNodes.length;++i)
{var childNode=node.childNodes[i];if(childNode.namespaceURI)
{if(childNode.namespaceURI==ns)
{if(childNode.localName&&(childNode.localName==localName))
{result.push(childNode);}
else if(childNode.tagName==(nsPrefix+":"+localName))
{result.push(childNode);}}}
else
{if((ns=="")&&(childNode.tagName==localName))
{result.push(childNode);}}}
return result;}
function getFirstChildElementByTagNameNS(node,ns,nsPrefix,localName)
{var children=getChildElementsByTagNameNS(node,ns,nsPrefix,localName);return(children.length>0)?children[0]:null;}
function getChildElementTextByTagName(node,tagName)
{var result="";if(node!==null)
{var children=node.getElementsByTagName(tagName);if(children.length>1)
{throw"MultipleResults";}
if(children.length==1)
{result=getTextFromNode(children[0]);}}
return result;}
function getChildElementTextByTagNameNS(node,ns,nsPrefix,localName)
{var result="";if(node)
{var children=getChildElementsByTagNameNS(node,ns,nsPrefix,localName);if(children.length>1)
throw"MultipleResults";if(children.length==1)
{result=getTextFromNode(children[0]);}}
return result;}
function adjustNodeIds(node,suffix)
{var undefined;if(node.id!="")
{node.id+=("$"+suffix);}
$(node).childElements().each(function(e){adjustNodeIds(e,suffix);});}
function substituteSpans(parentNode,replacements)
{$H(replacements).each(function(pair)
{var selector="span."+pair.key;$(parentNode).select(selector).each(function(node)
{var contentType=pair.value[0];var newContent=pair.value[1];if(contentType=="text")
{node.update(newContent);}
else if(contentType=="html")
{node.innerHTML=newContent;}});});}
Element.addMethods({selectFirst:function(element,tag_name){var elements=$(element).select(tag_name);return(elements.length>0)?$(elements[0]):null;},setVisibility:function(element,visible){element=$(element);if(visible)
{element.style.display='inline';}
else
{element.style.display='none';}
return element;},ensureHasLayoutForIE:function(element)
{element=$(element);if(windowsInternetExplorer&&effectiveBrowserVersion<8)
{if(!element.currentStyle.hasLayout)
{element.style.zoom=1;}}},setFilter:function(element,filterName,filterParams)
{element=$(element);var regex=new RegExp(filterName+'\\([^\\)]*\\);','gi');element.style.filter=element.style.filter.replace(regex,'')+
filterName+'('+filterParams+'); ';return element;},killFilter:function(element,filterName)
{element=$(element);var regex=new RegExp(filterName+'\\([^\\)]*\\);','gi');element.style.filter=element.style.filter.replace(regex,'');return element;},cloneNodeExcludingIDs:function(element,deep)
{var clone=element.cloneNode(deep);if(deep)
{var descendantsWithID=clone.select("[id]");for(var i=0,length=descendantsWithID.length;i<length;++i){descendantsWithID[i].id="";}}
clone.id="";return clone;}});Object.extend(Array.prototype,{contains:function(value)
{return $A(this).indexOf(value)!=-1;},isEqual:function(that)
{if(this.length==that.length)
{for(var i=0;i<this.length;++i)
{if(this[i]!=that[i])
return false;}
return true;}
return false;},minusArray:function(that)
{var result=$A(this);$A(that).each(function(e){result=result.without(e);});return result;}});String.stringWithFormat=function(format)
{var formatted="";var nextArgument=1;var formatPattern=/%((\d+)\$)?([%s])?/;while(true)
{foundIndex=format.search(formatPattern);if(foundIndex==-1)
{formatted+=format;break;}
if(foundIndex>0)
{formatted+=format.substring(0,foundIndex)}
var matchInfo=format.match(formatPattern);var formatCharacter=matchInfo[3];if(formatCharacter=="%")
{formatted+="%";}
else
{if(matchInfo[2])
{argumentNumber=parseInt(matchInfo[2]);}
else
{argumentNumber=nextArgument++;}
argument=(argumentNumber<arguments.length)?arguments[argumentNumber]:"";if(formatCharacter=="s")
{formatted+=argument;}}
format=format.substring(foundIndex+matchInfo[0].length);}
return formatted;}
Object.extend(String.prototype,{lastPathComponent:function()
{return this.substr(this.lastIndexOf("/")+1);},pathExtension:function()
{var lastSeparatorIndex=this.lastIndexOf("/");var lastDotIndex=this.lastIndexOf(".");return((lastDotIndex>lastSeparatorIndex+1)&&lastDotIndex>0)?this.slice(lastDotIndex+1):this;},stringByDeletingLastPathComponent:function()
{return this.substr(0,this.lastIndexOf("/"));},stringByDeletingPathExtension:function()
{var lastSeparatorIndex=this.lastIndexOf("/");var lastDotIndex=this.lastIndexOf(".");if((lastDotIndex>lastSeparatorIndex+1)&&lastDotIndex>0)
return this.slice(0,lastDotIndex);return this;},stringByAppendingPathComponent:function(component)
{return this.endsWith("/")?(this+component):(this+"/"+component);},stringByAppendingAsQueryString:function(parameters)
{return this+'?'+$H(parameters).toQueryString();},stringByUnescapingXML:function()
{var str=this.replace(/&lt;/g,'<');str=str.replace(/&gt;/g,'>');str=str.replace(/&quot;/g,'"');str=str.replace(/&apos;/g,"'");str=str.replace(/&amp;/g,'&');return str;},stringByEscapingXML:function(escapeAdditionalCharacters)
{var str=this.replace(/&/g,'&amp;');str=str.replace(/</g,'&lt;');if(escapeAdditionalCharacters)
{str=str.replace(/>/g,'&gt;');str=str.replace(/"/g,'&quot;');str=str.replace(/'/g,'&apos;');}
return str;},stringByConvertingNewlinesToBreakTags:function()
{return this.replace(/\n\r|\n|\r/g,'<br />');},urlStringByDeletingQueryAndFragment:function()
{var result=this;var lastIndex=result.lastIndexOf("?");if(lastIndex>0)
return result.substr(0,lastIndex);lastIndex=result.lastIndexOf("#");if(lastIndex>0)
result=result.substr(0,lastIndex);return result;},toRelativeURL:function(baseURL)
{var result=this;if(baseURL&&this.indexOf(baseURL)==0)
{var chop=baseURL.length;if(this.charAt(chop)=='/')
++chop;result=this.substring(chop);}
return result;},toAbsoluteURL:function()
{var result=this;if(this.indexOf(":/")==-1)
{var pageURL=document.URL.urlStringByDeletingQueryAndFragment();var pathURL=pageURL.stringByDeletingLastPathComponent();result=pathURL.stringByAppendingPathComponent(this);}
return result;},toRebasedURL:function(baseURL)
{return this.toRelativeURL(baseURL).toAbsoluteURL();},httpURLRegExp:function()
{if(String.m_httpurlRegExp==undefined)
{var alpha="[A-Za-z]";var digit="[0-9]";var safe="[$_.+-]";var extra="[!*'(),]";var unreserved="("+alpha+"|"+digit+"|"+safe+"|"+extra+")";var hex="("+digit+"|"+"[A-Fa-f])";var escapeSeq="(%"+hex+hex+")";var uchar="("+unreserved+"|"+escapeSeq+")";var alphadigit="("+alpha+"|"+digit+")";var digits=digit+"+";var hostnumber="("+digits+"\\."+digits+"\\."+digits+"\\."+digits+")";var toplabel="(("+alpha+"("+alpha+"|"+"-)*"+alphadigit+")|"+alpha+")";var domainlabel="(("+alphadigit+"("+alphadigit+"|"+"-)*"+alphadigit+")|"+alphadigit+")";var hostname="(("+domainlabel+"\\.)*"+toplabel+")";var host="("+hostname+"|"+hostnumber+")";var port=digits;var hostport="(("+host+")(:"+port+")?)";var hsegment="((("+uchar+")|[;:@&=])*)";var search="((("+uchar+")|[;:@&=])*)";var hpath="("+hsegment+"(/"+hsegment+")*)";var httpurl="((http)|(feed)|(https))://"+hostport+"(/"+hpath+"(\\?"+search+")?)?"
String.m_httpurlRegExp=new RegExp(httpurl);}
return String.m_httpurlRegExp;},isHTTPURL:function()
{var matchResult=this.match(this.httpURLRegExp());return matchResult?(matchResult[0]==this):false;},firstHTTPURL:function()
{var matchResult=this.match(this.httpURLRegExp());return matchResult?matchResult[0]:undefined;},httpURLQueryString:function()
{var charIndex=this.indexOf("?");charIndex=((charIndex==-1)?this.indexOf("&"):charIndex);return(charIndex==-1)?"":this.slice(charIndex+1);},plaintextgsub:function(pattern,replacement)
{var value=this;while(true)
{var index=value.indexOf(pattern);if(index==-1)
break;value=value.substr(0,index)+replacement+value.substr(index+pattern.length);}
return value;}});function IWURL(urlString)
{try
{if((arguments.length==0)||(arguments.length==1&&(urlString==""||urlString==null)))
{this.p_initWithParts(null,null,null,null,null);}
else if(arguments.length==1)
{urlString.replace("file://localhost/","file:///");var urlParts=urlString.match(/^([A-Z]+):\/\/([^/]*)((\/[^?#]*)(\?([^#]*))?(#(.*))?)?/i);if(urlParts)
{this.p_initWithParts(urlParts[1],urlParts[2],urlParts[4]||"/",urlParts[6]||null,urlParts[8]||null);}
else
{urlParts=urlString.match(/^([^?#]*)(\?([^#]*))?(#(.*))?/);if(urlParts)
{this.p_initWithParts(null,null,urlParts[1],urlParts[3]||null,urlParts[5]||null);}
else
{}}}}
catch(e)
{print("Exception Parsing URL:"+e);}}
Object.extend(IWURL,{p_normalizePathComponents:function(components)
{var index=0;while(index<components.length)
{var component=components[index];if(component==""||component==".")
{components.splice(index,1);}
else if(component=="..")
{if(index>0)
{var previousComponent=components[index-1];if(previousComponent=="/")
{components.splice(index,1);}
else if(previousComponent!="..")
{components.splice(index-1,2);index-=1;}
else
{index+=1;}}
else
{index+=1;}}
else
{index+=1;}}
return components;}});Object.extend(IWURL.prototype,{p_initWithParts:function(inProtocol,inAuthority,inPath,inQuery,inFragment)
{this.mProtocol=inProtocol;this.mAuthority=inAuthority;this.mQuery=inQuery;this.mFragment=inFragment;this.mPathComponents=null;if(inPath)
{this.mPathComponents=inPath.split('/');if(this.mPathComponents[0]=="")
this.mPathComponents[0]='/';for(var i=0;i<this.mPathComponents.length;++i)
{this.mPathComponents[i]=decodeURIComponent(this.mPathComponents[i]);}
this.mPathComponents=IWURL.p_normalizePathComponents(this.mPathComponents);}},copy:function()
{var copy=new IWURL();copy.mProtocol=this.mProtocol;copy.mAuthority=this.mAuthority;copy.mQuery=this.mQuery;copy.mFragment=this.mFragment;copy.mPathComponents=null;if(this.mPathComponents)
{copy.mPathComponents=[];for(var i=0;i<this.mPathComponents.length;++i)
{copy.mPathComponents[i]=String(this.mPathComponents[i]);}}
return copy;},toString:function()
{var path="null";if(this.mPathComponents)
{path="";this.mPathComponents.each(function(component)
{if(path=="")
path="[ "+component;else
path+=", "+component;});if(path=="")
path="[]";else
path+=" ]";}
var result="{"+this.mProtocol+", "+this.mAuthority+", "+path+", "+this.mQuery+", "+this.mFragment+"}";return result;},isAbsolute:function()
{return(this.mPathComponents&&this.mPathComponents.length&&this.mPathComponents[0]=="/");},isRelative:function()
{return(this.mProtocol==null);},encodedPathComponents:function()
{var result=[];var index=0;while(index<this.mPathComponents.length)
{if((index==0)&&(this.mPathComponents[0]=="/"))
{result.push("/");}
else
{result.push(encodeURIComponent(this.mPathComponents[index]));}
index+=1;}
return result;},encodedPath:function()
{if(this.isAbsolute())
{return"/"+this.encodedPathComponents().slice(1).join("/");}
else
{return this.encodedPathComponents().join("/");}},toURLString:function()
{if(this.isRelative())
{return this.encodedPath()+
(this.mQuery?"?"+this.mQuery:"")+
(this.mFragment?"#"+this.mFragment:"");}
else
{return this.mProtocol+":"+"//"+this.mAuthority+this.encodedPath()+
(this.mQuery?"?"+this.mQuery:"")+
(this.mFragment?"#"+this.mFragment:"");}},isEqual:function(that)
{var pathMatches=true;if((this.mPathComponents)&&(that.mPathComponents)&&(this.mPathComponents.length==that.mPathComponents.length))
{for(var index=0;index<this.mPathComponents.length;++index)
{if(this.mPathComponents[index]!=that.mPathComponents[index])
{pathMatches=false;break;}}}
else
{pathMatches=false;}
return(this.mProtocol==that.mProtocol)&&(this.mAuthority==that.mAuthority)&&pathMatches&&(this.mQuery==that.mQuery)&&(this.mFragment==that.mFragment);},resolve:function(base)
{if(!this.isRelative())
return new IWURL(this.toURLString());var resolved=base.copy();resolved.mQuery=null;resolved.mFragment=null;if(resolved.mPathComponents==null)
{resolved.mPathComponents=[];}
this.mPathComponents.each(function(component)
{resolved.mPathComponents.push(component);});resolved.mPathComponents=IWURL.p_normalizePathComponents(resolved.mPathComponents);return resolved;},relativize:function(base)
{if(base&&(base.mPathComponents&&base.mPathComponents.length>0)&&(this.mProtocol==base.mProtocol)&&(this.mAuthority==base.mAuthority))
{var commonAncestorIndex=0;for(var index=0;index<Math.min(this.mPathComponents.length,base.mPathComponents.length);++index)
{if(this.mPathComponents[index]==base.mPathComponents[index])
commonAncestorIndex=index;else
break;}
var relativePath=[];for(var up=base.mPathComponents.length-1;up>commonAncestorIndex;--up)
{relativePath.push("..");}
for(var down=commonAncestorIndex+1;down<this.mPathComponents.length;++down)
{relativePath.push(this.mPathComponents[down]);}
var relativized=new IWURL();relativized.mPathComponents=IWURL.p_normalizePathComponents(relativePath);relativized.mQuery=this.mQuery;relativized.mFragment=this.mFragment;return relativized;}
else
{return this.copy();}}});

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_http_thorn_ws_reduceron_Reduceron_Practical_Reduceron_html/index.html version [560f50e554].























>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE HTML>
<html>
<head>
    <title>Redirection</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta http-equiv="Refresh" content="0; url=./bonnet/thorn.ws/reduceron/Reduceron/Practical_Reduceron.html">
</head>
<body id="the_document_body">
<p>Redirecting...</p>
</body>
</html>

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/ColinR.pdf version [5846ca49b8].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/Meta10Presentation.pdf version [7bd204c23c].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/epsrc.png version [8d2b4ed77a].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/flite.tar.gz version [b6d48747e4].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/hfl09.pdf version [153afabcc0].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/icfp2010talk.pdf version [2e166d7c60].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/index.html version [95e4f16dc4].

















































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
<html>

<head>
<title>The Reduceron</title>
</head>

<body>

<center>
<table width=680>


<tr><td align=justify>
<center>
<h1>The Reduceron</h1>
<a href="http://www.cs.york.ac.uk/~mfn/">Matthew Naylor</a>,
<a href="../../~colin/index.html">Colin Runciman</a> and
<a href="../../~jason/index.html">Jason Reich</a>

<br><br><p>Sponsored by

<p><img src="epsrc.png"><br><br>
Engineering and Physical<br>
Sciences Research Council, UK<br><br>

<p>FPGA kit provided by

<p><img src="xilinx.png"><br><br>


</center>

<h3>Introduction</h3>

<p>From October 2008 to March 2010, we worked on the
<a href="http://www.epsrc.ac.uk/">EPSRC</a>-funded project
<!--
<p><center><i><a href="proposal.pdf">The Reduceron: High-Level Symbolic
Computing on FPGA</a></i>,</center></p>-->
<i>The Reduceron: High-Level Symbolic Computing on FPGA</i>.
This web-page makes available the results of the project.

<h3>Published material</h3>

<p>The design, implementation, and evaluation of the Reduceron are
described in detail in our paper

<p><center><i><a href="jfp-reduceron.pdf">The Reduceron reconfigured and
re-evaluated</a><br>
(to appear in the Journal of Functional Programming)</i>
</center>

<p>This is an extended version of a <a href="report.pdf">paper</a>
accepted to <a
href="http://www.icfpconference.org/icfp2010/">ICFP'10</a>.
Other relevent published material includes our paper
<a href="../../plasma/publications/pdf/ReichNaylorRuncimanM10.pdf">Supercompilation and the Reduceron</a>
       (accepted to <a
               href="http://meta2010.pereslavl.ru/">META'10</a>)
       and, from the beginning of the project,
       our <a href="hfl09.pdf">talk proposal</a>
(accepted to <a
href="http://keel.martin-robson.com/hfl/hfl09/">HFL'09</a>).

<p>The benchmark programs used in our experiments are available <a
href="programs.tar">here</a>.

<h3>Presentations</h3>

<ul>
<li>
Colin <a href="icfp2010talk.pdf">presented</a> on 27 September 2010
about the Reduceron at ICFP'10, Baltimore.
</li>

<li>Colin <a href="ColinR.pdf">presented</a> on 21
July 2010 about guaranteed primitive redex speculation at the Nottingham FP
group away day; we hope to have more on this topic soon.
</li>

<li>Matthew 
<a href="memos/Memo41.pdf">presented</a> on 26 November 2009 about the
Reduceron at FitA, Microsoft Cambridge.
</li>

<li>Jason
<a href="Meta10Presentation.pdf">presented</a> on 3 July 2010 about
Supercompilation and the Reduceron at Meta'10, Russia.
</li>

<li>Matthew also presented at Birmingham University (invited talk), HFL'09,
and YDS'08 (invited talk).

</ul>

<h3>Implementation</h3>

The implementation of the Reduceron comprises three pieces.

<p>


<table width=100% border=1>
<tr>
<td><a href="york-lava.tar.gz">York Lava</a></td>
<td>The Haskell library used to describe the Reduceron</td>
</tr>
<tr>
<td><a href="flite.tar.gz">F-lite</a></td>
<td>A compiler for the language that runs on the Reduceron</td>
</tr>
<tr>
<td><a
href="reduceron.tar.gz">Reduceron</a></td>
<td>The reduction machine itself</td>
</tr>
</table>

<p>F-lite and York Lava are discussed in memos
<a href="memos/Memo9.txt">9</a> and
<a href="memos/Memo23.txt">23</a> respectively.  More recent versions
of these packages may be available on
<a href="http://hackage.haskell.org/">hackage</a>.

<h3>Memos</h3>

<p>The following memos record our <i>work-in-progress</i> thoughts and
ideas.  In general, they are drafty in nature: some are sketchy, some
are incomplete, and some are subsumed by later memos and published
papers.

</p>
<table width=100% border=1>

<tr><td width=6%>1</td>
<td><a href="memos/Memo1.txt">Widening function bodies</a></td></tr>

<tr><td>2</td>
<td><a href="memos/Memo2.txt">Widening function bodies revisited</a></td></tr>

<tr><td>3</td>
<td><a href="memos/Memo3.txt">
Head reduction, arity raising and shared subexpressions</a></td></tr>

<tr><td>4</td>
<td><a href="memos/Memo4.txt">Chunky lists</a></td></tr>

<tr><td>5</td>
<td><a href="memos/Memo5.txt">
Recursive nested-case function bodies and in-lining</a></td></tr>


<tr><td>6</td>
<td><a href="memos/Memo6.txt">
Case factorisation and special treatment of constructors</a></td></tr>

<tr><td>7</td>
<td><a href="memos/Memo7.txt">Memory layout</a></td></tr>

<tr><td>8</td>
<td><a href="memos/Memo8.txt">
Speculative evaluation of primitive redexes</a></td></tr>

<tr><td>9</td>
<td><a href="memos/Memo9.txt">F-lite: a core subset of Haskell</a></td></tr>

<tr><td>10</td>
<td><a href="memos/Memo10.txt">Experminents in inlining, arity-raising,
and indirection-avoidance</a></td></tr>

<tr><td>11</td>
<td><a href="memos/Memo11.txt">
Enabling arity-raising by sharing analysis</a></td></tr>

<tr><td>12</td>
<td><a href="memos/Memo12.lhs">An algorithm for arity-reduction</a></td></tr>

<tr><td>13</td>
<td><a href="memos/Memo13.txt">Compiling case expressions</a></td></tr>

<tr><td>14</td>
<td><a href="memos/Memo14.txt">First quarterly review</a></td></tr>

<tr><td>15</td>
<td><a href="memos/Memo15.lhs">Towards a spineless Reduceron</a></td></tr>

<tr><td>16</td>
<td><a href="memos/Memo16.txt">Configuring the XUPV5</a></td></tr>

<tr><td>18</td>
<td><a href="memos/Memo18.lhs">Spineless versus Spinefull</a></td></tr>

<!--
<tr><td>19</td>
<td><a href="memos/Memo19.pdf">
HFL-York Talk: Three improvements to the Reduceron</a></td></tr>

<tr><td>20</td>
<td><a href="memos/Memo20.pdf">Brimingham Talk: The Reduceron</a></td></tr>
-->

<tr><td>21</td>
<td><a href="memos/Memo21.txt">Second quarterly review</a></td></tr>

<tr><td>22</td>
<td><a href="memos/Memo22.lhs">Compiling F-lite to C</a></td></tr>

<tr><td>23</td>
<td><a href="memos/Memo23.txt">York Lava, by example</a></td></tr>

<tr><td>25</td>
<td><a href="memos/Memo25.txt">Bounded template instantiation</a></td></tr>

<tr><td>26</td>
<td><a href="memos/Memo26.lhs">Rotation and one-hot addition</a></td></tr>

<tr><td>27</td>
<td><a href="memos/Memo27.lhs">Design of the Octostack</a></td></tr>

<tr><td>28</td>
<td><a href="memos/Memo28.txt">Mark-Compact in O(survivors) time</a></td></tr>

<tr><td>31</td>
<td><a href="memos/Memo31.txt">
Design proposal for speculative evaluation of primitive redexes</a></td></tr>

<tr><td>32</td>
<td><a href="memos/Memo32.txt">
Thoughts about Reducera -- a Plural Reduceron</a></td></tr>

<tr><td>33</td>
<td><a href="memos/Memo33.txt">Fourth quarterly review</a></td></tr>

<tr><td>34</td>
<td><a href="memos/Memo34.txt">Expected clock-tick run-times for
flite-reduceron programs</a></td></tr>

<tr><td>35</td>
<td><a href="memos/Memo35.txt">Order of compilation passes</a></td></tr>

<tr><td>36</td>
<td><a href="memos/Memo36.txt">Case of known construction -- at
compile-time and run-time</a></td></tr>

<tr><td>37</td>
<td><a href="memos/Memo37.txt">
Counting the clock cycles needed to
instantiate a combinator body</a></td></tr>

<tr><td>38</td>
<td><a href="memos/Memo38.txt">
Benefits of a primitive-value stack</a></td></tr>

<tr><td>39</td>
<td><a href="memos/Memo39.txt">
The Force-and-Rebind transformation</a></td></tr>

<tr><td>40</td>
<td><a href="memos/Memo40.txt">
Forcing arguments to primitive functions, revisited</a></td></tr>

<tr><td>41</td>
<td><a href="memos/Memo41.pdf">
FitA-Cambridge Talk: Dynamic analysis in the Reduceron</a></td></tr>

<tr><td>44</td>
<td><a href="memos/Memo44.txt">
An objective look at PRS</a></td></tr>

<tr><td>45</td>
<td><a href="memos/Memo45.txt">
Hardware support for exploiting Static PRS</a></td></tr>


<tr><td>46</td>
<td><a href="memos/Memo46.txt">
Checking for a fixed-point
using needed narrowing</a></td></tr>

<tr><td>47</td>
<td><a href="memos/Memo47.txt">
Descriptions of several F-lite programs
</a></td></tr>

<tr><td>48</td>
        <td><a href="memos/Memo48.txt">
Primitive redexes in the spine position
</a></td></tr>

<tr><td>49</td>
        <td><a href="memos/Memo49.txt">
hf: a tool that translates (a subset of) Haskell to Flite
</a></td></tr>


<tr><td>50</td>
        <td><a href="memos/Memo50.txt">
Finding and increasing PRS candidates
</a></td></tr>


</table>

<h3>Previous work</h3>

<p>The Reduceron was first developed as part of Matthew's thesis,
circa 2007.  Here are some of the materials that arose from the thesis
work.

<p>
<table width=100% border=1>
<tr>
<td><a href="../../ftpdir/reports/2009/YCST/02/YCST-2009-02.pdf">Thesis</a></td>
<td>Matthew's thesis, see Chapter 2</td>
</tr>
<tr>
<td><a href="reduceron.pdf">Paper</a></td>
<td>Published at
<a href="http://proglang.informatik.uni-freiburg.de/IFL2007/">IFL'07</a></td>
</tr>
<tr>
<td><a href="reduceron-old.tar.gz">Source code</a></td>
<td>Of the thesis implementation</td>
</tr>
</table>


</td></tr>




</table>

<script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
_uacct = "UA-1994021-1";
urchinTracker();
</script>

</center>
</body>
</html>

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/jfp-reduceron.pdf version [90aa6be4d8].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo1.txt version [f9d88d5c57].

























































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
============================
REDUCERON MEMO 1
Widening function bodies
Matthew N, 13 November 2008
============================

Take the standard definition of list-reversal using an accumulator.

  data List a = Nil | Cons a (List a)

  rev xs acc =
    case xs of
      Nil -> acc
      Cons x xs -> rev xs (Cons x acc)

The Reduceron compiler turns this definition into the following set of
combinator definitions.

  Nil n c       = n                        (1)
  Cons x xs n c = c x xs                   (2)
  rev  v acc    = v acc (rev' acc)         (3)
  rev' acc x xs = rev xs (Cons x acc)      (4)

The Reduceron can apply any one of these rewrite rules in

  3 + (n `div` 8)

clock-cycles, where n is the number of nodes in the right-hand-side.
One can see that the Reduceron is fast at instantiating large function
bodies --- the bigger the better!

However, function bodies often contain case expressions and, in
general, case expressions cause function bodies to be split up into
small pieces when compiled to combinator expressions.  To be precise,
a new combinator definition is created for each case-alternative whose
pattern is a non-zero-arity constructor.  For example, equation (4)
above captures the second case alternative in the original definition
of rev.

Can we avoid this splitting up, allowing larger functions bodies to
remain?  One way would be to support lambda expressions and to redefine
equation (3) as follows.

  rev v acc  = v acc (\x xs -> rev xs (Cons x acc))

The right-hand-size is definitly bigger, but now there is a new
problem.  How do we instantiate a function-body containing
lambda-bound variables?  When we apply rev, we don't know what to
substitute for x and xs.  We have to build a closure for the lambda
expression, but that corresponds to introducing a new combinator
definition and creating a partial application of that combinator to
the "environment" (in this case "acc").

My tentative conclusion is that inlining case alternatives does not
seem feasible.  But there is an alternative way to widen combinator
bodies.

Widening constructors
---------------------

Let us consider the possibility of a non-empty list containing four
heads (say) as well as just a single head.

  data List a = Nil | Cons a (List a) | Cons4 a a a a (List a)

The rev function must now be adjusted to handle Cons4 constructors.

  rev xs acc =
    case xs of
      Nil -> acc
      Cons x xs -> rev xs (Cons x acc)
      Cons4 x0 x1 x2 x3 xs -> rev xs (Cons4 x3 x2 x1 x0 acc)

Now take a look at the resulting combinators.

  Nil   n c c4                = n
  Cons  x xs n c c4           = c x xs
  Cons4 x0 x1 x2 x3 xs n c c4 = c4 x0 x1 x2 x3 xs
  rev   v acc                 = v acc (rev' acc) (rev'' acc)
  rev'  acc x xs              = rev xs (Cons x acc)
  rev'' acc x0 x1 x2 x3 xs    = rev xs (Cons4 x2 x3 x1 x0 acc)

They are quite a bit larger.  What's more they contain wider
application nodes which the Reduceron can fetch very quickly.

Of course, many questions remain.  Can constructors such as Cons4 be
introduced automatically *and* without breaking laziness?  If not, we
could define a library for "chunked" lists where the chunks are
considered spine-strict --- how well would this work?  Can other
functions, besides reverse, benifit from wider constructors too?
Widening constructors opens the possibility for vector arithmetic ---
can this be usefully exploited?

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo10.txt version [1ffb21ecfb].































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
========================================
REDUCERON MEMO 10
Experminents in inlining, arity-raising,
and indirection-avoidance
Matthew N, 3 December 2008
========================================

Inlining
--------

Recall from Memo 2 the following rule.

  In-line saturated applications of non-primitive functions that
  do not have directly recursive definitions.

Applying this rule has the following impact on the numbers of
clock-cycles consumed by a range of F-lite programs.

             ORIGINAL        INLINING      % SAVING
  MSS       151967140       137601295           9.5
  OrdList   149583463       118232316          20.9
  PermSort   71759053        60278393          16.0
  Adjoxo    224101439       203954019           9.0
  Queens    126972754       113051007          11.0
  Queens2   111123642        89323322          19.6
  Sudoku     82204574        73035899          11.1
  Clausify  105859724        93708139          11.4
  While     170548771       154640245           9.3

In many cases inlining and making function bodies wider is a win.  But
very large bodies take space to store in code memory and on the heap.
Furthermore, instantiation of a large body may be a waste of time if,
due to lazy evaluation, a large amount of the body is often never
evaluated.  So it is worth nothing that wider bodies are not always
better.

Arity-raising
-------------

In Memo 3 Colin proposed that functions can in some cases be
arity-raised to increase oppertunities for the inlining rule.  Here
are some measurements.

             INLINING   +ARITY-RASING      % SAVING
  MSS       137601295               ?             ?
  OrdList   118232316       101049057          14.5
  PermSort   60278393        53132888          11.6
  Adjoxo    203954019       203628546             0
  Queens    113051007       112837779           1.8
  Queens2    89323322       151462605         -69.5
  Sudoku     73035899        75028197          -2.7
  Clausify   93708139       103427251         -10.3
  While     154640245       150698191           2.5

The '?' in the 'MSS' means 'a long time'.  The problem with doing
arity-raising is that sometimes sharing is lost.

Indirection-avoidance
---------------------

Section 2.10.4 of my thesis explains that the Reduceron can be easily
improved by avoiding creating some kinds of indirection chains.  Here
are some measurements of this improvement.

             INLINING    +CHAIN-AVOID      % SAVING
  MSS       137601295       109782887          20.2   
  OrdList   118232316       118232316             0
  PermSort   60278393        60278393             0
  Adjoxo    203954019       196951019           3.4
  Queens    113051007       113051007             0
  Queens2    89323322        84118102           5.8
  Sudoku     73035899        56199547          23.0
  Clausify   93708139        92490351           1.3
  While     154640245       154640245             0

Here are the percentages of time taken to perform unwinding of
indirections, even after chain-avoidance.

                 % Time spent unwinding indirections
  MSS            17
  OrdList         0
  PermSort        0
  Adjoxo          9
  Queens         10
  Queens2         6
  Sudoku         47
  Clausify        0
  While           0

Further indirections could be avoided by writing the spine of a
function body not on to the end of the heap but over the top of the
current redex.  This depends on how much space the redex occupies, and
whether it is large enough to hold the new spine.  Provided there is
enough space, it is easy to do: just write the spine to a different
address.  The problem is determining if there is enough space.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo11.txt version [924951ff1f].



































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
==========================================
REDUCERON MEMO 11 (ABANDONED)
Enabling arity-raising by sharing analysis
Matthew N, 3 December 2008
==========================================

Suppose that for every function 'f' in a program we introduce a
function 'f*', the arity-raised version of 'f'.

What we'd like to do is to go through a program and replace calls to
'f' with calls to 'f*'.  Under what circumstances can we do this,
without losing sharing?

  Any application of 'f' being passed as an argument to a function that
  is linear in that argument can be replaced with an application of
  'f*'.

What does it mean for a function to be linear in an argument?

The body of a function 'f' is linear in argument 'v' if 

  it is any expression containing 0 references to 'v'

  OR

  it is an application of a function 'g' of arity 'n' to 'm' arguments
  AND one of the first 'min n m' arguments contains one reference to 'v'
  AND that argument is linear in 'v'
  AND the body of 'g' is linear in the variable at that argument position
  AND none of the final 'nat(m-n)' arguments contains a reference to 'v'

  OR

  it is a case expression
  AND the subject does not contain a reference to 'v'
  AND each alternative is linear in 'v'

  OR

  it is a case expression
  AND the subject does contain a reference to 'v'
  AND the subject is linear in 'v'
  AND each alternative does not contain a reference to 'v'
  AND each alternative is linear in its each of its pattern-bound variables

  OR

  it is a let expression
  AND the body references 'v'
  AND the body is linear in 'v'
  AND no binding references 'v'

  OR

  it is a let expression
  AND the body does not reference 'v'
  AND one binding binds a variable 'w' to an expression referencing 'v'
  AND that expression is linear in 'v'
  AND the let expression is linear in 'w'

  OR

  it is a variable

(ABANDONED)

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo12.lhs version [fc1d57629d].





























































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
================================
REDUCERON MEMO 12
An algorithm for arity-reduction
Matthew N, 7 January 2009
================================

The Reduceron reads the top eight stack elements into a register file
on every clock cycle.  This register file has eight read ports,
allowing fast block-instantiation of function bodies.  The problem is
that functions must take no more than eight arguments because then a
single block-read from stack memory is no longer sufficient.  Instead,
multiple reads would be needed, and block-reads would be of little
help when instantiating an application that refers to stack elements
eight or more addresses apart.  Things become more complicated and
less efficient without the arity limitation.

In Memo 7, I described a solution using an eight-port stack --- any
eight elements can be read in parallel, not just the top eight.
Indeed, this requires that we perform multiple reads, one for each
block of template nodes to be instantiated.  Although these reads can
be pipelined (reading of the next block of needed arguments can be
done in parallel with instantiation of the current block of template
nodes), there remains the one-cycle overhead of priming the pipeline
when instantiating the first application in the body.  There is no
such overhead in the original method because we always know exactly
what stack elements we'll need, namely the top eight, so we can always
pre-fetch the arguments a clock-cycle in advance.

So it seems that the current approach works quite well assuming that,
a lot of the time, functions take eight or fewer arguments anyway.
Still, the arity limitation must be solved.  To do so, we will
transform programs so that no function takes more than eight
arguments.  This memo defines such a transformation, called
arity-reduction.

Turner's abstraction algorithm
------------------------------

It is first useful to observe that arity-reduction is indeed possible.
Given a function of 'n' arguments, Turner's abstraction algorithm
produces one of 'n-1' arguments.  In the process, auxiliary functions
such as 'S' and 'K' might be introduced, but these functions
themselves take only three and two arguments respectively.  So
Turner's algorithm can be applied repeatedly until all functions in a
program take no more than eight arguments.

The only problem is that this may result in a lot of fine-grained
reductions of 'S' and 'K' being performed when executing functions of
more than eight arguments.  The algorithm below aims to be more
efficient; the introduced combinators do not need to be so
fine-grained, and they do not need to be the same for all programs.

Dijkstra's abstraction algorithm
--------------------------------

In reaction to Turner's work on combinators, Dijkstra developed his
own abstraction algorithm.  I find Dijkstra's algorithm clearer than
Turner's, and more amenable to tweaking.  So our algorithm is based on
Dijkstra's.

Dijkstra's main idea is to annotate the interior nodes of an
applicative expression with strings of combinators, rather than
introduce combinators into the expression by additional applicative
structure --- i.e. new identifiers and application nodes.  Dijkstra's
syntax for expressions is

> data Exp = Id String | App [Com] Exp Exp | Empty
>   deriving Show

An identifier is a variable (an argument to a function) or a function
name. A Com (combinator) is an S, B or C.

> data Com = S | B | C
>   deriving Show

An application containing an empty sequence of combinators is just a
standard binary application.  An application with a sequence of n > 0
combinators and sub-expressions e_0 and e_1 can be viewed as a lambda
expression of the form

  \v_1 .. v_n -> (e_0 vs) (e_1 ws)

where vs and ws are sequences containing zero or more of the variables
v_1 .. v_n as defined by the sequence of combinators.  More
specifically, if the i^th combinator in the sequence is S then v_i is
passed down to both e_0 and e_1; if it is B then v_i is passed down
only to e_1; if it is C then v_i is passed down only to e_0.

EXAMPLE 1. For some expressions e_0 and e_1, the expression

  App [S,B,C] e_0 e_1

can be viewed as the lambda expression

  \x y z -> (e_0 x z) (e_1 x y)

EXAMPLE 2. An expression can also be 'Empty'.  For some expression e,
the expression

  App [S] e Empty

can be viewed as the lambda expression

  \x -> (e x) x

'Empty' expressions are introduced during the abstraction process.

Here is Dijkstra's abstraction algorithm, defined in Haskell.

> dijkstra :: String -> Exp -> Exp
> dijkstra x (Id y) | x == y = Empty
> dijkstra x (App coms e0 e1)
>   | x `occursIn` e0 && x `occursIn` e1 =
>       App (S:coms) (dijkstra x e0) (dijkstra x e1)
>   | x `occursIn` e0 = App (C:coms) (dijkstra x e0) e1
>   | x `occursIn` e1 = App (B:coms) e0 (dijkstra x e1)

The auxiliary function 'occursIn' is defined as you'd expect.

> occursIn :: String -> Exp -> Bool
> x `occursIn` Empty = False
> x `occursIn` (Id y) = x == y
> x `occursIn` (App coms e0 e1) = x `occursIn` e0 || x `occursIn` e1

Dijkstra assumes that the variable to be abstracted from an expression
does indeed occur in that expression.  We will later remove this
assumption.

Converting Dijkstra's Expressions to Combinator Expressions
-----------------------------------------------------------

Dijkstra's expressions aren't supported by standard functional
evaluators such as the Reduceron, so we'd like to convert them to
expressions which are.  To do this, we move the combinator annotations
to identifiers occurring in the applicative structure.  So Dijkstra's
algorithm, composed with the conversion function below, has a similar
function to Turner's algorithm.

We assume that the identifiers I, B, C, and S are defined by the
following equations.

  I x = x
  B k f g x = k f (g x)
  C k f g x = k (f x) g
  S k f g x = k (f x) (g x)
 
With a left-associative operator for function application,

> infixl @@
> (@@) :: Exp -> Exp -> Exp
> e0 @@ e1 = App [] e0 e1

the conversion is defined as follows.

> convert :: Exp -> Exp
> convert Empty = Id "I"
> convert (Id x) = Id x
> convert (App [] e0 e1) = e0 @@ e1
> convert (App cs e0 e1) = 
>   foldr (@@) (Id "I") (map (Id . show) cs) @@ convert e0 @@ convert e1

(The third equation is unnecessary, but saves symbols.)

EXAMPLE 3.  The value of the expression

> example3 = convert (App [S,B,C] (Id "e0") (Id "e1"))

is 'S (B (C I)) e0 e1' (pretty-printed).

Vectored applications
---------------------

We now modify Dijkstra's algorithm to work on vectored applications,
rather than just binary ones.  This makes combinators more
coarse-grained because they don't just say "pass left" or "pass
right", but "pass to the i^th sub-expression".

First of all, the syntax is changed as follows.

> data Exp2 = Id2 String | App2 [Dir] [Exp2]
>   deriving Show

Strings of combinators are no longer represented as lists of S, B, or
C combinators, but as lists of director strings.

> type Dir = [Bool]

The i^th element of a director string states whether or not an
argument is passed to the i^th element of the application.

EXAMPLE 4. The application

  App [S,B] e_0 e_1

is now represented by

  App2 [[True,True],[False,True]] [e_0, e_1]

That is, S is equivalent to the director string [True, True] (pass the
argument to both sub-expressions) and B is equivalent to [False, True]
(pass the argument to the right-hand sub-expression only).

Note that an all-False director string is possible, e.g. [False,
False].  This allows us to abstract a variable from an expression in
which that variable does not occur.

We no longer have an explicit constructor for empty expressions; these
can be sensibly represented as nullary applications.

> empty :: Exp2
> empty = App2 [] []

The modified abstraction algorithm is as follows.

> dijkstra2 :: String -> Exp2 -> Exp2
> dijkstra2 x (Id2 y)
>   | x == y = empty
>   | otherwise = App2 [[False]] [Id2 y]
> dijkstra2 x (App2 dirs es) = App2 (bs:dirs) es'
>   where bs = map (x `occursIn2`) es
>         es' = [if b then dijkstra2 x e else e | (e, b) <- zip es bs]

This function can indeed abstract a variable from an expression in
which that variable does not occur.

The auxiliary function 'occursIn2' is defined as you'd expect.

> occursIn2 :: String -> Exp2 -> Bool
> x `occursIn2` (Id2 y) = x == y
> x `occursIn2` (App2 dirs es) = any (x `occursIn2`) es

The Modified Conversion Algorithm
---------------------------------

We must now modify the conversion algorithm for vectored applications.
In a further effort to increase the granularity of the combinators, we
attempt to introduce a single combinator for each sequence of director
strings rather than one for each director string.  As there is a limit
on the number of arguments that a function can take, this is not
always possible.  So the algorithm below takes the arity-limit as an
argument and introduces combinators that encode chunks of director
strings, where the chunks are as large as possible without breaking
the arity-limit.

> convert2 :: Int -> Exp2 -> Exp2
> convert2 n (Id2 x) = Id2 x
> convert2 n (App2 [] []) = Id2 "I"
> convert2 n (App2 [] es) = App2 [] es
> convert2 n (App2 ds es) =
>     App2 [] (foldr apply (Id2 "I") combs : map (convert2 n) es)
>   where
>     m = n - length es - 1
>     combs = map (\ds -> App2 ds []) (groupN m ds)
>     apply a b = App2 [] [a, b]

This function assumes that variable 'm' is never less than or equal to
zero.  Equivalently, vectored applications must be shorter than the
arity-limit minus one.  This condition can be easily established in an
initial application-splitting pass.

Above, we use the syntax 'App2 ds []' to represent a combinator,
specifically the combinator defined by 'comb ds'.

> comb :: [Dir] -> (Int, Exp2)
> comb (d:ds) = (1 + n + m, body)
>   where
>     n = length d
>     m = length (d:ds)
>     body = App2 [] (Id2 "0" : args)
>     args = zipWith apply [1..n] is
>     is = map (\bs -> [i | (b,i) <- zip bs [n+1..], b]) (transpose (d:ds))
>     apply n is = App2 [] (map (Id2 . show) (n:is))
 
This function takes a sequence of director strings and generates a
combinator definition in the form of a pair whose first element is the
arity of the combinator and whose second element is the body of the
combinator.
 
EXAMPLE 5.  The expression

> example5 = comb [[True,False,True],[True,False,False]]

generates the following combinator definition (pretty-printed).

  \v0 v1 v2 v3 v4 v5 -> v0 (v1 v4 v5) v2 (v3 v4)

Remaining Questions
-------------------

How do we deal with let expressions?

Auxiliary functions
------------------

> transpose :: [[a]] -> [[a]]
> transpose [] = []
> transpose [x] = map (: []) x
> transpose (x:xs) = zipWith (:) x (transpose xs)

> groupN :: Int -> [a] -> [[a]]
> groupN n [] = []
> groupN n xs = take n xs : groupN n (drop n xs)

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo13.txt version [b929536860].







































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
===================================
REDUCERON MEMO 13 (Subsumes Memo 6)
Compiling case expressions
Matthew N, 8 January 2009
===================================

We consider case-elimination, a transformation that removes data
constructors and case expressions from a program.  We observe three
weaknesses, and propose a new transformation in which these weaknesses
are removed.  The new version has a weakness of its own, but it also
opens up a new possibility: the selection of a case alternative in
zero clock-cycles.

Standard case-elimination
-------------------------

Case expressions can be removed by defining data constructors as
functions and transforming case expressions to function applications.
Specifically, for each constructor 'c_i' of a data type with 'n'
constructors, a function definition 

  c_i v_1 .. v_(#c_i) w_1 .. w_n = w_i v_1 v_(#c_i)

is introduced, where '#c' denotes the arity of the constructor 'c'.
Now case expressions of the form

  case e of
    c_1 v_1 .. v_(#c_1) -> e_1
    ..
    c_n v_1 .. v_(#c_n) -> e_n

are transformed to

  e (\v_1 .. v_(#c_1) -> e_1)
    ..
    (\v_1 .. v_(#c_n) -> e_n)

Even the lambda expressions can be eliminated by introducing a
function for each alternative

  alt_1 x_1_1 .. x_1_* v_1 .. v_(#c_1) = e_1
  ..
  alt_n x_n_1 .. x_n_* v_1 .. v_(#c_n) = e_n

and transforming the case expression to

  e (alt_1 x_1_1 .. x_1_*) .. (alt_n x_n_1 .. x_n_*)

where 'x_i_1 .. x_i_*' are the non-pattern-bound variables in occurring
in 'e_i'.  The resulting programs contain no data constructors, no
case expressions, and no lambda expressions (assuming they had no
lambda expressions to begin with).  Functional language implementors
now only need to worry about how to do function application
efficiently.

EXAMPLE 1. The standard definition of list concatenation,

  append xs ys =
    case xs of
      Nil -> ys
      Cons x xs -> Cons x (append xs ys)

is transformed to

  Nil n c = n
  Cons x xs n c = c x xs
  append xs ys = xs ys (consCase ys)
  consCase ys x xs = Cons x (append xs ys)

A new function does not need to be introduced for the nil case because
the data constructor 'Nil' has no arguments.

EXAMPLE 2. Given the data type

  data Exp = X | Y | Neg Exp | Add Exp Exp | Sub Exp Exp

the evaluator,

  eval x y X = x
  eval x y Y = y
  eval x y (Neg n) = 0 - eval x y n
  eval x y (Add n m) = eval x y n + eval x y m
  eval x y (Sub n m) = eval x y n - eval x y m

is transformed to

  X x y neg add sub = x
  Y x y neg add sub = y
  Neg n m x neg add sub = neg n
  Add n m x y neg add sub = add n m
  Sub n m x y neg add sub = sub n m
  eval x y e = e x y (negAlt x y) (addAlt x y) (subAlt x y)
  negAlt x y n = 0 - eval x y n
  addAlt x y n m = eval x y n + eval x y m
  subAlt x y n m = eval x y n - eval x y m

Weaknesses
----------

We have three main complaints about the above transformation.

1. Linear variables may become non-linear.  Look at the definition of
'append' in Example 1; originally, it is clearly linear in 'ys', but
not after the transformation is performed.  Although 'append' passes
'ys' down to both case alternatives, we know that only one case
alternative will be chosen at run-time.  This knowledge is lost by the
transformation.

2. The same value may be written onto the heap many times.  We have
just seen that if a variable is referenced in more than one case
alternative, then it is passed down to each one separately.  Look at
the definition of 'eval' in Example 2; 'x' and 'y' are each referenced
four times in the right-hand-side.  It takes time to write these
values onto the heap, space to store them, and more time to unwind
them onto the stack when there are needed.

3. Case alternatives are pushed onto the stack.  Look at the
definition of 'eval' in Example 2; the spine contains a 6-node
application.  It takes time to write such a long application
(containing the case alternatives) onto the stack, and stack space to
store it.

The new approach
----------------

For the case alternatives, let us instead introduce the functions

  alt_1 v_1 .. v_(#c_1) alts x_1 .. x_m = e_1
  ..
  alt_n v_1 .. v_(#c_n) alts x_1 .. x_m = e_n

and transform case expressions to

  e alts x_1 .. x_m

where 'x_1 .. x_m' is the union of all the non-pattern-bound variables
in occurring in each 'e_i' and 'alts' is the lookup table defined by

  alts = [| alt_1, .., alt_n |]

When a constructor 'c_i' appears on top of the stack 's', it is
reduced to

  s[sp+#c_i][i]

assuming the stack 's' grows from index 0 onwards and 'sp' is the
index of the top stack element. (Recall that '#c' denotes the arity of
the constructor 'c'.)

EXAMPLE 1 (REVISITED). The definition of 'append' is now transformed to

  append xs ys = xs appendAlts ys
  appendAlts = [| nilCase, consCase |]
  nilCase alts ys = ys
  consCase x xs alts ys = Cons x (append xs ys)

EXAMPLE 2 (REVISITED).  The definition of 'eval' is now transformed to

  eval x y e = e evalAlts x y
  evalAlts = [| xCase, yCase, negCase, addCase, subCase |]
  xCase x y alts = x
  yCase x y alts = y
  negCase x y alts n = 0 - eval x y n
  addCase x y alts n m = eval x y n + eval x y m
  subCase x y alts n m = eval x y n - eval x y m

Comparison
----------

The new approach removes the three weaknesses of the old one.

1. Linear variables remain linear.  All the variables needed in the
case alternatives are pushed onto the stack once, and the case
alternative simply picks out the variables it requires.  Look at the
definition of 'append'; it refers to 'ys' once.

2. The same variable is no longer written onto the heap once for each
case alternative that refers to it.  Look at the definition of 'eval';
it refers to 'x' and 'y' once.  Also, no unwinding is needed when
fetching a case alternative.

3. Each case alternative is no longer pushed onto the stack.  The case
alternatives are stored in the 'alts' table (for example, see
'evalAlts'), and are never written to the stack or the heap.

There are also some potential drawbacks.

1. The biggest drawback is that functions are introduced even for case
alternatives that have no pattern-bound variables.  Look at 'xCase'
and 'yCase' in the definition of 'eval'.  It will take time to apply
these functions, which aren't introduced in the original
transformation.

2. The new approach requires new evaluation machinery, namely the
special treatment of constructors.  A double array lookup is not a big
implementation challenge, but still, the obvious implementation of a
double array lookup consumes two clock cycles.  Can we do better?  The
next section suggests that it can be done in zero cycles.

Zero-cycle case selection
-------------------------

Recall that when a constructor 'c_i' appears on top of the stack 's',
it is reduced to

  s[sp+#c_i][i]

Here we essentially have a double indirection to follow:

  1. Determine the address of the lookup table for the case
     alternatives by indexing the stack.

  2. Determine the address of the appropriate case alternative by
     indexing the lookup table.

Each of these steps consumes a clock-cycle.

First observe that the second indirection can be removed.  Instead of
storing a lookup table of code addresses (for the case alternatives),
we can align the case alternatives in code memory so that the
Reduceron can jump straight to appropriate code.  However, case
alternatives may have different body sizes, so we must either:

  1. Require that all the case alternatives occupy the same amount of
     space in code memory, or

  2. Put 'next' pointers at appropriate places in code memory.  At the
     moment the Reduceron reads the next block of nodes from code memory
     while instantiating the current block; the only difference
     would be that 'next' is not 'address of current block+1' but
     'whatever the current block states next is'.

The reason for the first indirection is that the address of the lookup
table must come after the data construction.  As different
constructors have different arities, the constructor must be known
before the offset of the lookup table on the stack can be determined.
One way to remove this indirection is to introduce a new stack, let's
call it the LUT stack, for storing the addresses of the lookup tables.
Since we always have the top of LUT stack to hand, we don't need to
index the main stack anymore.  Now when a constructor 'c_i' appears on
top of the main stack, it can be reduced to the code address 'lut+i'
where 'lut' is the value on top of the LUT stack.  Such a simple
addition can be performed without consuming any clock-cycles.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo14.txt version [75179dc56a].



























































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
=========================
REDUCERON MEMO 14
First quarterly review
Matthew N, 9 January 2009
=========================

Three months after starting on the Reduceron project, this memo
summarises our main results and thoughts to date.

1. F-lite
---------

Quite early on, Matthew decided to re-implement the Reduceron compiler
from scratch.  There were a few motivations, the main two being (1)
compiling full Haskell'98, as we previously attempted, is a lot of
work and not necessary for the research, and (2) we wanted a simple,
standalone compiler that does not rely on a large and complex codebase
such as Yhc.  So we developed F-lite, a simple functional language,
and implemented a compiler for it.  (See Memo 9 for details.)

2. Inlining
-----------

One of the first problems we considered was that of how to widen
function bodies, to take advantage of the Reduceron's fast
block-instantiation capability.

We observed that functions which perform case analysis are often split
into several smaller functions by the case-elimination algorithm, one
new function being introduced for each case alternative.  This
splitting has the effect of turning directly-recursive functions
into sets of mutually-recursive ones.

We found a very simple yet effective inlining rule that is to some
extent able to reunite the split-up functions, and make them directly
recursive again.  Somewhat counter-intuitively, the trick was to
inline the case analysis function into the case alternatives, rather
than the other way around!  (See Memo 2 for details.)

Subsequently, Colin observed that nested case analysis results in a
set of mutually-recursive functions being introduced, which are not
made directly-recursive again after applying the inlining rule [Memo 5].

After translating our benchmark programs to F-lite, and adding
some new ones, we performed some measurements.  We found that our
simple inlining rule gave an 9%-20% improvement on all nine programs,
a mean speed-up of 13% [Memo 10].

3. Arity-raising
----------------

Colin observed the potential of eta-expansion to increase
opportunities for inlining [Memo 3].  We implemented this
transformation, but after benchmarking we soon realised that it
doesn't always preserve sharing, causing a big slow-down in some
programs.  However, in some programs there is indeed a good speed-up
[Memo 10], so Matthew started to think about using a sharing analysis
to determine cases in which arity-raising is safe [Memo 11].  But this
line of work has been suspended due to more pressing matters discussed
below.

4. Single-cycle reads
---------------------

In the current Reduceron, reading the heap takes two clock-cycles
because of the delay introduced by the cascading multiplexor and the
rotation logic used to implement quad-word memories.  Matthew proposed
to abandon quad-word memories in favour of quadrupling the word-size,
and also to cascade block RAMs differently.  Together these ideas will
allow single-cycle heap-reads, and save a lot of logic.  Single-cycle
reads will in turn reduce the time taken to unwind and unfold by one
cycle each.  Another benefit of having a fixed application size is
that updating can be easily done by overwriting the root of the redex
rather than introducing indirections.  The downside is that there will
be some wasted capacity, both in terms of memory and processor
utilisation, when the lengths of applications are not multiples of
four.  (See Memo 7 for details.)

5. Parallel stack accesses
--------------------------

Also in Memo 7, we recalled that the way in which the Reduceron
implements parallel access to the top eight stack elements has the
limitation that functions cannot take more than eight arguments.  We
proposed to overcome the problem using an eight-port stack, where any
eight stack elements can be read in parallel, not just the top eight
[Memo 7].  After some more thought, we realised this approach has a
clock-cycle overhead associated with it, putting the original approach
in better light.  To overcome the arity-limitation, Matthew modified
Dijkstra's abstraction algorithm to transform functions into ones that
take no more than eight arguments.  (See Memo 12 for details.)

6. Spinelessness
----------------

In a spineless machine, the spine of a function body is written only
to the heap.  In the Reduceron, the spine is written to the heap and
the stack, but time is saved by performing these two writes in
parallel.  Nevertheless, there are two potential benefits in making
the Reduceron a spineless machine.  

First of all, not having to write the spine to the heap and not having
to update the heap on every application frees up the heap, allowing
the information needed for the next reduction to be fetched one
clock-cycle earlier.  But the cost of updating must be taken into
account as a distinct process.  Depending on how often updates are
avoided (see next section), there will be an overall saving of 0 to 1
cycles per unfold.  Combine this with single-cycle reads and we open
up the possibility of single-cycle unfolding (whenever the update can
be avoided, and the body contains two applications or less).  In the
current Reduceron, unfolding always takes at least three cycles.

Furthermore, since a spineless machine builds up a stack of updates,
it may be possible to perform two updates per cycle, exploiting the
dual-port heap.

The second benefit is that a spineless machine does not need to
introduce indirections in any circumstances.  This is attractive
because long indirection chains are sometimes built by the current
Reduceron [Memo 10].

7. Unshared applications
------------------------

To implement an efficient spineless machine, Peyton Jones proposes to
distinguish between two types of applications: unshared and
possibly-shared.  Updating can then be avoided after evaluating an
unshared application to head normal form.

Determining which applications are unshared can be done by statically
or dynamically.  The latter, which Peyton Jones calls "dashing" is
more precise (less conservative), but Peyton Jones writes "we strongly
suspect that the cost of dashing may greatly outweigh the advantage of
precision".  However, in hardware, we strongly suspect that dashing
has no (time) cost.

This technique could also be useful in the current Reduceron (not
spineless), for example to reduce indirection chains.

8. Compiling case expressions
-----------------------------

Matthew proposed an alternative way to compile case expressions.  The
current Reduceron may require time to (1) write closures for each case
alternative onto the stack --- possibly several cycles when there are
many case alternatives, (2) select one of the case alternatives --- 3
cycles, and (3) fetch one of these closures when selected --- 2 cycles.
The new method opens up the possibility for zero-cycle case-alternative
selection, and removes each of the above overheads.  The downside is
that some implementation simplicity is sacrificed.  (See Memo 13 for
details; Memo 6 presents an earlier view of a similar idea.)

9. Speculative evaluation
-------------------------

Colin proposes to do speculative evaluation of primitive redexes
during instantiation of function bodies [Memo 8].  For example, if we
are to instantiate 'x+y' in the body of 'f' where 'x' and 'y' are
already-evaluated arguments of 'f', then we can perform the addition
speculatively.  Colin thinks it will be best to use a static analysis
to discover such redexes at compile-time, whereas Matthew thinks that
it will be cheap to discover such redexes at run-time.  Either way,
the idea looks promising.

10. Garbage collection
----------------------

Garbage collection is one area in particular that needs more thought.
It would be nice to do garbage collection in parallel with the
mutator.  With this aim, Colin is interested in on-the-fly collection
(coarse-grained parallelism), whereas Matthew is more interested in a
hardware implementation of reference-counting (fine-grained
parallelism).

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo15.lhs version [5271d52123].





















































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
=============================
REDUCERON MEMO 15
Towards a spineless Reduceron
Matthew N, 13 January 2009
=============================

We define the semantics of a spineless template-instantiation machine
with vectored applications, and its compiler.  We distinguish between
non-shared and possibly-shared applications by a dynamic analysis,
allowing updates of definitly non-shared applications to be avoided.
We measure the number of updates saved accross a range of programs,
and estimate the performance of a hardware implementation of the
machine.  Finally, we point out the main ways in which we intend to
improve the machine and compiler.

We begin with a few imports.

> import List
> import System.IO.Unsafe(unsafePerformIO)

Use of 'unsafePerformIO' is confined to the implementation of the
'emit' and 'emitInt' primitives of F-lite.

Semantics
---------

The heap is modelled as a list of applications, and can be indexed by
a heap-address.

> type Heap = [App]
> type HeapAddr = Int

An application is a list of nodes.

> type App = [Node]

Typically, function application is represented as a binary operator.
A list is a useful generalisation because we can make efficient use of
a machine with a wide word size.

A node is an application pointer, an integer, a function,
a primitive function, or an argument.

> data Node =
>     APP Shared HeapAddr
>   | INT Int
>   | FUN Arity [Instr]
>   | PRI PrimOp
>   | ARG Shared Int

An application may be unshared or possibly-shared, represented by
False and True respectively.

> type Shared = Bool

Similarly, arguments can be unshared or shared, depending on how many
times they are referenced in the body of a function.  Argument nodes
should never appear on the heap, only in template memory.

> type Arity = Int

Function nodes contain an arity and a list of instructions to execute
when the function is applied.  So template memory is implicit.  An
instruction either builds an application on the heap, or pushes an
application on the stack.

> data Instr =
>     MKAP App
>   | PUSH App

For example, the function 's' defined by

  s f g x = f x (g x)

would be represented by the following node.

  FUN 3 [ MKAP [ARG False 1, ARG True 2]
        , PUSH [ARG False 0, ARG True 2, APP False 0]
        ]

The application node 'APP False 0' is a relative address, referring
to the application at address 'hp+0' where 'hp' is the value of the
heap pointer before entering the code for 's'.

The only difference between an application made using MKAP and one
made using PUSH is that the latter application represents the spine of
the function body.  In a spineless machine, this application need only
be built on the stack, not the heap.

In the compilation scheme presented below, function nodes always
contain exactly one PUSH instruction.  However, in a machine with a
finite application size, it may be useful to perform several PUSHes.

For efficiency, it is useful to perform PUSHes after MKAPs as they do
not access the heap.  Consequently, if the new top-of-stack is an
application node, then the application can be pre-fetched from the
heap, saving a clock-cycle.  What's more serious is that performing
PUSHes before MKAPs would destroy the stack before the MKAPs have
fetched the necessary arguments.

F-lite supports the following 7 primitive operations: (+), (-), (==),
(/=), (<=), emit, and emitInt.

> type PrimOp = String

The stack is modelled as a list of nodes, and can be indexed by a
stack-address.

> type Stack = [Node]
> type StackAddr = Int

A spineless machine also contains an update-stack.

> type UStack = [Update]

An update is a stack-address/heap-address pair.  When a function
demands arguments beyond the stack-address of the top update, the
value at the corresponding heap-address is updated.  See below for
details.

> data Update = Update StackAddr HeapAddr

Here is a small-step operational semantics for the machine.  It
operates over a triple containing the stack, the update-stack and the
heap.  It assumes that the expression to be evaluated is of type
integer.

> eval :: Stack -> UStack -> Heap -> Int
> eval (APP sh addr:s) u h = eval (dashApp sh (h !! addr) ++ s) u' h
>   where u' = if sh then Update (length s+1) addr:u else u
> eval s (Update saddr haddr:u) h
>   | updateCheck s saddr = eval s' u h'
>   where (s', h') = performUpdate s h saddr haddr
> eval [INT i] u h = i
> eval (INT i:a:s) u h = eval (a:INT i:s) u h
> eval (PRI p:s) u h = eval (applyPrim p s) u h
> eval (FUN arity code:s) u h = eval s' u h'
>    where (args, rest) = splitAt arity s
>          (s', h') = foldl (exec args (length h)) (rest, h) code

The following sections elaborate on each of the rewrite rules.

Unfolding
---------

To unfold a function, we execute its instructions.  To do this, we
need to know the arguments to the function, the heap pointer (to
convert relative addresses in template-memory into absolute ones on
the heap), the argument-less stack (on which the spine application
will pushed) and the heap (where the other applications will be
written to).  During execution, the stack and the heap are modified.

> exec :: [Node] -> HeapAddr -> (Stack, Heap) -> Instr -> (Stack, Heap)
> exec args base (s, h) (MKAP app) = (s, h')
>   where h' = h ++ [map (inst args base) app]
> exec args base (s, h) (PUSH app) = (map (inst args base) app ++ s, h)

> inst :: [Node] -> HeapAddr -> Node -> Node
> inst args base (APP shared offset) = APP shared (base + offset)
> inst args base (ARG shared index)  = dash shared (args !! index)
> inst args base n                   = n

When substituting for a shared argument we must ensure that, if the
substituted value is an application, then it is marked as shared.
Peyton Jones calls this operation "dashing".

> dash :: Bool -> Node -> Node
> dash True (APP _ addr) = APP True addr
> dash _    n            = n

Unwinding
---------

Unwinding an application is not simply a case of copying it from the
heap to the stack.  If it is a shared application, we must dash each
node in order to propagate the sharing information.

> dashApp :: Bool -> App -> App
> dashApp b = map (dash b)

Furthermore, if it is a shared application, we must push an update on
the update-stack.  This allows us to detect when evaluation of the
application has reached head normal form, and tells us the address to
write the result to.

Updating
--------

An update-check is performed before every reduction (but not before
unwinding).  The check returns true if the arity of the function to
reduce is larger that the difference between the current stack-pointer
and the stack-address of the top update on the update-stack.

> updateCheck :: Stack -> StackAddr -> Bool
> updateCheck s@(top:_) saddr = arity top > length s - saddr

> arity :: Node -> Int
> arity (FUN arity code) = arity
> arity (PRI p) = 2
> arity (INT i) = 1

All primitives have an arity of two.  Perhaps surprisingly, an integer
has an arity of one, but indeed it is useful to treat integers as
unary functions as we'll see.

To perform the update, we compute the length of the normal-form, read
it from the stack, and write it to the heap.  Quite subtly, we must
also dash the normal-form on the stack, because the process of writing
it to the heap could duplicate an otherwise unshared reference.

> performUpdate :: Stack -> Heap -> StackAddr -> HeapAddr -> (Stack, Heap)
> performUpdate s h saddr haddr = (app ++ drop n s, update haddr app h)
>   where n = 1 + length s - saddr
>         app = dashApp True (take n s)

> update :: Int -> a -> [a] -> [a]
> update i a xs = [if i == j then a else x | (j, x) <- zip [0..] xs]

Primitives
----------

To force evaluation of the arguments to strict binary-arithmetic
primitives, primitive applications occuring in the F-lite program are
transformed by the rule

  a x y   -->   y (x a)

where 'a' is an arithmetic primitive and 'x' and 'y' are expressions.
Emit primitives, which are binary operations requiring only their
first arguments to be evaluated, are handled by the similar rule

  e x k   -->   x e k

where 'e' is an emit primitive and 'x' and 'k' are expressions.

These transformations treat integers as unary functions.
Specifically, an integer 'i' is reduced by the following rule

  i k     -->   k i

which corresponds to a swap operation in the reduction machine.

Applying a primitive function is now rather straightforward because
we know the arguments must be evaluated.

> applyPrim :: PrimOp -> Stack -> Stack
> applyPrim "(+)"     (INT a:INT b:s) = INT (a + b):s
> applyPrim "(-)"     (INT a:INT b:s) = INT (a - b):s
> applyPrim "(==)"    (INT a:INT b:s) = bool (a == b):s
> applyPrim "(/=)"    (INT a:INT b:s) = bool (a /= b):s
> applyPrim "(<=)"    (INT a:INT b:s) = bool (a <= b):s
> applyPrim "emit"    (INT a:k:s)     = emitStr [toEnum a] k:s
> applyPrim "emitInt" (INT a:k:s)     = emitStr (show a) k:s

We assume that data constructors and case expressions have been
compiled using Jansen's encoding.  So the constructors 'True' and
'False' are represented by the following nodes.

> bool :: Bool -> Node
> bool False = FUN 2 [PUSH [ARG False 0]]
> bool True  = FUN 2 [PUSH [ARG False 1]]

The 'emitStr' function takes two arguments and returns its second.  It
has the side-effect of printing its first argument.

> emitStr :: String -> a -> a
> emitStr s k = unsafePerformIO (putStr s) `seq` k

This completes the small-step semantics.  Now we define a compiler for
the machine.

Compilation
-----------

Programs contain function definitions, 

> type Prog = [Defn]

and a function definition consists of a function identifier, its
arity, a list of let-bindings, and its body.

> type Defn = (Id, Int, [Bind], Exp)

> type Id = String

> type Bind = (Id, Exp)

Expressions are defined as follows.

> data Exp =
>     Apply [Exp]
>   | Fun Id
>   | Prim Id
>   | Var Id
>   | Arg Shared Int
>   | Int Int

Function arguments are referred to by position using the 'Arg'
constructor; we assume they are already marked as unshared or shared.
One reason for this is that Jansen's encoding may hide the true
linearity of variables in case alternatives, so if Jansen's encoding
is used, the sharing analysis, albeit simple, needs to be done prior
to the removal of case expressions.  In this memo, we do not wish to
consider how best to implement case expressions, but see Memo 13 for
some thoughts on this.

Variables refer only to local let-bindings in a function's definition.

The compiler turns a program into a node which, when evaluated,
represents the result of the program.

> compile :: Prog -> Node
> compile p = head [FUN arity code | ("main", arity, code) <- p']
>   where p' = [(f, arity, comp p' bs e) | (f, arity, bs, e) <- p]

> comp p bs e = compSpine p binds code e
>   where (vs, es) = unzip bs
>         binds = zip vs ns
>         (code, ns) = mapAccumL (compExp p binds) [] es

> compSpine p binds code (Apply es) = fst (compApp p binds True code es)
> compSpine p binds code e = compSpine p binds code (Apply [e])

> compExp p binds code (Int i) = (code, INT i)
> compExp p binds code (Var v) = (code, n)
>   where n = head [dash True n | (w, n) <- binds, v == w]
> compExp p binds code (Arg shared i) = (code, ARG shared i)
> compExp p binds code (Prim f) = (code, PRI f)
> compExp p binds code (Fun f) = (code, n)
>   where n = head [FUN arity is | (g, arity, is) <- p, f == g]
> compExp p binds code (Apply es) = compApp p binds False code es

> compApp p binds spine code es =
>   (code' ++ [if spine then PUSH ns else MKAP ns], APP False (length code'))
>   where (code', ns) = mapAccumL (compExp p binds) code es

Running a program
-----------------

The result of a program is defined by the following function.

> run :: Prog -> Int
> run p = eval [compile p] [] []

Performance measurements: updates avoided
-----------------------------------------

Here are the numbers of updates performed, applications unwound,
instructions executed, swaps and primitives reductions accross a range
of benchmark programs.

  PROGRAM   UPDATES   UNWINDS    INSTRS    SWAPS    PRIMS
  Fib       16718       33471     75316    25092    20913
  Sudoku    16839       39274     94298     8138     5391
  OrdList   40400      227358    611949        0        1
  Queens    55220      169355     98500    76546    43435
  Queens2   70339      146681    374976    17122     9044
  PermSort  65515      139635    364867    17325     8674
  MSS       96805      187826    266116    83388    41726
  Clausify  77848      243064    769995    24964    14317
  While     68815      160735    447688    31451    15726

To see the impact of the dynamic sharing analysis, look at the
differences between the numbers of updates and unwinds.  Without any
sharing analysis, these numbers would be the same.  On average, the
dynamic sharing analysis avoids 60% of updates.

Performance estimate
--------------------

How well would such a machine perform in hardware?  We make the
following estimate and compare it with the Reduceron from Matthew's
PhD.

  PROGRAM        PHD REDUCERON    SPINELESS REDUCERON
  Fib                   230135                 129666
  Sudoku                435996                  97397
  OrdList              1614678                 455078     
  Queens                962788                 443056
  Queens2              1100675                 373775
  PermSort              932722                 353090
  MSS                  1485660                 468804
  Clausify             1662671                 639097
  While                1019827                 453986

What assumptions do we make about the hardware implementation of the
spineless Reduceron?  Well, many of these have been discussed in
previous memos, but here is a recap:

  1. Unwinding takes a single cycle, due to of memory-aligned
     applications.  Indeed, in the spineless experiments, all applications
     have been transformed so they have a maximum length of 4.

  2. Executing an instruction takes a single cycle, but two
     instructions in the same instruction sequence can be executed in
     parallel, thanks to the dual-port heap.  Again, all PUSH and MKAP
     instructions deal with applications of maximum length 4.

  3. A swap and a primitive reduction both take one cycle each.

  4. Updating takes '1 + n `div` 4' cycles where 'n' is the length of
     the normal-form.

Future developments
-------------------

Before attempting a hardware implementation of this machine, there are
three main improvements we'd like to explore: handling of case
expressions, speculative evaluation of primitives, and garbage
collection.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo16.txt version [a475419f72].































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
==========================
REDUCERON MEMO 16
Configuring the XUPV5
Matthew N, 12 January 2008
==========================

An XUPV5, kindly donated by Xilinx, has been sitting dormant on my
desk for two months now.  Today, I decided to configure it.  Looking
at the Xilinx webpages, there are many fancy demos that I can try
which show off the various features of the board.  Or I can follow the
getting started tutorial and create my own project using the Base
System Builder (BSB).  The BSB generates a Microblaze-based
(soft-processor) design which can be customised in various ways by
point and click.  Anyway, this is all quite extravagant --- whatever
happened to connecting the LEDs to the DIP switches as a first design?

With this aim, I first connected my PC to the Platform Cable USB to
the XUPV5 via the supplied USB and ribbon cables, and gave the XUPV5
some power.  Then I loaded Xilinx ISE, created a new project for an
XC5VLX110T chip (package FF1136, speed-grade -1), and added the
following VHDL file named 'top.vhd'.

  library IEEE;
  use IEEE.STD_LOGIC_1164.ALL;
  use IEEE.STD_LOGIC_ARITH.ALL;
  use IEEE.STD_LOGIC_UNSIGNED.ALL;

  entity top is
  port ( clock : in std_logic
       ; leds : out std_logic_vector(7 downto 0)
       ; switches : in std_logic_vector(7 downto 0));
  end top;

  architecture Behavioral of top is
  begin
    leds <= switches;
  end Behavioral;

I right-clicked on 'Generate Programming file', selected 'Properties'
then 'Startup Options', then set 'FPGA Start-Up Clock' to 'JTAG
Clock'.  Now all I had to do was state which pins on the FPGA are
connected to the clock, LEDs and DIP switches on the XUPV5.  All this
I gleaned from the 'ucf' file sitting in the archive
'xupv5-lx110t_bsb_std_ip.zip' supplied by Xilinx (can be obtained by
Googling the file name).  I put the required info in a file
'xupv5.ucf' (shown below), added it to the project, and clicked
'Generate Programming File' to generate 'top.bit'.

I fired up Impact to download the programming file.  It detected the
XUPV5, so I right-clicked on the FPGA, assigned it the file 'top.bit'
and then clicked 'Program'.  And there it was: eight little LEDs (just
below the LCD) mirroring the eight DIP switches (to the right of the
LCD).  Everything went smoothly.

What next?
----------

This little exercise turned out easy, but it does raise an important
question: how should the Reduceron be interfaced with the outside
world?  Switches and LEDs?  Cursor keys and LCD display?  Keyboard and
VGA?  UART terminal?  USB client?

As the tranditional interface to a functional language is the
read-eval-print loop, perhaps a UART terminal interface is just fine.
It's straightforward and cheap to implement in hardware with only a
few avilable commands (read char and write char) needed in F-lite.

Contents of 'xupv5.ucf'
-----------------------

Net clock TNM_NET = sys_clk_pin;
Net clock LOC = AH15;
Net clock IOSTANDARD=LVCMOS33;

# 8Bit set of LEDs

Net leds<0> LOC = AE24;
Net leds<0> IOSTANDARD=LVCMOS18;
Net leds<0> PULLDOWN;
Net leds<0> SLEW=SLOW;
Net leds<0> DRIVE=2;
Net leds<1> LOC = AD24;
Net leds<1> IOSTANDARD=LVCMOS18;
Net leds<1> PULLDOWN;
Net leds<1> SLEW=SLOW;
Net leds<1> DRIVE=2;
Net leds<2> LOC = AD25;
Net leds<2> IOSTANDARD=LVCMOS18;
Net leds<2> PULLDOWN;
Net leds<2> SLEW=SLOW;
Net leds<2> DRIVE=2;
Net leds<3> LOC = G16;
Net leds<3> IOSTANDARD=LVCMOS25;
Net leds<3> PULLDOWN;
Net leds<3> SLEW=SLOW;
Net leds<3> DRIVE=2;
Net leds<4> LOC = AD26;
Net leds<4> IOSTANDARD=LVCMOS18;
Net leds<4> PULLDOWN;
Net leds<4> SLEW=SLOW;
Net leds<4> DRIVE=2;
Net leds<5> LOC = G15;
Net leds<5> IOSTANDARD=LVCMOS25;
Net leds<5> PULLDOWN;
Net leds<5> SLEW=SLOW;
Net leds<5> DRIVE=2;
Net leds<6> LOC = L18;
Net leds<6> IOSTANDARD=LVCMOS25;
Net leds<6> PULLDOWN;
Net leds<6> SLEW=SLOW;
Net leds<6> DRIVE=2;
Net leds<7> LOC = H18;
Net leds<7> IOSTANDARD=LVCMOS25;
Net leds<7> PULLDOWN;
Net leds<7> SLEW=SLOW;
Net leds<7> DRIVE=2;

# 8Bit switches

Net switches<0> LOC=U25;
Net switches<0> IOSTANDARD=LVCMOS18;
Net switches<0> PULLDOWN;
Net switches<0> SLEW=SLOW;
Net switches<0> DRIVE=2;
Net switches<1> LOC=AG27;
Net switches<1> IOSTANDARD=LVCMOS18;
Net switches<1> PULLDOWN;
Net switches<1> SLEW=SLOW;
Net switches<1> DRIVE=2;
Net switches<2> LOC=AF25;
Net switches<2> IOSTANDARD=LVCMOS18;
Net switches<2> PULLDOWN;
Net switches<2> SLEW=SLOW;
Net switches<2> DRIVE=2;
Net switches<3> LOC=AF26;
Net switches<3> IOSTANDARD=LVCMOS18;
Net switches<3> PULLDOWN;
Net switches<3> SLEW=SLOW;
Net switches<3> DRIVE=2;
Net switches<4> LOC=AE27;
Net switches<4> IOSTANDARD=LVCMOS18;
Net switches<4> PULLDOWN;
Net switches<4> SLEW=SLOW;
Net switches<4> DRIVE=2;
Net switches<5> LOC=AE26;
Net switches<5> IOSTANDARD=LVCMOS18;
Net switches<5> PULLDOWN;
Net switches<5> SLEW=SLOW;
Net switches<5> DRIVE=2;
Net switches<6> LOC=AC25;
Net switches<6> IOSTANDARD=LVCMOS18;
Net switches<6> PULLDOWN;
Net switches<6> SLEW=SLOW;
Net switches<6> DRIVE=2;
Net switches<7> LOC=AC24;
Net switches<7> IOSTANDARD=LVCMOS18;
Net switches<7> PULLDOWN;
Net switches<7> SLEW=SLOW;
Net switches<7> DRIVE=2;

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo18.lhs version [74f96afad1].





























































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
==========================
REDUCERON MEMO 18
Spineless versus Spinefull
Matthew N, 23 January 2008
==========================

In Memo 15, we presented a semantics for a spineless Reduceron and, at
the end, some figures suggesting that it will be more efficient than
the existing Reduceron.  However, we did not intend to suggest that the
efficiency improvement is entirely due to moving from a spinefull
machine to a spineless one.  The real cause of the speed-up is surely
the use of memory-aligned applications, which is not specific to a
spineless machine.  Peyton Jones states that the spineless G-machine
is more efficient that the original (spinefull) G-machine, but he is
concerned with the PC architecture.  If the heap and stack can be
accessed in parallel (as in the Reduceron), then the spine can be
written to both at the same time, absorbing the main inefficiency of a
spinefull machine.

So the question is: which is better, a spineless or a spinefull
Reduceron?  This memo presents a semantics and a compiler for
spinefull Reduceron with memory-aligned applications, discusses its
strengths and weaknesses compared to the spineless Reduceron, and
finally presents some performance figures.

> import List
> import System.IO.Unsafe(unsafePerformIO)

Semantics
=========

> type Heap = [App]
> type HeapAddr = Int

> type App = [Node]

The first difference from the spineless machine is that application
and argument nodes do not contain a 'possibly-shared' flag.  However,
it does seem possible to utilise such sharing information in a
spinefull machine, and we comment on this below.

> data Node =
>     APP HeapAddr
>   | INT Int
>   | FUN Arity [Instr]
>   | PRI PrimOp
>   | ARG Int

> type Shared = Bool

> type Arity = Int

> type PrimOp = String

> data Instr =
>     MKAP App
>   | PUSH App

> type Stack = [Node]
> type StackAddr = Int

The update stack is a still list of updates like in the spineless
machine.  But the update stack now stores the heap address of every
node on the update stack.  The update stack in the spineless machine
contained only update records for the possibly-shared applications
currently under evaluation.

> type UStack = [Update]

An update record now contains the heap address of the application to
update, and an offset within that application. 

> data Update = Update HeapAddr Int

The offset is needed to correctly update an over-saturated
application.  For example, if the application 'f a b' at address 'n'
is unwound onto the stack, then the stack and address stack (which
both grow downwards) look as follows.

  STACK     USTACK
  b         n,3
  a         n,2
  f         n,1

If 'f a' reduces to 'z', we must update the 'f a b' at address 'n'
with 'z b'.  This can be done efficiently if the nodes of an
application are stored in reverse order on the heap.

The spinefull evaluator differs in several places from the spinless
one:

  1. To unwind an application of length 'n', 'n' nodes must be written
     both onto the node and address stacks.  To do this efficiently,
     we need a wide update stack as well as a wide node stack.  In
     the spineless machine, we didn't need a wide update stack.

  2. No "update-check" is required.  Instead, an update is performed
     on every reduction, including primitive and non-primitive
     function application.

  3. When executing a PUSH instruction, an update is performed,
     utilising one of the ports on the heap. If the number of
     instructions to execute is even, then both ports of the heap are
     in use on the final clock-cycle, so we must delay for a clock-cycle
     to read the next application.  In the spineless machine, PUSH
     instructions never access the heap.

  4. Although the spine can be written to the heap and stack in
     parallel, heap space is wasted when unnecessarily writing the
     spine to the heap.

  5. Like in the spineless machine, updating is done by overwriting
     the redex with the result (not with an indirection).  However,
     there is a special case where an indirection is required: when an
     application contained within an over-saturated application
     has been reduced.  Such an update requires two heap accesses: one
     to write the indirection, and one to write the application.

If 'possibly-shared' applications were implemented, as in the
spineless machine, the spinefull machine could in sometimes avoid
updating, freeing up the heap for other uses.

> eval :: Stack -> UStack -> Heap -> Int
> eval (APP addr:s) (_:u) h = eval (ap ++ s) (us ++ u) h
>   where ap = h !! addr
>         us = map (Update addr) [1..length ap]
> eval [INT i] u h = i
> eval (INT i:e:s) u h = eval (e:INT i:s) u h
> eval (PRI p:s) u h = eval s' u' h'
>   where res = applyPrim p s
>         (s', u', h') = performUpdate (drop 2 s) (drop 3 u) h (u !! 2) [res]
> eval (FUN arity code:s) u h = eval s' u'' h'
>    where (args, rest) = splitAt arity s
>          root = u !! arity
>          u' = drop (arity+1) u
>          (s', u'', h') = foldl (exec args (length h) root) (rest, u', h) code

> exec args base root (s, u, h) (MKAP app) = (s, u, h')
>   where h' = h ++ [map (inst args base) app]
> exec args base root (s, u, h) (PUSH app) = performUpdate s u h root app'
>   where app' = map (inst args base) app

> performUpdate s u h (Update a i) app
>   | length app <= i = let addrs = map (Update a) [1..length app] in
>       (app ++ s, addrs ++ u, update a app h)
>   | otherwise       = let addrs = map (Update (length h)) [1..length app] in
>       (app ++ s, addrs ++ u, update a [APP (length h)] h ++ [app])

> update :: Int -> a -> [a] -> [a]
> update i a xs = [if i == j then a else x | (j, x) <- zip [0..] xs]

> inst :: [Node] -> HeapAddr -> Node -> Node
> inst args base (APP offset) = APP (base + offset)
> inst args base (ARG index)  = args !! index
> inst args base n            = n

> applyPrim :: PrimOp -> Stack -> Node
> applyPrim "(+)"     (INT a:INT b:s) = INT (a + b)
> applyPrim "(-)"     (INT a:INT b:s) = INT (a - b)
> applyPrim "(==)"    (INT a:INT b:s) = bool (a == b)
> applyPrim "(/=)"    (INT a:INT b:s) = bool (a /= b)
> applyPrim "(<=)"    (INT a:INT b:s) = bool (a <= b)
> applyPrim "emit"    (INT a:k:s)     = emitStr [toEnum a] k
> applyPrim "emitInt" (INT a:k:s)     = emitStr (show a) k

> bool :: Bool -> Node
> bool False = FUN 2 [PUSH [ARG 0]]
> bool True  = FUN 2 [PUSH [ARG 1]]

> emitStr :: String -> a -> a
> emitStr s k = unsafePerformIO (putStr s) `seq` k

Compiler
========

> type Prog = [Defn]

> type Defn = (Id, Int, [Bind], Exp)

> type Id = String

> type Bind = (Id, Exp)

> data Exp =
>     Apply [Exp]
>   | Fun Id
>   | Prim Id
>   | Var Id
>   | Arg Shared Int
>   | Int Int

> compile :: Prog -> Node
> compile p = head [FUN arity code | ("main", arity, code) <- p']
>   where p' = [(f, arity, comp p' bs e) | (f, arity, bs, e) <- p]

> comp p bs e = compSpine p binds code e
>   where (vs, es) = unzip bs
>         binds = zip vs ns
>         (code, ns) = mapAccumL (compExp p binds) [] es

> compSpine p binds code (Apply es) = fst (compApp p binds True code es)
> compSpine p binds code e = compSpine p binds code (Apply [e])

> compExp p binds code (Int i) = (code, INT i)
> compExp p binds code (Var v) = (code, n)
>   where n = head [n | (w, n) <- binds, v == w]
> compExp p binds code (Arg shared i) = (code, ARG i)
> compExp p binds code (Prim f) = (code, PRI f)
> compExp p binds code (Fun f) = (code, n)
>   where n = head [FUN arity is | (g, arity, is) <- p, f == g]
> compExp p binds code (Apply es) = compApp p binds False code es

> compApp p binds spine code es =
>   (code' ++ [if spine then PUSH ns else MKAP ns], APP (length code'))
>   where (code', ns) = mapAccumL (compExp p binds) code es

> run :: Prog -> Int
> run p = eval [compile p] [Update 0 1] [undefined]

Estimated performance
=====================

Number of clock ticks taken by spineless and spinefull
implementations on a range of benchmarks:

  +-----------+------------+-----------+
  | PROGRAM   | SPINELESS  | SPINEFULL |
  +-----------+------------+-----------+
  | Fib       | 129666     | 158955    |
  | Sudoku    | 97397      | 133665    |
  | OrdList   | 455078     | 531051    |
  | Queens    | 443056     | 473734    |
  | Queens2   | 373775     | 392223    |
  | PermSort  | 353090     | 358934    |
  | MSS       | 468804     | 516212    |
  | Clausify  | 639097     | 793645    |
  | While     | 453986     | 512452    |
  +-----------+------------+-----------+

Conclusion
==========

The spineless Reduceron appears to have a few small advantages over a
spinefull one.

  1. It is slightly faster.
  2. It uses less heap space.
  3. It doesn't require sometimes introducing indirections when
     updating a redex.
  4. Its update stack is simpler; only one element ever needs to be
     pushed or popped at a time.  Its update stack also doesn't grow as
     large.
  5. It is more modular, as it separates updating from application,
     making application quite a bit simpler.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo2.txt version [3f7683c0f7].





































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
==================================
REDUCERON MEMO 2
Widening function bodies revisited
Colin R, 14 November
Discussion added 15 November
==================================

Yesterday, in Memo 1, Matt considered the question of how to achieve wider
function bodies for Reduceron.  The running example was

  data List a = Nil | Cons a (List a)

  rev xs acc =
    case xs of
      Nil -> acc
      Cons x xs -> rev xs (Cons x acc)

which currently gets compiled into the following equations.

  Nil n c       = n                        (1)
  Cons x xs n c = c x xs                   (2)
  rev  v acc    = v acc (rev' acc)         (3)
  rev' acc x xs = rev xs (Cons x acc)      (4)

Matt observes that if lambda-expressions could somehow be supported
then (4) could be in-lined in (3) like this

  rev v acc  = v acc (\x xs -> rev xs (Cons x acc))

but then points to several difficulties in the way of effective
support for lambda including the apparent need for some new
mechanism for dealing with variables such as x and xs.

The troublesome functional expression can be rewritten to avoid the
explicit lambda, by using the standard repertoire of small combinators
such as 'flip' and 'comp'.

    \x xs -> rev xs (Cons x acc)
  = \x xs -> flip rev (Cons x acc) xs
  = \x -> flip rev (Cons x acc)
  = \x -> flip rev (flip Cons acc x)
  = \x -> comp (flip rev) (flip Cons acc) x
  = comp (flip rev) (flip Cons acc)

Flipped constructors are simple enough.  They are no more
expensive than their originals -- but we might hope to eliminate
them anyway.

      snoc = flip Cons
  <=> snoc xs x n c = c x xs

Now define

      ver = flip rev
  <=> ver acc v = rev v acc
  <=> ver acc v = v acc (comp ver (snoc acc))

And

      verSnoc acc = comp ver (snoc acc)
  <=> verSnoc acc x = ver (snoc acc x)
  <=> verSnoc acc x v = ver (snoc acc x) v
  <=> verSnoc acc x v = v (snoc acc x) (comp ver (snoc (snoc acc x)))
  <=> verSnoc acc x v = v (snoc acc x) (verSnoc (snoc acc x))
  <=> verSnoc acc x v = v (Cons x acc) (verSnoc (Cons x acc))

So we obtain

  rev v acc = v acc (verSnoc acc)
  verSnoc acc x v = v (Cons x acc) (verSnoc (Cons x acc))
 
Discussion (added 15 November)
------------------------------

Matt points out another solution to the in-lining problem for
rev: instead of in-lining the rev' alternative "back into" the body
of rev, we can in-line rev at the point of the recursive call in rev'.
We obtain in a single step:

  rev v acc = v acc (rev' acc)
  rev' acc x xs = xs (Cons x acc) (rev' (Cons x acc))

What is more, upto renaming of rev'/verSnoc and xs/v, this is
*the same* solution as the one obtained before by in-lining the
other way followed by the argument-flipping transormations!

More generally, to achieve wider function bodies, here's one
simple rule:

  In-line saturated applications of functions that
  do not have directly recursive definitions.

For *single-level* constructor-case analysis this rule yields
a directly recursive function with a nice fat body for each
recursive alternative.  But what about nested cases, for example
when there is case analysis of more than one argument?  That's
a good question for another day.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo21.txt version [c7226830b8].































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
========================
REDUCERON MEMO 21
Second quarterly review
Matthew N, 28 April 2009
========================

Memo 15 formalised the semantics of the "Spineless Reduceron",
including a new heap layout allowing single-cycle access, and the
dynamic update avoidance technique outlined by Peyton Jones's in his
"Spineless G-Machine" paper.  The memo predicted that dynamic update
avoidance would be cheap to implement in hardware, that 60% of updates
could be avoided, and that a factor of 2.8 speed-up should be possible
over the PhD Reduceron.

Subsequently, we felt that Memo 15 was slightly misleading: the true
source of the speed-up is not due to spinelessness.  So Memo 18
defined a new spinefull machine for comparison.  It predicted that the
spineless machine would be 15% faster, and suggested that
spinelessness brings a number of small benefits, rather than one big
benefit.  (The possibility of dynamic update avoidance was not
considered here; but it should be useful to both spineless and
spinefull machines.)

Reduceron II
------------

With Memo 15 as a basis, implementation of the "Reduceron II" on FPGA
began.  A somewhat rushed first-cut was finished in time for HFL'09.

The new heap layout was straightforward to implement, but we
discovered that a similar technique could not be used to obtain
single-cycle access to the stack - unlike the heap, the stack cannot
contain gaps.  So we implemented the stack using registers connected
to a wide RAM via spill-handling hardware.  The resulting stack design
is quite tricky; we did manage to find fairly nice design, but it does
contain the critical path.

Dynamic update avoidance is indeed simple and cheap to implement in
Hardware.

The new method of evaluating case expressions (Memo 13) was also
implemented without difficulty.  Indeed it is possible, but with added
complexity, to do zero-cycle matching using a parallel case-table
stack.

In the rush, some short-cuts where taken.  In particular, there is no
garbage collector, and the maximum arity of functions is just 4!  On
the positive side, the new method of handling case expressions greatly
reduces the arity of 'case-alternative' functions, and all the
benchmarks programs met the restriction.

The new implementation runs 3 times faster than the PhD Reduceron, in
terms of clock-cycles, and its clock frequency is roughly the same.
Using the new FPGA, both designs clock about 30% faster.

The new implementation is written in Blarney, a new Lava clone
supporting multi-output primitives, RAMs, easy creation of new
primitives and back-ends, behavioural description, and sized-vectors.
(To-do: write a memo about Blarney, perhaps by way of a short
tutorial.)

Talk at HFL 2009
----------------

Memo 19 contains the slides I presented at HFL'09 in March.  The talk
was entitled "Three improvements to the Reduceron".  My overall
feeling is that the talk didn't go well.  Judging by the nodding
heads, I had the audience's attention in the beginning when discussing
how and why the Reduceron widens the memory bottleneck.  But after
that I think I went too much into gory details, and some of my points
were a bit laboured.  I don't think I dealt well with audience
negativity in two areas:

  1. The use of block-RAMs rather than large off-chip memories.

  2. The Reduceron still does not beat GHC-compiled programs
     running on a PC.

I recall that the following two suggestions were made:

  1. It would be interesting to see if the Reduceron can exploit
     the fine-grained parallelism that is abundant in functional
     programs, especially as conventional hardware has struggled here.

  2. It would be interesting to compare the Reduceron with GHC-compiled
     programs running on an FPGA soft-core.

Invited talk at Birmingham University
-------------------------------------

Memo 20 contains the slides of my invited talk at Birmingham
University in April.  This talk went a bit better.  I started out
motivating the Reduceron and discussing the potential advantages,
which went fine.  I introduced FPGAs, and this led to some useful
discussion.  I gave a very quick overview of lazy evaluation, which I
think was worthwhile, although it did cause one guy to fall asleep!
The slides about widening the memory bottleneck again went down well.
This time, I followed up naturally with a few punchier points about
the parallel update-stack and case-table memories.  I then discussed
two dynamic analyses: update avoidance and speculative evaluation of
primitive redexes.  Perhaps the update avoidance slides were still a
bit laboured.  At the end, there was plenty of discussion and even the
snoozer woke up to ask lots of questions.  There were some positive
comments about the ideas, the talk, and even the slides.  The worries
about poor memory capacity and performance arose again, but at least
this time I was better prepared to discuss them.  My general feeling
was that they liked the work but were dissapointed by the current
results.

What next?
----------

To complete and document:

  1. Expand the arity-limit of 4.
  2. The stack design.
  3. The Reduceron II.
  4. Performance measurements & comparison.
  5. Blarney.

To explore:

  1. Make the sequential reducer use only 1 port of the heap, freeing
     the other port for garbage collection and parallel reduction.
  2. A garbage collector.
  3. A parallel garbage collector.
  4. A hybrid parallel garbage collector and parallel reducer.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo22.lhs version [ff9070d3f3].



















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
======================
REDUCERON MEMO 22
Compiling F-lite to C
Matthew N, 13 May 2009  
======================

This memo defines a compiler from supercombinators to portable C.  It
is intended as a back-end to the F-lite implementation.  The aim is to
run F-lite programs on an FPGA soft-core, such as the Microblaze, for
comparison with the Reduceron.

> module CompileBackend where

> import List

Heap layout
-----------

A node is a tagged pointer, storable in a single word of memory.

> nodeType = "typedef unsigned int Node;"

The least-significant bit of a node is a tag stating whether the node
is an AP, containing a pointer to an application (a sequence of nodes)
on the heap, or an OTHER, containing something else.

  typedef enum {AP = 0, OTHER = 1} Tag;

The 2nd least-significant bit of a node is a flag stating whether or
not the node is the final node of an application.

> macros = unlines
>   [ "#define isFinal(n) ((n) & 2)"
>   , "#define clearFinal(n) ((n) & (~2))"
>   , "#define setFinal(n) ((n) | 2)"
>   , "#define markFinal(n,final) ((final) ? setFinal(n) : clearFinal(n))"

If a node is an AP, its remaining 30 bits is a word-aligned heap
address.

>   , "#define getAP(n) ((Node *) ((n) & (~3)))"

If the node is an OTHER, its 3rd least-significant bit contains a
sub-tag stating whether the the node is an INT or a FUN.

  typedef enum {INT = 0, FUN = 1} Subtag;

If a node is an INT, its remaining 29-bits is an unboxed integer.

>   , "#define getINT(n) ((n) >> 3)"

If a node is a FUN, its remaining 29-bits contains a 6-bit arity and a
23-bit function identifier.

>   , "#define getARITY(n) (((n) >> 3) & 63)"
>   , "#define getFUN(n) ((n) >> 9)"

More precisely:

>   , "#define isAP(n) (((n) & 1) == 0)"
>   , "#define isINT(n) (((n) & 5) == 1)"
>   , "#define isFUN(n) (((n) & 5) == 5)"
>   , "#define makeAP(a,final) ((unsigned int) (a) | ((final) << 1))"
>   , "#define makeINT(i,final) (((i) << 3) | ((final) << 1) | 1)"
>   , "#define makeFUN(arity,f,final) " ++
>              "(((f) << 9) | ((arity) << 3) | ((final) << 1) | 5)"
>   , "#define arity(n) (isFUN(n) ? getARITY(n) : 1)"
>   ]

Update records
--------------

An update record is a pair containing a stack pointer (to detect when
a head normal form has been reached) and a heap pointer (stating where
to write the head normal form).

> updateType = "typedef struct { Node *s; Node *h; } Update;"

Registers
---------

> registers = unlines
>   [ "Node top;"           {- top of stack -}
>   , "Node *sp;"           {- stack pointer -}
>   , "Node *hp;"           {- heap pointer -}
>   , "Node *tsp;"          {- to-space pointer -}
>   , "Update *usp;"        {- update-stack pointer -}
>   , "unsigned int dest;"  {- destination address for computed jumps -}
>   ]

Swapping
--------

The following code swaps the top two elements of the stack.  It is
used in the evaluation of strict primitive functions.

> swapCode = unlines
>   [ "{"
>   , "  Node tmp;"
>   , "  tmp = top;"
>   , "  top = sp[-1];"
>   , "  sp[-1] = tmp;"
>   , "}"
>   ]

Unwinding
---------

Unwinding copies an application from the heap onto the stack, and
pushes an update record onto the update stack.

> unwindCode = unlines
>   [ "{"
>   , "  Node *p;"
>   , "  p = getAP(top);"
>   , "  usp++; usp->s = sp; usp->h = p;"
>   , "  for (;;) {"
>   , "    top = *p++;"
>   , "    if (isFinal(top)) break;"
>   , "    *sp++ = top;"
>   , "  }"
>   , "}"
>   ]

Updating
--------

The following code determines if a normal form has been reached, and
if so, performs an update.

> updateCode = unlines
>   [ "{"
>   , "  unsigned int args, ari;"
>   , "  Node *base;"
>   , "  Node *p;"
>   , "  ari = arity(top);"
>   , "  if (sp - ari < stack) goto EXIT;"
>   , "  DO_UPDATE:"
>   , "  args = ((unsigned int) (sp - usp->s));"
>   , "  if (ari > args && usp > ustack) {"
>   , "    base = hp;"
>   , "    p = sp - args;"
>   , "    while (p < sp) *hp++ = clearFinal(*p++);"
>   , "    *hp++ = setFinal(top);"
>   , "    *(usp->h) = makeAP(base, 1);"
>   , "    usp--;"
>   , "    goto DO_UPDATE;"
>   , "  }"
>   , "}"
>   ]

Evaluation driver
-----------------

Evalution proceeds depedning on the element on top of the stack.

> evalCode = unlines
>   [ "EVAL:"
>   , "if (isAP(top)) {"
>   ,    unwindCode
>   , "  goto EVAL;"
>   , "}"
>   , "else {"
>   , "  EVAL_OTHER:"
>   , "  if (hp > heapFull) collect();"
>   ,    updateCode
>   , "  if (isFUN(top)) {"
>   , "    dest = getFUN(top);"
>   , "    goto CALL;"
>   , "  }"
>   , "  else {"
>   ,      swapCode
>   , "    goto EVAL;"
>   , "  }"
>   , "}"
>   ]

Abstract syntax of source code
------------------------------

The body of a function is a list of identifier/application pairs.  The
first element in the list contains the spine application of the
function.

> type Binding = (Id, App)

> type Body = [Binding]

An application is a list of nodes.

> type App = [Node]

> data Node
>   = VAR Id        {- variable reference -}
>   | ARG Int       {- argument reference -}
>   | FUN Arity Id  {- function identifier -}
>   | INT Int       {- integer -}
>   deriving Show

> type Id = String

A function definition consists of an identifier, an arity, and a body.

> type Defn = (Id, Arity, Body)

> type Arity = Int

For example, the F-lite function definition

   s f g x = f x (g x);

is represented in abstract syntax as follows.

  ("s", 3, [ ("v0", [ARG 0, ARG 2, VAR "v1"])
           , ("v1", [ARG 1, ARG 2])
           ])

A data constructor consists of identifier, an arity, and an index.

> type Cons = (Id, Arity, Index)

> type Index = Int

A program consists of a list of constuctors and a list of function
definitions.

> type Program = ([Cons], [Defn])

Function calling
----------------

Each function body is implemented as a case alternative in a large
switch statement.  To jump to the code for a function, place the
function's identifier in the 'dest' register and then 'goto CALL'.
This double jump is a bit inefficient, but its not obvious how to do
better in C.

> switchCode (cs, ds) = unlines
>   [ "CALL:"
>   , "switch (dest)"
>   , "{"
>   , prims          -- primitive definitions
>   , constrs cs     -- constructor definitions
>   , defns ds       -- function definitions
>   , "}"
>   ]

Constructor compilation
-----------------------

Each constructor C used in the program is treated as a function with
the following definition.

  Ci v1 ... vn f = (f+i) v1 ... vn f

where i is the index of the constructor, n is the artiy of the
constructor, and (f+i) represents the function occuring i definitions
after the definition of f in the program code.  It is assumed that
case alternatives occur contiguously, ordered by index.  For example,
the F-lite program

  rev acc Nil = acc;
  rev acc (Cons x xs) = rev (Cons x acc) xs;

is transformed down to

  rev acc xs = xs revCons acc;
  revCons x xs acc = rev (Cons x acc) xs;
  revNil acc = acc;

if Cons has index 0 and Nil has index 1.

(See Memo 13 for a more detailed explanation of how constructors and
case expressions are treated.)

> cons :: Cons -> String
> cons (f, n, i) = unlines
>   [ "case " ++ fun f ++ ":"
>   , "{"
>   , "dest = getFUN(sp[-" ++ show (n+1) ++ "]) + " ++ show i ++ ";"
>   , "goto CALL;"
>   , "}"
>   , "break;"
>   ]

NB. No update is required because a case expression is not a normal form.

> constrs :: [Cons] -> String
> constrs = concatMap cons

Function compilation
--------------------

> arg :: Int -> String
> arg i = "ARG_" ++ show i

> var :: Id -> String
> var v = "VAR_" ++ v

Map F-lite primitives to suitable C identifiers.

> fun :: Id -> String
> fun "(+)" = "PRIM_PLUS"
> fun "(-)" = "PRIM_MINUS"
> fun "(<=)" = "PRIM_LEQ"
> fun "(==)" = "PRIM_EQ"
> fun "(/=)" = "PRIM_NEQ"
> fun "emit" = "PRIM_EMIT"
> fun "emitInt" = "PRIM_EMITINT"
> fun "_|_" = "PRIM_UNDEFINED"
> fun f = "FUN_" ++ f

> declareArgs :: Int -> String
> declareArgs n = unlines $ map save [1..n]
>   where save i = "Node " ++ arg i ++ " = sp[-" ++ show i ++ "];"

> declareLocals :: String
> declareLocals = "Node *base = hp;"

> type Locs = [(Id, Int)]

> node :: String -> Locs -> String -> Node -> String
> node r vs final (INT i) =
>   r ++ " = makeINT(" ++ show i ++ "," ++ final ++ ");"
> node r vs final (ARG i) =
>   r ++ " = markFinal(" ++ arg (i+1) ++ "," ++ final ++ ");"
> node r vs final (VAR v) =
>   r ++ " = makeAP(base+" ++ offset ++ "," ++ final ++ ");"
>   where offset = show $ head [i | (w, i) <- vs, v == w]
> node r vs final (FUN n f) = 
>   r ++ " = makeFUN(" ++ show n ++ "," ++ fun f ++ "," ++ final ++ ");"

> app :: Locs -> App -> String
> app vs app = unlines $ zipWith (node "*hp++" vs) finals app
>   where finals = map (const "0") (init app) ++ ["1"]

> spine :: Locs -> App -> String
> spine vs ns = unlines
>   [ unlines $ map (node "*sp++" vs "0") (init ns)
>   , node "top" vs "0" (last ns)
>   ]

> varLocs :: Body -> Locs
> varLocs body = zip vs (scanl (+) 0 (map length apps))
>   where (vs, apps) = unzip body

> body :: App -> Body -> String
> body s b = unlines
>   [ concatMap (app vs . snd) b
>   , spine vs s
>   , "goto EVAL;"
>   ] where vs = varLocs b

> defn :: Defn -> String
> defn (f, n, bs) = unlines
>   [ "case " ++ fun f ++ ":"
>   , "{"
>   , declareArgs n
>   , declareLocals
>   , "sp -= " ++ show n ++ ";"
>   , body (snd s) b
>   , "}"
>   , "break;"
>   ]
>   where s:b = [(v, reverse a) | (v, a) <- bs]

> defns :: [Defn] -> String
> defns = concatMap defn

Primitives
----------

> primIds :: [Id]
> primIds =
>   [ "(+)" , "(-)" , "(<=)" , "(==)", "(/=)", "emit", "emitInt", "_|_" ]

Apply primitive arithmetic operator to 2nd and 3rd stack elements;
store result in top.

> arithPrim :: Id -> String -> String
> arithPrim p op = unlines
>   [ "case " ++ fun p ++ ":"
>   , "{"
>   , "top = makeINT(getINT(sp[-1]) " ++ op ++ " getINT(sp[-2]),0);"
>   , "sp -= 2;"
>   , "goto EVAL;"
>   , "}"
>   , "break;"
>   ]

Ditto for boolean operator.

> boolPrim :: Id -> String -> String
> boolPrim p op = unlines
>   [ "case " ++ fun p ++ ":"
>   , "{"
>   , "top = (getINT(sp[-1]) " ++ op ++ " getINT(sp[-2])) ? "
>       ++ "makeFUN(1," ++ fun "True"  ++ ",0) "
>       ++ ": makeFUN(1," ++ fun "False" ++ ",0);"
>   , "sp -= 2;"
>   , "goto EVAL;"
>   , "}"
>   , "break;"
>   ]

Print the second stack element.

> emitPrim :: Id -> String -> String
> emitPrim p format = unlines
>   [ "case " ++ fun p ++ ":"
>   , "{"
>   , "top = sp[-2];"
>   , "printf(\"" ++ format ++ "\", getINT(sp[-1]));"
>   , "sp -= 2;"
>   , "goto EVAL;"
>   , "}"
>   , "break;"
>   ]

> undefPrim :: String
> undefPrim = unlines
>   [ "case " ++ fun "_|_" ++ ":"
>   , "{"
>   , "printf(\"ERROR: bottom!\\n\");"
>   , "goto EXIT;"
>   , "}"
>   ]

> prims :: String
> prims = unlines
>   [ arithPrim "(+)" "+"
>   , arithPrim "(-)" "-"
>   , boolPrim "(<=)" "<="
>   , boolPrim "(==)" "=="
>   , boolPrim "(/=)" "!="
>   , emitPrim "emit" "%c"
>   , emitPrim "emitInt" "%i"
>   , undefPrim
>   ]

Garbage collection
------------------

> copyAPCode = unlines
>   [ "Node *copyAP(Node *src) {"
>   , "  Node n;"
>   , "  Node *from = src;"
>   , "  Node *dst = tsp;"
>   , "  n = *from;"
>   , "  if (isAP(n)) {"
>   , "    Node *loc = getAP(n);"
>   , "    if (loc >= toSpace && loc < toSpaceEnd) return loc;"
>   , "  }"
>   , "  do {"
>   , "    n = *from++; *tsp++ = n;"
>   , "  } while (! isFinal(n));"
>   , "  *src = (Node) dst;"
>   , "  return dst;"
>   , "}"
>   ]

> copyCode = unlines
>   [ "void copy() {"
>   , "  Node n;"
>   , "  Node *low = toSpace;"
>   , "  while (low < tsp) {"
>   , "    n = *low;"
>   , "    if (isAP(n)) {"
>   , "      Node *loc = copyAP(getAP(n));"
>   , "      *low = markFinal((Node) loc, isFinal(n));"
>   , "    }"
>   , "    low++;"
>   , "  }"
>   , "}"
>   ]

> collectCode = unlines
>   [ "void collect () {"
>   , "  Node n;"
>   , "  Node *p1;"
>   , "  Update *p2;"
>   , "  Update *p3;"
>   , "  tsp = toSpace;"
>   , "  p1 = stack;"
>   , "  while (p1 < sp) {"
>   , "    n = *p1;"
>   , "    if (isAP(n)) *p1 = (Node) copyAP(getAP(n));"
>   , "    p1++;"
>   , "  }"
>   , "  if (isAP(top)) top = (Node) copyAP(getAP(top));"
>   , "  copy();"
>   , "  p2 = ustack+1;"
>   , "  p3 = ustack;"
>   , "  while (p2 <= usp) {"
>   , "    n = *(p2->h);"
>   , "    if (isAP(n) && getAP(n) >= toSpace && getAP(n) <= toSpaceEnd) {"
>   , "      p3++;"
>   , "      p3->s = p2->s;"
>   , "      p3->h = getAP(n);"
>   , "    }"
>   , "    p2++;"
>   , "  }"
>   , "  usp = p3;"
>   , "  hp = tsp;"
>   , "  p1 = toSpace; toSpace = heap; heap = p1;"
>   , "  p1 = toSpaceEnd; toSpaceEnd = heapEnd; heapEnd = p1;"
>   , "  p1 = toSpaceFull; toSpaceFull = heapFull; heapFull = p1;"
>   , "}"
>   ]

Global variables
----------------

We need to store the beginning and end address of each memory
partition, to detect termination and exhaustion.

> globals :: String
> globals = unlines
>   [ "Node *heap;"
>   , "Node *heapEnd;"
>   , "Node *heapFull;"
>   , "Node *toSpace;"
>   , "Node *toSpaceEnd;"
>   , "Node *toSpaceFull;"
>   , "Node *stack;"
>   , "Node *stackEnd;"
>   , "Update *ustack;"
>   , "Update *ustackEnd;"
>   ]

Memory allocation
-----------------

> allocate :: Int -> Int -> String
> allocate heapSize stackSize = unlines
>   [ "heap = (Node *) malloc(sizeof(Node) * " ++ show heapSize ++ ");"
>   , "hp = heap;"
>   , "heapEnd = heap + " ++ show heapSize ++ ";"
>   , "heapFull = heapEnd - 1000;"
>   , "toSpace = (Node *) malloc(sizeof(Node) * " ++ show heapSize ++ ");"
>   , "tsp = toSpace;"
>   , "toSpaceEnd = toSpace + " ++ show heapSize ++ ";"
>   , "toSpaceFull = toSpaceEnd - 1000;"
>   , "stack = (Node *) malloc(sizeof(Node) * " ++ show stackSize ++ ");"
>   , "sp = stack;"
>   , "stackEnd = stack + " ++ show stackSize ++ ";"
>   , "ustack = (Update *) malloc(sizeof(Update) * " ++ show stackSize ++ ");"
>   , "usp = ustack;"
>   , "ustackEnd = ustack + " ++ show stackSize ++ ";"
>   ]

Program compilation
-------------------

> funIds :: Program -> [String]
> funIds (cs, ds) = map first cs ++ map first ds
>   where first (x, y, z) = x

> declareFuns :: Program -> String
> declareFuns p =
>   unlines $ [def f i | (f, i) <- zip (primIds ++ funIds p) [0..]]
>   where def f i = "#define " ++ fun f ++ " " ++ show i

> program :: Program -> String
> program p = unlines
>   [ "#include <stdio.h>"
>   , "#include <stdlib.h>"
>   , nodeType
>   , updateType
>   , macros
>   , declareFuns p
>   , registers
>   , globals
>   , copyAPCode
>   , copyCode
>   , collectCode
>   , "int main(void) {"
>   , allocate 8000000 1000000
>   , "dest = " ++ fun "main" ++ ";"
>   , switchCode p
>   , evalCode
>   , "EXIT:"
>   , "return 0;"
>   , "}"
>   ]

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo23.txt version [7639a36676].













































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
======================
REDUCERON MEMO 23
York Lava, by example
Matthew N, 22 May 2009
======================

York Lava is Haskell library for describing digital circuits.
Descriptions can be simulated in Hugs or GHC, and converted to VHDL
compatible with XST, the Xilinx Synthesis Tool.  It is largely
compatible with Chalmers Lava, but also offers a few new bits and
bobs.

Simulation
----------

York Lava provides an abstract data type 'Bit' and a set of primitive
operators over values of this type.  For example, the operator for a
two-input AND gate is written '<&>' and has type

  Bit -> Bit -> Bit

Expressions involving values of type 'Bit' can be simulated at the
Lava prompt.

  Lava> low <&> high
  low

  Lava> map inv [low, high, low]
  [high,low,high]

By default, only the output of the circuit on the 1st clock-cycle is
displayed.  More generally, 'simulateN' gives the outputs on the first
N cycles.

  Lava> simulateN 3 $ map inv [low, high, low]
  [[high,low,high],[high,low,high],[high,low,high]]

  Lava> simulateN 4 $ delay low high
  [low,high,high,high]

The 'delay' operator represents a D-type flip-flop; it outputs its
first argument on the first clock-cycle, and thereafter outputs its
second argument delayed by one clock-cycle.

Alternatively, 'simulate' gives the outputs on all cycles.

  Lava> simulate $ let x = delay low (inv x) in x
  [low,high,low,high,low,high,...

Note the feedback loop in this example: the flip-flop's output is
connected to its own input via an inverter.

And 'simulateSeq' allows a constant waveform to be specified as input.

  Lava> simulateSeq (delay low) [low, high, low, high, low]
  [low,low,high,low,high]

A conversion function from bits to booleans ('bitToBool') is provided;
this is useful for defining custom simulation routines, and also for
testing circuit properties using tools such as QuickCheck.

  Lava> takeWhile bitToBool $ simulate $ delay high (delay high low)
  [high,high]

Synthesis
---------

Further to simulation, Haskell functions over bits can also be
synthesised.  For example, the function 'halfAdd'

  halfAdd :: Bit -> Bit -> (Bit, Bit)
  halfAdd a b = (sum, carry)
    where
      sum   = a <#> b
      carry = a <&> b

can be converted to a VHDL entity with inputs named "a" and "b" and
outputs named "sum" and "carry".

  synthesiseHalfAdd :: IO ()
  synthesiseHalfAdd =
    writeVhdl "HalfAdd"
              (halfAdd (name "a") (name "b"))
              (name "sum", name "carry")

The VHDL is written to the directory specified by the first argument
of 'writeVhdl'.  (Sometimes more than one source file is generated,
for example when block-RAMs are used.)
 
  Lava> synthesiseHalfAdd
  Creating directory 'HalfAdd/'
  Writing to 'HalfAdd/HalfAdd.vhd'
  Done.

Sized vectors
-------------

Values of type 'Vec n a' are vectors containing 'n' elements of type 'a'.
Bit-vectors are a commonly-used special case.

  type Word n = Vec n Bit

Valid numeric types are 'Z', 'S Z', 'S (S Z)' and so on; the type
constructors 'N0' to 'N127' are pre-defined as type synonyms.  The
'Word' type is a member if Haskell's 'Num' class.

  Lava> 13 :: Word N4
  Vec [high,low,high,high]

  Lava> 4+3 :: Word N8
  Vec [high,high,high,low,low,low,low,low]

Note the least-significant bit comes first.

Each numeric type contains exactly one value.  For example, the type
'S Z' contains the value 'S Z'.  All type-level numbers are members of
the type class 'N'. 

  class N n where
    value :: n -> Int

The values 'n0' to 'n127', with types 'N0' to 'N127' respectively, are
predefined as constants.

  Lava> value n4
  4

The 'value' function is non-strict; its result is computed entirely
from the *type* of its argument.

The function 'vat' indexes a vector. 

  Lava> (13 :: Word N4) `vat` n2
  low

A static type error is given if the index is out of bounds.

Vectors can be turned into lists, for more convenient processing.

  Lava> velems (13 :: Word N4)
  [high,low,high,high]

Many other operations on vectors are provided - see the module
'Lava.Vector'.

Generic circuits
----------------

Many of the functions exported by Lava are generic in the sense that
they operate over arbitrary collections of bits, not simply single
bits.  For example, the function '===' has type

  Generic a => a -> a -> Bit

and can be used to compare any two structures of bits.

  Lava> low === high 
  low

  Lava> (low, high, low) === (low, high, low)
  high

Examples of other generic circuits include:

  Lava> orG (low, (low, low), high)
  high

  Lava> low ? ([low, high], [high, low])
  [high, low]

  Lava> pickG [(low, 0), (low, 1), (high, 2), (low, 3)] :: Word N4
  Vec [low,high,low,low]

  Lava> simulateN 4 $ delay (low, high) (high, high)
  [(low,high),(high,high),(high,high),(high,high)]

See the 'Lava.Prelude' module for more generic circuits.

It is straightforward to make any algebraic data type a member of the
'Generic' class using the helper functions 'cons' and '><'.  For
example, here is the list instance:

  instance Generic a => Generic [a] where
    generic [] = cons []
    generic (a:as) = cons (:) >< a >< as

Memory
------

York Lava supports primitive components which produce multiple output
bits.  Random-access memories (RAMs), which have data-output buses,
are a prime example.

  ram :: [Integer] -> RamAlgorithm -> RamInputs n m -> Word n

The 1st argument states what the locations beginning at address 0
should be initialised to.  The list of initial values may have a
length smaller than the number of locations in the RAM; any locations
whose initial values are unspecified are initialised to 0.

The 2nd argument specifies to the Xilinx Core Generator which
cascading algorithm should be used when making big RAMs out of
individual block-RAMs.

  data RamAlgorithm =
    MinArea | Width1 | Width2 | Width4 | Width9 | Width18 | Width36

Choosing 'MinArea' causes the "Minimum Area" algorithm to be used; any
other choice results in the "Fixed Primitives" algorithm being used.
See the Xilinx Core Generator documentation for details on these
algorithms.

The 3rd argument species the data-input and address buses, along with
a write-enable signal.  The type parameters 'n' and 'm' are the widths
of the data and address buses respectively.

  data RamInputs n m =
    RamInputs {
      ramData    :: Word n
    , ramAddress :: Word m
    , ramWrite   :: Bit
    }

True dual-port RAMs are also provided through the 'dualRam' primitive,
the main difference being that they take a pair of 'RamInputs' and
produce a pair of data-output buses.  Memory collisions are resolved
using the Xilinx "WRITE_FIRST" semantics.

Behavioural description
-----------------------

York Lava provides the following constructs for writing behavioural
descriptions.

  Skip  :: Recipe
  Tick  :: Recipe
  (<==) :: Var n -> Word n -> Recipe
  Seq   :: [Recipe] -> Recipe
  Par   :: [Recipe] -> Recipe
  (|>)  :: Bit -> Recipe -> Recipe
  While :: Bit -> Recipe -> Recipe

(For a precise semantics of this language, see Memo 24.)

There are two steps to writing behavioural descriptions in York Lava.
The first is to define a state type (say 'State') that contains all
the state variables to be used in the description.  And the second is
to define a function of type 'State -> Recipe' which manipulates the
state in desired way.

To illustrate, consider the implementation of a sequential multiplier
using the shift-and-add algorithm.  In this case, the state type
contains three variables: the two inputs to multiply, and the result
of the multiplication.

  data Mult n = Mult { a, b, result :: Var n }

A value of type 'Mult n' is created by 'newMult'.

  newMult :: N n => New (Mult n)
  newMult = return Mult `ap` newReg `ap` newReg `ap` newReg

The shift-and-add recipe takes a value of type 'Mult n'.

  shiftAndAdd s =
    While (s!b!val =/= 0) $
      Seq [ s!a <== s!a!val!shr
          , s!b <== s!b!val!shl
          , s!b!val!vhead |>
              s!result <== s!result!val + s!a!val
          , Tick
          ]

  shr x = low +> vinit x

  shl x = vtail x <+ low

Three remarks are in order:

  1. The '!' operator is flipped application with a high precedence.

       infixl 9 !
       (!) :: a -> (a -> b) -> b
       x!f = f x

     This gives descriptions an appropriate object-oriented flavour.

  2. The value of a variable is obtained using the function:

       val :: Var n -> Word n

  3. The functions '+>' and '<+' perform cons and snoc operations
     on vectors, 'vhead' takes the head of a vector, and '=/=' is
     generic disequality.

To actually perform a multiplication, the input variables need to be
initialised.

  multiply x y s = Seq [ s!a <== x, s!b <== y, Tick, s!shiftAndAdd ]

For example, an eight-bit multiplier that computes 5*5.

  example :: Mult N8 -> Recipe
  example s = s!multiply 5 5

The example can simulated using 'simRecipe', which a value of type
'New s' and passes the state (of type 's') to a function of type
's -> Recipe'.  It performs the recipe, and then applies a projection
function to the final state, to select the desired outputs.

  simExample = simRecipe newMult example result

  Lava> simExample
  Vec [high,low,low,high,high,low,low,low]

The example can be synthesised by calling 'recipe' which is similar to
'simRecipe' but takes a single-cycle pulse indicating when to start,
and produces a single-cycle pulse indicating when the recipe has
finished.

  synExample =
    do let (s, done) = recipe newMult example (delay high low)
       writeVhdl "MultExample"
                 (s!result!val, done)
                 (nameWord "result", name "done")

Poly: a simple stack processor
------------------------------

To complete this overview, a simple processor is defined.  The aim of
the processor is to evaluate expressions of the form

  data Expr = X | N Integer | Expr :+: Expr | Expr :*: Expr

For example:

  expr = (X :+: X) :+: (N 2 :*: X) :+: X

The semantics of expressions is as follows.

  eval :: Expr -> Integer -> Integer
  eval X n = n
  eval (N i) n = i
  eval (e1 :+: e2) n = eval e1 n + eval e2 n
  eval (e1 :*: e2) n = eval e1 n * eval e2 n

For example:

  Lava> eval expr 3
  15

A stack machine to evaluate expressions might have the following
instructions.

  data INSTR = LIT Integer | DUP | REV | ADD | MUL | HALT

Expressions can be compiled to a sequence of such instructions.

  compile :: Expr -> [INSTR]
  compile e = comp e ++ [HALT]

  comp X = []
  comp (N n) = [LIT n]
  comp (e1 :+: e2) = DUP:comp e2 ++ [REV] ++ comp e1 ++ [ADD]
  comp (e1 :*: e2) = DUP:comp e2 ++ [REV] ++ comp e1 ++ [MUL]

For example:

  Lava> compile expr
  [DUP,REV,DUP,DUP,REV,LIT 2,MUL,REV,DUP,REV,ADD,ADD,ADD,HALT]

The semantics of the stack machine is as follows.

  exec :: [INSTR] -> [Integer] -> Integer
  exec (LIT m : c) (n:s)   = exec c (m:s)
  exec (DUP   : c) (m:s)   = exec c (m:m:s)
  exec (REV   : c) (m:n:s) = exec c (n:m:s)
  exec (ADD   : c) (m:n:s) = exec c (m+n:s)
  exec (MUL   : c) (m:n:s) = exec c (m*n:s)
  exec (HALT  : c) (n:s)   = n

To implement the machine at the digital-circuit level, a bit-level
encoding of the instruction set is required.

  encode :: INSTR -> Integer
  encode (LIT n) = 1 + 2*fromIntegral n
  encode DUP     = 2
  encode REV     = 4
  encode ADD     = 8
  encode MUL     = 16
  encode HALT    = 32

  type DataWidth = N8
  type Instr = Word DataWidth
  type Value = Word DataWidth

  isLIT, isDUP, isREV, isADD, isMUL, isHALT :: Instr -> Bit
  isLIT i  = i `vat` n0
  isDUP i  = inv (isLIT i) <&> (i `vat` n1)
  isREV i  = inv (isLIT i) <&> (i `vat` n2)
  isADD i  = inv (isLIT i) <&> (i `vat` n3)
  isMUL i  = inv (isLIT i) <&> (i `vat` n4)
  isHALT i = inv (isLIT i) <&> (i `vat` n5)

  getLIT :: Instr -> Value
  getLIT i = vtail i <+ low

Note the bit-width of the machine is parameterised by 'DataWidth'.

The machine state consists of an instruction stack, a value stack, a
multiplier, and a register storing the top element of the value stack.

  data Poly = Poly
    { code  :: Stack DataWidth AddrWidth
    , stack :: Stack DataWidth AddrWidth
    , mult  :: Mult DataWidth
    , rtop  :: Var DataWidth
    }

  type AddrWidth = N10

The implementation of the 'Stack' data type is not be presented here
(but can be found in the examples directory); it is sufficient to know
that it supports the following operations.

  top  :: Stack n m -> Word n
  pop  :: Stack n m -> Recipe
  push :: Word n -> Stack n m -> Recipe

The function 'newPoly' takes the program (used to initialise the
instruction stack) and an initial value for the top of the value
stack.

  newPoly :: [Integer] -> Integer -> New Poly
  newPoly code x =
         return Poly
    `ap` newStack code
    `ap` newStack []
    `ap` newMult
    `ap` newRegInit (fromIntegral x)

The behaviour of the processor is as follows.

  poly :: Poly -> Recipe
  poly s =
    let instr = s!code!top in
      Seq [ Tick
          , While (instr!isHALT!inv) $
              Seq [ isLIT instr |> s!rtop <== getLIT instr
                  , isDUP instr |> s!stack!push (s!rtop!val)
                  , isREV instr |>
                      Seq [ s!rtop <== s!stack!top
                          , s!stack!pop
                          , s!stack!push (s!rtop!val)
                          ]
                  , isADD instr |>
                      Seq [ s!rtop <== s!rtop!val + s!stack!top
                          , s!stack!pop
                          ]
                  , isMUL instr |>
                      Seq [ s!mult!multiply (s!rtop!val) (s!stack!top)
                          , s!rtop <== s!mult!result!val
                          , s!stack!pop
                          ]
                  , s!code!pop
                  , Tick
                  ]
          ]

Notice that each instruction executes in one clock-cycle, apart from
multiplication which may take up to 'DataWidth' clock-cycles.

To test the processor, a custom simulation routine is useful.

  simPoly :: Expr -> Integer -> Value
  simPoly e x = simRecipe (newPoly code x) poly (val . rtop)
    where code = reverse $ map enc $ compile e

  Lava> simPoly expr 3
  Vec [high,high,high,high,low,low,low,low]

The function 'simPoly' has almost the same type has 'eval', and it is
straightforward to define a correctness property for the processor.

  prop_poly :: Expr -> Integer -> Bool
  prop_poly e x = eval e x == wordToInt (simPoly e x)

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo25.txt version [dc023be5c3].

























































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
==============================
REDUCERON MEMO 25
Bounded template instantiation
Matthew N, 24 June 2009
==============================

This memo measures the effect of maximum spine length, maximum
application length, and maximum instantiations-per-cycle on reduction
speed and heap wastage in the Reduceron.  The precise semantics of the
Reduceron used to perform the measurements is not (yet) presented
here.

Templates
---------

In spineless template instantiation, a template is essentially a tuple
of the form

  (Pop, Push, App_1, ..., App_n)

The 'Pop' field states how many elements should be popped off the
stack.  The remaining fields are all applications, which may refer to
each other relatively (but may not refer to 'Push').  In other words,
together these applications represent a term graph rooted by the
'Push' application.  The difference between 'Push' and 'App_1' to
'App_n' is that 'Push' is instantiated and pushed onto the stack,
whereas 'App_1' to 'App_n' are instantiated and appended to the heap.

EXAMPLE 1. The following combinator handles the 'Cons' case in the
definition of 'map'.

  mapCons x xs f = Cons (f x) (map f xs)

As a template, it could be represented as

  mapCons = (3, Cons +0 +1, @3 @1, map @3 @2)

where '+i' is a pointer to the ith application beyond the current heap
pointer, and '@i' is the ith element from the top of the stack.

Our aim in the Reduceron is to instantiate whole templates in a single
clock-cycle.  On an FPGA, this is made possible by parallel RAMs which
can be built with more-or-less arbitrary bus widths.  But these bus
widths must be decided statically, before the FPGA is programmed.  In
particular, the following values must be bounded:

  1. The maximum length of the 'Push' application, referred to as
     "maximum spine length".  For example, the length of the
     application 'Cons (f x) (map f xs)' is 3.

  2. The maximum length of the applications in 'App_1 ... App_n',
     referred to as "maximum application length".  This need not be
     the same as the maximum spine length.

  3. The number of applications in 'App_1 ... App_n'.  This number
     (plus one to account for the spine application, if it has length
     larger than one) is referred to as "maximum instantiations per cycle".

No matter how we bound the length and number of applications, the
programmer will always be able to write a program that exceeds them.
The bounds should be relaxed enough to allow high speed reduction, but
constrained enough to avoid poor memory and hardware utilisation.
Below, we try to find "good" bounds by taking a quantitative approach.

Application splitting
---------------------

It is straightforward to split long applications in order to meet
maximum application length bound.  To illustrate, simply observe that
the expression 'f a b c d' which contains one application, is
equiavalent, for example, to '(f a b) c d' which contains two
applications.  The maximum spine length bound can also be enforced
using this principle.

Template splitting
------------------

Template splitting can be used when the bound on the number of
applications is exceeded.  One big template is split into a number of
smaller ones.

EXAMPLE 2.  If the maximum number of applications in a
template is 1, then 'mapCons' could be represted as follows.

  mapCons  = (0, mapCons2, @3 @1)
  mapCons2 = (3, Cons -1 +0, map @3 @2)

Here, 'mapCons' instantiates the '@3 @1' application on the heap and
replaces the top stack element with 'mapCons2', which instantiates the
rest.  Now '-1', not '+0', is used to refer to the application '@3 @1',
because the heap pointer has been incremented as a result of
instantiating 'mapCons'.

Measurements 1: two improvements to Memo 19
-------------------------------------------

First, we measure the impact of two improvements to the Reduceron
since Memo 19.  The first improvement is to take into account normal
forms in the update avoidance scheme; every application is tagged with
an extra bit stating whether or not it is a normal form, and when a
normal form is unwound, an update marker is not pushed onto the update
stack.  The second improvement is to inline all non-recursive
functions whose bodies contain just a single application, which often
avoids a function call in recursive case-analysing functions (recall
Memo 2).

PROGRAM     % UPDATES AVOIDED       % TIME SAVED DUE TO INLINING
            -----------------       ----------------------------
Adjoxo                     97                                8.3
Clausify                   85                               15.5
MSS                        96                               12.3
OrdList                    89                               29.8
Parts                      82                               17.9
PermSort                   87                               22.6
Queens                     95                               15.1
Queens2                    66                               21.8
Sudoku                     71                               16.6
While                      99                                6.8
            -----------------       ----------------------------
AVERAGE                    87                               17.8

According to Memo 15 (that is, before taking into account normal
forms), 60% of updates were avoided by the update-avoidance scheme.

Measurements 2: effect of bounds on reduction speed
---------------------------------------------------

The following tables contain "speed-up factors" that result from using
various values for the different bounds.  The speed-up is relative to
the time take by the Reduceron with a maximum spine length of 2, a
maximum application length of 2, a maximum number of instantiations
per cycle of 1.

        SPEED-UP      |        APPLICATION LENGTH
   (SPINE LENGTH = 3) |     2     3     4     5     6
  --------------------+------------------------------
                    1 |  1.20  1.58  1.65  1.71  1.72
   INSTANTIATIONS   2 |  1.45  1.87  1.93  1.99  2.00
      PER CYCLE     3 |  1.56  1.98  2.05  2.10  2.10
                    4 |  1.62  1.99  2.06  2.11  2.11
                    5 |  1.65  2.01  2.07  2.12  2.12

        SPEED-UP      |        APPLICATION LENGTH
   (SPINE LENGTH = 4) |     2     3     4     5     6
  --------------------+------------------------------
                    1 |  1.35  1.78  1.82  1.88  1.88
   INSTANTIATIONS   2 |  1.64  2.11  2.15  2.20  2.20
      PER CYCLE     3 |  1.76  2.23  2.27  2.33  2.33
                    4 |  1.82  2.25  2.30  2.34  2.34
                    5 |  1.84  2.26  2.30  2.36  2.36

        SPEED-UP      |        APPLICATION LENGTH
   (SPINE LENGTH = 5) |     2     3     4     5     6
  --------------------+------------------------------
                    1 |  1.45  1.86  1.89  1.94  1.94
   INSTANTIATIONS   2 |  1.73  2.17  2.20  2.25  2.25
      PER CYCLE     3 |  1.84  2.30  2.32  2.38  2.38
                    4 |  1.91  2.33  2.35  2.41  2.41
                    5 |  1.94  2.34  2.37  2.42  2.42

        SPEED-UP      |        APPLICATION LENGTH
   (SPINE LENGTH = 6) |     2     3     4     5     6
  --------------------+------------------------------
                    1 |  1.50  1.93  1.96  2.03  2.03
   INSTANTIATIONS   2 |  1.79  2.27  2.30  2.37  2.37
      PER CYCLE     3 |  1.92  2.41  2.44  2.50  2.50
                    4 |  2.00  2.44  2.47  2.53  2.53
                    5 |  2.03  2.45  2.48  2.54  2.54

        SPEED-UP      |        APPLICATION LENGTH
   (SPINE LENGTH = 7) |     2     3     4     5     6
  --------------------+------------------------------
                    1 |  1.52  1.96  2.00  2.06  2.06
   INSTANTIATIONS   2 |  1.83  2.31  2.35  2.41  2.41
      PER CYCLE     3 |  1.96  2.46  2.49  2.56  2.56
                    4 |  2.03  2.49  2.53  2.58  2.58
                    5 |  2.07  2.50  2.54  2.60  2.60


Measurements 3: effect of bounds on heap wastage
------------------------------------------------

The following table contains "deflation factors", that is, the number
of times smaller the heap.  The factor is relative to the heap
consumption (in words) of the Reduceron with a maximum spine length of
2, a maximum application length of 2, a maximum number of
instantiations per cycle of 1.

    DEFLATION FACTOR  |        APPLICATION LENGTH
                      |     2     3     4     5     6
  --------------------+------------------------------
                    3 |  1.31  1.30  1.06  0.91  0.76
                    4 |  1.58  1.63  1.29  1.01  0.91
    SPINE-LENGTH    5 |  1.76  1.78  1.38  1.17  0.98
                    6 |  1.90  1.95  1.51  1.30  1.09
                    7 |  1.97  2.04  1.60  1.37  1.14

Conclusions
-----------

Bear in mind that although the speed-up factors are quite small, they
are relative to something which is already a fair bit faster (at least
a factor of 2 faster) than the old Reduceron, which is at the moment
our main reference point. 

The speed-up increases quite readily with spine-length; a spine-length
of 6 has a comfortable advantage over 3, 4, and 5, but a spine-length
of 7 only has a small advantage over 6.

There is not a big win in having applications of lengths larger than
3.  Furthermore, applications of length 3 result in very good memory
utilisation.

There is reasonable win in having 3 instantiations per cycle rather
than 2, but there is only a small win in moving to 4 or 5.
Implenenting 2 is easy due to dual-port RAMs; implementing 3 is
certainly possible, but may incur memory wasting due to alignment
problems.

Appendix: note about the arity bound
------------------------------------

Another important bound, not considered above, is the maxmimum arity
of a function.  This note simply points out that a similar technique
to template splitting *can* be used when the arity bound is exceeded.

EXAMPLE 3.  If the maximum number of applications in a template is 2
and the maximum arity is 2, the template 'f'

  f = (4, g +0 +1 +2, h1 @1 @3, h1 @2 @3, h2 @4)

can be split as follows.

  f  = (2, f1, h1 @1, h1 @2)
  f1 = (0, f2, -2 @1, -1 @1)
  f2 = (2, g -2 -1 +0, h2 @2)

Here, 'f' deals with the two references to arguments 1 and 2, and pops
them off the stack; 'f1' and 'f2' deal with the remaining references
to arguments 3 and 4.

In the worst case, resorting to binary applications allows any arity
bound larger than 0 to be handled by template splitting.

However, there is a slight complication when using this method to do
with updating.  Normal forms may still arise which are longer that the
maximum application length, in which they must be split into chunks,
and written to the heap chunk-wise.  The only difficulty here is the
increased complexity of update operation.

An alternative way to deal with the arity bound is to use an
abstraction algorithm - see Memo 12.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo26.lhs version [404cc4ace7].































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
=============================
REDUCERON MEMO 27
Rotation and one-hot addition
Matthew N, 25 June 2009
=============================

Bit-vector rotation and one-hot addition are two useful operations in
the implementation of the Reduceron's wide stack.  The former
operation rotates a sequence of bits by a given number of places, and
the latter sums two one-hot numbers to give a resulting one-hot
number.  We define these two operations only to learn that they are
one and the same!  In the process we find an interesting property of
each operation which by equivalence also holds, somewhat surprisingly,
for the other.

This memo is literate Haskell.

> import List (transpose)

Unary numbers
-------------

A one-hot non-negative integer n is a list of booleans whose (n+1)th
element is True and whose other elements are all False.  For example,
the one-hot numbers of width 4, in increasing order, beginning with 0,
are:

  [True , False, False, False],
  [False, True , False, False],
  [False, False, True , False],
  [False, False, False, True ].

The negative one-hot numbers are defined such that for all non-zero
integers n, negate n = reverse n.

Rotation
--------

The function rot,

  rot :: [Bool] -> [Bool] -> [Bool]

is to be defined such that rot n xs rotates the list of bits xs to the
right by n places where n is a one-hot number.  It can be assumed that n
and xs have the same length.

Unary addition
--------------

The function add,

  add :: [Bool] -> [Bool] -> [Bool]

is to be defined such that add n m returns the one-hot representation of
(i+j) `mod` w, where i and j are the integers represented by n and m,
and w is the width of both n and m.

Challenge
---------

Please feel free to define these operations for yourself.  To ensure
that the functions are realisable as digital circuits, case analysis
on boolean values should be avoided; only standard boolean connectives
such as not, && and || should be used to process the boolean-valued
inputs.

Solution: Rotation
------------------

To rotate a bit-vector xs to the right by n places, we iteratively
compute all the right-rotations of xs and select nth one.

> left [] = []
> left (x:xs) = xs ++ [x]

> right = reverse . left . reverse

> rights xs = take (length xs) (iterate right xs)

> dot xs ys = or (zipWith (&&) xs ys)

> sel n xs = map (dot n) (transpose xs)

> rot n xs = sel n (rights xs)

Solution: Addition
------------------

To compute the nth bit of the one-hot sum, we find all the combinations
of pairs of input bits that add to n, and return the sum of the
products of this list of pairs.

> split [] = []
> split (x:xs) = ([x], xs) : [(x:y, z) | (y, z) <- split xs]

> tac (xs, ys) = reverse xs ++ reverse ys

> add a b = map (dot a) (map tac (split b))

Equivalence
-----------

After defining these two functions it becomes apparant that if

  map tac . split = transpose . rights

then the two are equivalent, which is indeed the case.

Observation 1: The add operation, by its specification, is
commutative, and if it is equivalent to rot, then rot must also be
commutative.

Observation 2: The add operation, by its specification, only needs to
rotate one-hot numbers, but if it is equivalent to rot, then it must be
able to rotate arbitrary bit-vectors too.

These two facts were quite surprising to learn!

Array rotation
--------------

The following function, used to implement the wide stack, rotates a
list of bit-vectors to the right by n places, where n is a given one-hot
number.

> rotate :: [Bool] -> [[Bool]] -> [[Bool]]
> rotate n = transpose . map (add n) . transpose 

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo27.lhs version [4060a77de6].





















































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
=======================
REDUCERON MEMO 27
Design of the Octostack
Matthew N, 6 July 2009
=======================

This memo presents various hardware designs of a stack data structure
allowing:

  * the top N elements of the stack to be observed;
  * up to N elements to be popped off the stack;
  * and up to N elements to be pushed onto the stack.

In the Reduceron, where N is defined to be 8, this data structure is
referred to as the "Octostack".  The intention is that, on FPGA, the
pushing and popping operations each take a single clock-cycle to
perform (and can be performed in parallel), and the top elements can
always be observed in zero clock-cycles.

> import List
> import Maybe
> import Array

Interface
---------

The stack is parameterised by N, now referred to as 'blockSize'.

> blockSize :: Int
> blockSize = 8

A block is a container storing exactly 'blockSize' elements, although
this constraint is not enforced by the type system.

> type Block a = [a]

The data structure provides four operations.

> class Stack s where
>   empty  :: s a
>   size   :: s a -> Int
>   tops   :: s a -> Block (Maybe a)
>   update :: Int -> Block (Maybe a) -> s a -> s a

The 'Maybe' type is used to represent partial-definedness.  If the top
block of stack elements is requested and the stack contains fewer than
'blockSize' elements, then the returned block will contain some
undefined elements.  Similarly, when writing a new block of
elements to the stack, only the defined elements in the block are
written, not the undefined ones.

The 'update' operation is the only slightly unusual one.  It takes
three arguments:

  * an integer offset in the range '-blocksize' to 'blocksize';
  * a block of elements to write onto the top of the stack;
  * and a stack.

And it returns a new stack whose size is equal to the size of the
input stack plus the offset, and whose top 'blockSize' elements are
overwritten with the defined values in the write-block.

Example
-------

To illustrate the stack interface, let us start with the empty stack
and perform a few pushes and pops.

> s1, s2, s3, s4, s5 :: Stack s => s Int
> s1 = empty

We will use the following shorthands for 'Nothing' and 'Just'.

> n :: Maybe a
> n = Nothing

> j :: a -> Maybe a
> j a = Just a

To push the elements 1 to 5:

> s2 = update 5 [j 1,j 2,j 3,j 4,j 5,n,n,n] s1

To push the elements 10 to 13:

> s3 = update 4 [j 10,j 11,j 12,j 13,n,n,n,n] s2

To pop two elements:

> s4 = update (-2) [n,n,n,n,n,n,n,n] s3

To pop three elements, and in parallel, push the element 20:

> s5 = update (-2) [j 20,n,n,n,n,n,n,n] s4

Notice that the offset (passed as the first argument to 'update') is
in general equal to the number of pushes minus the number of pops.

Implementation 1
----------------

Since we are targeting FPGAs, we will make use of RAMs to implement
the stack.  The fact that the locations in a RAM may be uninitialised
is captured by the 'Maybe' type.

> type RAM a = Array Int (Maybe a)

It is important to account for the fact that RAMs have a capacity.

> ramCapacity :: Int
> ramCapacity = 1024

The actual value doesn't really matter here, but it is needed to
compute correct array indexes.  In particular, if a negative number,
say -n, is placed on the address bus of a RAM then it is reasonable to
expect the nth element from the end of the RAM appear on the data bus.
This is because, on FPGA, RAM addresses are in twos complement form.

> wrap :: Int -> Int
> wrap n = (if n < 0 then n + ramCapacity else n) `mod` ramCapacity

We represent a stack by a size/contents pair.

> data Stack1 a =
>   Stack1 {
>     size1 :: Int
>   , contents1 :: RAM a
>   }

Given the size of a stack, the following function computes the
addresses of the top 'blockSize' elements.

> addresses1 :: Int -> [Int]
> addresses1 n = [wrap (n-i) | i <- [1..blockSize]]

Now for the implementation.

> instance Stack Stack1 where
>   empty = Stack1 { size1 = 0, contents1 = ram }
>     where ram = listArray (0, ramCapacity-1) (replicate ramCapacity Nothing)
>
>   size s = size1 s
>
>   tops s = map (contents1 s !) (addresses1 (size1 s))
>
>   update offset writes s =
>     Stack1 { size1 = newSize, contents1 = contents1 s // updates }
>     where
>      newSize = size1 s + offset
>      updates = [u | u@(_, Just _) <- zip (addresses1 newSize) writes]

> example1 = tops (s5 :: Stack1 Int)

As a quick test, 'example1' has the following value.

  [Just 20,Just 2,Just 3,Just 4,Just 5,Nothing,Nothing,Nothing]

Nasty property of Implementation 1
----------------------------------

If the top 'blockSize' elements are to be read/written in a single
clock-cycle, then the above design requires a RAM with 'blockSize'
read/write ports.  Implementing a RAM with an arbitrary number of read
ports is easy on an FPGA, but to implement one with more than two
write ports is very difficult - in fact, it seems only possible using
flip-flops, which is not very scalable.

Nice property of Implementation 1
---------------------------------

An attractive property is that the same addresses are used both when
reading from and writing to the top 'blockSize' locations of the RAM.
This need not necessarily be the case: to write three elements onto
the stack, we only need to set three addresses correctly; it doesn't
matter what the other five addresses are set to.

However, if the addresses are the same when reading as when writing
then reading and writing can be done in parallel.  In particular,
after writing to the stack, we do not have to wait until the next
clock cycle in order to schedule a read of the new top elements.  We
can, for example, pop four elements from the stack, push two new ones
and, *at the same time*, schedule a read the top eight elements.  

(All this is thanks to the fact that FPGA block RAMS have separate
data busses for reading and writing, with a "WRITE_FIRST" semantics.)

Rotation functions
------------------

The following general-purpose functions will be useful in the next
stack implementations.

Rotate a list 'n' elements to the left; 'n' must be non-negative.

> left :: Int -> [a] -> [a]
> left n xs = drop n xs ++ take n xs

Rotate a list 'n' elements to the right; 'n' must be non-negative.

> right :: Int -> [a] -> [a]
> right n = reverse . left n . reverse

A few algebraic properties.

    reverse . right n
  = { definition of right }
    reverse . reverse . left n . reverse
  = { reverse . reverse = id }
    left n . reverse

Observing that

  left n = reverse . right n . reverse

we also have 

  reverse . left n = right n . reverse

Rotate a list 'n' elements to the right; if 'n' is negative the list is
rotated '|n|' elements to the left.

> rotate :: Int -> [a] -> [a]
> rotate n = if n < 0 then left (-n) else right n

(See Memo 26 for circuit-level implementations of these functions.)

Implementation 2
----------------

To avoid the need for a single RAM with 'blockSize' ports, we now use
'blockSize' RAMs, each with a single port.

> data Stack2 a =
>   Stack2 {
>     size2 :: Int
>   , contents2 :: Block (RAM a)
>   }

If the RAMs are numbered 0 to 7 then the RAM numbered 'n' stores the
elements with the following stack indices.  (Note about the term
'stack index': the element at the bottom of the stack has index 0, and
the element at the top has index 'm-1' where 'm' is the number of
elements on the stack.)

  n, n+blockSize, n+2*blockSize, n+3*blockSize, ...

For example, when 'blockSize' = 8:

  RAM   Stack indices of elements stored
  0     0,  8, 16, ...
  1     1,  9, 17, ...
  2     2, 10, 18, ...
  3     3, 11, 19, ...
  4     4, 12, 20, ...
  5     5, 13, 21, ...
  6     6, 14, 22, ...
  7     7, 15, 23, ...

Given the index of an element on the stack, we can determine which RAM
it is stored in.

> lower :: Int -> Int
> lower i = i `mod` blockSize

We can also determine the RAM location where an element is stored.

> upper :: Int -> Int
> upper i = i `div` blockSize

Given the size of the stack, the following function computes, for each
RAM 0 to 7, the address of that RAM's topmost element.

> addresses2 :: Int -> [Int]
> addresses2 n = [upper (wrap (n-i)) | i <- [1..blockSize]]

> instance Stack Stack2 where
>   empty = Stack2 { size2 = 0, contents2 = replicate blockSize ram }
>     where ram = listArray (0, ramCapacity-1) (replicate ramCapacity Nothing)
>
>   size s = size2 s

For example, considering a stack of size 19, the addresses of the
topmost element in RAMs 0 to 7 are 2,2,2,1,1,1,1,1 respectively.
Indexing the RAMs by these addresses gives the stack elements with
indices 16,17,18,11,12,13,14,15.  To obtain these top elements in
order, we rotate them by 'n' positions (here 3) to the left, where 'n'
is the number of the RAM holding of the top element.  More formally,
'n = lower m' where 'm' is the size of the stack.  Finally, we can
reverse this sequence to give the top elements in reverse order, so
that the top stack element comes first.  Recall that rotating left and
then reversing is equivalent to reversing and then rotating right.  We
prefer the latter.

>   tops s = right (lower (size2 s)) (reverse ramOuts)
>     where ramOuts = zipWith (!) (contents2 s) (addresses2 (size2 s))

A similar rotation method is used when updating the stack.  The main
difference is that instead of reversing and rotating the RAM outputs,
we reverse and rotate the RAM inputs.

>   update offset writes s =
>     Stack2 { size2 = newSize, contents2 = zipWith (//) (contents2 s) uss }
>     where
>       newSize = size2 s + offset
>       writes' = reverse (left (lower newSize) writes)
>       updates = zip (addresses2 newSize) writes'
>       uss     = [[u | isJust x] | u@(_,x) <- updates]

> example2 = tops (s5 :: Stack2 Int)

Implementation 3
----------------

The problem with the above implementation is that there are large
logic delays on the input and output busses of the RAMs --- observing
and updating the top stack elements incurs the RAM-access delay plus
the delay of the rotation logic.  One way to reduce this delay is to
store the top elements in registers.  The following is a wrapper
around the above implementation which caches the top 'blockSize'
elements in registers.

The cache is a block of registers, storing the top elements, with the
top element coming first.

> type Cache a = Block (Maybe a)

> data Stack3 a =
>   Stack3 {
>     cache :: Cache a
>   , stack :: Stack2 a
>   }

> instance Stack Stack3 where
>   empty = Stack3 { cache = replicate blockSize Nothing, stack = empty }
>
>   size s = size (stack s)
>
>   tops s = cache s

To update the stack:

  * the defined elements in the write-block are written into the cache
    registers;

  * if 'offset' is positive, then the values of the upper 'offset'
    cache registers are pushed onto the RAM stack;

  * if 'offset' is negative, then the top '|offset|' values on the RAM
    stack are popped and written into the upper '|offset|' cache
    registers;

  * any cache register 'n' not modified by the above three cases is
    updated to contain the value of cache register 'wrap (n+offset)'.

>   update offset writes s =
>     Stack3 {
>       cache = newCache
>     , stack = update offset push (stack s)
>     }
>     where
>       rotated  = rotate offset (cache s)
>       push     = [ if i <= offset then x else Nothing
>                  | (i, x) <- zip [1..blockSize] rotated ]
>       pushMask = [isJust x | x <- writes]
>       popMask  = reverse [(-i) >= offset | i <- [1..blockSize]]
>       ramTops  = rotate offset (tops (stack s))
>       newCache = zipWith5 choose writes pushMask ramTops popMask rotated

> choose push pushBit pop popBit rot
>   | pushBit   = push
>   | popBit    = pop
>   | otherwise = rot

> example3 = tops (s5 :: Stack3 Int)

Fusion
------

If we inline the 2nd implementation into the 3rd, two fusion
possibilities arise.

  1. The list 'ramTops' is defined to be the top RAM elements rotated
     by 'offset' positions.  The values returned by 'tops' in the 2nd
     implementation are rotated right by 'n' positions, where 'n' is
     the size of the stack.  These two rotations may be fused into a single
     rotation by 'n + offset' positions.

  2. The list 'writes'' is computed by left rotating a list 'writes'
     by 'n+offset' positions.  And the list 'writes' is computed by
     right rotating the cache by 'offset' positions.  The left and
     right rotations to some extent cancel each other out; the same
     result can be obtained by simply left rotating the cache by 'n'
     positions.
  
Testing
-------

> test :: Stack s => [(Int, Block (Maybe a))] -> s a
> test = foldl f empty 
>   where f s (offset, writes) = update offset writes s

> block :: [a] -> Block (Maybe a)
> block xs = take blockSize (map Just xs ++ repeat Nothing)

> test1 :: Stack s => s Char
> test1 = test [ (3, block "one")        -- Push 3
>              , (3, block "two")        -- Push 3
>              , (5, block "three")      -- Push 5
>              , (4, block "four")       -- Push 4
>              , (4, block "five")       -- Push 4
>              , (3, block "six")        -- Push 3
>              , (-7, block "")          -- Pop 7
>              , (-5, block "12345")     -- Pop 5
>              , (1, block "ok!")        -- Pop 2, Push 3
>              , (4, block "I am")       -- Push 4
>              , (-1, block "XY")        -- Pop 2, Push 1
>              ]

Final remarks
-------------

The FPGA implementation of the Octostack closely follows the above
code for 'Stack3' - see 'CachingOctostack.hs' in the Reduceron source
code.

For high performance, the user of the stack should compute the
'offset' argument to 'update' with as little delay as possible.  This
is because the 'offset' input is connected directly to the address
busses of the internal block RAMs.  In our experience, this is where
the critical path in a design often lies.

In future, it may be helpful to draw a diagram of this stack design.
A diagram of Implementation 2 is however already available in section
4.3 of [1].

References
----------

[1] Matthew Naylor and Colin Runciman.  Widening the von Neumann
Bottlneck for Graph Reduction using an FPGA.  In Implementation and
Application of Functional Languages (Revised Selected Papers),
Freiberg, Germany, 2007.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo28.txt version [c955ed899f].

































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
=================================
REDUCERON MEMO 28
Mark-Compact in O(survivors) time
Matthew N, 6 August 2009
=================================

One of the big advantages of copying collectors is that they take time
linear in size of the live graph.  Most collectors take time linear in
the size of the heap.  In functional language implementations, the
live graph is typically small compared to the heap, due to many
short-lived allocations.  This makes a copying collector very
attractive in a functional setting.

On the other hand, one of the big disadvantages of copying collectors
is that they require the heap to be split into two sub-spaces: a
"from-space" where the live graph and the garbage resides and a
"to-space" where the live graph is written to.  The result is that
maximum residency of the live graph is halved, wasting precious
memory.

This memo simply points out that a one-space compacting collector that
takes time linear in the size of the live graph is possible.  The size
of the live graph is sometimes referred to as the number of survivors,
hence the time-complexity of the algorithm is O(survivors).  The
algorithm is a mild variation of the Two-Finger Mark-Compact
algorithm.

The Two-Finger Mark-Compact algorithm
-------------------------------------

Jones and Lins define Edwards's Two-Finger algorithm as follows.

  numSurvivors = mark();
  relocate();
  updatePointers(numSurvivors);
  heapPointer = numSurvivors + 1;

The "mark" phase marks the live cells in the heap, and takes time
proportional to the size of the live graph.

The "relocate" phase makes use of a left pointer and a right pointer.
The left pointer starts at the left end of the heap and advances from
left to right.  The right pointer starts at the right end of the heap
and advances from right to left.  On each iteration, the left pointer
is advanced to point to the next free cell, and the right pointer is
advanced to point to the next live cell; the leftmost free cell is
overwritten with the rightmost live cell, and the righmost live cell
is overwritten its new address, as pointed to by the left pointer.
The "relocation" phase finishes when the left pointer is larger than
the right pointer.

Finally, the "updatePointers" phase iterates through the compacted
live graph and updates any pointers to relocated cells with their new
addresses.

The algorithm is linear in the heap size because every cell on the
heap will have, at some stage during the algorithm, been pointed to by
either the left or right pointer.

Mild variation
--------------

Let us replace the right pointer with a traversal pointer that
advances along the live graph, beginning at the root.  Now on every
iteration, advance the traversal pointer to point the to next live cell
whose address is larger than "numSurvivors".  Relocate the live cell
to address pointed to by the left pointer, as above.

This variant of the algorithm traverses "numSurvivors" cells three
times: once in the mark phase, once when advancing the left pointer
from address 0 to address numSurvivors, and once when advancing the
traversal pointer along the live graph.

Other considerations
--------------------

It is simple to implement a copying collector, and even simpler to do
so with without using stack!  However, the mark phase in Mark-Compact
does require either a stack or the pointer-reversal technique, and
these are both somewhat complicated to implement.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo3.txt version [fdc7e48779].

































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
========================================================
REDUCERON MEMO 3
Head reduction, arity raising and shared subexpressions
Colin R, 17 November
(Minor revision: mainly the part about non-linear args.)
========================================================

In the simple compiler of the Reduceron prototype case alternatives are
split off as separate functions.  Memo 2, after discussion, concluded
that a useful way to achieve recombination (hence widening) in many
cases is to in-line the case distinguishing function in the recursive
alternatives.  So we have identified originally recursive call sites in
case alternatives as prime candidates for inlining.

Basic Observation and First Example
-----------------------------------

This memo looks at another class of in-lining sites.  Consider the head
applications (ie. the leftmost outermost applications) in each function
body.  Wherever a *named function*, as distinct from an argument variable,
occurs in this position, the application can be reduced. Sometimes,
arity raising must be performed first to make this reduction possible.

For example, consider the list-append function.  After simple compilation
we obtain:

  Cons x xs n c = c x xs

  app xs ys = xs ys (app' ys)
  app' ys x xs' = Cons x (app xs' ys)

Now we in-line app in the recursive alternative:

  app' ys x xs' = Cons x (xs ys (app' ys))

With this definition, once recursion "gets going" computation is mainly
by the directly recursive and not-too-thin app' combinator.  Apart,
that is, from the Cons in the head position.  In order to reduce the
Cons at compile time, we first raise the arity of app'

  app' ys x xs' n c = Cons x (xs ys (app' ys)) n c

then apply the Cons rule:

  app' ys x xs' n c = c x (xs ys (app' ys))

Shared Subexpressions
---------------------

The app function may itself occur in the head position of another
function body.  For example, consider the concat function:

  concat xss = xss Nil concat'
  concat' xs xss' = app xs (concat xss')

In-lining in the recursive alternative:

  concat' xs xss' = app xs (xss' Nil concat')

As app occurs in the head position, and its application is already
saturated, it can be reduced:

  concat' xs xss' = xs (xss' Nil concat') (app' (xss' Nil concat'))

But look what has happened: because the app body is non-linear the
second argument expression has been duplicated.  We know that in any
one application of concat' only one occurrence will be evaluated,
because xs is a projective function.  Even so a repeated subexpression
is undesirable: it makes the body not merely wide but obese.  In addition
to the possible extra cost of instantiation, an obese body takes up more
space in the combinator store and increases pressure on the heap.

In general, when reducing at compile-time an application of a function
with a non-linear body, a local variable definition may need to be
introduced to avoid repeated subexpressions.  The only purpose of such
local definitions is to express a term-graph rather than a term-tree as
the body:

  concat' xs xss' = xs c (app' c)
                    where
                    c = xss' Nil concat'

Another Example
---------------

This time, consider the power-list function.  We shall need as auxiliary
the map function:

  map f xs = xs Nil (map' f)
  map' f x xs' = Cons (f x) (map f xs')

In-lining in the recursive alternative:

  map' f x xs' = Cons (f x) (xs' Nil (map' f))

Arity raising and reducing the head application:

  map' f x xs' n c = c (f x) (xs' Nil (map' f))
          
Now to the power-list function itself:

  pow xs = xs (Cons Nil Nil) pow'
  pow' x xs' = app p (map (Cons x) p)
               where
               p = pow xs'

In-lining in the recursive alternative:

  pow' x xs' = app p (map (Cons x) p)
               where
               p = xs' (Cons Nil Nil) pow'
               
And reducing the head application:

  pow' x xs' = p ys (app' ys)
               where
               p = xs' (Cons Nil Nil) pow'
               ys = map (Cons x) p
               

Other Observations
------------------

If *all* applications of a function are either in the head position or
in recursive calls in alternatives, then all these applications can be
reduced at compile-time and the function definition can be discarded,
saving space in the combinator store.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo31.txt version [7280d228ae].





























































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
===============================
REDUCERON MEMO 31
Design proposal for speculative
evaluation of primitive redexes
Matthew N, 16 September 2009
===============================

Almost a year ago (in Memo 8) Colin proposed the idea of reducing
primitive redexes during instantiation of function bodies.  This idea
has become very attractive, now that, after improvements to the
Reduceron, primitive reductions account for large portions of the
runtimes of many programs, even "symbolic-intensive" ones.

There appear to be two main ways to do this: by static or dynamic
analysis.  In the static approach, primitive redexes are identified at
compile time.  The analysis is conservative and needs to be defined,
although Colin does say "I think this analysis should be fairly
simple".

In the dynamic approach, the analysis really is simple: just look at
the arguments in a primitive application; if they are evaluated apply
the primitive; if not, build the application on the heap as is
currently done.

One possible advantage of the static approach is that the circuitry
required to implement it will have a smaller logic delay than that for
the dynamic approach.  This is because all speculative evaluations are
decided at compile time - no runtime decisions are made.  However, it
is not clear how significant this saving is: it is perhaps just one
level of logic in the form of a multiplexer, and what's more, it is
not even on the current critical path.

Therefore, I propose we first try the simpler dynamic approach.  Below
I outline a fairly conservative design, but nevertheless, one that
should be quite effective.

Syntax
------

In the Reduceron, applications are sequences of atoms.

  data App = APP Bool [Atom]

The boolean flag states whether or not the application is in normal
form, and is used for update-avoidance.  Atoms are defined as follows.

  data Atom =
      INT Int           -- Primitive integer
    | ARG Shared Int    -- Argument index, can only occur in code memory 
    | PTR Shared Int    -- Pointer to an application
    | CON Arity Index   -- Constructor id
    | FUN Arity Int     -- Function id
    | PRI Arity PrimOp  -- Primitive id

  type Shared = Bool

  type Arity = Int

  type PrimOp = String

Note that ARG atoms can only appear in code memory, not on the heap.

To support evaluation of primitive applications during instantiation,
I propose to add a special register file to the machine, for storing
the results of speculative evaluations.  Other applications in the
body of the function may then refer to these results as many times as
required.  Remember, there may be many references to the result of a
primitive application, if it is let-bound.

  type RegId = Int

  data Atom = ... | REG RegId

Like ARG atoms, REG atoms can only appear in code memory.

Furthermore, I propose to add an optional register field to
applications.

  data App = APP (Maybe RegId) Bool [Atom]

Note that applications can appear both in code memory and in the heap.
However, when in the heap, the new register field always contains
Nothing.  When in code memory, the new register field *may* specify a
register that should be updated with the result of an *attempted*
on-the-fly speculative reduction of the application.

Semantics
---------

We must now modify the machine to deal with this new kind of
application.  As it can only occur in code memory, we need only
decide how to instantiate it.  There are two cases.

  1. If the application is that of a primitive to some arguments and
     all the arguments are fully-evaluated integers, then apply the
     primitive and store the resulting atom in the stated register.

  2. Otherwise, the application should be instantiated onto the heap
     as normal, and the stated register assigned to an atom
     that points to this application.

We must also define the semantics of the register file.  There are two
choices.

  1. Assignments to a register take one clock-cycle to come into
     effect.

  2. Assignments to a register come into effect immediately.

Option (2) allows applications being instantiated in the same clock
cycle as a primitive redex to refer to the result of the primitive
redex.  However, option (2) has a much more significant logic delay
than option (1), and I suspect it is not feasible from a clock-rate
point of view.

When a primitive redex is reduced speculatively, the result is
unboxed, that is, inserted directly in place of any reference to it.
Consequently:

  1. No unwinding step is required to fetch the result if is
     later required.

  2. A number of swaps may be avoided, which are otherwise be necessary
     to force evaluation of the primitive's arguments.

  3. There is increased scope for further speculative evaluation: the
     more unboxed values in the graph, the greater the opportunity for
     further speculative evaluations - it is a self-feeding process.

  4. The order in which speculative evaluations are attempted should
     be carefully decided at compile-time, to maximise the
     self-feeding effect.

Discussion
----------

The is all pretty much as Colin originally proposed in Memo 8, with
the register file taking care of primitive bindings.  One difference
is that the decision to perform speculative evaluation is decided at
runtime.  A few new details are:

  1. A finite register file is required - its maximum capacity is
     another machine parameter, like, for example, maximum application
     length.

  2. The results of primitive redexes can only be accessed *after* the
     clock-cycle in which they are reduced.

  3. If the maximum number of applications that can be instantiated
     per cycle is limited to 2, then so is the maximum number of
     speculative reductions.

  4. When a primitive redex is detected, the heap bandwidth allocated
     to instantiate that redex is not needed, and hence wasted.

On the positive side, when a primitive redex is detected we are likely
to avoid 2 swaps, 2 unwinds, and a primitive reduction - that's five
clock cycles - not to mention a possible avoided update.

Some open issues:

  1. What percentage of speculative reductions are in fact needed
     subsequently?

  2. Compiler should identify applications which cannot possibly be
     primitive redexes, in order to avoid possible overhead.  An
     example such application would be "1 + sum xs".

  3. Feedback for programmers: modify the emulator to tell the
     programmer which primitive applications could not be performed
     speculative (and how many there were).

  4. What about speculative evaluation of semi-strict operators such
     (&&) and (||)?

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo32.txt version [baab9c7253].



























































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
=============================================
REDUCERON MEMO 32
Thoughts about Reducera -- a Plural Reduceron
Colin Runciman, 18 August 2009
=============================================

In this memo I'll set out my rough first thoughts about a machine
combining more than one Reduceron evaluators working cooperatively in
parallel to perform a single computation.

This first version is a raw stream of half-baked thought! I am
deliberately writing it without looking back at any of the literature on
parallel graph reduction.  When I have looked again at that literature I
may change my mind.  Come to that, just thinking about it again another
day may be enough to change my mind too!

* Twin Reducera

Combining just two evaluators is already a challenge.  I consider
such a twin combination to be a natural first step.  It offers certain
special advantages.  For example, it means that there is a uniquely
identified other Reduceron for each of the evaluators. At onother level,
the FPGA hardware has twin memory ports.

Even so, it would be nice to avoid over-dependence on two-ness.  Ideally,
the principles of combination would scale naturally to any number of
evaluators.

* Templates, Patterns and Annotations?

One school of thought argues from the slogan "If you want parallel
computation, you must write a parallel program".  So, the argument
goes, the programmer must use parallel control functions that capture
certain common patterns -- such as a parallel map, or a parallel
tree-structured fold.  Or else, or in addition, the programmer must
add annotations to indicate where parallel evaluation should be used
-- even though evaluation would not be needed under a standard lazy
evaluation strategy.

People who argue this way may have a point, but the big pain is that
programs have to be rewritten.  This whole approach has been rather
over-worked in my view, and I am not very keen on it.  I want to
evaluate ordinary programs, without programmers having to use special
functions or add special annotations.

* Speculation

Others would argue more generally that since lazy evaluation is inherently
a sequential strategy, a degree of speculative evaluation is essential
in any effective system for parallel evaluation.

On the contrary, I suggest that speculative evaluation should only be
performed (1) in the course of needed evaluation by the same evaluator,
and (2) when the additional work-load is very small and involves no
extra heap pressure.  The form of such evaluation we already have in
mind is the contraction of any redex for a total primitive function to
its result during the instantiation of function bodies.

Experience suggest that speculative evaluation beyond such cases is
hard to get right (ie. to make consistent with standard evaluation
semantics) and involves surprisingly tricky machinery.  There is also
the fundamental drawback that speculative computation may prove to be
unnecessary computation, which is a bit sad if significant resources
have been used to do it.

* Strictness and Cost/Weight

So where can we find scope for needed parallel reduction in a lazy
language?  We need to identify component expressions of the computation
whose value is strictly needed.  More than that, their likely cost of
evaluation must be sufficient to outweigh the extra cost involved in
coordinating the work of parallel evaluators.

Strictness analysis can be used to discover many instances of needed
expressions.  When a function is strict in more than one argument
position, it may be worth evaluating the two arguments in parallel,
but only if the weight of computation is sufficient in each case.

One simple approximate test for a sufficiently weighty computation is to
consider only the applications of recursive functions.  If only a simple
strictness analysis is used, this test may need to be strengthened to
consider only applications of functions with simple results.

* Expressions and Evaluators: Who waits for whom? 

What if there are more candidate parallel expressions for parallel
evaluations than there are evaluators to perform them?  This state of
affairs is quite likely with only twin Reduceron evaluators.  Should there
be a pool or queue of expressions awaiting evaluation, like customers
awaiting collection at a taxi rank?  Or should parallel computation be
initiated only if there is a processor available, just as travellers
arriving at a taxi rank with no taxi waiting may all decide to press on
by other means?

The attraction of the expression-queue approach is its apparent
uniformity: whenever an expression satisfies the criteria for parallel
evaluation, we just put it on the queue.  Simple.  But then the queue
must be maintained, and it may grow unboundedly -- unless we abandon
the simple uniformity after all.  So I am inclined not to have an
expression-queue of hopeful parallel tasks.

Conversely, what if there are more evaluators than expressions currently
needing evaluation?  This will be the state of affairs initially, at
least.  So there needs to be a pool or queue of evaluators waiting for
work to do.  Note that unlike the expression queue, this one is inherently
bounded by the total number of Reduceron evaluators in the machine.

So, when there is an expression satisfying the conditions for parallel
evaluation *and* there is an available processor, the expression is
tagged as "under evaluation" for the benefit of other interested parties,
and off they go.  When evaluation is complete the "under evaluation"
tag is cleared.

* Blocking and Resuming

So what do we do when a computation comes across an expression it needs
that is marked "under evaluation"?  We attach the computation's state to
the expression, freeing the evaluator for other work.  When the evaluator
of the expression returns to clear the "under evaluation" tag, the
computation can continue.  So we need a queue of resuming computations.
When an evaluator completes its current job, it should first look in this
queue before declaring itself available for fresh parallel activity --
just as a taxi driver checks his car's internal display for any jobs
already available before illuminating its external FOR HIRE sign.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo33.txt version [358c8c7b2c].























































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
============================
REDUCERON MEMO 33
Fourth quarterly review
Matthew N, 20 September 2009
============================

Since the second quarterly review (Memo 21), we have enhanced the FPGA
implementation in three ways:

  1. The arity-limit has been lifted from 4 to 7.

  2. The constraint that case expressions can only appear in the
     spines of function bodies has been lifted, allowing much greater
     opportunity for inlining.

  3. A two-space copying garbage collector has been implemented.  A
     one-space collector was considered (Memo 28), which also takes
     time linear in the size of the live heap, but has not been
     implemented.

We made following two performance improvements, discussed in Memo 25:

  1. Improved update-avoidance by tagging each application with a bit
     sating whether or not it is a normal form.  Applications which
     are already in normal form do not need to be updated.  The result
     is that, on average, 87% of updates are avoided instead of 60%.

  2. Inlining all non-recursive functions whose bodies contain just a
     single application gives an average 18% speed-up.  The reason for
     this speed-up is discussed in Memos 2 and 25.

All of the above modifications were made without lowering the maximum
clock frequency of the design.

We made the following measurements.

  1. We carried out some parameter-tuning experiments (Memo 25),
     using the machine semantics (implemented in Haskell).  We
     measured the effect of max application length, max spine length,
     and max instantiations per cycle on execution time.  We found
     that the values 4, 6, and 2 respectively were good choices, but
     that there was a mild-to-moderate benefit in increasing max
     instantiations per cycle to 3.

  2. We measured the performance of the FPGA implementation (Memo 30)
     and found that, in terms of number of clock-cycles, it is over
     four times faster than Matthew's thesis implementation.  On the new
     FPGA, both the new and the thesis implementations clock around
     the same frequency of 120MHz.

  3. We profiled the FPGA implementation, and found that the average
     time taken by garbage collector is now 2%, compared to
     11% in the thesis implementation (Memo 30).  Explanations for
     this include spinelessness and a larger heap.  We also found that
     on average 22% of time is now taken performing swaps and
     primitive reductions.

Consequently, we have decided not to take the concurrent garbage
collector route, at least for now.  On the other hand, speculative
evaluation of primitive redexes now looks very worthwhile, and we
sketched a possible design in Memo 31.  Once implemented, we expect
the percentage time spent doing function unfolding to increase
significantly, in which case increasing the max instantiation limit
will become a much bigger win.

We have implemented an efficient emulator for the Reduceron in C,
allowing us to quickly explore ideas.

Our very own Lava implementation has been polished and released on
Hackage.  There is haddock documentation, a range of examples, and a
feature overview (Memo 23).  One notable improvement is that the
Recipe library for behavioural description has been rewritten.  It is
more concise, and supports static analyses (including an optimiser)
and shared procedure calls.

We documented the design of the Caching Octostack in Memo 27, an
important part of the FPGA implementation.  A preliminary experiment
showed that the caching version has a significantly lower logic delay
than the non-caching version, as expected.

We wrote a compiler from F-lite to C, allowing F-lite programs to
be run on an FPGA soft-core, which could be useful for comparative
purposes.

We added several more F-lite programs, including CountDown, Cichelli,
Mate, and KnuthBendix.

We started thinking about the design of a twin Reduceron (Memo 32).

What next?
----------

Lots to do:

  1. Implement "speculative evaluation of primitive redexes", first on
     emulator, then FPGA.

  2. Write a technical report, detailing the new Reduceron.

  3. Prepare talk for Fun in the Afternoon, Cambridge, 26 November,
     assuming my talk offer is accepted.

  4. Improve compiler, by more adventurous inlining and
     body-splitting, and maybe even incorporate Jason's supercompiler
     pass.

  5. Prototype a twin Reduceron.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo34.txt version [8426bc370b].





































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
===========================================================
REDUCERON MEMO 34
Expected clock-tick run-times for flite-reduceron programs:
a discussion based on micro-examples
Version 2 (note revised title)
Colin Runciman, 1 October 2009
===========================================================

As we examine the performance of the Reduceron for benchmark programs we
naturally want to include substantial examples of symbolic computing,
such as the Knuth-Bendix completion program that I recently added to
our collection of test programs.

However, when running a complex program, it is hard to say how many
clock ticks we *expect* a computation to take.  We may be pleased that it
takes fewer clock-ticks than before, or now runs faster on the Reduceron
than when compiled using ghc! Yet we may have no clear idea whether the
run-time in clock-ticks is now as good as we could reasonably hope for,
or is still some way short of that ideal.

Another class of benchmark tests, of complementary value, uses
micro-examples of simple computational tasks.  Because the task is
simple, we can more readily estimate how many clock cycles we think
it should need.  Or, at least, we can reason about plausible lower and
upper bounds for the number of cycles.

Examples where actual computation time is well above the estimated lower
bound may motivate and suggest improvements.  And any where it is well
above the estimated upper bound even more so!

The rest of the memo discusses three examples, then draws brief
conclusions.  As we are currently very interested in the potential impact
of primitive redex speculation, it will be considered as we go along.
As "primitive redex speculation" is a weighty phrase to use repeatedly,
I shall instead use the acronym PRS.

Example 1 (list length)
-----------------------

Consider this program:

{
length  =  lengthPlus 0 ;
 
lengthPlus a Nil          =  a ;
lengthPlus a (Cons x xs)  =  lengthPlus ((+) a 1) xs ;

main  =  emitInt (length "quick brown fox jumps over the lazy dog") 0 ;
}

How might the flite-reduceron programmer reason about the expected
performance of this program?

(1) The application of length will be in-lined in main.
(2) Reducing to the correct case alternative for the 2nd argument of
    lengthPlus will take just one cycle as even the Cons alternative
    contains just two small applications.
(3) The primitive applications ((+) a 1) will each require 3 cycles
    (1 unwind, 1 swap, 1 prim). 
(4) There are then no off-spine applications, so the recursive call
    will take just one cycle.
(5) A few extra cycles are needed to reduce main and emitInt.
(6) So if the string contains N characters the computation should
    take something like 5N+few cycles.
(7) The test string contains 39 characters, so perhaps the program will
    run in a bit more than 200 clock-cycles (or 1 microsecond on the
    current Xilinx implementation).

Indeed, if we compile to reduceron code by

$ Flite -i1 -r6:4:2:1 stringlength.hs > stringlength.red

and run stringlength.red using the C emulator, the report is as
follows.

==== EXECUTION REPORT ====
Result      =            0
Ticks       =          219
Swap        =          18%
Prim        =          18%
Unwind      =          35%
Update      =           0%
Apply       =          27%
==========================

The combinator code (with the big string constant in main elided) is:

length                    =  lengthPlus 0;
lengthPlus v0 v1          =  v1 [lengthPlus#1,lengthPlus#2] v0;
lengthPlus#1 v0 v1 v2 v3  =  v1 [lengthPlus#1,lengthPlus#2] ((+) v3 1);
lengthPlus#2 v0 v1        =  v1;
main                      =  emitInt (lengthPlus 0 (...))

The length definition has indeed been in-lined and it is dead code.
The core computation using lengthPlus needs 5 cycles per recursive step,
much as expected.  it takes 1 cycle to examine the case subject, 1 to
look up and apply the appropriate alternative (lengthPlus#1 every time
except the last), and 3 more cycles for the primitive (+) application
it contains (1 unwind, 1 swap, 1 prim).  The additional 20 cycles or
so are needed to apply main, including heap instantiation of the large
constant list of characters.

When PRS is implemented, we expect the cost per (+) application to fall
from 3 cycles to 1 cycle.  So the program will run in about 140 cycles,
about 37% less -- an attractive speed-up!

Example 2 (string comparison)
-----------------------------

Here's another program:

{
con True  x  =  x ;
con False x  =  False ;

equalStrings Nil         Nil          =  True ;
equalStrings Nil         (Cons y ys)  =  False ;
equalStrings (Cons x xs) Nil          =  False ;
equalStrings (Cons x xs) (Cons y ys)  =  con ((==) x y) (equalStrings xs ys) ;

bit False  =  0 ;
bit True   =  1 ;

main  =  emitInt (bit (equalStrings "quick brown fox jumps over the lazy dog"
                                    "quick brown fox jumps over the lazy don")) 0 ;
}

This one is a bit more tricky, but here's some outline reasoning:

* The applications of con and bit will be in-lined.
* Examining the two equalStrings arguments, each as a case subject, will take
  2 clock-cycles.
* In the double-Cons recursive case, the reduction of the (==) application
  without PRS will require 4 cycles (including 2 swaps because neither
  argument is syntactically constant).
* It will take another 1 cycle to examine the subject of the case inlined
  from con.
* Instantiating the recursive call alternative takes another 1 cycle.
* So it's something like N*8 + constant, where N is around 40 and the
  constant is roughly double the previous example (as there are two large
  string constants rather than one).
* In all, we might expect the program to run in around 360 clock-cycles.

If we compile to Reduceron code and run it using redemu, the report
shows that it takes rather longer than estimated:

==== EXECUTION REPORT ====
Result      =            0
Ticks       =          471
Swap        =          16%
Prim        =           8%
Unwind      =          32%
Update      =           0%
Apply       =          41%
==========================

It seems that there are 3 more clock cycles needed for each recursive
step that were not included in the reasoning for estimate.  The current
combinator code for this program, with both string constants in main
elided, is as follows.

con v0 v1                      =  v0 [con#1,con#2] v1;
con#1 v0 v1                    =  False;
con#2 v0 v1                    =  v1;
equalStrings v0 v1             =  v0 [equalStrings#5,equalStrings#6] v1;
equalStrings#1 v0 v1 v2 v3 v4  =  (==) v3 v0 [con#1,con#2]
                                             (v [equalStrings#5,equalStrings#6] v1);
equalStrings#2 v0 v1 v2        =  False;
equalStrings#3 v0 v1 v2        =  False;
equalStrings#4 v0              =  True;
equalStrings#5 v0 v1 v2 v3     =  v3 [equalStrings#1,equalStrings#2] v0 v1;
equalStrings#6 v0 v1           =  v1 [equalStrings#3,equalStrings#4];
bit v0                         =  v0 [bit#1,bit#2];
bit#1 v0                       =  0;
bit#2 v0                       =  1;
main                           =  emitInt (... [equalStrings#5,equalStrings#6]
                                           ... [bit#1,bit#2]) 0;

The definitions of con, bit and equalStrings itself are all dead code as
they are everywhere inlined.  In the equalStrings computation, alternative
#1 is the common-case choice rather than #2 (and less significantly
#5 rather than #6).  The body of equalStrings#1 is sufficiently large
that its instantiation could account for 2 more clock cycles. Perhaps
equalStrings#5 also takes another 1 cycle?

When PRS is implemented, the (==) applications should take 1 cycle rather
than 4, and so the total of 11 cycles per recursive step can be expected
to fall to 8.

Example 3 (nfib)
----------------

Finally, a classic example from the early days of functional-language
implementation.  The result of nfib n is the number of nfib applications
required to compute it.  Also, nfib n = 2 * fib n - 1.

{
nfib n  =  case (<=) n 1 of {
           True  -> 1 ;
           False -> (+) 1 ((+) (nfib ((-) n 2)) (nfib ((-) n 1))) ;
           } ;
main    =  emitInt (nfib 20) ;
}

The result of nfib 20 is 21891.  In nfib applications involved, the True
alternative is taken 10946 times, and the False alternative 10945 times.
So what preliminary estimate of the number of clock-cycles can we make?

* For each nfib call we need 1 cycle to unwind it (never in spine
  position) and 1 to instantiate it (small enough with case alternatives
  split off).  Then there's 2 further cycles (1 swap, 1 prim) to evaluate
  the (<=) comparison at the top of the spine, and another 1 cycle to
  dispatch on it as the case subject.

* In 10946 calls there is no further cost.

* In 10945 calls there is the cost of instantiating a large body: perhaps
  as much as 5 clock cycles if primitives applications are handled one
  argument at a time.

* Then in these same calls, there is the cost of 3 cycles for each of
  the two subtractions, 4 cycles for the inner (+) and 3 cycles for the
  outer one.  That's 13 cycles for primitive arithmetic!

* So in all we have 21891 * 5 + 10945 * 18 = 306465 cycles plus a few
  more for evaluation of main.  The total should be around 306470.

Compiling with Flite and running using redemu as before, we find
this preliminary estimate is not far out, but once again it is an
underestimate:

==== EXECUTION REPORT ====
Result      =            0
Ticks       =       339298
Swap        =          22%
Prim        =          19%
Unwind      =          32%
Update      =           6%
Apply       =          19%
==========================

The combinator code for this program, compiled with -i1, is as follows:

nfib v0       =  (<=) v0 1 [nfib#1,nfib#2] v0;
nfib#1 v0 v1  =  let { v2 = (-) v1 2; v3 = (-) v1 1 }
                 in (+) 1 ((+) ((<=) v2 1 [nfib#1,nfib#2] v2)
                               ((<=) v3 1 [nfib#1,nfib#2] v3));
nfib#2 v0 v1  =  1;
main          =  emitInt ((<=) 20 1 [nfib#1,nfib#2] 20) 0;

The definition of nfib itself is dead code, and its in-lining everywhere
is the factor that the initial estimate didn't take into account.
The double inlining of nfib makes an even more sizable body for nfib#1
than the estimate allowed for, accounting for another 3 cycles to build
each instance.  Another consequence of this inlining in nfib#1 is that
the (<=) applications are no longer in spine positions, adding another
2 cycles each time.  These factors together add around 55000 extra
clock cycles. The inlining of every nfib call only saves around 22000
cycles, or perhaps 44000 cycles at most.  So right now, it seems that
the inlining of nfib is not a win!

However, consider the situation as it will be with PRS.

Without nfib inlined: Each comparison takes 1 cycle rather than 2,
and the two subtractions together take 1 cycle rather than 6.  Also,
with the elimination of the curried (-) applications, the size of the
instantiated body for the larger alternative drops from 10 applications
to 6; so the number of clock cycles to form it fall from 5 to 3.  In all,
we can expect the nfib cycle count to reduce to 21891 * 4 + 10945 * 11 =
207959, a saving of 33%.

With nfib inlined: The two (-) applications and the two (<=) applications
in nfib#1 together take just 1 cycle rather than 12, and instantation of
the nfib#1 body requires just 4 cycles rather than 8.  So we expect to
avoid 10945 * 15 = 164175 cycles of work, 49% of the original run-time,
giving an even lower final cycle count of 175123.  (That's around 17
million "nfibs per second" with a 120MHz clock.)

Conclusions
-----------

Micro-examples can be instructive.

It would be nice to have a further option for the flite-reduceron
compiler that gives brief reports on the cost of applying each combinator.
For example, it might say "in-lined", or "clock-cycles per application
(including primitives in body): min <m>, max <M>".

For all three examples, we can expect significant gains from PRS.

Even greater benefits could be obtained, in all three examples, where
PRS gives boolean results that are case subjects.  This is a common form:
it corresponds to conditionals, and it also results from the inlining of
flite equivalents of (&&) and (||).  Could the primitive simplifying rules

False [alt#1,alt#2] ==> alt#1
True  [alt#1,alt#2] ==> alt#2

be built-in along with the PRS machinery?  And if alt#1 or alt#2 are
themselves True or False, could the effect be cascaded?

In Examples 2 and 3, instantiation of the largest (and most frequently
applied) bodies takes several clock cycles.  Even with PRS, instantiation
of nfib#1 will require 4 cycles.  And these are *micro*-examples.
Widening this bottleneck a little further could be all the more beneficial,
in terms of the fraction of run-time saved, after PRS.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo35.txt version [c80b56e025].











































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
===========================================
REDUCERON MEMO 35
Order of compilation passes: when to inline
Matthew N, 5 October 2009
===========================================

Consider the Cons-Cons case of the equalStrings function that Colin
discussed in Memo 34.

  equalStrings (Cons x xs) (Cons y ys) = con ((==) x y) (equalStrings xs ys);

The function con is boolean conjunction.

  con x y = case x of { False -> False; True -> y };

Compiling away the case expressions in con, as discussed in Memo 13,
gives:

  con x y = x [con#1,con#2] y;
  con#1 alts y = False;
  con#2 alts y = y;

In the Cons-Cons case of equalStrings, the call to con can be inlined.
The question is: should inlining be performed before case compilation,
or after, or both before and after?

If case compilation is performed first, followed by inlining, then the
body of the Cons-Cons case becomes

  (==) x y [con#1,con#2] (equalStrings xs ys);

At runtime, the expression "equalStrings xs ys" must be created on the
heap, and subsequently, if ever demanded, unwound from the heap.

Alternatively, if inlining is performed first then body of Cons-Cons
case first becomes

  case (==) x y of { False -> False ; True -> equalStrings xs ys };

and then after case elimination

  (==) x y [consCons#1, consCons#2] xs ys;

where

  consCons#1 alts xs ys = False;
  consCons#2 alts xs ys = equalStrings xs ys;

Now the expression "equalStrings xs ys" is never created on the heap.
Since it is in the spine position of a function body, it is only ever
pushed onto the stack, and even then, only whenever the first conjunct
is True.

So the order makes quite a big difference.  How does it affect
performance?  Here are some clock-tick counts on a handfull of
programs.  The inlining parameter to the F-lite compiler is "-i1",
i.e. only functions containing a maximum of one application are
inlined.

  +----------------+-----------+-----------+----------------+
  | PROGRAM        |     AFTER |    BEFORE | BEFORE & AFTER |
  +----------------+-----------+-----------+----------------+
  | EqualStrings   |       471 |       433 |            394 |
  | PermSort       |  17860578 |  20529552 |       16715749 |
  | Queens         |  48337765 |  43739670 |       41241925 |
  | While          |  46327136 |  50576148 |       46327136 |
  | Cichelli       |  55795881 |  61663685 |       55795880 |
  | Mate           | 856693903 | 849056292 |      798054513 |
  | KnuthBendix    |  19619755 |  22907202 |       19061795 |
  +----------------+-----------+-----------+----------------+

Notice that two cycles per recursive step are saved in the
equalStrings functon when inlining before & after: one is saved when
creating the recursive application, and the other when unwinding it.

Remarks:

  * Are there any programs where inlining before case compilation is a
    bad idea?  (Sometimes, one large function body might be preferable
    to serveral small ones.)

  * Is there any added benefit in increasing the "-i" control parameter?

  * The control parameters for inlining might usefully be different
    depending on whether it is done before or after case compilation.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo36.txt version [01cf81a9a3].





































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
===========================================================
REDUCERON MEMO 36
Case of known construction -- at compile-time and run-time
Colin Runciman, 7 October 2009
===========================================================

Let's go straight into a fairly meaty example.  The Mate chess end-game
program includes the following function to test whether player c's king
on board bd is in check from an opposing force f on square (x,y).

kingInCheckFrom c bd (Pair f (Pair x y)) =
  case kingSquare c bd of {
  Pair xk yk -> 
    case f of {
		King   -> con ((<=) (abs ((-) x xk)) 1)
                  ((<=) (abs ((-) y yk)) 1) ;
		Queen  -> dis (kingInCheckFrom c bd (Pair Rook   (Pair x y)))
                  (kingInCheckFrom c bd (Pair Bishop (Pair x y))) ;
		Rook   -> dis (con ((==) x xk)
                       (emptyAtAll bd (filePath xk y yk)))
                  (con ((==) y yk)
                       (emptyAtAll bd (rankPath yk x xk))) ;
		Bishop -> dis (con ((==) ((-) x y) ((-) xk yk))
                       (emptyAtAll bd (diagPath (-) ((-) xk yk) x xk)))
                  (con ((==) ((+) x y) ((+) xk yk))
                       (emptyAtAll bd (diagPath (+) ((+) xk yk) x xk))) ;
		Knight -> dis (con ((==) (abs ((-) x xk)) 2)
                       ((==) (abs ((-) y yk)) 1))
                  (con ((==) (abs ((-) x xk)) 1)
                       ((==) (abs ((-) y yk)) 2)) ;
		Pawn   -> con ((==) (abs ((-) x xk)) 1)
			            ((==) yk (onFor c y )) ;

After compilation, with -i1 as usual, here is how this function is
currently encoded at the reduceron combinator level (with kingInChekFrom
abbreviated to kICF and arguments elided on left-hand sides):

kICF   v0 ... v2  =  v2 [kICF#9] v0 v1;
kICF#1 v0 ... v6  =  (==) ((-) v1 v2) ((-) v3 v4) [con#1,con#2]
                       (v5 [emptyAtAll#1]
                         (diagPath (-) ((-) v3 v4) v1 v3)) [dis#1,dis#2]
                           ((==) ((+) v1 v2) ((+) v3 v4) [con#1,con#2]
                             (v5 [emptyAtAll#1]
                               (diagPath (+) ((+) v3 v4) v1 v3)));
kICF#2 v0 ... v6  =  let { v7 = (-) v1 v3; v8 = (-) v2 v4 } in
                     (<=) ((<=) 0 v7 [abs#1,abs#2] v7) 1 [con#1,con#2]
                       ((<=) ((<=) 0 v8 [abs#1,abs#2] v8) 1);
kICF#3 v0 ... v6  =  let { v7 = (-) v1 v3; v8 = (-) v2 v4;
                           v9 = (-) v1 v3; v10 = (-) v2 v4 } in
                     (==) ((<=) 0 v7 [abs#1,abs#2] v7) 2 [con#1,con#2]
                       ((==) ((<=) 0 v8 [abs#1,abs#2] v8) 1) [dis#1,dis#2]
                         ((==) ((<=) 0 v9 [abs#1,abs#2] v9) 1 [con#1,con#2]
                           ((==) ((<=) 0 v10 [abs#1,abs#2] v10) 2));
kICF#4 v0 ... v6  =  let { v7 = (-) v1 v3 } in
                     (==) ((<=) 0 v7 [abs#1,abs#2] v7) 1 [con#1,con#2]
                       ((==) v4 (v6 [onFor#1,onFor#2] v2));
kICF#5 v0 ... v6  =  Pair Rook (Pair v1 v2) [kICF#9]
                       v6 v5 [dis#1,dis#2]
                         (Pair Bishop (Pair v1 v2) [kICF#9] v6 v5);
kICF#6 v0 ... v6  =  (==) v1 v3 [con#1,con#2]
                       (v5 [emptyAtAll#1] (filePath v3 v2 v4)) [dis#1,dis#2]
                         ((==) v2 v4 [con#1,con#2]
                           (v5 [emptyAtAll#1] (rankPath v4 v1 v3)));
kICF#7 v0 ... v7  =  v3 [kICF#1,kICF#2,kICF#3, kICF#4,kICF#5,kICF#6]
                       v4 v5 v0 v1 v6 v7;
kICF#8 v0 ... v5  =  kingSquare v3 v4 [kICF#7] v5 v0 v1 v4 v3;
kICF#9 v0 ... v4  =  v1 [kICF#8] v3 v4 v0;

I shall use references of the form #n to refer to the auxiliary combinators.

At Compile-time
---------------

In #5 we have, in the now-church-encoded Pair applications, two instances
of a case with a known construction as subject:

kICF#5 v0 ... v6  =  Pair Rook (Pair v1 v2) [kICF#9]
                       v6 v5 [dis#1,dis#2]
                         (Pair Bishop (Pair v1 v2) [kICF#9] v6 v5);

Considering first the Pair Rook application, we can apply the sole
alternative

kICF#5 v0 ... v6  =  kICF#9 Rook (Pair v1 v2) [*]
                       v6 v5 [dis#1,dis#2]
                         (Pair Bishop (Pair v1 v2) [kICF#9] v6 v5);

where [*] represents an unused look-up table argument. Now in-lining #9:

kICF#5 v0 ... v6  =  (Pair v1 v2) [kICF#8]
                       v6 v5 Rook [dis#1,dis#2]
                         (Pair Bishop (Pair v1 v2) [kICF#9] v6 v5);

The same situation again!  We apply the sole alternative

kICF#5 v0 ... v6  =  kICF#8 v1 v2 [*] v6 v5 Rook [dis#1,dis#2]
                       (Pair Bishop (Pair v1 v2) [kICF#9] v6 v5);

and then in-line it:

kICF#5 v0 ... v6  =  kingSquare v6 v5 [kICF#7]
                       Rook v1 v2 v5 v6 [dis#1,dis#2]
                         (Pair Bishop (Pair v1 v2) [kICF#9] v6 v5);

The kingSquare function is recursive, so it is not helpful to in-line
its application.  But the Pair Bishop application can be handled in the
same way as Pair Rook.  We end up with:

kICF#5 v0 ... v6  =  kingSquare v6 v5 [kICF#7]
                       Rook v1 v2 v5 v6 [dis#1,dis#2]
                         (kingSquare v6 v5 [kICF#7]
                            Bishop v1 v2 v5 v6) ;

We cannot apply the sole-alternative principle any further, because
we don't yet see the Pair in either case, and kingSquare might have
an undefined result.  But we have saved 5-10 clock cycles for each
application of #5.

These sorts of applications with paired arguments explicit at compile-time
may not be that frequent, but they do occur -- eg. the KnuthBendix
program has several.  And they might occur more with more aggressive
in-lining or other transformations.

At Run-time
-----------

At run-time we can hope for big improvements from PRS for the kingInCheck
function, particularly if functions such as 'abs' become primitives too.
Look at combinator #3 for example:

kICF#3 v0 ... v6  =  let { v7 = (-) v1 v3; v8 = (-) v2 v4;
                           v9 = (-) v1 v3; v10 = (-) v2 v4 } in
                     (==) ((<=) 0 v7 [abs#1,abs#2] v7) 2 [con#1,con#2]
                       ((==) ((<=) 0 v8 [abs#1,abs#2] v8) 1) [dis#1,dis#2]
                         ((==) ((<=) 0 v9 [abs#1,abs#2] v9) 1 [con#1,con#2]
                           ((==) ((<=) 0 v10 [abs#1,abs#2] v10) 2));

Assuming that

* v1 .. v4 are already evaluated (as they almost always will be under PRS)

* 'abs' is included among primitives eligible for PRS

* PRS cascades, with 4 (-) applications in a first wave, 4 'abs'
  applications in a second and 4 (==) comparisons in a third (optimistic?)

here's a typical run-time instantiation of a #3 application.

                                 True [con#1,con#2]
                                   False [dis#1,dis#2]
                                     (False [con#1,con#2]
                                       True)

Once again, we have two instances of cases with known constructors in
encoded form, but now at run-time.  Is there some way to detect them
on-the-fly during instantiation?

If so, then the instantiated body becomes still smaller and
computationally cheaper:

                                 con#2 [*]
                                   False [dis#1,dis#2]
                                     con#1 [*] True

The combinators for the (&&) and (||) alternatives used here are as follows.
 
con#1 v0 v1 = False;
con#2 v0 v1 = v1;

dis#1 v0 v1 = v1;
dis#2 v0 v1 = True;

Is there any way that these could themselves be recognised as primitive,
and their applications reduced at little cost, by analogy with the PRS
scheme?  If so, and all three ingredients can be combined -- (1) PRS,
(2) application of known-case alternatives, and (3) primitive logical
alternatives -- the gains for this example would be immense!

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo37.txt version [025af09819].























































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
================================================
REDUCERON MEMO 37
Determining the number of clock cycles needed to
instantiate a combinator body.
Version 2
Colin Runciman, 9 October 2009
================================================

Memos 34 and 36 made assumptions about the cost of instantiating
combinator bodies.  Subsequent white-board discussion with Matt has
corrected some assumptions that were wrong or imprecise.

This memo gives a simple way to determine the number of clock cycles
per instantiation of a given combinator body, which should be accurate
for the current implementation.

Case-count, length and weight of an application
-----------------------------------------------

The case-count of an application e1 ... en is the number of expressions
ei that are case-alternative tables.

The length of a application e1 ... en is n minus the case-count of
the application if the application is spinal, or n if the application
is interior (non-spinal).

The weight of an application of a primitive binary function, with
neither of the first two arguments constant, is 2. The weight of all
other applications is 1.

Parameters
----------

There are four key parameters, corresponding to the Flite -r compile-time
values, which together limit the size and complexity of expressions that
can be instantiated in a single cycle:

SLMAX = maximum spinal application length
ILMAX = maximum interior (non-spinal) application length
CTMAX = maximum number of case-alternative tables
APMAX = maximum number of applications

The values in these parameters in the current Reduceron implementation
are SLMAX = 6, ILMAX = 4, CTMAX = 1, APMAX = 2.

The Method
----------

(1) Consider the spinal application of the combinator body.  If the
    application length exceeds SLMAX or the case-count exceeds CTMAX,
    bracket the maximal partial application of length at most ILMAX and
    case-count at most CTMAX, and proceed from (1).

(2) Consider each non-spinal application.  If any length exceeds IMAX or
    any case-count exceeds CTMAX, in each such case bracket the maximal
    partial application of length at most ILMAX and case-count at most
    CTMAX and proceed from (2).

(3) Let WSUM be the sum of the weights of all applications in the body.
    Clock cycles per instantiation = ceiling (WSUM / APMAX).

Examples
--------

Consider two of the intermediate-stage combinators for the kingInCheckFrom
function, as in Memo 36.

kICF#5 v0 ... v6  =  Pair Rook (Pair v1 v2) [kICF#9]
                       v6 v5 [dis#1,dis#2]
                         (Pair Bishop (Pair v1 v2) [kICF#9] v6 v5);

kICF#6 v0 ... v6  =  (==) v1 v3 [con#1,con#2]
                       (v5 [emptyAtAll#1] (filePath v3 v2 v4)) [dis#1,dis#2]
                         ((==) v2 v4 [con#1,con#2]
                           (v5 [emptyAtAll#1] (rankPath v4 v1 v3)));

(1) The length of the spinal application in #5 is 6, and in #6 it is 5.
But the case-count of both spinal applications is 2, exceeding CTMAX.
So we introduce brackets in each case:

kICF#5 v0 ... v6  =  (Pair Rook (Pair v1 v2) [kICF#9] v6 v5)
                       [dis#1,dis#2]
                         (Pair Bishop (Pair v1 v2) [kICF#9] v6 v5);

kICF#6 v0 ... v6  =  ((==) v1 v3 [con#1,con#2]
                       (v5 [emptyAtAll#1] (filePath v3 v2 v4)))
                       [dis#1,dis#2]
                         ((==) v2 v4 [con#1,con#2]
                           (v5 [emptyAtAll#1] (rankPath v4 v1 v3)));

Now the spinal length is 2 in each case, and the spinal case-count is 1.

(2) In #5 both Pair applications have length 6, exceeding ILMAX, so
brackets must be introduced.  In #6 both (==) applications have length
5, exceeding ILMAX, so more brackets are needed.  In both #5 and #6, all other
application lengths are no more than ILMAX, and there is no problem
with case-counts.

kICF#5 v0 ... v6  =  ((Pair Rook (Pair v1 v2) [kICF#9]) v6 v5)
                       [dis#1,dis#2]
                         ((Pair Bishop (Pair v1 v2) [kICF#9]) v6 v5);

kICF#6 v0 ... v6  =  (((==) v1 v3 [con#1,con#2])
                       (v5 [emptyAtAll#1] (filePath v3 v2 v4)))
                       [dis#1,dis#2]
                         (((==) v2 v4 [con#1,con#2])
                           (v5 [emptyAtAll#1] (rankPath v4 v1 v3)));

(3) The sum of the application weights in #5 is 7, so 4 cycles are needed
to instantiate it.  The sum of the application weights in #6 is 11,
so 6 cycles are needed to instantiate it.

Let expressions
---------------

The instantiation costs for applications defined in let bodies need only
be paid once, of course.  In applications where the let variable is used,
the costs are the same as for argument variables.

Impact of PRS
-------------

These se instantiation costs will be unaltered by the PRS scheme Matt
is currently implementing.  The primitive applications with simple
arguments are *candidates* for evaluation.  Each is only evaluated to
its result during instantiation if it is indeed found to be a redex.
So the instantiation timing must allow for instances of candidate
primitive applications that are non-redexes.

Conclusions
-----------

It is not so hard to formulate an exact clock-count for instantiation,
based on a combinatory form that is still quite readable for programmers.

It would be helpful if the Flite options -d and -r could be used together.
After each combinator dumped by -d there could be a report of the number
of clock cycles per instantiation, assuming the -r parameter values.

Currently the most severe of the *MAX limiting parameter values is
CTMAX = 1.  In both examples, the spinal application had to be split
solely because it included two case-table arguments.  In discussion,
Matt has said there should be no difficulty in raising CTMAX to 2.

The parameter APMAX = 2 is also a significant limiting factor. As Matt
showed in Memo 25, a significant speed-up could be expected if APMAX were
raised from 2 to 3. This is hardly surprising given the role of APMAX as
divisor in step (3): with APMAX = 3 the clock-cycles per instantiation
fall from 4 to 3 for #5 and from 6 to 4 for #6.  With the introduction
of PRS the case for increasing APMAX is still stronger: the number of
primitive-redex evaluations per instantiation cycle is also limited
by APMAX.  If the redesign at the Reduceron level would take a bit of
work, would it be worth doubling APMAX 4 to get a larger pay-off from
the effort, or would that risk lengthening the critical path?

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo38.txt version [ac5c5d41d3].

















































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
===================================
REDUCERON MEMO 38
Benefits of a primitive-value stack
Matthew N, 14 October 2009
===================================

Currently the Reduceron compiler translates primitive applications
according to the rule
  
  p n m     -->     m (n p)                (1)

which forces evaluation of integer arguments n and m before applying
primitive function p.  This is assuming the reduction rule

  i e       -->     e i                    (2)

for any fully evaluated integer i and arbitrary expression e.

No extra stack is needed to store the evaluated arguments to a
primitive.  However, it would be cheap to introduce such a
primitive-value stack into the Reduceron.  Then primitive applications
could be translated by the rule

  p n m     -->     m n p                  (3)

and the reduction rule would now be

  i e       -->     e                      (4)

with the *side-effect* of pushing i onto the primitive-value stack.
By the time p reaches the top of the evaluation stack, its
fully-evaluated arguments will be avilable on the primitive-value
stack.

The main consequence of this new approach is: expressions are smaller
and shallower.  The RHS of rule (1) contains 2 applications and has
depth 2; the RHS of rule (3) contains 1 application and has depth 1.
Smaller and shallower expressions are faster to instantiate and result
in less unwinding during evaluation.

EXAMPLE 1. Consider the following case where a primitive application
itself contains a primitive application.

  (+) ((-) a b) ((-) c d)

This could be compiled to reverse polish notation:

  d c (-) b a (-) (+)

EXAMPLE 2.  More generally, the expression

  (+) (f x) (f y)

could be compiled to:

  f y f x (+)

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo39.txt version [4d4c9e5f7f].































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
===================================
REDUCERON MEMO 39
The Force-and-Rebind transformation
Matthew N, 20 October 2009
===================================

Consider reduction of the fromTo function with primitive redex
speculation (PRS) enabled.

  fromTo m n =
    case (<=) m n of {
      False -> Nil;
      True -> Cons m (fromTo ((+) m 1) n);
    };

If fromTo is passed literal integers as arguments, e.g. "fromTo 1 10",
then both the comparison and the addition in the body of fromTo will
be detected as primitive redexes and reduced on-the-fly.
Consequently, the arguments to the recursive call will also be literal
integers, and so the comparison and addition will be primitive redexes
on every iteration.

Now suppose that fromTo is passed unevaluated expressions, e.g.
"fromTo (length xs) (length ys)".  The first time the comparison is
encountered, it will not be detected as a primitive redex, and "length
xs" and "length ys" will be forced via the default machinery for
dealing with strict primitives.  One might hope that by the time the
second case alternative is taken, since the values of m and n are then
known, the addition will be detected as a primitive redex.  However,
this is not the case: although m and n are fully evaluated, they are
represented as pointers to literal integers on the heap - they are
not themselves literal integers.  Sadly, on every iteration, no
primitive redexes will be detected.

It is easy to make fromTo more robust by defining it in a
worker-wrapper fashion.

  fromTo m n = n (m fromToWork);

  fromToWork m n =
    case (<=) m n of {
      False -> Nil;
      True -> Cons m (fromToWork ((+) m 1) n);
    };

The wrapper forces evaluation of m and n and then calls the worker.
In doing so, m and n are rebound to literal, unboxed values.  This can
be done because fromToWork is clearly strict in both arguments.

Below I outline a transformation - called Force-and-Rebind - for
automatically introducing these worker-wrapper style functions.

The transformation
------------------

STEP 1. Look for functions of the form

  f ... = ... case p e1 e2 of { False -> alt1 ; True -> alt2 } ...

where p is a primitive function strict in both arguments returning a
boolean, and alt1 or alt2 can lead to another call of f.

STEP 2. Take all the strictly-needed variables of type integer
referred to in e1 or e2 that also referred to in alt1 or alt2.  Call
them v1..vn.  Proceed only if v1..vn is non-empty.

STEP 3. Abstract the expression of interest into a function h:

  f ... = ... h v1..vn w1..wn ...

  h v1..vn w1..wn = case p e1 e2 of { False -> alt1 ; True -> alt2 };

where w1..wn are the free variables, other than v1..vn, in the
case expression.

STEP 4. Create function f' like f but which forces evaluation of
v1..vn before applying h:

  f' ... = ... vn (..(v1 h)) w1..wn ...

STEP 5. Now calls to f can be replaced by calls to f'.  However, as
primed functions are meant to be wrappers, only calls to f which occur
in a function that is NOT call-reachable from f should be replaced.

Results
-------

Saving due to Force-and-Rebind transformation:

  +-------------+------------+
  | PROGRAM     |   % SAVING |
  +-------------+------------+
  | Adjoxo      |       -1.5 |
  | Fib         |          0 |
  | OrdList     |          0 |
  | Queens      |       -7.9 |
  | Queens2     |          0 |
  | Cichelli    |       12.8 |
  | KnuthBendix |       -2.1 |
  | Parts       |          0 |
  | SumPuz      |       28.6 |
  | Clausify    |       -4.1 |
  | MSS         |          0 |
  | PermSort    |       -4.7 |
  | Taut        |       -5.8 |
  | CountDown   |       22.3 |
  | Mate        |        0.3 |
  | While       |       -5.0 |
  +-------------+------------+
  | AVERAGE     |          2 |
  +-------------+------------+

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo4.txt version [19da93a164].





























































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
======================
REDUCERON MEMO 4
Chunky lists
Matthew N, 17 November
======================

In Memo 1, I considered the possibility of a "chunky list", that is a
list that can be constructed from (say) four heads as well as just
from one.

  data List a = Nil | Cons a (List a) | Cons4 a a a a (List a)

Let's take a few standard functions over lists: a consumer
(sum), a processor (map), and a producer (replicate).

  sumL :: List Int -> Int
  sumL Nil = 0
  sumL (Cons x xs) = x + sumL xs

  mapL f Nil = Nil
  mapL f (Cons x xs) = Cons (f x) (mapL f xs)

  replicateL :: Int -> a -> List a
  replicateL n a = if n <= 0 then Nil else Cons a (replicateL (n-1) a)

Currently these functions ignore the Cons4 constructor.  The intuition
behind Cons4 is that is satisfies the following law.

  Cons4 x0 x1 x2 x3 xs = Cons x0 (Cons x1 (Cons x2 (Cons x3 xs)))   (1)

Now we want to synthesise a right-hand-side for

  sumL (Cons4 x0 x1 x2 x3 xs)

which by compile-time evaluation proceeds as follows

    sumL (Cons4 x0 x1 x2 x3 xs)
  = sumL (Cons x0 (Cons x1 (Cons x2 (Cons x3 xs))))
  = x0 + sumL (Cons x1 (Cons x2 (Cons x3 xs)))
    ...
  = x0 + x1 + x2 + sumL xs
  
We can take a similar attack on map.

    mapL f (Cons4 x0 x1 x2 x3 xs)
  = mapL f (Cons x0 (Cons x1 (Cons x2 (Cons x3 xs))))
  = Cons (f x0) (mapL f (Cons x1 (Cons x2 (Cons x3 xs))))
    ...
  = Cons (f x0) (Cons (f x1) (Cons (f x2) (Cons (f x3) (mapL f xs))))

By equation (1) we get

  Cons4 (f x0) (f x1) (f x2) (f x3) (mapL f xs)

In replicate, we can inline the recursive call three times.

  if n <= 0 then Nil else Cons a
    (if n-1 <= 0 then Nil else Cons a
      (if n-2 <= 0 then Nil else Cons a
        (if n-3 <= 0 then Nil else Cons a (replicateL (n-4) a))))

If the condition cannot evaluate to _|_ then a condtional can be
rewritten using the distribution law

  f (if cond then e0 else e1) = if cond then f e0 else f e1

Since n is evaluated in the outermost condition, the distribution law
can be applied in both its branches.

  if n <= 0 then Nil else
    if n <= 1 then Cons a Nil else
      if n <= 2 then Cons a (Cons a Nil) else
        if n <= 3 then Cons a (Cons a (Cons a Nil)) else
          Cons a (Cons a (Cons a (Cons a (replicateL (n-4) a))))

By equation (1) we get

    if n <= 0 then Nil else
      if n <= 1 then Cons a Nil else
        if n <= 2 then Cons a (Cons a Nil) else
          if n <= 3 then Cons a (Cons a (Cons a Nil)) else
            Cons4 a a a a (replicateL (n-4) a)

Experimental results
--------------------

The number of clock-cycles taken to compute

  sumL (mapL sumL (replicateL 100 (replicateL 100 0)))

using the original definitions is 269043 and using the wide
definitions is 170992.  That's a 36% improvement.

Conclusion
----------

Without sacrificing laziness we can transform programs to use chunky
lists, giving a nice performance improvement (at least in one
artificial example, which could probably be better improved using
fusion!)

The extent to which the transformation can be reliably done
automatically remains unknown.

Discussion (added 18 November)
------------------------------

The chunky benchmark would be even more efficient if you transformed
replicateL to test for the chunky case *first*, and in other cases to
use a binary chop:

   if n > 3 then Cons4 ...
   else if n > 1 then
           if n > 2 then ...
           else ...
        else
           if n > 0 then ...
           else ...

As you say the extent to which listy functions can be automatically
extended to chunky functions is uncertain.  So there is the likely
problem of switching to and fro between representations.  The overhead
of expanding Cons4 for the benefit of non-chunky functions may not be
too bad.  But there is the much more awkward issue of whether it is
possible to chunkify long lists those functions may produce in
standard Cons-Nil form.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo40.txt version [edd61987d4].

























































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
===================================================
REDUCERON MEMO 40
Forcing arguments to primitive functions, revisited
Matthew N, 21 October 2009
===================================================

This is an alternative proposal to Memo 38.

Quick recap
-----------

Currently the Reduceron compiler translates primitive applications
by the rule

  p n m     -->     m (n p)                (1)

which forces evaluation of integer arguments n and m before applying
primitive function p.  This is assuming the reduction rule

  i e       -->     e i                    (2)

for any fully evaluated integer i and arbitrary expression e.

Mild generalisation
-------------------

For every binary primitive function p, let us introduce a new
primitive swap:p, a version of p that expects its arguments flipped.

Now translate binary primitive applications by the rule

  p n m          -->     n p m             (3)

and introduce the reduction rules

  i p e          -->     e swap:p i        (4)

  i p j          -->     p i j             (5)

for any fully evaluated integers i and j, unevaluated expression e,
and primitive function p.

Note that the evaluator can actually perform the primitive application
in (5) rather than merely instantiating it.

Symmetry
--------

For symmetry, we can add the rule:

  i swap:p e     -->     e p i             (6)

for evaluated integer i, and unevaluated expression e.

Now transformation rule (3) could just as sensibly be:

  p n m          -->     m swap:p n        (8)

In the interest of efficiency, choice of (3) and (8) could be informed
by compile-time knowledge of whether n or m is expected to be already
evaluated.

Performance
-----------

Consider the number of clock cycles to compute "(+) a b" using the
swap-based approach to force arguments:

  * "(+) a b" is translated to "b (a (+))"
  * Application of rule (2) is required after "b" is evaluated.
  * One unwind is required to fetch argument "a (+)" from heap.
  * Application of rule (2) is required after "a" is evaluated.
  * A primitive reduction is required.
  * That's 4 cycles in total.

Using the approach described in Memo 38:

  * "(+) a b" is translated to "b a (+)"
  * After "b" is evaluated, it must be pushed onto the PV stack.
  * After "a" is evaluated, it must be pushed onto the PV stack.
  * A primitive reduction is required.
  * That's 3 cycles in total.

Using the approach described in this memo:

  * "(+) a b" is translated to "a (+) b"
  * Application of rule (4) is required after "a" is evaluated.
  * Application of rule (5) is required after "b" is evaluated.
  * That's 2 cycles in total.

Also note that "a (+) b" comprises one application whereas "b (a (+))"
comprises two, so the former is cheaper to instantiate.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo41.pdf version [1b30916cac].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo44.txt version [41976b2ad5].





































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
==========================
REDUCERON MEMO 44
An objective look at PRS
Matthew N, 2 December 2009
==========================

In the Reduceron, an integer can be represented as a literal value
(unboxed) or as pointer to a literal value (boxed).  As discussed in
Memo 39, the boxed form prevents sucessfull PRS.  A possible
workaround is to introduce recursive function wrappers which force
strict integer arguments to their unboxed forms, allowing worker
functions to enjoy successfull PRS.  This memo looks objectively at
PRS, and considers the possibility of using the results of strictness
analysis in a machine *without* PRS.

CPS-based primitives
--------------------

For every primitive function (+) which takes two integers and produces
an integer, we introduce a CPS version (+') which additionally takes a
continuation that is passed the result of the function.  For example,

  (+') 1 2 k

would be reduced to

  k 3.

Example 1
---------

One of the programs on which PRS has a large impact is MSS.  MSS
contains the following function.

  sum xs = sumAcc 0 xs;

  sumAcc acc Nil = acc;
  sumAcc acc (Cons x xs) = sumAcc ((+) x acc) xs;

By observing that sumAcc is strict in acc, we can avoid instantiating
"(+) x acc" on the heap by transforming sumAcc to:

  sumAcc acc Nil = acc;
  sumAcc acc (Cons x xs) = (+') x acc sumAcc xs;

The end result appears to be a version of sum that performs as well
without PRS as it does with PRS.  However, the recursive call to
sumAcc can no longer be inlined, making it more costly than the PRS
version.

Example 2
---------

Another function used in the MSS program is:

  fromTo n m = case (<=) n m of {
                 True -> Cons n (fromTo ((+) n 1) m);
                 False -> Nil;
               };

A strictness analyser can easily spot that fromTo is strict in both
arguments.  We could therefore avoid instantiation of "(+) n 1" on the
heap by evaluating it before the recursive call to fromTo.  This could
be achieved by transforming fromTo to

  fromTo n m = case (<=) n m of {
                 True -> Cons n ((+') n 1 fromTo m);
                 False -> Nil;
               };

The end result is a version of fromTo that seems to perform as well
without PRS as it does with PRS.  However, once again the recursive
call to fromTo cannot be inlined.  Furthermore, notice that the 2nd
argument to Cons is a 5-node application which may have to be split in
two.

Closing thought
---------------

A robust PRS implementation seems to require strictness information.
Such information is useful in a Reduceron implementation without PRS,
however the PRS mechanism still brings some benifits.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo45.txt version [8887d45483].











































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
==========================================
REDUCERON MEMO 45 (IN PROGRESS)
Hardware support for exploiting Static PRS
Matthew N, 21 January 2009
==========================================

This memo considers an alternative PRS design to that presented in
Memo 31.  It has one major difference: it assumes that primitive
redexes are identified at compile-time, not run-time.  The compilation
is more complex (see Memo 50), but the hardware simpler and more
efficient.

Syntax
------

To support PRS, we introduce a new kind of atom:

  data Atom = ... | PREDEX Op Arg Arg

  data Arg = Lit Int | Index Int

A primitive redex contains a primitive operator and two arguments.  An
argument is either a stack index or an integer literal.  

Since an atom is currently represented by 18 bits, and a primitive
redex may contain two integer literals, it is clearly the case that
the integer literals in a primitive redex must have a limited range.
For example, an expression such as "(+) x 5" could be implemented as a
primitive redex whereas "(+) x 500" could not.  No big loss.

Note that primitive redex atoms can only appear in code memory, not on
the stack or heap.

Semantics
---------

We must now extend the instantiation function to deal with primitive
redexes.  To instantiate a primitive redex, we simply evaluate it.
Currently, around ten atoms can be instantiated per clock cycle on the
Reduceron.  Consequently, up to ten primitive redexes can be evaluated
per clock cycle.  And since we no longer have to account for candidate
primitive redexes that need to be instantiated on the heap, reducing
primitive redexes need not incur a clock-cycle overhead.

Identifying primitive redexes
-----------------------------

See Memo 50.

Let-bound primitive redexes
---------------------------

Suppose we have a primitive redex a+b bound in a let expression:

  let x = a+b in f x (g x)

Ideally, we would like to remove the variable x as it will be
implemented as a pointer to a value on the heap which will be costly
to dereference.  To do this, we can simply inline it, giving:

  f (a+b) (g (a+b))

Although we duplicated an expression, the two occurences will be
computed in parallel.  So the first arguments passed to f and g can be
made unboxed integers, at no run-time cost.

Nested primitive redexes
------------------------

Suppose we have the expression

  f ((a+b)+c)

where a, b, and c are known to be unboxed integers.  In such a case,
only a+b can be made a primitive redex.  We can however transform the
above expression to

  g (a+b)

where

  g x c = f (x+c)

Now both additions are primitive redexes, although they will be
computed one after the other.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo46.txt version [e07f09130f].





































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
===============================
REDUCERON MEMO 46 (IN PROGRESS)
Checking for a fixed-point
using needed narrowing
Matthew N, 8 December 2009
===============================

In Chapter 22 of "The Implementation of Functional Programming
Languages", Peyton Jones outlines a basic strictness analyser which
uses abstract interpretation.  In abstract interpretation, every
function f in a program is lifted to an abstract version f#

  f# x1 ... xn = e

where e is an expression whose type is the two-point domain {0,1};
0 denotes "does not terminate" and 1 denotes "may terminate".
Strictness of function f in parameter xi can be determined by
evaluating f# with 0 passed in for xi and 1 passed in for all other
parameters.

A problem arises when f is a recursive function

  f x1 ... xn = ... f ...

because then f# is also recursive.  If the compiler wishes to
determine the strictness of f, it may never terminate!

The solution is to pick a non-recursive function f#i from the set

  f#0 x1 ... xn = 0
  f#1 x1 ... xn = ... f#0 ...
  f#2 x1 ... xn = ... f#1 ...
  f#3 x1 ... xn = ... f#2 ...
  ...

where

  f#i x1 ... xn = f#i+1 x1 ... xn

for any x1 ... xn.  The function f#i is called the fixed-point of f#.
It can be computed in finite time since x1 ... xn have finite domains
and can be enumerated exhaustively.

Computable yes, but the fixed-point check is exponentially expensive in
the number of function arguments, and according to Hudak this is
necessarily the case.  On the bright side, Peyton Jones reports that
rapid convergence is typical, and that heuristics can be used to
handle common cases very efficiently.  Still, the check feels very
expensive.

I suspect there is a great deal to be gained by implementing the
abstract interpeter using the needed-narrowing evaluation strategy, as
opposed to lazy evaluation or eager evaluation.  That is, to determine
if f#i is a fixed point, simply evaluatate the expression

  f#i x1 ... xn  /=  f#i+1 x1 ... xn

by needed narrowing where x1 ... xn are unbound logical variables.  If
no solution is found, f#i is a fixed-point.  Needed narrowing should
be very effective at pruning the search space because the bodies of
f#i and f#i+1 are basically large compositions of conjunctions and
disjunctions over values in the abstract domain {0,1}.  As such, they
are likely to be lazy - often yielding results without demanding all
of the arguments.

To be continued...

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo47.txt version [ca145f44ba].

















































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
=======================================
REDUCERON MEMO 47
Descriptions of several F-lite programs
Colin R, Matthew N
4 May 2010
=======================================

Line counts are for F-lite sources including all required auxiliary
functions.

Adjoxo (106 lines)

An adjudicator for the game noughts and crosses, aka. tic-tac-toe.
The input is a game position, and the output is one of the three
values Win, Draw or Loss indicating the outcome with best play for
each of the players whose turn it might be.  The method is the usual
minimax recursive evaluation of completed game trees.

Braun (51 lines)

A Braun tree is a balanced binary tree offering an efficient yet
simple implementation of flexible arrays.  The program tests the
property that converting a list to a Braun tree and back again is
equivalent to the identity function.

Cichelli (200 lines)

Finds a perfect hash function for Haskell keywords.  It uses a
backtracking search to find an assignment of natural-number values to
each letter that starts or ends a keyword such that hash values for
keywords, computed as start-value + end-value + length, are unique and
occupy a small integer range without gaps.

Clausify (131 lines)

Puts propositional formulae in clausal form using a multi-stage
transformation of formula-trees.  Almost a purely symbolic application,
with hardly any arithmetic.

Fib (10 lines)

Computes the Nth number in the fibonacci sequence using a simple but
naive doubly-recursive function definition.  A purely arithmetic
program involving no data structures at all.

Knuthbendix (533 lines)

The Knuth-Bendix completion method tries to derive a convergent
term-rewriting system for a given equational theory and
symbol-weighting scheme.  It is a typical symbolic computing
application from computer algebra.  The example input used in the
program gives group-theoretic axioms from which ten rewriting rules
are derived.

Mate (393 lines)

Solves chess end-game problems of the form "P to move and mate in N".
The method is brute-force search in an explicit AND-OR game tree
developing the given position to depth 2N-1.  Boards are represented
by a square-piece assocation list for each player, where squares are
coded as rank-file numeric pairs, so there is a fair amount of
primitive arithmetic and comparison.

MSS (47 lines)

Computes the maximum segment sum of a list of integers.  Works by
dividing the input list into all sub-lists, computing the sum of each,
and returning the maximum.

OrdList (46 lines)

Checks the property that insertion of a number into an ordered list of
numbers results in a list that is still ordered.  Numbers are
represented as peano numerals, so this is a purely symbolic program.

Parts (54 lines)

Computes a celebrated number-theoretic function, the number of
partitions of n, where a partition is a bag of positive integers that
sum to n.  There is a sophisticated closed formula for this number,
but the method here is to list and count partitions explicitly.

PermSort (39 lines)

Enumerates the permutations of a list of numbers, and returns the
first ordered permutation.

Queens (47 lines)

Solves a programming problem made famous by Wirth: place N queens on
an NxN chess board so that no two of queens occupy a common rank, file
or diagonal.  The solution involves backtracking, list processing and
an inner recursive loop that testing the safety of each candidate
position for a new queen by primitive arithmetic comparisons with the
coded positions of queens already in place.

Queens2 (62 lines)

A purely symbolic solution to the N-queens problem.  Represents the
board as a list of lists.  Places a queen on one row at a time,
maintaining a grid of threatened squares, and backtracks if a queen
cannot be placed.

Sudoku (209 lines)

A Sudoku solver due to Richard Bird.  Fills the blank cells on a
Sudoku board with valid digits, pruning many possible choices that
cannot possibly lead to a solution.

Taut (95 lines)

A tautology checking program based on an example from Hutton's book.
The method is a brute-force evaluation for all possible boolean
assignments to variables.

While (96 lines)

A structural operational semantics of Nielson and Nielson's While
language applied to a program that computes the number of divisors of
given integer.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo48.txt version [142064864d].





































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
======================
REDUCERON MEMO 48
Primitive redexes
in the spine position
Matthew N, 11 May 2010
======================

Consider the combinator for the recursive case of the elem function
which contains a primitive application in the spine position.

  elem#3 v0 v1 v2 v3 = (==) v3 v0 [elem#1,elem#2] v3 v1;

Should the expression (==) v3 v0 be marked as a PRS candidate or not?

If YES
------

If v3 and v0 are INTs at runtime then 

  * 1 cycle is consumed doing PRS, and
  * 1 cycle is consumed pushing the spine of elem#3

If v3 and v0 are not INTs at runtime then

  * 1 cycle is consumed doing PRS,
  * 1 cycle is consumed pushing the spine of elem#3,
  * 1 cycle is consumed fetching the failed PRS candidate from heap, and
  * 1 or 2 cycles are consumed reducing PRS candidate, depending
    on how many of the arguments need to be forced.

If NO
-----

If v3 and v0 are INTs at runtime then

  * 1 cycle is consumed pushing the body of elem#3
  * 1 cycle is consumed reducing (==) v3 v0

If v3 and v0 are not INTs at runtime then

  * 1 cycle is consumed pushing the body of elem#3
  * 1 or 2 cycles are consumed reducing (==) v3 v0, depending
    on how many of the arguments need to be forced.

Basic Conclusion
----------------

In general, primitive applications in the spine position should not be
marked as PRS candidates because there is no gain in doing so - in
fact, there may be a loss.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo49.txt version [72ce4640a9].















































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
=========================================================
REDUCERON MEMO 49
hf: a tool that translates (a subset of) Haskell to Flite
Colin R, 17 May 2010
=========================================================

Roughly speaking, Flite is a Haskell subset.  But the process of
hand-translating Haskell to Flite is actually quite a chore -- I speak
from personal experience translating programs like Mate and KnuthBendix!
Not only are there the inherent constraints of a subset, there are
various lexical differences too.

Could the process be automated?  A full press-the-button translator
from Haskell 98 to Flite is infeasible.  For example, the only numbers
in Flite are bounded integers, and IO operations are unavailable.

This memo describes hf tool.  It can *assist* the human translator
in their work.  It takes Haskell source as its standard input.
The standard output gives Flite equivalents of the *translatable*
top-level declarations in the input, with comments to explain why the
missing declarations could not be translated.

Example
-------

Suppose ListFuns.hs contains the following:

  duplicates :: Eq a => [a] -> [a]
  duplicates []      =  []
  duplicates (x:xs)  =
    if not (contains d x) && contains xs x then x:d else d 
    where
    d  =  duplicates xs

  prefixes :: [a] -> [[a]]
  prefixes []      =  [[]]
  prefixes (x:xs)  =  [] : map (x:) (prefixes xs)

  suffixes :: [a] -> [[a]]
  suffixes []      =  [[]]
  suffixes (x:xs)  =  (x:xs) : suffixes xs

  perms :: [a] -> [[a]]
  perms []      =  [[]]
  perms xs      =  [x:p | (x,xs') <- picks xs, p <- perms xs']

  picks :: [a] -> [(a,[a])]
  picks []      =  []
  picks (x:xs)  =  (x,xs) : [(x',x:xs') | (x',xs') <- picks xs]

We apply hf:

  $ hf < ListFuns.hs 
  duplicates Nil = Nil ;
  duplicates (Cons x xs) =
    let { d = duplicates xs ; } in
      case
        case not (contains d x) f
          { True -> contains xs x ; False -> False ; }
        of {
        True -> Cons x d ;
        False -> d ;
        } ;

  -- hf could not translate prefixes (operator section)

  suffixes Nil = Cons Nil Nil ;
  suffixes (Cons x xs) = Cons (Cons x xs) (suffixes xs) ;

  -- hf could not translate perms (non-uniform pattern matching)

  -- hf could not translate picks (list comprehension)

What is translatable?
---------------------

The prototype translates little more than the Flite-equivalent subset of
Haskell.  However, some commonly occurring forms that have no immediate
equivalent are handled.

I have avoided mangling or inventing names.  For example, as (&&) is
not recognised in F-lite, infix expressions involving && are translated
directly to case expressions, to avoid conflicts in the name-space.

The scope of translation could be significantly increased with the
introduction of a lambda lifter, and I intend to add one.  For example,
hf could then translate:

* lambda expressions
* sections
* local function definitions
* comprehensions
* monadic code

The hf translation is purely syntactic.  There is no type-checking
or type-inference, and therefore no attempt to translate type-class
machinery.  Imports and exports between modules are ignored.  These
limitations might be overcome by the use of hf in combination with
other tool.

Uses of hf
----------

As any sequence of one or more declarations is *syntactically* a valid
Haskell source, hf can be applied to *extracts* from of Haskell programs.
For example:

(1) translating function declarations for use in an Flite program;
(2) translating test-data declarations;  
(3) checking that Haskell programs still work with a translated
    version of one more function declarations in place of the originals. 

Closing lexical gaps
--------------------

Some lexical restrictions in F-lite get in the way.  Of the uses noted
above (1) is limited in part by lexical problems, (2) should be
unnecessary, and (3) can be obstructed by lexical issues.

Could we relax F-lite notation to allow at least:

* _ as a pattern
* identifiers including ' and _
* [] (:) and the () (,) (,,) (,,,) ... family as constructors
* (++) and similar as function names

It would also be helpful to allow comments in F-lite.  The comments
currently generated by hf must be addressed and removed by hand.

Download
--------

Source code available from:

  http://www.cs.york.ac.uk/fp/reduceron/hf.tar.gz

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo5.txt version [e48eb35494].





















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
===================================================
REDUCERON MEMO 5
Recursive nested-case function bodies and in-lining
Colin R, 17 November
===================================================

The technique of inlining the top-level case-analysis part of a
function into the functions for recursive alternatives seems to work
well for single-level case-analysis of a single argument.  But what
about nested cases?

Consider zipWith.  If the source definition is

  zipWith f xs ys =
    case xs of
    Nil        -> Nil
    Cons x xs' -> case ys of
                  Nil        -> Nil
                  Cons y ys' -> Cons (f x y) (zipWith f xs' ys')

the result of initial simple compilation is

  zipWith f xs ys = xs Nil (zipWith' f ys)
  zipWith' f ys x xs' = ys Nil (zipWith'' f x xs')
  zipWith'' f x xs' y ys' = Cons (f x y) (zipWith f xs' ys') .

We can in-line the zipWith application in zipWith'':

  zipWith'' f x xs' y ys' = Cons (f x y) (xs' Nil (zipWith' f ys'))

But if our goal is a single directly recursive residue of zipWith we seem
to be stuck.  We can neither in-line the partial application of zipWith'
in zipWith'' nor vice versa.

The only other improvement seems to be head reduction in zipWith'',
as in Memo 3.

  zipWith'' f x xs' y ys' n c = c (f x y) (xs' Nil (zipWith' f ys'))

Perhaps having two mutually recursive functions is inevitable when
traversing two list arguments in the continuation-cons style.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo50.txt version [7494cbaafd].



















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
========================================
REDUCERON MEMO 50
Finding and increasing PRS candidates
Colin Runciman
(post-implementation version)
23 June 2010
========================================

When PRS was first proposed, in Memo 8, the closing paragraphs suggested
that PRS candidates could be determined statically.  The idea was
first to determine by iterative analysis which arguments are always
passed as primitive values, then to use that information to identify
PRS expressions.

However, the current PRS implementation detects primitive redexes
dynamically.  A dynamic solution seemed more in keeping with the Reduceron
approach, and it avoided the complication of a compile-time analysis.

The dynamic scheme does work well in many respects, but it has drawbacks.
Expressions still have to be identified at compile-time as *possible*
PRS candidates. For such candidates, PRS register slots are reserved.
But then at run-time, some candidate primitive applications may turn
out not to be redexes after all: so these slots are wasted, and the
fall-back mechanism to construct the primitive application for normal
evaluation is quite costly.

In this memo, I pick up the thread from memo 8 and propose a static
analysis of programs to determine *guaranteed* PRS instances.
The proposed method uses an abstraction for the degree of evaluation
of algebraic data, so that PRS opportunities can often be detected even
when argument values are components of larger structures. The analysis
procedure also increases the opportunities for PRS by *cloning* functions
of which some but not all applications give rise to PRS opportunities.
However, the method set out here does not extend to higher-order programs;
I plan to address that extension in a subsequent memo.

An Example
----------

Consider the following F-lite program.

01 sumRange m n = sum (range m n) ;

03 range m n = case (<=) m n of {
04             True -> Cons m (range ((+) m 1) n) ;
05             False -> Nil ;
06             } ;

08 sum xs = sumInto 0 xs ;

10 sumInto a xs = case xs of {
11                Nil -> a ;
12                Cons y ys -> sumInto ((+) a y) ys ;
13                } ;

Assume that the program as a whole is identified with the sumRange
function, and that literal values for m and n are supplied as input.
Here's how we might reason informally about whether the primitive
applications of (<=) on line 03, and of (+) on lines 04 and 12, can be
evaluated by PRS.

- In the call of range on line 01, m and n are values.  Now if m is
  a value, then under PRS the (+) m 1 application on line 04 can be
  evaluated speculatively, providing a value result as the first argument
  in the recursive call of range. So under PRS *all* calls of range have
  value arguments. So the applications of (<=) and (+) in its body can
  both be evaluated by PRS.

- The *result* of range is a list of integers.  The only Cons application
  used to build this list, on line 04, has m as its first argument.
  As we have seen, m is always bound to a value.  So every application
  of range is a "valuable" list: if it is lazily evaluated to Cons y ys
  then y is a value and ys is valuable.

- So the argument to sum is valuable.  When sumInto is called from sum,
  a is a value and xs is valuable. Therefore, in the Cons alternative
  on line 12, both a and y are values, and (+) a y is a PRS candidate.
  That means the first argument in the recursive call of sumInto is a
  value and the second is valuable, and *every* instance of (+) a y will
  be a PRS candidate.

So it turns out that in this example *all three* primitive applications,
on lines 03, 04 and 12, can be marked for PRS evaluation.

Formulating the analysis
------------------------

Let's now formulate the analysis we have just conducted informally for
the sumRange example.

For now, I shall assume *first-order* programs with non-recursive local
definitions.So the syntax of programs is as follows:

p ::= d*             -- 1st d is the main function;

d ::= f v* = e       -- single-equation definition;

e ::= n              -- number;
    | v              -- variable;
    | p e1 e2        -- saturated primitive binary application;
    | c e*           -- saturated construction;
    | f e*           -- saturated application of named function;
    | case e of a*   -- case with exhaustive alternatives;
    | let b in e     -- local definition with no shadowing or recursion;

a ::= c v* -> e      -- alternative with flat pattern and no shadowing;

b ::= v = e          -- non-recursive local binding.

type oracles and $ annotations
------------------------------

In some places the analysis needs to know whether an expression is
definitely of some (unspecified) algebraic data type.  I assume a *type
oracle* in the form of a predicate isAlg.  (See appendix for one
way to provide this oracle, without doing full type inference.)

I shall use a $ annotation to indicate *valuable* expressions $e or
variables $v.  These $-annotations are introduced to represent the
results of analysis.

An annotation $e asserts that e is *valuable*:

(1) if e is integer then under PRS every instance of e during a run
    of the program is an integer value;
(2) if isAlg e then every instance of e when lazily evaluated to an
    outermost construction has only valuable arguments.

Valuable annotations are idempotent: $($e) = $e.

It will be convenient to have a predicate isVal that is true of a variable
or expression exactly if it is $-annotated.

When a program defines a function f, during analysis there may be
distinct variants of f with argument variables differently $-annotated.
Looking at the definitions, the variation is immediately apparent:

plus $m $n = ...

plus  m  n = ...

When referring to plus itself, where convenient the pattern of variable
annotations will be added as a suffix, with ? indicating no annotation.
So the two plus variants above may be referred to as plus$$ and plus??.

Outline of the analysis process
-------------------------------

The analysis applies to a *pool* of $-annotated variants of definitions
in the program.

The pool initially contains just one definition.  It is the first
definition in the program -- the main function -- with each argument
variable $-annotated.

The process of analysis introduces (1) additional $-annotations and (2)
additional definitions.

The analysis involves the application of three distinct "passes":

* The F pass propagates $-annotations Forwards from binding to
  applied occurrences of variables.

* The B pass propagates $-annotations Backwards from expressions
  to bindings, and also outwards from subexpressions to containing
  expressions; another output from the B pass is a set of
  requests for additional definitions to be added to the pool.

* The D pass is so-called because it is applied to strong
  components of the Dependency graph between definitions in the
  pool (in non-descending order) -- its purpose is to detect
  and exploit valuable algebraic *results* of defined functions.

As we shall see the D "pass" is a bit of a misnomer, because it may
recursively invoke the entire analysis!

The F pass
----------

The F pass is fairly straightforward.  It takes a pool of definitions
as input and returns the same definitions, but with applied occurrences
$-annotated to match binding occurrences. The auxiliary parameter V is
a set of variables to be $-annotated.

F [ds]                 = F* [ds]

F [f vs = e]           = f vs = F_V [e]
                         where
                         V = {v | v in vs, isVal v}

F_V [n]                = n
F_V [v]                = if v in V then $v else v
F_V [p e1 e2]          = p (F_V [e1]) (F_V [e2])
F_V [c es]             = c (F_V* [es])
F_V [f es]             = f (F_V* [es])
F_V [case e of as]     = case F_V[e] of F_V* [as]
F_V [let v = e1 in e2] = let v = F_V[e1] in F_V'[e2]
                         where
                         V' = V union {v | isVal v}

F_V [c vs -> e]        = c vs -> F_V' [e]
                         where
                         V' = V union {v | v in vs, isVal v}

The B pass
----------

The B pass also takes a pool of definitions as input, but its output is a
pair of things: (1) the same pool of definitions, but with $-annotations
propagated where the rules allow, and (2) a set of applications for
which no matching definition was found in the pool.

In support of (2), the pool of definitions is made an explicit parameter.
An auxiliary maybeDefnOf ds f es searches the pool of definitions ds for a
variant of function f with $-annotations matching those of the argument
list es.  The result may be Nothing or Just a matching definition.
An auxiliary reqMerge combines two lists of applications for which new
declarations are requested, to avoid duplicate requests.

B [ds]                  = ( ds' , rqs )
                          where
                          (ds',rqss) = unzip ((B_ds)* [ds])
                          rqs        = foldr reqMerge empty rqss

B_ds [f vs = e]         = ( f vs = e' , rqs )
                          where
                          (e',rqs)   = B_ds [e] 

B_ds [n]                = ( $n , [] )
B_ds [v]                = ( v , [])
B_ds [p e1 e2]          = ( if isVal e1' and isVal e2' then $e' else e'
                          , reqMerge rqs1 rqs2 )
                          where
                          (e1',rqs1) = B_ds [e1]
                          (e2',rqs2) = B_ds [e2]
                          e'         = p e1' e2'
B_ds [c es]             = ( if all isVal es' then $(c es') else c es'
                          , rqs )
                          where
                          (es',rqss) = unzip ((B_ds)* [es])
                          rqs        = foldr reqMerge empty rqss
B_ds [f es]             = case md of
                          Nothing -> (e', e':rqs) 
                          Just [f args body] ->
                            if isAlg body && isVal body then ($e', rqs)
                            else (e', rqs)
                          where
                          (es',rqss) = unzip ((B_ds)* [es])
                          rqs        = foldr reqMerge empty rqss
                          md         = maybeDefnOf ds f es'
                          e'         = f es'
B_ds [case e1 of as]    = ( if all isVal (rhs* as') && isAlg e' then $e'
                            else e'
                          , rqs )
                          where
                          (e1',rqs1) = B_ds [e1]
                          (as',rqss) = unzip (B_ds_(isVal e1')* [as])
                          e'         = case e1' of as'
                          rqs        = foldr reqMerge rqs1 rqss
B_ds [let v = e1 in e2] = ( passVal e2' e'
                          , union rqs1 rqs2 )
                          where
                          (e1',rqs1) = B_ds [e1]
                          (e2',rqs2) = B_ds [e2]
                          v'         = passVal e1' v
                          e'         = let v' = e1' in e2'

B_ds_q [c vs -> e]      = ( c vs' -> e' , rqs )
                          where
                          vs' = if q then $* vs else vs
                          (e',rqs) = B ds [e]

The D pass
----------

The D pass is applied to the list of strong-components in the dependency
graph of function definitions in a pool P, where components are listed
in non-descending order.

(1) If the first component C contains only a single non-recursive
    definition, OR
    if every algebraically-typed body of a definition in C is
    already annotated, OR
    if every algebraically-typed application in C of a definition
    also in C is already $-annotated THEN
    proceed to consider the next component (if any).

Otherwise:

(2) Make a temporary pool P' containing a copy of all definitions in C
    or components below it in the dependency ordering.  In P' $-annotate
    every algebraically-typed application of a function defined in C.

(3) Apply the entire analysis method to the pool P'.

(4) If *all* the definitions of functions originally defined in C, with
    algebraic bodies, now have $-annotated bodies in P':
   (4a) add P' to P, over-riding any existing definitions of the
        same functions;
   (4b) apply the analysis method to the amended pool P.

Otherwise:

(5) Consider the next component, if any.

Analysis functions defined
--------------------------

PRSanalyse :: [Def] -> [Def]
PRSanalyse prog = valuable prog [f ($* vs) = e]
  where
  (f vs = e) = head prog

valuable :: [Def] -> [Def] -> [Def]
valuable prog pool = passD prog pool'
  where
  (pool',[]) = fixFrom (forwardAndBack prog) (pool,[])

forwardAndBack :: [Def] -> ([Def],[Req]) -> ([Def],[Req])
forwardAndBack prog (pool,reqs) = 
  passB (passF pool')
  where
  defs  = map (variant prog) reqs
  pool' = defs ++ pool

Example revisited
-----------------

Recalling the sumRange example, the initial pool contains just:

  sumRange $m $n = sum (range m n) ;

Apply forwardAndBack.  Only forward propagation has any effect on the
single definition in the pool

  sumRange $m $n = sum (range $m $n) ;

but backward propagation issues requests for range$$ and sum?.  After a
further iteration of forwardAndBack the pool contains

  sumRange $m $n = sum (range $m $n) ;

  range $m $n = case $((<=) $m $n) of {
                True -> Cons $m (range $((+) $m $1) $n) ;
                False -> $Nil ;
                } ;

  sum xs = sumInto $0 xs ;

with  both primitive applications in range$$ $-annotated, indicating
that they can be evaluated by PRS.  There is a request for  sumInto$?.

In a further two applications of forwardAndBack, the pool is extended
by the addition of both sumInto$? and sumInto??. 

sumInto $a xs = case xs of {
                Nil -> $a ;
                Cons y ys -> sumInto ((+) $a y) ys ;
                } ;

sumInto a xs = case xs of {
               Nil -> a ;
               Cons y ys -> sumInto ((+) a y) ys ;
               } ;

A fixpoint of forwardAndBack has been reached.

The call-graph of the definitions in the pool is as follows:

    sumRange$$
    |      |      
  sum?     range$$ --
   |            |    |
  sumInto$?      ----
   |
  sumInto??--
        |    |
         ----

Applying the D pass, the first component {sumInto??} fails the condition
at step (4), as does {sumInto$?}.  The component {sum?} is skipped at step
(1).

However, for the component {range$$} the following refined definition
is obtained.

  range $m $n = $(case $((<=) $m $n) of {
                  True -> $(Cons $m $(range $((+) $m $1) $n)) ;
                  False -> $Nil ;
                  }) ;

The temporary pool P' contains just this definition.  It is unchanged by
application of valuable, and it now replaces the previous version of
range$$ in the pool P.

Reapplying the analysis to the amended pool P, forwardAndBack yields
the following refinement of sumRange$$

  sumRange $m $n = sum $(range $m $n) ;

and a request for a new variant sum$.  Applying forwardAndBack once more
the sum$$ definition becomes

  sum $xs = sumInto $0 $xs ;

with a request for sumInto$$.  Finally forwardAndBack refines sumInto$$
as follows

  sumInto $a $xs = case $xs of {
                   Nil -> $a ;
                   Cons $y $ys -> sumInto $((+) $a $y) $ys ;
                   } ;

and so the third and final primitive application in the program is
$-annotated for evaluation by PRS.

A fixpoint is reached and the analysis concludes.  The three definitions
sum?, sumInto$? and sumInto?? are no longer reachable from sumRange$$,
so they can be discarded.

Conclusions and Further Work
----------------------------

* I described this analysis problem, and a sketch of the possible analysis
  method, to John Hughes.  He pointed out that a type-based analysis
  (cf. boxed and unboxed integers) would give more accurate results.
  He may be right.  However, the property of being a valuable integer
  does not transfer from the body of f to an application of f.  It might
  also be harder to extract the variations, and the type-distinctions
  might be *too* refined, generating too many variations.

* It remains to deal with higher-order functions and with recursive
  lets.  One approach to higher-order functions is to specialize them
  to first-order variants wherever that is straightforward, and to
  approximate wildly where it isn't.  Recursive lets could be tamed by
  a simplified version of the D pass.

* Concerning efficiency, I claim only that the method terminates --
  essentially because there are finitely many $-annotations and variant
  definitions.  By maintaining dependency information, one could do
  better than re-application of passes, or even the complete analysis
  procedure, to an entire pool of definitions.

* No strictness analysis has been used, but strictness information might
  be a useful way to improve the results obtained.  For example, even
  with no prior value annotation on variables m and n the primitive
  subtractions in

  case (<=) m n of {
  False -> (-) m n ;
  True  -> (-) n m ;
  }

  might be marked for PRS evaluation.  (If each alternative becomes
  a function over arguments m and n, both must already be evaluated
  in view of the strict context of the (<=) comparison.)

* Concerning the application to the Reduceron, there are three issues
  that may need further thought. (1) The analysis places no bound on
  the number of PRS-evaluated expressions in a function body, and
  it assumes that every $-annotated primitive application will be
  evaluated by PRS. (2) There is no distinction between "unboxed"
  integer values, directly available on the stack or in registers, and
  "boxed" values that are at the other end of a heap reference.
  (3) Is there a way to support the in-principle extension to apply
  PRS on the basis of a strict evaluation context for arguments?

APPENDIX: a poor-man's type oracle
----------------------------------

Let the @ annotation of an expression e mean that e is known to be of
algebraic type.  Here are two passes for introducing @ annotations,
to be applied iteratively until a fixpoint is reached.  The predicate
isAlg e is true exactly if e is @-annotated.

The A-pass (for Ana-pass) propagates annotations upwards and outwards,
from expressions to parents and from applied occurrences to bindings.
The C-pass (for Cata-pass) propagates information downwards and inwards
from expressions to children and from bindings to applied occurrences.

The A-pass rules assume an auxiliaries liftAlg e v that @-annotates v if
any applied occurrence of it in e is @-annotated.

liftAlg e v = if anyAlg e v then @v else v

anyAlg [n]                 v = False
anyAlg [v']                v = v==v' && isAlg v'
anyAlg [p e1 e2]           v = anyAlg e1 v || anyAlg e2 v
anyAlg [c es]              v = any (flip anyAlg v) es
anyAlg [f es]              v = any (flip anyAlg v) es
anyAlg [case e of as]      v = anyAlg e v || any (flip anyAlg v . rhs) as
anyAlg [let v' = e1 in e2] v = anyAlg e1 v || anyAlg e2 v 

The auxiliary passAlg is also useful:

passAlg e1 e2 = if isAlg e1 then @e2 else e2

A [ds]               = A* [ds]

A [f vs = e]         = f vs' = e'
                       where
                       vs' = (liftAlg e')* vs
                       e'  = A [e]

A [n]                = n
A [v]                = v
A [p e1 e2]          = p (A [e1]) (A [e2])
A [c es]             = c (A* [es])
A [f es]             = f (A* [es])
A [case e of as]     = if any isAlg (rhs* as') then @e' else e'
                       where
                       as' = A* [as]
                       e'  = case @(A [e]) of as'
A [let v = e1 in e2] = passAlg e2' e'
                       where
                       e1' = A [e1]
                       e2' = A [e2]
                       v'  = if isAlg e1' then @v else liftAlg e2' v
                       e'  = let v' = e1' in e2'

A [c vs -> e]        = c vs' -> e'
                       where
                       e'  = A [e]
                       vs' = (liftAlg e')* vs

The C pass uses as auxiliary information the top-level definitions and
the variables in scope.  It assumes an auxiliary definitionOf to lookup
the definition of a named function.

Here are the rules.

C [ds]                        = C_ds* [ds]

C_ds [f vs = e]               = f vs = C_ds_V [e]
                                where
                                V = {v | v in vs, isAlg v}

C_ds_V [n]                    = n
C_ds_V [v]                    = if v in V then @v else v
C_ds_V [p e1 e2]              = p (C_ds_V [e1]) (C_ds_V [e2])
C_ds_V [c es]                 = @(c (C_ds_V* [es]))
C_ds_V [f es]                 = if isAlg rhs df then @e' else e'
                                where
                                (f vs = rhs) = definitionOf f ds
                                e' = f ((C_ds_V* . zipWith bindAlg vs) [es])
C_ds_V (e=[case e1 of as])    = case @(C_ds_V [e1]) of as'
                                where
                                as' = C_ds_V_e* as 
C_ds_V (e=[let v = e1 in e2]) = let v = C_ds_V' [e1] in
                                C_ds_V' (passAlg e e2)
                                where
                                V' = V union {v | isAlg v}

C_ds_V_e0 [c vs -> e1]        = c vs -> C_ds_V' (passAlg e0 e1)
                                where
                                V' = V union {v | v in vs, isAlg v}

The approach here is conservative so far as polymorphism is concerned.
A polymorphic function may *sometimes* be applied to an argument of
algebraic type, and it may *sometimes* return a data structure.  But
@-annotation indicates that *every* instance of the expression will
be of algebraic type.  So the above rules propagate @-annotations from
function definitions to applied occurrences, but not the other way round.

An alternative approach would be to generate variants of function
definitions for distinct @-annotations of arguments, in much the same
way that variants are generated for distinct $-annotations.  But that
would make programs larger, and the usefulness of the variations would
be less immediately apparent.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo6.txt version [c24b7c2cc4].

























































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
========================================================
REDUCERON MEMO 6
Case factorisation and special treatment of constructors
Matthew N, 17 November
========================================================

Compilation of the program

  type Var = Int
  data Exp = Const Bool | Var Var | And Exp Exp

  eval :: [(Var,Bool)] -> Exp -> Bool
  eval env (Const b) = b
  eval env (Var v) = lookup v env
  eval env (And e0 e1) = eval env e0 && eval env e1

produces the following combinators.

  eval env e    = e const (var env) (add env)     (1)
  const b       = b                               (2)
  var env v     = lookup v env                    (3)
  add env e0 e1 = eval env e0 && eval env e1      (4)

Look at the RHS of eval: the variable "env" must be passed down to
each case alternative that uses it.  In general, several variables may
need to be passed down to each case alternative and the number of case
alternatives could be large.  It is a waste of time and space to write
these variables onto the heap several times.

One possible solution is to factorise out the environment as follows.

  eval env e    = e const var add env             (5)
  const b env   = b                               (6)
  var v env     = lookup v env                    (7)
  add e0 e1 env = eval env e0 && eval env e1      (8)

Notice that env is only referred to once in (5) rather than twice as
in (1).

Not only is the body of (5) three nodes smaller than that of (1) but
it also contains one wide application and no small applications.

Case memory
-----------

Factorising the environment also opens a new oppertunity.  Notice that
in the body of (5) each case alternative is a *constant*.  Instead of
writing a group of constants on the heap every time eval is applied,
they could just be stored once in "case memory".  

To illustrate, we might define an array to represent the case
alternative constants for eval as

  evalAlts = [| const, var, add |]

and redefine equation (5) as

  eval env e = e evalAlts env

Now constructors must be encoded differently.  Instead of encoding
them by the equations

  Const x c v a   = c x
  Var   x c v a   = v x
  And   x y c v a = a x y

we would define them as

  Const x   alts = alts[0] x
  Var   x   alts = alts[1] x
  And   x y alts = alts[2] a x y

where square brackets represent array lookup.  Case memory could be
accessible in parallel with other memories, reducing any potential
contentions.  It does not need to be a wide memory.

Special constructors
--------------------

There is a more efficient way of dealing with these new kinds of
constructors if we avoid treating them as functions.  First, observe
that a constructor contains two pieces of information:

  1. Its index in the list of all constructors of the same type, say i.
  2. Its arity, say a.

A constructor can be represented by the packed pair (i,a).  When such
a pair appears on top of the stack, it can be reduced simply to

  s[sp+a][i]

where s is the stack, sp the stack pointer.  The expression s[sp+a]
points to the array of alternatives in case memory.  The ith element
of this array up contains the case alternative corresponding the the
constructor being scrutinised.

To allow constructors to be reduced in this way, we need to make every
case alternative ignore the case table sitting "a" places from the top
of the stack.  For example, we'd need to redefine the case
alternatives (equations 6, 7, 8) as follows.

  const b alts env   = b
  var v alts env     = lookup v env
  add e0 e1 alts env = eval env e0 && eval env e1

Notice that alts is ignored.

Potential impact
----------------

Case expressions with many alternatives can be handled more
efficiently and constructors can be reduced in a single cycle.

Open questions
--------------

Colin asked:

  What is the criterion for deciding when this kind of factorisation
  is applied? 

  I suppose that even with such a new fast encoding of constructors, it
  will still be worth in-lining/reducing "standard" constructors at
  compile/transform time?

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo7.txt version [7211eff5af].























































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
======================
REDUCERON MEMO 7
Memory layout
Matthew N, 18 November
======================

The stack
---------

To allow fast substition, the Reduceron reads the top eight elements
from the stack once, stores them in registers, and indexes this block
of registers in parallel using eight multiplexors during instantiation
of a body.

But there is a limitation: a function cannot take more than eight
arguments!  We can relax this limitation by simply increasing the
block-size.  But we do not know how well this will scale: the
multiplexor will get larger and we will either need to increase the
width of stack memory or do multiple reads before instantiation.

There is a much nicer solution.  Instead of having a wide stack in
which eight consecutive nodes can be accessed together, we can use an
eight-port stack.  We can then access any eight elements of the stack
in parallel.  The number of arguments a function takes is now
unlimited and instantiating any block of eight nodes in a function
body can still be done in a single cycle.  Another advantage is that
we no longer need a multiplexor to do the stack indexing, meaning less
logic and less delay.  

By "eight-port stack" I mean a stack with *eight read ports* and two
write ports.  Such an eight-port RAM can be made from four dual port
block RAMs.  Note that we only get the capacity of one block RAM even
though we use four --- each one stores exactly the same information.
So the disadvantage is that we loose some memory capacity.

The heap
--------

In section 2.10.3 of my thesis I wrote:

  Another implementation choice is whether to use quad-word memories or
  to quadruple the word-size.  Quad-word memories have the advantage
  that nodes can be addressed individually, but they require extra logic
  on the address and data busses of block RAMs.  Quadrupling the
  word-size requires no such extra logic, but nodes would have to be
  appropriately aligned on word-boundaries, probably wasting storage due
  to unused nodes inside words.

The best choice is not clear cut.  But my gut feeling is that the
simplicity of quadrupling the word size outweighs wasted nodes on the
heap.  In the worst case, we would "only" waste half of the memory
capacity.  The simplicity also brings a potential speed-up.  See next
section.

Cascading techniques
--------------------

The way block RAMs are cascaded in the current Reduceron is perhaps
not the best.  In section 2.10.3 of my thesis I wrote:

  There are alternative ways to configure and cascade block RAMs. For
  example, block RAMs can by configured as 1k by 18-bit, or as 16k by
  1-bit.  Instead of making a 16k by 18-bit memory by multiplexing the
  outputs of 16 1k by 18-bit block RAMs, an alternative would be to use
  18 16k by 1-bit block RAMs without the need for a multiplexor, simply
  by concatenating the data busses. The latter approach saves logic at
  the cost of a few extra block RAMs.

At the moment, memory-reads take two cycles because of the delay
introduced by the cascading multiplexor and the rotation logic used to
implement quad-word memories.  I think we will find that memory reads
can be single cycle when we use the alternative cascasing technique
and the quadrupling word-size technique.  This has the potential to
reduce unwinding from 2 cycles to 1, and unfolding from
  
  3 + (n `div` 8) cycles

to

  2 + (n `div` 8).

Conclusion
----------

In my thesis I made three main design decisions in favour memory
efficiency.  If we are willing to sacrifice a little memory, there is
potential for a simpler, more general, faster Reduceron that uses less
logic than the existing one.

Discussion (added 26 November)
------------------------------

Colin asked:

  1. Won't the needed arguments for each eight-node block need to be
     pre-fetched from the stack, requiring an extra cycle?

Suppose one cycle is needed to fetch a block of nodes from code
memory.  One more cycle is needed to fetch the appropriate values from
the stack.  So instantiating each block requires two cycles.  However,
these two steps can be pipelined.  While fetching a block of arguments
from the stack, we can fetch the next block of nodes from code memory.
But yes, there would be a clock-cycle overhead in the beginning, to
prime the pipeline.

  2. In the worst case, isn't 75% of the memory wasted?  (We only use
     the first byte in a four-byte word.)

The only way a one-node application can arise is when the Reduceron
introduces an indirection to implement sharing.  Such indirections can
be removed during garbage collection.  Correction: one can imagine a
five-node application being split into two four-word blocks in such a
way that 75% of memory is wasted.

  3. Are the two techniques [heap layout and cascading] applicable
     independently?  What would be the gain from (say) the
     new configuration of block RAM *alone*?

Yes, they are applicable independently.  Together they require no
logic on the memory bus, so I'm confident we'd get single-cycle
access.  I'm not so confident we'll get this if we just cascade the
block RAMs differently (we'll still need the rotation logic on the
memory bus).

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo8.txt version [99cd54fef9].































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
===========================================
REDUCERON MEMO 8
Speculative evaluation of primitive redexes
during instantiation of function bodies
Colin R, 21 November
(Revised: minor corrections and a note
on deriving # annotations)
===========================================

Consider a function such as this one, which performs the safety-checking
inner loop in the Queens program:

safe :: Int -> Int -> [Int] -> Bool
safe x d []    = True
safe x d (q:qs) = x /= q && x /= q+d && x /= q-d && safe x (d+1) qs

Or in semi-compiled form:

safe x d qs = qs True (safe' x d)
safe' x d q qs = x /= q && x /= q+d && x /= q-d && safe x (d+1) qs

Just look at the body of safe'.  All those primitive operations!
I haven't worked out the current compiled form of this body, but I
imagine it leads to a rather fragmented and therefore slow computation.

A Hand-Tracing Analogy
----------------------

Suppose I am tracing the program by hand.  Quite early on I need the
value of

    safe 1 1 [2]
==> safe' 1 1 2 []

If am feeling really keen and pedantic, I suppose I *might* just reduce
this to the instance of the safe' body with x=1, d=1, q=2 and qs=[].

    safe' 1 1 2 []
==> 1 /= 2 && 1 /= 2+1 && 1 /= 2-1 && safe 1 (1+1) []

But if I am in a hurry to obtain the result, I shall instead take the
opportunity to do a bit of speculative evaluation.  I reduce the four
primitive redexes as I go along, even though I may not yet be able to
see which of their results will actually be needed.  So this step in my
trace becomes instead:

    safe' 1 1 2 []
==> True && 1 /= 3 && 1 /= 1 && safe 1 2 []

Now, I could just appeal to the definition of &&

    True && 1 /= 3 && 1 /= 1 && safe 1 2 []
==> 1 /= 3 && 1 /= 1 && safe 1 2 []

but I might rather take the opportunity to do some more speculative
reduction of the primitive redexes.

    True && 1 /= 3 && 1 /= 1 && safe 1 2 []
==> True && True && False && safe 1 2 []

And so on.

What Might Reduceron Do?
------------------------

The Reduceron compiler could work out, and make explicit, where there
is opportunity for speculative primitive reduction.  For example,
revisiting the "semi-compiled" version of safe', and borrowing the #
notation for unboxed values:

safe' #x #d #q qs = _A && x /= _B && x /= _C && safe x _D qs
  where
  #_A = x /= q
  #_B = q + d
  #_C = q - d
  #_D = d + 1

A # attached to a binding occurrence of an argument means "this argument
will be already evaluated to a primitive value".  A # attached to a
binding occurrence of a local variable means "this primitive value should
be computed and substituted at the points of each applied occurrence
during instantiation of the body".

This kind of speculative reduction of primitive redexes would presumably
require an extra clock-cycle or two during instantiation of function
bodies to which it is applicable.  But it would be worth it.

As in the hand trace, a further wave of speculation may be possible using
the results of the first wave.

safe' #x #d #q qs = _A && _E && _F && safe x _D qs
  where
  #_A = x /= q
  #_B = q + d
  #_C = q - d
  #_D = d + 1
  #_E = x /= _B
  #_F = x /= _C

However, without specific values available, this is far more speculative.
If _A turns out to be False, the second-wave evaluation of _E and _F is
a waste of time.

Boolean Speculation and Associative Transformation
--------------------------------------------------

I am not sure what sort of speculation might be effective for the
*semi-strict* Boolean primitives && and ||, when the strict argument
(only) is available as a value .  One problem is that the size of
the result, which must form part of the instantiated body, cannot be
determined at compile-time.  The result of something like

  _B && expr

is the primitive value False if _B is False, but it is expr (which might
be of any size) if _B is True.

Associative transformations could help here.  For the reason just given,
it is awkward to do speculative reduction of && in the safe' body

safe' #x #d #q qs = _A && (_E && (_F && safe x _D qs))

because every && application involves a non-strict argument not yet
evaluated.  But as && is associative, the body could be transformed to
maximise opportunities to combine primitive values.

safe' #x #d #q qs = ((_A && _E) && _F) && safe x _D qs

This kind of associative transformation would also be applicable in the
numeric case for chains and combinations of addition and multiplication.

Deriving # Annotations on Arguments
-----------------------------------

The compiler needs some way of determining which arguments can be #
annotated.  I think this analysis should be fairly simple.  Initially
assume *all* integer and boolean arguments can be # annotated, and then
do an iterative refinement until a fixed point is reached.

Where recursive call sites can sustain # status, but some initial calls
from other sites break it, we could user the worker-wrapper idea as
in GHC.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/memos/Memo9.txt version [c73af6bc00].



































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
================================
REDUCERON MEMO 9
F-lite: a core subset of Haskell
Matthew N, 26 November 2008
================================

F-lite is a core subset of Haskell.  Unlike GHC Core and Yhc Core,
F-lite has a friendly concrete syntax.  You can write F-lite programs
in a file, and pass them to the F-lite interpreter or compiler.
Another way to view F-lite is as a minimalist lazy functional
language.

F-lite is untyped
-----------------

But as it is a subset of Haskell, you can use a Haskell implementation
to type-check F-lite programs.  

EXAMPLE 0: F-lite definition of 'append'.  Definitions of 'Nil' and
'Cons' are not required - there is no need to define algebraic data
types.

  append Nil ys = ys;
  append (Cons x xs) ys = Cons x (append xs ys);

(The use of semi-colons to seperate equations is mandatory.)

F-lite supports uniform pattern matching
----------------------------------------

Pattern matching is uniform if and only if the order of equations
doesn't matter (Wadler '86).  Uniform pattern matching can be easily
and efficiently compiled to core case expressions.  A core case
expression is one whose patterns all have the form 'constructor
applied to zero or more variables'.  The fact that the order of
equations doesn't matter is also useful when transforming functional
programs, for example by fold/unfold transformations.

EXAMPLE 1: F-lite definition of 'zipWith', illustrating uniform
pattern matching.

  zipWith f Nil ys = Nil;
  zipWith f (Cons x xs) Nil = Nil;
  zipWith f (Cons x xs) (Cons y ys) = Cons (f x y) (zipWith f xs ys);

EXAMPLE 2: F-lite definition of 'init', illustrating nested,
incomplete, uniform pattern matching.

  init (Cons x Nil) = Nil;
  init (Cons x (Cons y ys)) = Cons x (init (Cons y ys));

EXAMPLE 3: F-lite definition of 'init', using a case expression.

  init xs = case xs of {
              Cons x Nil -> Nil;
              Cons x (Cons y ys) -> Cons x (init (Cons y ys));
            };

(The use of semi-colons to seperate case alternatives is mandatory.)

F-lite supports 'let'-expressions
---------------------------------

But they may only bind expressions to variables (not patterns).

EXAMPLE 4: F-lite definition of 'pow', the power-list function,
illustrating a let expression.

  pow Nil = Cons Nil Nil;
  pow (Cons x xs) = let { rest = pow xs } in
                      append rest (map (Cons x) rest);

EXAMPLE 5: F-lite definition of 'repeat', using a let expression to
introduce a cyclic data structure.

  repeat x = let { xs = Cons x xs } in xs;

F-lite supports primitive integers
----------------------------------

Finite precision integers along with the following arithmetic
functions are allowed: (+), (-), (<=), (==), (/=).  The latter three
return 'True' or 'False' accordingly.  These operators must be written
in prefix form and cannot be partially applied.

EXAMPLE 6: F-lite definition of 'negate'.

  negate n = (-) 0 n;

EXAMPLE 7:  F-lite definition of 'fromTo'.

  fromTo n m = case (<=) n m of {
                 True -> Cons n (fromTo ((+) n 1) m);
                 False -> Nil;
               };

F-lite supports printing
------------------------

Two primitives, 'emit' and 'emitInt', are provided for printing characters
and integers respectively.

EXAMPLE 8: Printing the string "hi!" in F-lite.

  sayHi k = emit 'h' (emit 'i' (emit '!' k))

When evaluated, 'sayHi k' will print "hi!" and return 'k' (the
continuation).

EXAMPLE 9: 'Hello world' in F-lite.

  emitStr Nil k = k;
  emitStr (Cons x xs) k = emit x (emitStr xs k);

  main = emitStr "Hello world!\n" 0;

String literals are internally translated to 'Nil'-'Cons' lists of
characters.  The result of the 'main' function is expected to be an
integer - the displaying of any output must be done explicitly by the
programmer.

EXAMPLE 10: Full F-lite program to display the 10th fibonacci number.

  {

  fib n = case (<=) n 1 of {
            True  -> 1;
            False -> (+) (fib ((-) n 2)) (fib ((-) n 1));
          };

  emitStr Nil k = k;
  emitStr (Cons x xs) k = emit x (emitStr xs k);

  main = emitStr "fib(10) = " (emitInt (fib 10) (emit '\n' 0));

}

The braces enclosing the program are indeed mandatory.  The primitive
'emitInt' function is like 'emit' but prints an integer rather than a
character.  Both 'emit' and 'emitInt' must be applied to at least one
argument.

Other syntactic sugar
---------------------

If-then-else expressions are allowed, and are desugared as follows.

  if e1 then e2 else e3    -->    case e1 of { True -> e2 ; False -> e3 }

Our implementation
------------------

Our F-lite implementation includes both an interpreter (written in
Haskell) and a compiler (to C code - see Memo 22).  It works in both
Hugs and GHC.  For example, in the source directory, using Hugs:

  > runhugs Flite.hs ../examples/Fib.hs
  fib(10) = 89

and likewise using GHC:

  > ghc -O2 --make Flite.hs -o Flite

  > ./Flite ../examples/Fib.hs
  fib(10) = 89

To compile F-lite programs, use the '--compile' command-line option,
and redirect the output to a C file of your choice.

  > ./Flite --compile ../examples/Fib.hs > /tmp/Fib.c

The resulting C file can be compiled (with optimisations) using GCC:

  > gcc -O3 /tmp/Fib.c -o Fib

  > ./Fib
  fib(10) = 89

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/programs.tar version [0b2e52e65b].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/reduceron-old.tar.gz version [827733ce15].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/reduceron.pdf version [93d4a88c4e].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/reduceron.tar.gz version [1dcf550259].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/report.pdf version [52abbfa131].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/xilinx.png version [6e1199e4c7].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/fp/reduceron/york-lava.tar.gz version [67f680ddde].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/ftpdir/reports/2009/YCST/02/YCST-2009-02.pdf version [00e17cc3ad].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/plasma/publications/pdf/ReichNaylorRuncimanM10.pdf version [395237ae86].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/robots.txt version [b2d4b20b29].





































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
User-agent: *
Disallow: */closed/*
Disallow: */CLOSED/*
Disallow: */internal/*
Disallow: */INTERNAL/*
Disallow: /login
Disallow: /login-its
Disallow: */login/*
Disallow: */private/*
Disallow: */PRIVATE/*
Disallow: */reserved/*
Disallow: */RESERVED/*
Disallow: */restricted/*
Disallow: */RESTRICTED/*
Allow: /media/css
Allow: /media/global
Allow: /media/computerscience/images
Disallow: /media

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/~colin/index.html version [6cd1ed8648].













































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<head><title> Colin Runciman's Home Page </title></head>
<body bgcolor="#ffffff">

<h1> Colin Runciman </h1>

I am a Professor in the <a href="http://www.cs.york.ac.uk/">
Department of Computer Science at the University of York, England</a>.
<p>
For a list of research publications see my
<a href="http://scholar.google.co.uk/citations?user=-AsG2jwAAAAJ">
Google Scholar profile</a>.
<p>
<pre>
E-MAIL: Colin.Runciman at cs.york.ac.uk

PHONE:  +44 1904 325628

POST:   Dept. of Computer Science, The University of York                  
        Deramore Lane, Heslington, YORK YO10 5GH            
        United Kingdom           
</pre>
</body>

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/bonnet/www.cs.york.ac.uk/~jason/index.html version [b261e088da].













































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript">

  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-11301170-2']);
  _gaq.push(['_trackPageview']);

  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga);
  })();

  function addEvent(obj, type, fn)  {
    if (obj.attachEvent) {
      obj['e'+type+fn] = fn;
      obj[type+fn] = function(){obj['e'+type+fn](window.event);}
      obj.attachEvent('on'+type, obj[type+fn]);
    } else
      obj.addEventListener(type, fn, false);
  }
</script>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>About Me - Jason S. Reich</title>
<link rel="stylesheet" type="text/css" href="http://www-users.cs.york.ac.uk/~jason/css/style.css" media="screen" />

<link rel="stylesheet" href="http://www-users.cs.york.ac.uk/~jason/twitter/twitter_statuses.css" type="text/css" media="screen" />
<script type="text/javascript" src="http://www-users.cs.york.ac.uk/~jason/twitter/mtaTwitterStatuses.js"></script>
<script type="text/javascript">
(function() {
    function async_load(){
        makkintosshu.twitterStatuses.elementId = 'mtaTwitter';
        makkintosshu.twitterStatuses.tweetCount = 7;
        makkintosshu.twitterStatuses.filterReplies = false;
        makkintosshu.twitterStatuses.filterNonCS = true;
        makkintosshu.twitterStatuses.showIcon = false;
        var s = document.createElement('script');
        s.type = 'text/javascript';
        s.async = true;
        s.src = 'http://www.twitter.com/statuses/user_timeline/jasonreich.json?skip_user=true&callback=makkintosshu.twitterStatuses.twitterCallback&count=70';
        var x = document.getElementsByTagName('script')[0];
        x.parentNode.insertBefore(s, x);
    }
    if (window.attachEvent)
        window.attachEvent('onload', async_load);
    else
        window.addEventListener('load', async_load, false);
})();
</script>

<!--
<link rel="stylesheet" type="text/css" href="shadowbox/shadowbox.css" media="screen">
<script type="text/javascript" src="shadowbox/shadowbox.js"></script>
<script type="text/javascript">
Shadowbox.init({ overlayOpacity: 0.7 });
</script>
</head>
-->
<body>
<div id="wrapper">
	<div id="header" class="container">
		<div id="logo">
			<h1><a href="http://www-users.cs.york.ac.uk/~jason/index.html">Jason S. Reich</a></h1>
			<p>RS in <a href="http://www.cs.york.ac.uk/plasma/">PLASMA</a> @ <a href="http://www.cs.york.ac.uk/">The University of York</a></p>
		</div>
		<div id="banner"><img src="http://www-users.cs.york.ac.uk/~jason/image/img01.jpg" width="667" height="118" alt="" /></div>
	</div>
	<div id="menu" class="container">
		<ul>
			<li class="active"><a href="http://www-users.cs.york.ac.uk/~jason/index.html">About Me</a></li>
			<li class="$projects$"><a href="http://www-users.cs.york.ac.uk/~jason/projects.html">Projects</a></li>
			<li class="$publications$"><a href="http://www-users.cs.york.ac.uk/~jason/publications.html">Publications</a></li>
			<li class="$calendar$"><a href="http://www-users.cs.york.ac.uk/~jason/calendar.html">Calendar <img src="http://www-users.cs.york.ac.uk/~jason/image/padlock_closed.png" style="height: 1.5ex" /></a></li>
		</ul>
	</div>
	<div id="top-bar" class="container">
		<div class="bar">
			<div class="text"><a href="http://www.haskell.org">Haskell</a> fanatic studying for his Ph.D. at the <a href="http://www.cs.york.ac.uk">University of York</a></div>
		</div>
	</div>
	<div id="page" class="container">
		<div id="staticcontent">
			<div class="post">
				<div class="staticentry">
					<h1 id="about-me">About Me</h1>
<p>I am a Ph.D student within the <a href="http://www.cs.york.ac.uk/plasma/" title="PLASMA Research Group homepage">Programming Languages and Systems</a> (PLASMA) research group. My Ph.D, co-supervised by Professors <a href="http://www.cs.york.ac.uk/people/?username=colin" title="Professor Colin Runciman&#39;s homepage">Colin Runciman</a> and <a href="http://www.cs.york.ac.uk/people/?username=paige" title="Professor Richard Paige&#39;s homepage">Richard Paige</a>, is investigating the formal verification of functional language implementations. My interests generally lie within the topics of functional languages (in particular, <a href="http://www.haskell.org" title="Haskell - HaskellWiki">Haskell</a>), semantics and formal methods.</p>
<p>See the <a href="http://www-users.cs.york.ac.uk/~jason/projects.html">projects</a> page for a list of active interests.</p>
<h1 id="responsibilities">Responsibilities</h1>
<ul>
<li>Previous
<ul>
<li><a href="http://www.york.ac.uk/colleges/alcuin/">Alcuin College</a>, <a href="http://www-users.cs.york.ac.uk/~jason/image/tutor.pdf">College Tutor</a></li>
<li><a href="http://www.cs.york.ac.uk/student/committees/bos/members/">Board of Studies</a>, Research students’ temporary representative</li>
<li><a href="http://www.cs.york.ac.uk/courses/cgo.html">Code Generation &amp; Optimisation (CGO)</a> demonstrator</li>
<li><a href="http://www.cs.york.ac.uk/courses/fun.html">Functional Programming (FUN)</a> demonstrator</li>
<li><a href="http://www.cs.york.ac.uk/courses/2009/icm.html">Introduction to Computer Mathematics (ICM)</a> demonstrator</li>
<li><a href="http://www.cs.york.ac.uk/courses/2010/lsa.html">Lexical and Syntax Analysis of Programming Languages (LSA)</a> demonstrator</li>
<li><a href="http://www.cs.york.ac.uk/courses/2010/lpa.html">Logic Programming &amp; Artificial Intelligence (LPA)</a> demonstrator</li>
<li><a href="http://bit.ly/LSCITS11">LSCITS PGR Workshop 2011</a>, Co-chair</li>
<li><a href="http://www.cs.york.ac.uk/courses/mfcs.html">Mathematics for Computer Science (MFCS)</a> demonstrator</li>
<li><a href="http://www.cs.york.ac.uk/pst/pbl/">PST group on Problem-Based Learning</a> member</li>
<li>Recruiting and supervising a <a href="http://optimusprime.posterous.com/summer-internship-with-plasma">PLASMA Summer Intern</a>, funded by <a href="http://www.lscits.org">LSCITS</a></li>
<li><a href="http://www.cs.york.ac.uk/courses/sepr.html">Software Engineering Project (SEPR)</a> demonstrator</li>
<li><a href="http://www.cs.york.ac.uk/courses/syac.html">Systems Software &amp; Compilers (SYAC)</a> demonstrator</li>
<li><a href="http://www.cs.york.ac.uk/yds/">York Doctoral Symposium 2010</a>, Organising Committee member</li>
<li><a href="http://www.cs.york.ac.uk/yds/">York Doctoral Symposium 2011</a>, Programme Committee member</li>
</ul></li>
</ul>
<h1 id="contact">Contact</h1>
<ul>
<li>E-mail: <code>&lt;my first name&gt;@cs.york.ac.uk</code></li>
<li>Telephone: <code>+44 (0)1904 325623</code></li>
<li><a href="http://www.cs.york.ac.uk/people/?username=jason">People directory entry</a></li>
<li><a href="https://github.com/jasonreich/">My GitHub profile</a></li>
<li><a href="http://scholar.google.co.uk/citations?user=-KJK_hMAAAAJ">My profile on Google Scholar Citations</a></li>
<li><a href="http://www.mendeley.com/profiles/jason-s-reich/">My profile on Mendeley</a></li>
</ul>
				</div>
			</div>
		</div>
		
		<div id="sidebar">
			<ul>
				<li>
					<h2 style="margin-bottom: 0">Latest / <a href="http://www.twitter.com/jasonreich">Twitter</a></h2>
					<div id="mtaTwitter">Loading...</div>
				</li>
			</ul>
		</div>
		
		<div class="clearfix">&nbsp;</div>
	</div>
</div>
<div id="footer" class="container">
	<p> (c) 2010 Jason Reich. Rendered using <a href="http://jaspervdj.be/hakyll/">Hakyll</a>. Design by <a href="http://www.nodethirtythree.com/">nodethirtythree</a> + <a href="http://www.freecsstemplates.org/">Free CSS Templates</a></p>
</div>
<!--
<script type="text/javascript">
var links = document.links;
for(var i = 0; i < links.length; i++) {
	if( links[i].href.match(/publications\//) ) {
		links[i].rel = "shadowbox;width=640;height=480";
	}
}
</script>
-->
</body>
</html>

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/doc/2018_01_24_wget_copy_of_https_www_cs_york_ac_uk_fp_reduceron/index.html version [4c0c66a91d].























>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE HTML>
<html>
<head>
    <title>Redirection</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta http-equiv="Refresh" content="0; url=./bonnet/www.cs.york.ac.uk/fp/reduceron/index.html">
</head>
<body id="the_document_body">
<p>Redirecting...</p>
</body>
</html>

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/some_dependencies/york-lava-0.2.tar.gz version [d1431ba245].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/src_fom_GitHub/pull_new_version_from_git_repository.bash version [395b6da6fe].



































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#!/usr/bin/env bash
#==========================================================================
# Initial author: Martin.Vahi@softf1.com
# This file is in public domain.
#==========================================================================
S_FP_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
#--------------------------------------------------------------------------
# For copy-pasting to the ~/.bashrc
#
#     alias mmmv_cre_git_clone="cp $PATH_TO_THE<$S_FP_DIR>/pull_new_version_from_git_repository ./; mkdir -p ./the_repository_clones;"
#
#--------------------------------------------------------------------------


fun_assert_exists_on_path_t1 () {
    local S_NAME_OF_THE_EXECUTABLE=$1 # first function argument
    local S_TMP_0="\`which $S_NAME_OF_THE_EXECUTABLE 2>/dev/null\`"
    local S_TMP_1=""
    local S_TMP_2="S_TMP_1=$S_TMP_0"
    eval ${S_TMP_2}
    if [ "$S_TMP_1" == "" ] ; then
        echo ""
        echo "This bash script requires the \"$S_NAME_OF_THE_EXECUTABLE\" to be on the PATH."
        echo "GUID=='ded81e29-c63f-437c-b230-a06231a132e7'"
        echo ""
        exit 1 # exit with error
    fi
} # fun_assert_exists_on_path_t1

fun_assert_exists_on_path_t1 "ruby"
fun_assert_exists_on_path_t1 "grep"
fun_assert_exists_on_path_t1 "date"
fun_assert_exists_on_path_t1 "git"


#--------------------------------------------------------------------------
S_TMP_0="`uname -a | grep -E [Ll]inux`"
if [ "$S_TMP_0" == "" ]; then
    S_TMP_0="`uname -a | grep -E [Bb][Ss][Dd]`"
    if [ "$S_TMP_0" == "" ]; then
        echo ""
        echo "  The classical command line utilities at "
        echo "  different operating systems, for example, Linux and BSD,"
        echo "  differ. This script is designed to run only on Linux and BSD."
        echo "  If You are willing to risk that some of Your data "
        echo "  is deleted and/or Your operating system instance"
        echo "  becomes permanently flawed, to the point that "
        echo "  it will not even boot, then You may edit the Bash script that "
        echo "  displays this error message by modifying the test that "
        echo "  checks for the operating system type."
        echo ""
        echo "  If You do decide to edit this Bash script, then "
        echo "  a recommendation is to test Your modifications "
        echo "  within a virtual machine or, if virtual machines are not"
        echo "  an option, as some new operating system user that does not have "
        echo "  any access to the vital data/files."
        echo "  GUID=='3f2b6a5b-13ac-492c-b530-a06231a132e7'"
        echo ""
        echo "  Aborting script without doing anything."
        echo ""
        echo "GUID=='aad96c55-95ab-4644-b130-a06231a132e7'"
        echo ""
        exit 1 # exit with error
    fi
fi


#--------------------------------------------------------------------------

S_TIMESTAMP="`date +%Y`_`date +%m`_`date +%d`_T_`date +%H`h_`date +%M`min_`date +%S`s"
S_FP_ARCHIVE="$S_FP_DIR/archives/$S_TIMESTAMP"
S_FP_THE_REPOSITORY_CLONES="$S_FP_DIR/the_repository_clones"
mkdir -p $S_FP_THE_REPOSITORY_CLONES

#--------------------------------------------------------------------------
S_ARGV_0="$1"
SB_SKIP_ARCHIVING="f"
SB_RUN_GIT_GARBAGE_COLLECTOR_ON_LOCAL_GIT_REPOSITORY="f"

fun_init_sb_archive_and_archives_folder(){
    #--------
    if [ "$S_ARGV_0" == "skip_archiving" ]; then 
        SB_SKIP_ARCHIVING="t"
    fi
    if [ "$S_ARGV_0" == "ska" ]; then # abbreviation of "skip_archiving"
        SB_SKIP_ARCHIVING="t"
    fi
    #--------
    if [ "$S_ARGV_0" == "skip_archiving_gc" ]; then 
        SB_SKIP_ARCHIVING="t"
        SB_RUN_GIT_GARBAGE_COLLECTOR_ON_LOCAL_GIT_REPOSITORY="t"
    fi
    if [ "$S_ARGV_0" == "ska_gc" ]; then # abbreviation of "skip_archiving_gc"
        SB_SKIP_ARCHIVING="t"
        SB_RUN_GIT_GARBAGE_COLLECTOR_ON_LOCAL_GIT_REPOSITORY="t"
    fi
    if [ "$S_ARGV_0" == "skagc" ]; then  # abbreviation of "skip_archiving_gc"
        SB_SKIP_ARCHIVING="t"
        SB_RUN_GIT_GARBAGE_COLLECTOR_ON_LOCAL_GIT_REPOSITORY="t"
    fi
    #--------
    if [ "$SB_SKIP_ARCHIVING" != "t" ]; then 
        mkdir -p $S_FP_ARCHIVE
    fi
    #--------
} # fun_init_sb_archive_and_archives_folder

fun_init_sb_archive_and_archives_folder

#--------------------------------------------------------------------------

AR_REPO_FOLDER_NAMES=()

fun_assemble_array_of_repository_clone_folder_names () {
    cd $S_FP_THE_REPOSITORY_CLONES
    local S_TMP_0="`ruby -e \"ar=Array.new; Dir.glob('*').each{|x| if File.directory? x then ar<<x end}; puts(ar.to_s.gsub('[','(').gsub(']',')').gsub(',',' '))\"`"
    cd $S_FP_DIR
    local S_TMP_1="AR_REPO_FOLDER_NAMES=$S_TMP_0"
    eval ${S_TMP_1}
} # fun_assemble_array_of_repository_clone_folder_names 

fun_assemble_array_of_repository_clone_folder_names 


fun_update () {
    #--------
    local S_FP_FUNC_UPDATE_ORIG="`pwd`"
    #--------
    for s_iter in ${AR_REPO_FOLDER_NAMES[@]}; do
         S_FOLDER_NAME_OF_THE_LOCAL_COPY="$s_iter"
         echo ""
         #----
         if [ "$SB_SKIP_ARCHIVING" != "t" ]; then 
             echo "            Archiving a copy of $S_FOLDER_NAME_OF_THE_LOCAL_COPY"
             cp -f -R $S_FP_THE_REPOSITORY_CLONES/$S_FOLDER_NAME_OF_THE_LOCAL_COPY $S_FP_ARCHIVE/
         else
             echo "            Skipping the archiving a copy of $S_FOLDER_NAME_OF_THE_LOCAL_COPY"
         fi
         #----
         cd $S_FP_THE_REPOSITORY_CLONES/$S_FOLDER_NAME_OF_THE_LOCAL_COPY
         echo "Checking out a newer version of $S_FOLDER_NAME_OF_THE_LOCAL_COPY"
         #--------
         # Downloads the newest version of the software to that folder.
         nice -n10 git checkout --force # overwrites local changes, like the "svn co"
         nice -n10 git pull --all --recurse-submodules --force # gets the submodules
         #----
         # http://stackoverflow.com/questions/1030169/easy-way-pull-latest-of-all-submodules
         nice -n10 git submodule update --init --recursive --force
         if [ "$SB_RUN_GIT_GARBAGE_COLLECTOR_ON_LOCAL_GIT_REPOSITORY" == "t" ]; then 
             echo ""
             echo "Running the git garbage collector on the local repository..."
             # A citation from 
             #
             #     https://git-scm.com/docs/git-gc
             #
             #     --prune=<date>  Prune loose objects older 
             #                     than date (default is 2 weeks ago, 
             #                     overridable by the config variable 
             #                     gc.pruneExpire). --prune=all prunes 
             #                     loose objects regardless of their age
             #                     and increases the risk of corruption 
             #                     if another process is writing to 
             #                     the repository concurrently; 
             nice -n15 git gc --aggressive --prune=all 
             nice -n10 git pull --all --recurse-submodules --force # to be sure
         fi
         #--------
         cd $S_FP_DIR
    done
    cd $S_FP_FUNC_UPDATE_ORIG
} # fun_update 

fun_update # is a call to the function
echo ""

#==========================================================================

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/src_fom_GitHub/the_repository_clones/Reduceron/.git/FETCH_HEAD version [b9c5b34755].





>
>
1
2
8ab9ecb494aff7023fe606a070e1d2ffdc2705ca		branch 'master' of https://github.com/tommythorn/Reduceron
2bc03641125693087fdfc9386a1a75837162a25c	not-for-merge	branch 'deadend/refcnt-gc-apply' of https://github.com/tommythorn/Reduceron

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/src_fom_GitHub/the_repository_clones/Reduceron/.git/HEAD version [acbaef275e].



>
1
ref: refs/heads/master

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/src_fom_GitHub/the_repository_clones/Reduceron/.git/ORIG_HEAD version [cb8d72f883].



>
1
8ab9ecb494aff7023fe606a070e1d2ffdc2705ca

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/src_fom_GitHub/the_repository_clones/Reduceron/.git/config version [d58126f52d].























>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
[remote "origin"]
	url = https://github.com/tommythorn/Reduceron.git
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
	remote = origin
	merge = refs/heads/master

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/src_fom_GitHub/the_repository_clones/Reduceron/.git/description version [9635f1b7e1].



>
1
Unnamed repository; edit this file 'description' to name the repository.

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/src_fom_GitHub/the_repository_clones/Reduceron/.git/hooks/applypatch-msg.sample version [86b9655a9e].































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/sh
#
# An example hook script to check the commit log message taken by
# applypatch from an e-mail message.
#
# The hook should exit with non-zero status after issuing an
# appropriate message if it wants to stop the commit.  The hook is
# allowed to edit the commit message file.
#
# To enable this hook, rename this file to "applypatch-msg".

. git-sh-setup
test -x "$GIT_DIR/hooks/commit-msg" &&
	exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
:

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/src_fom_GitHub/the_repository_clones/Reduceron/.git/hooks/commit-msg.sample version [ee1ed5aad9].

















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/sh
#
# An example hook script to check the commit log message.
# Called by "git commit" with one argument, the name of the file
# that has the commit message.  The hook should exit with non-zero
# status after issuing an appropriate message if it wants to stop the
# commit.  The hook is allowed to edit the commit message file.
#
# To enable this hook, rename this file to "commit-msg".

# Uncomment the below to add a Signed-off-by line to the message.
# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
# hook is more suited to it.
#
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"

# This example catches duplicate Signed-off-by lines.

test "" = "$(grep '^Signed-off-by: ' "$1" |
	 sort | uniq -c | sed -e '/^[ 	]*1[ 	]/d')" || {
	echo >&2 Duplicate Signed-off-by lines.
	exit 1
}

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/src_fom_GitHub/the_repository_clones/Reduceron/.git/hooks/post-update.sample version [b614c2f63d].

















>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
#!/bin/sh
#
# An example hook script to prepare a packed repository for use over
# dumb transports.
#
# To enable this hook, rename this file to "post-update".

exec git update-server-info

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/src_fom_GitHub/the_repository_clones/Reduceron/.git/hooks/pre-applypatch.sample version [42fa415649].





























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/sh
#
# An example hook script to verify what is about to be committed
# by applypatch from an e-mail message.
#
# The hook should exit with non-zero status after issuing an
# appropriate message if it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-applypatch".

. git-sh-setup
test -x "$GIT_DIR/hooks/pre-commit" &&
	exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
:

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/src_fom_GitHub/the_repository_clones/Reduceron/.git/hooks/pre-commit.sample version [36aed8976d].



































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments.  The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit".

if git rev-parse --verify HEAD >/dev/null 2>&1
then
	against=HEAD
else
	# Initial commit: diff against an empty tree object
	against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

# If you want to allow non-ASCII filenames set this variable to true.
allownonascii=$(git config --bool hooks.allownonascii)

# Redirect output to stderr.
exec 1>&2

# Cross platform projects tend to avoid non-ASCII filenames; prevent
# them from being added to the repository. We exploit the fact that the
# printable range starts at the space character and ends with tilde.
if [ "$allownonascii" != "true" ] &&
	# Note that the use of brackets around a tr range is ok here, (it's
	# even required, for portability to Solaris 10's /usr/bin/tr), since
	# the square bracket bytes happen to fall in the designated range.
	test $(git diff --cached --name-only --diff-filter=A -z $against |
	  LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
then
	cat <<\EOF
Error: Attempt to add a non-ASCII file name.

This can cause problems if you want to work with people on other platforms.

To be portable it is advisable to rename the file.

If you know what you are doing you can disable this check using:

  git config hooks.allownonascii true
EOF
	exit 1
fi

# If there are whitespace errors, print the offending file names and fail.
exec git diff-index --check --cached $against --

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/src_fom_GitHub/the_repository_clones/Reduceron/.git/hooks/pre-push.sample version [b4ad74c989].













































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/bin/sh

# An example hook script to verify what is about to be pushed.  Called by "git
# push" after it has checked the remote status, but before anything has been
# pushed.  If this script exits with a non-zero status nothing will be pushed.
#
# This hook is called with the following parameters:
#
# $1 -- Name of the remote to which the push is being done
# $2 -- URL to which the push is being done
#
# If pushing without using a named remote those arguments will be equal.
#
# Information about the commits which are being pushed is supplied as lines to
# the standard input in the form:
#
#   <local ref> <local sha1> <remote ref> <remote sha1>
#
# This sample shows how to prevent push of commits where the log message starts
# with "WIP" (work in progress).

remote="$1"
url="$2"

z40=0000000000000000000000000000000000000000

IFS=' '
while read local_ref local_sha remote_ref remote_sha
do
	if [ "$local_sha" = $z40 ]
	then
		# Handle delete
		:
	else
		if [ "$remote_sha" = $z40 ]
		then
			# New branch, examine all commits
			range="$local_sha"
		else
			# Update to existing branch, examine new commits
			range="$remote_sha..$local_sha"
		fi

		# Check for WIP commit
		commit=`git rev-list -n 1 --grep '^WIP' "$range"`
		if [ -n "$commit" ]
		then
			echo "Found WIP commit in $local_ref, not pushing"
			exit 1
		fi
	fi
done

exit 0

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/src_fom_GitHub/the_repository_clones/Reduceron/.git/hooks/pre-rebase.sample version [5885a56ab4].



















































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/bin/sh
#
# Copyright (c) 2006, 2008 Junio C Hamano
#
# The "pre-rebase" hook is run just before "git rebase" starts doing
# its job, and can prevent the command from running by exiting with
# non-zero status.
#
# The hook is called with the following parameters:
#
# $1 -- the upstream the series was forked from.
# $2 -- the branch being rebased (or empty when rebasing the current branch).
#
# This sample shows how to prevent topic branches that are already
# merged to 'next' branch from getting rebased, because allowing it
# would result in rebasing already published history.

publish=next
basebranch="$1"
if test "$#" = 2
then
	topic="refs/heads/$2"
else
	topic=`git symbolic-ref HEAD` ||
	exit 0 ;# we do not interrupt rebasing detached HEAD
fi

case "$topic" in
refs/heads/??/*)
	;;
*)
	exit 0 ;# we do not interrupt others.
	;;
esac

# Now we are dealing with a topic branch being rebased
# on top of master.  Is it OK to rebase it?

# Does the topic really exist?
git show-ref -q "$topic" || {
	echo >&2 "No such branch $topic"
	exit 1
}

# Is topic fully merged to master?
not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
if test -z "$not_in_master"
then
	echo >&2 "$topic is fully merged to master; better remove it."
	exit 1 ;# we could allow it, but there is no point.
fi

# Is topic ever merged to next?  If so you should not be rebasing it.
only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
only_next_2=`git rev-list ^master           ${publish} | sort`
if test "$only_next_1" = "$only_next_2"
then
	not_in_topic=`git rev-list "^$topic" master`
	if test -z "$not_in_topic"
	then
		echo >&2 "$topic is already up-to-date with master"
		exit 1 ;# we could allow it, but there is no point.
	else
		exit 0
	fi
else
	not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
	/usr/bin/perl -e '
		my $topic = $ARGV[0];
		my $msg = "* $topic has commits already merged to public branch:\n";
		my (%not_in_next) = map {
			/^([0-9a-f]+) /;
			($1 => 1);
		} split(/\n/, $ARGV[1]);
		for my $elem (map {
				/^([0-9a-f]+) (.*)$/;
				[$1 => $2];
			} split(/\n/, $ARGV[2])) {
			if (!exists $not_in_next{$elem->[0]}) {
				if ($msg) {
					print STDERR $msg;
					undef $msg;
				}
				print STDERR " $elem->[1]\n";
			}
		}
	' "$topic" "$not_in_next" "$not_in_master"
	exit 1
fi

exit 0

################################################################

This sample hook safeguards topic branches that have been
published from being rewound.

The workflow assumed here is:

 * Once a topic branch forks from "master", "master" is never
   merged into it again (either directly or indirectly).

 * Once a topic branch is fully cooked and merged into "master",
   it is deleted.  If you need to build on top of it to correct
   earlier mistakes, a new topic branch is created by forking at
   the tip of the "master".  This is not strictly necessary, but
   it makes it easier to keep your history simple.

 * Whenever you need to test or publish your changes to topic
   branches, merge them into "next" branch.

The script, being an example, hardcodes the publish branch name
to be "next", but it is trivial to make it configurable via
$GIT_DIR/config mechanism.

With this workflow, you would want to know:

(1) ... if a topic branch has ever been merged to "next".  Young
    topic branches can have stupid mistakes you would rather
    clean up before publishing, and things that have not been
    merged into other branches can be easily rebased without
    affecting other people.  But once it is published, you would
    not want to rewind it.

(2) ... if a topic branch has been fully merged to "master".
    Then you can delete it.  More importantly, you should not
    build on top of it -- other people may already want to
    change things related to the topic as patches against your
    "master", so if you need further changes, it is better to
    fork the topic (perhaps with the same name) afresh from the
    tip of "master".

Let's look at this example:

		   o---o---o---o---o---o---o---o---o---o "next"
		  /       /           /           /
		 /   a---a---b A     /           /
		/   /               /           /
	       /   /   c---c---c---c B         /
	      /   /   /             \         /
	     /   /   /   b---b C     \       /
	    /   /   /   /             \     /
    ---o---o---o---o---o---o---o---o---o---o---o "master"


A, B and C are topic branches.

 * A has one fix since it was merged up to "next".

 * B has finished.  It has been fully merged up to "master" and "next",
   and is ready to be deleted.

 * C has not merged to "next" at all.

We would want to allow C to be rebased, refuse A, and encourage
B to be deleted.

To compute (1):

	git rev-list ^master ^topic next
	git rev-list ^master        next

	if these match, topic has not merged in next at all.

To compute (2):

	git rev-list master..topic

	if this is empty, it is fully merged to "master".

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/src_fom_GitHub/the_repository_clones/Reduceron/.git/hooks/prepare-commit-msg.sample version [2b6275eda3].









































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/bin/sh
#
# An example hook script to prepare the commit log message.
# Called by "git commit" with the name of the file that has the
# commit message, followed by the description of the commit
# message's source.  The hook's purpose is to edit the commit
# message file.  If the hook fails with a non-zero status,
# the commit is aborted.
#
# To enable this hook, rename this file to "prepare-commit-msg".

# This hook includes three examples.  The first comments out the
# "Conflicts:" part of a merge commit.
#
# The second includes the output of "git diff --name-status -r"
# into the message, just before the "git status" output.  It is
# commented because it doesn't cope with --amend or with squashed
# commits.
#
# The third example adds a Signed-off-by line to the message, that can
# still be edited.  This is rarely a good idea.

case "$2,$3" in
  merge,)
    /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;

# ,|template,)
#   /usr/bin/perl -i.bak -pe '
#      print "\n" . `git diff --cached --name-status -r`
#	 if /^#/ && $first++ == 0' "$1" ;;

  *) ;;
esac

# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/src_fom_GitHub/the_repository_clones/Reduceron/.git/hooks/update.sample version [39355a0759].

































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/bin/sh
#
# An example hook script to blocks unannotated tags from entering.
# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
#
# To enable this hook, rename this file to "update".
#
# Config
# ------
# hooks.allowunannotated
#   This boolean sets whether unannotated tags will be allowed into the
#   repository.  By default they won't be.
# hooks.allowdeletetag
#   This boolean sets whether deleting tags will be allowed in the
#   repository.  By default they won't be.
# hooks.allowmodifytag
#   This boolean sets whether a tag may be modified after creation. By default
#   it won't be.
# hooks.allowdeletebranch
#   This boolean sets whether deleting branches will be allowed in the
#   repository.  By default they won't be.
# hooks.denycreatebranch
#   This boolean sets whether remotely creating branches will be denied
#   in the repository.  By default this is allowed.
#

# --- Command line
refname="$1"
oldrev="$2"
newrev="$3"

# --- Safety check
if [ -z "$GIT_DIR" ]; then
	echo "Don't run this script from the command line." >&2
	echo " (if you want, you could supply GIT_DIR then run" >&2
	echo "  $0 <ref> <oldrev> <newrev>)" >&2
	exit 1
fi

if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
	echo "usage: $0 <ref> <oldrev> <newrev>" >&2
	exit 1
fi

# --- Config
allowunannotated=$(git config --bool hooks.allowunannotated)
allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
denycreatebranch=$(git config --bool hooks.denycreatebranch)
allowdeletetag=$(git config --bool hooks.allowdeletetag)
allowmodifytag=$(git config --bool hooks.allowmodifytag)

# check for no description
projectdesc=$(sed -e '1q' "$GIT_DIR/description")
case "$projectdesc" in
"Unnamed repository"* | "")
	echo "*** Project description file hasn't been set" >&2
	exit 1
	;;
esac

# --- Check types
# if $newrev is 0000...0000, it's a commit to delete a ref.
zero="0000000000000000000000000000000000000000"
if [ "$newrev" = "$zero" ]; then
	newrev_type=delete
else
	newrev_type=$(git cat-file -t $newrev)
fi

case "$refname","$newrev_type" in
	refs/tags/*,commit)
		# un-annotated tag
		short_refname=${refname##refs/tags/}
		if [ "$allowunannotated" != "true" ]; then
			echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
			echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
			exit 1
		fi
		;;
	refs/tags/*,delete)
		# delete tag
		if [ "$allowdeletetag" != "true" ]; then
			echo "*** Deleting a tag is not allowed in this repository" >&2
			exit 1
		fi
		;;
	refs/tags/*,tag)
		# annotated tag
		if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
		then
			echo "*** Tag '$refname' already exists." >&2
			echo "*** Modifying a tag is not allowed in this repository." >&2
			exit 1
		fi
		;;
	refs/heads/*,commit)
		# branch
		if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
			echo "*** Creating a branch is not allowed in this repository" >&2
			exit 1
		fi
		;;
	refs/heads/*,delete)
		# delete branch
		if [ "$allowdeletebranch" != "true" ]; then
			echo "*** Deleting a branch is not allowed in this repository" >&2
			exit 1
		fi
		;;
	refs/remotes/*,commit)
		# tracking branch
		;;
	refs/remotes/*,delete)
		# delete tracking branch
		if [ "$allowdeletebranch" != "true" ]; then
			echo "*** Deleting a tracking branch is not allowed in this repository" >&2
			exit 1
		fi
		;;
	*)
		# Anything else (is there anything else?)
		echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
		exit 1
		;;
esac

# --- Finished
exit 0

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/src_fom_GitHub/the_repository_clones/Reduceron/.git/index version [1416b01944].

cannot compute difference between binary files

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/src_fom_GitHub/the_repository_clones/Reduceron/.git/info/exclude version [c879df015d].













>
>
>
>
>
>
1
2
3
4
5
6
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/src_fom_GitHub/the_repository_clones/Reduceron/.git/info/refs version [1ddb371689].









>
>
>
>
1
2
3
4
8ab9ecb494aff7023fe606a070e1d2ffdc2705ca	refs/heads/master
8ab9ecb494aff7023fe606a070e1d2ffdc2705ca	refs/remotes/origin/HEAD
2bc03641125693087fdfc9386a1a75837162a25c	refs/remotes/origin/deadend/refcnt-gc-apply
8ab9ecb494aff7023fe606a070e1d2ffdc2705ca	refs/remotes/origin/master

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/src_fom_GitHub/the_repository_clones/Reduceron/.git/logs/HEAD version [33fedc497a].



>
1
4cf7158c6ecc7337a5dc83cc2a64d028275f7144 8ab9ecb494aff7023fe606a070e1d2ffdc2705ca Martin Vahi <martin.vahi@softf1.com> 1528335621 +0300	pull --all --recurse-submodules --force: Fast-forward

Added wiki_references/2017/hardware/CPUs/Reduceron_FPGA_Haskell_Machine/src_fom_GitHub/the_repository_clones/Reduceron/.git/logs/refs/heads/master version [33fedc497a].



>
1
<