Age Wizard¶
Download all the Jupyter notebooks from: https://github.com/HeloiseS/hoki/tree/master/tutorials
Initial imports¶
[1]:
from hoki.age_utils import AgeWizard
import hoki
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
plt.style.use('tuto.mplstyle')
A systematic ageing method¶
Motivation¶
One of the classic methods for ageing clusters is to fit isochrones to observational data in Colour-Magnitude Diagrams (CMDs) or in Hertzsprung-Russel Diagrams (HRDs).
There are a number of issues with this ubiquitous technique though:
Fitting is usually done by eye
They only take into account single stars
It cannot be used if your sample only contains a few sources
Our goal is to provide a method that answers all of the issues metioned above
How it works¶
By matching the observed properties of your sources to the BPASS models you can find the most likely age of a location on the HRD/CMD.
If all your sources belong to the same region, you can then combine the PDFs of your individual sources to find the age PDF of the whole cluster.
All of this can be done step by step using the chain find_coordinates()
-> calculate_pdfs
-> multiply_pdfs()
, or you could simply use AgeWizard()
which handles this pipeline and facilitates loading in the models.
Note: In this tutorial we will focus on AgeWizard()
.
Remember to exercise caution in your interpretation¶
This is true of all methods but it is worth repeating. This technique helps you get to the answer, but it doesn’t give you the answer.
The most likely age of the matching location is not necessarily the age of your source - as we’ll see below Helium Stars created through binary interactions and envelope stripping are found at similar locations as WR stars but at different ages.
Ages from Hertzsprung-Russell Diagram¶
First, we need some observational data to compare to our models.
AgeWizard()
expects a pandas.DataFrame
. If you want to use the HRD capabilities you will need to provide a logT
and a logL
column: * logT: log10 of the stellar effective temperature * logL: log10 of the stellar luminosity in units of Solar Luminosities
Additionally, your DataFrame will ideally contain a name
column with the name of the sources you’re working on. If you don’t provide one, AgeWizard()
will make it’s own sources names: source1, source2, etc…
Data source: McLeod et al. 2019, Stevance et al. in prep.
[2]:
stars = pd.DataFrame.from_dict({'name':np.array(['Star1','Star2','Star3','Star4',
'Star5','Star6','Star7','Star8','Star9',
'Star10','Star11','Star12','Star13','Star14',
'WR1', 'WR2']),
'logL':np.array([5.0, 5.1, 4.9, 5.9, 5.0, 5.4, 4.3, 5.7, 4.5, 4.5,
4.9, 4.5, 4.3, 4.5, 5.3, 5.3]),
'logT':np.array([4.48, 4.45, 4.46, 4.47, 4.48, 4.53, 4.52, 4.52, 4.52,
4.56, 4.46, 4.52, 4.52, 4.52, 4.9, 4.65])})
Now we initialise our AgeWizard()
. Each instance corresponds to one model and one model only: i.e. 1 IMF and 1 metalicity.
If we want to have a look at 2 different metallicities for example, we can just instanciate AgeWizard
twice:
[3]:
agewiz006 = AgeWizard(obs_df=stars, model='./data/hrs-bin-imf135_300.z006.dat')
agewiz008 = AgeWizard(obs_df=stars, model='./data/hrs-bin-imf135_300.z008.dat')
During instanciation, the observations and models are matched and the age PDFs are calculated. They are summarised in a pandas.DataFrame
that you can access as follows.
[4]:
agewiz006.pdfs.head(15)
[4]:
| name | Star1 | Star2 | Star3 | Star4 | Star5 | Star6 | Star7 | Star8 | Star9 | Star10 | Star11 | Star12 | Star13 | Star14 | WR1 | WR2 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0.000000 | 0.000000 | 0.000000 | 1.298460e-11 | 0.000000 | 0.000000 | 0.025706 | 5.594794e-11 | 0.021803 | 0.000000e+00 | 0.000000 | 0.021803 | 0.025706 | 0.021803 | 0.000000e+00 | 0.107142 |
| 1 | 0.000000 | 0.000000 | 0.000000 | 0.000000e+00 | 0.000000 | 0.000000 | 0.036928 | 0.000000e+00 | 0.030149 | 0.000000e+00 | 0.000000 | 0.030149 | 0.036928 | 0.030149 | 0.000000e+00 | 0.004603 |
| 2 | 0.000000 | 0.000000 | 0.000000 | 0.000000e+00 | 0.000000 | 0.000000 | 0.046310 | 0.000000e+00 | 0.035243 | 0.000000e+00 | 0.000000 | 0.035243 | 0.046310 | 0.035243 | 0.000000e+00 | 0.006676 |
| 3 | 0.000000 | 0.000000 | 0.000000 | 0.000000e+00 | 0.000000 | 0.000000 | 0.058174 | 0.000000e+00 | 0.052005 | 7.820472e-08 | 0.000000 | 0.052005 | 0.058174 | 0.052005 | 0.000000e+00 | 0.012032 |
| 4 | 0.000000 | 0.000000 | 0.000654 | 0.000000e+00 | 0.000000 | 0.000000 | 0.081692 | 0.000000e+00 | 0.057408 | 1.012331e-04 | 0.000654 | 0.057408 | 0.081692 | 0.057408 | 0.000000e+00 | 0.003365 |
| 5 | 0.000000 | 0.000000 | 0.015330 | 7.629112e-01 | 0.000000 | 0.000000 | 0.063921 | 4.216232e-03 | 0.074740 | 9.941176e-07 | 0.015330 | 0.074740 | 0.063921 | 0.074740 | 4.525980e-08 | 0.009378 |
| 6 | 0.075321 | 0.011572 | 0.142812 | 1.100859e-01 | 0.075321 | 0.003387 | 0.078904 | 2.914504e-01 | 0.086366 | 1.476180e-04 | 0.142812 | 0.086366 | 0.078904 | 0.086366 | 0.000000e+00 | 0.000145 |
| 7 | 0.238335 | 0.430026 | 0.233166 | 8.879082e-02 | 0.238335 | 0.558455 | 0.135883 | 4.335802e-01 | 0.093930 | 1.763084e-03 | 0.233166 | 0.093930 | 0.135883 | 0.093930 | 2.155362e-14 | 0.015148 |
| 8 | 0.295138 | 0.348300 | 0.279178 | 2.892240e-02 | 0.295138 | 0.345761 | 0.187286 | 1.529185e-01 | 0.147844 | 4.403392e-03 | 0.279178 | 0.147844 | 0.187286 | 0.147844 | 3.207127e-01 | 0.023512 |
| 9 | 0.324156 | 0.153717 | 0.267386 | 9.289685e-03 | 0.324156 | 0.044554 | 0.176904 | 8.005393e-02 | 0.204892 | 1.732683e-02 | 0.267386 | 0.204892 | 0.176904 | 0.204892 | 5.301331e-01 | 0.745544 |
| 10 | 0.023544 | 0.025031 | 0.023663 | 0.000000e+00 | 0.023544 | 0.032551 | 0.072708 | 3.722405e-02 | 0.165809 | 1.310383e-02 | 0.023663 | 0.165809 | 0.072708 | 0.165809 | 2.254815e-02 | 0.060667 |
| 11 | 0.016194 | 0.017432 | 0.015374 | 0.000000e+00 | 0.016194 | 0.011823 | 0.004711 | 5.566887e-04 | 0.006944 | 6.358691e-03 | 0.015374 | 0.006944 | 0.004711 | 0.006944 | 9.453766e-03 | 0.002344 |
| 12 | 0.017234 | 0.011523 | 0.014033 | 0.000000e+00 | 0.017234 | 0.003118 | 0.003887 | 0.000000e+00 | 0.006177 | 6.590932e-02 | 0.014033 | 0.006177 | 0.003887 | 0.006177 | 3.787228e-06 | 0.001189 |
| 13 | 0.008077 | 0.001189 | 0.006150 | 0.000000e+00 | 0.008077 | 0.000000 | 0.004152 | 0.000000e+00 | 0.006443 | 6.874719e-01 | 0.006150 | 0.006443 | 0.004152 | 0.006443 | 6.385924e-05 | 0.000039 |
| 14 | 0.000061 | 0.000322 | 0.001203 | 0.000000e+00 | 0.000061 | 0.000134 | 0.007283 | 0.000000e+00 | 0.003896 | 4.532046e-02 | 0.001203 | 0.003896 | 0.007283 | 0.003896 | 1.007283e-03 | 0.000000 |
But those are just numbers… PDFs are meant to be plotted:
[5]:
def plot_all_pdfs(agewiz):
# creating the figure
f, ax = plt.subplots(4, 4, figsize=(15,15))
plt.subplots_adjust(hspace=0.4)
axes = ax.reshape(16)
# We plot each star an individual axis
for source, axis in zip(agewiz.sources, axes):
# Plotting one PDF
axis.step(hoki.BPASS_TIME_BINS, agewiz.pdfs[source],where='mid')
axis.fill_between(hoki.BPASS_TIME_BINS, agewiz.pdfs[source], step='mid', alpha=0.3)
# Labels and limits
axis.set_title(source)
axis.set_ylabel('Probability (%)')
axis.set_xlabel('log(years)')
axis.set_ylim([0,0.6])
axis.set_xlim([6,8.5])
Most Likely Ages¶
The most likely age for each source is summarised by AgeWizard.most_likely_ages
. This is returned as a numpy array.
[8]:
agewiz006.most_likely_ages
[8]:
array([6.9, 6.7, 6.8, 6.5, 6.9, 6.7, 6.8, 6.7, 6.9, 7.3, 6.8, 6.9, 6.8,
6.9, 6.9, 6.9])
Different metallicities will give different results. In this example we summarise our results into a new DataFrame:
[9]:
most_likely_ages = pd.DataFrame.from_dict({'name': agewiz006.sources,
'z006': agewiz006.most_likely_ages,
'z008': agewiz008.most_likely_ages})
# Note that the attribute AgeWizard.sources is the list of names you provided, or if you didn't the ones it created
[10]:
most_likely_ages
[10]:
| name | z006 | z008 | |
|---|---|---|---|
| 0 | Star1 | 6.9 | 6.8 |
| 1 | Star2 | 6.7 | 6.7 |
| 2 | Star3 | 6.8 | 6.9 |
| 3 | Star4 | 6.5 | 6.5 |
| 4 | Star5 | 6.9 | 6.8 |
| 5 | Star6 | 6.7 | 6.7 |
| 6 | Star7 | 6.8 | 6.8 |
| 7 | Star8 | 6.7 | 6.5 |
| 8 | Star9 | 6.9 | 6.9 |
| 9 | Star10 | 7.3 | 7.3 |
| 10 | Star11 | 6.8 | 6.9 |
| 11 | Star12 | 6.9 | 6.9 |
| 12 | Star13 | 6.8 | 6.8 |
| 13 | Star14 | 6.9 | 6.9 |
| 14 | WR1 | 6.9 | 6.8 |
| 15 | WR2 | 6.9 | 6.9 |
IMPORTANT¶
The most_likely_ages
(and most_likley_age
, see below) tools do not give you a direct answer - they are a way to summarise the data and be used in your interpretation but please do not use these as a black box.
Looking at the PDFs plotted above and the table of most likely ages, we can see that a few stars stand out:
Star 4 has a lower (most likely) age than the rest of the sample
Star 10 has a higher (most likely) age than the rest of the sample
The WR PDFs show peaks beyond log(age/years) - which souldn’t be possible for WR stars.
All of these are discussed an interpreted in details in Stevance et al. (in prep). Star 4 is an example of a star that is probably the result of a merger and rejuvination, Star 10 is more likely to be on the young tail of the distribution and not as old as the most likley age implies, and the WR stars share their HRD locations with helium stars which appear at later times, which populate the later peak.
Plotting Aggregate Ages¶
If you are looking for the age of a whole population then you can ask AgeWizard
to combine the PDFs it calcualted on initialisation. This is done with the method AgeWizard.calcualte_sample_pdfs()
.
It can be called as such and it will multiply all the PDFs, or you can provide a list of columns to ignore in the paramter not_you
. You can also ask it to return the resulting PDF by setting return_df
to True. If you don’t, it will store the result in AgeWizard.sample_pdf
.
[15]:
# Returns None
agewiz006.calculate_sample_pdf()
[16]:
agewiz006.sample_pdf.head(10)
[16]:
| 0 | 0.018170 |
|---|---|
| 1 | 0.025543 |
| 2 | 0.030998 |
| 3 | 0.042060 |
| 4 | 0.052826 |
| 5 | 0.057152 |
| 6 | 0.085512 |
| 7 | 0.152308 |
| 8 | 0.196053 |
| 9 | 0.206209 |
You can also quickly retrieve the most likely age of the whole sample from the PDF you just calculated using AgeWizard.most_likely_age
.
[17]:
agewiz006.most_likely_age
[17]:
array([6.9])
[19]:
# Or return directly
agewiz008.calculate_sample_pdf(return_df=True).head(10)
[19]:
| 0 | 0.024695 |
|---|---|
| 1 | 0.025288 |
| 2 | 0.019613 |
| 3 | 0.034380 |
| 4 | 0.058792 |
| 5 | 0.072591 |
| 6 | 0.132265 |
| 7 | 0.174126 |
| 8 | 0.211425 |
| 9 | 0.190401 |
[20]:
agewiz008.most_likely_age
[20]:
array([6.8])
Down below we create a quick function to plot our aggregate ages for z=0.006 and z=0.008.
As an example of the
[22]:
cluster1_sources = ['Star1', 'Star2', 'Star3', 'Star4', 'WR1']
cluster2_sources = [item for item in agewiz006.sources if item not in cluster1_sources]
[23]:
def plot_aggregate_age(agewiz, ax):
# Aggregate age PDF for all sources
cluster12 = agewiz.calculate_sample_pdf(return_df=True).pdf
# Aggregate age PDF for cluster1
cluster1 = agewiz.calculate_sample_pdf(not_you=cluster2_sources, return_df=True).pdf
# Aggregate age PDF for cluster2
cluster2 = agewiz.calculate_sample_pdf(not_you=cluster1_sources, return_df=True).pdf
ax.step(hoki.BPASS_TIME_BINS, cluster1, where='mid', alpha=1, lw=3)
ax.fill_between(hoki.BPASS_TIME_BINS, cluster1, step='mid', alpha=0.1, label='Cluster1')
ax.step(hoki.BPASS_TIME_BINS, cluster2, where='mid', alpha=1, lw=3)
ax.fill_between(hoki.BPASS_TIME_BINS, cluster2, step='mid', alpha=0.3, label='Cluster2')
ax.step(hoki.BPASS_TIME_BINS, cluster12, where='mid',alpha=0.5, c='k', lw=3)
ax.fill_between(hoki.BPASS_TIME_BINS, cluster12, step='mid', alpha=0.2, label='All', color='k')
ax.set_ylabel('Probability')
ax.set_xlabel('log(ages)')
[24]:
f, ax = plt.subplots(ncols=2, nrows=1, figsize=(7,5))
plot_aggregate_age(agewiz006, ax[0])
plot_aggregate_age(agewiz008, ax[1])
for axis in ax:
axis.set_ylim([0,0.4])
axis.set_xlim([6,8.5])
# Cool legend
ax[0].legend(fontsize=14, loc='center right', bbox_to_anchor=(1.9, 1.1), ncol=3)
[24]:
<matplotlib.legend.Legend at 0x7f6b320e16d8>
Probability given an age range¶
The BPASS time bins are separated by 0.1 dex in log space (log(age/years) = 6.0, 6.1, 6.2, 6.3 etc..) and the PDFs for your individual sources will cover a range of BPASS time bins.
Consequently, giving a single age may not make a lot of sense. Also the PDFs will rarely, if ever, resemble a Gaussian distribution: that means that you can’t give an error bar in the “classical” sense.
Another interesting measure is the probability that the age of your star falls into a chosen age range. In this case our aggregate age is most likely to be between 6.7-6.9, so let’s investigate how likely it is for each individual star to fall in that range.
For that we use AgeWizard.calculate_p_given_age_range()
which returns a Series containing the probabilities corresponding to each source.
[28]:
agewiz006.calculate_p_given_age_range([6.7, 6.9])
[28]:
name
Star1 0.857630
Star2 0.932043
Star3 0.779729
Star4 0.127003
Star5 0.857630
Star6 0.948770
Star7 0.500073
Star8 0.666553
Star9 0.446666
Star10 0.023493
Star11 0.779729
Star12 0.446666
Star13 0.500073
Star14 0.446666
WR1 0.850846
WR2 0.784204
dtype: float64
We can collate those in one single DataFrame
[29]:
p_ages_679 = pd.DataFrame.from_dict({'name': agewiz006.sources,
'z006': agewiz006.calculate_p_given_age_range([6.7,6.9]),
'z008': agewiz008.calculate_p_given_age_range([6.7,6.9])})
[30]:
p_ages_679
[30]:
| name | z006 | z008 | |
|---|---|---|---|
| name | |||
| Star1 | Star1 | 0.857630 | 0.784335 |
| Star2 | Star2 | 0.932043 | 0.737951 |
| Star3 | Star3 | 0.779729 | 0.659319 |
| Star4 | Star4 | 0.127003 | 0.134967 |
| Star5 | Star5 | 0.857630 | 0.784335 |
| Star6 | Star6 | 0.948770 | 0.738479 |
| Star7 | Star7 | 0.500073 | 0.469008 |
| Star8 | Star8 | 0.666553 | 0.278005 |
| Star9 | Star9 | 0.446666 | 0.549145 |
| Star10 | Star10 | 0.023493 | 0.023109 |
| Star11 | Star11 | 0.779729 | 0.659319 |
| Star12 | Star12 | 0.446666 | 0.549145 |
| Star13 | Star13 | 0.500073 | 0.469008 |
| Star14 | Star14 | 0.446666 | 0.549145 |
| WR1 | WR1 | 0.850846 | 0.721919 |
| WR2 | WR2 | 0.784204 | 0.789553 |
To learn more about how we use and interpret this statistic, you can checkout Stevance et al. in prep an the associated Jupyter Notebook (link)


{{ define "main" }}
These website support packages are tailored to meet various businesses budgets and requirements.
If you have any queries please contact us for further information.
£30
per month
upfront £300
per year
£60
per month
upfront £600
per year
£120
per month
upfront £1080
per year


Plan for this week
Last week:
- How do we evaluate a program given its AST?
eval :: Env -> Expr -> Value
This week:
- How do we convert program text into an AST?
parse :: String -> Expr
Example: calculator with variables
AST representation:
data Aexpr
= AConst Int
| AVar Id
| APlus Aexpr Aexpr
| AMinus Aexpr Aexpr
| AMul Aexpr Aexpr
Evaluator:
eval :: Env -> Aexpr -> Value
...
Using the evaluator:
> eval [] (APlus (AConst 2) (AConst 6))
λ8
> eval [("x", 16), ("y", 10)] (AMinus (AVar "x") (AVar "y"))
λ6
> eval [("x", 16), ("y", 10)] (AMinus (AVar "x") (AVar "z"))
λ*** Exception: Error {errMsg = "Unbound variable z"}
But writing ASTs explicitly is really tedious, we are used to writing programs as text!
We want to write a function that converts strings to ASTs if possible:
parse :: String -> Aexpr
For example:
> parse "2 + 6"
λAPlus (AConst 2) (AConst 6)
> parse "(x - y) * 2"
λAMul (AMinus (AVar "x") (AVar "y")) (AConst 2)
> parse "2 +"
λ*** Exception: Error {errMsg = "Syntax error"}
Two-step-strategy
How do I read a sentence “He ate a bagel”?
- First split into words:
["He", "ate", "a", "bagel"]
- Then relate words to each other: “He” is the subject, “ate” is the verb, etc
Let’s do the same thing to “read” programs!
Step 1 (Lexing) : From String to Tokens
A string is a list of characters:
First we aggregate characters that “belong together” into tokens (i.e. the “words” of the program):
We distinguish tokens of different kinds based on their format:
- all numbers: integer constant
- alphanumeric, starts with a letter: identifier
+
: plus operator- etc
Step 2 (Parsing) : From Tokens to AST
Next, we convert a sequence of tokens into an AST
- This is hard…
- … but the hard parts do not depend on the language!
Parser generators
- Given the description of the token format generates a lexer
- Given the description of the grammar generates a parser
We will be using parser generators, so we only care about how to describe the token format and the grammar
Lexing
We will use the tool called alex
to generate the lexer
Input to alex
: a .x
file that describes the token format
Tokens
First we list the kinds of tokens we have in the language:
data Token
= NUM AlexPosn Int
| ID AlexPosn String
| PLUS AlexPosn
| MINUS AlexPosn
| MUL AlexPosn
| DIV AlexPosn
| LPAREN AlexPosn
| RPAREN AlexPosn
| EOF AlexPosn
Token rules
Next we describe the format of each kind of token using a rule:
-- Regex: Function: AlexPosn -> String -> Token
+ { \p _ -> PLUS p }
\- { \p _ -> MINUS p }
\* { \p _ -> MUL p }
\/ { \p _ -> DIV p }
\-> LPAREN p }
\( { \p _ -> RPAREN p }
\) { \p _ $alpha [$alpha $digit \_ \']* { \p s -> ID p s }
$digit+ { \p s -> NUM p (read s) }
Each line consist of:
- a regular expression that describes which strings should be recognized as this token
- a Haskell expression that generates the token
You read it as:
- if at position
p
in the input string - you encounter a substring
s
that matches the regular expression - evaluate the Haskell expression with arguments
p
ands
Regular Expressions
A regular expression has one of the following forms:
[c1 c2 ... cn]
matches any of the charactersc1 .. cn
[0-9]
matches any digit[a-z]
matches any lower-case letter
[A-Z]
matches any upper-case letter[a-z A-Z]
matches any letter
R1 R2
matches a strings1 ++ s2
wheres1
matchesR1
ands2
matchesR2
- e.g.
[0-9] [0-9]
matches any two-digit string
- e.g.
R+
matches one or more repetitions of whatR
matches- e.g.
[0-9]+
matches a natural number
- e.g.
R*
matches zero or more repetitions of whatR
matches
QUIZ
Which of the following strings are matched by [a-z A-Z] [a-z A-Z 0-9]*
?
(A) (empty string)
(B) 5
(C) x5
(D) x
(E) C and D
Answer: E
Back to token rules
We can name some common regexps like:
$digit = [0-9]
$alpha = [a-z A-Z]
and write [a-z A-Z] [a-z A-Z 0-9]*
as $alpha [$alpha $digit]*
+ { \p _ -> PLUS p }
\- { \p _ -> MINUS p }
\* { \p _ -> MUL p }
\/ { \p _ -> DIV p }
\-> LPAREN p }
\( { \p _ -> RPAREN p }
\) { \p _ $alpha [$alpha $digit \_ \']* { \p s -> ID p s }
$digit+ { \p s -> NUM p (read s) }
- When you encounter a
+
, generate aPLUS
token - …
- When you encounter a nonempty string of digits, convert it into an integer and generate a
NUM
- When you encounter an alphanumeric string that starts with a letter, save it in an `ID token
Running the Lexer
From the token rules, alex
generates a function alexScan
which
- given an input string, find the longest prefix
p
that matches one of the rules - if
p
is empty, it fails - otherwise, it converts
p
into a token and returns the rest of the string
We wrap this function into a handy function
parseTokens :: String -> Either ErrMsg [Token]
which repeatedly calls alexScan
until it consumes the whole input string or fails
We can test the function like so:
> parseTokens "23 + 4 / off -"
λRight [ NUM (AlexPosn 0 1 1) 23
PLUS (AlexPosn 3 1 4)
, NUM (AlexPosn 5 1 6) 4
, DIV (AlexPosn 7 1 8)
, ID (AlexPosn 9 1 10) "off"
, MINUS (AlexPosn 13 1 14)
, ]
> parseTokens "%"
λLeft "lexical error at 1 line, 1 column"
QUIZ
What is the result of parseTokens "92zoo"
(positions omitted for readability)?
(A) Lexical error
(B) [ID "92zoo"]
(C) [NUM "92"]
(D) [NUM "92", ID "zoo"]
Answer: D
Parsing
We will use the tool called happy
to generate the parser
Input to happy
: a .y
file that describes the grammar
Wait, wasn’t this the grammar?
-- Abstract Syntax Tree for expressions
data Aexpr
= AConst Int
| AVar Id
| APlus Aexpr Aexpr
| AMinus Aexpr Aexpr
| AMul Aexpr Aexpr
This was abstract syntax
Now we need to describe concrete syntax
- What programs look like when written as text
- and how to map that text into the abstract syntax
Grammars
A grammar is a recursive definition of a set of parse trees
A grammar is made of:
Terminals: the leaves of the tree (tokens!)
Nonterminals: the internal nodes of the tree
Production Rules that describe how to “produce” a non-terminal from terminals and other non-terminals
- i.e. what children each nonterminal can have:
Aexpr : -- Non-term Aexpr can be either:
| TNUM -- Term of format "number", or
| ID -- Term of format "identifier", or
| '(' Aexpr ')' -- Term '(', non-term Aexpr, term ')'
| Aexpr '*' Aexpr -- Non-term Aexpr, term '*', non-term Aexpr
| Aexpr '+' Aexpr -- Non-term Aexpr, term '+', non-term Aexpr
| Aexpr '-' Aexpr -- Non-term Aexpr, term '-', non-term Aexpr
Parse a string s
= find a parse tree from the grammar, whose leaves spell out s
- here “string” means “list of tokens” (output of the lexer)
Example: Here is a parse tree for the string (x + 2)
:
Aexpr
/ | \
'(' | ')'
Aexpr
/ | \
Aexpr '+' Aexpr
| |
'x' (ID) '2' (TNUM)
QUIZ
Which string cannot be parsed as Aexpr
?
Aexpr : TNUM
| ID
| '(' Aexpr ')'
| Aexpr '*' Aexpr
| Aexpr '+' Aexpr
| Aexpr '-' Aexpr
(A) x
(B) x 5
(C) (x +) 5
(D) x + 5 + 1
(E) B and C
Answer: E
Attribute Grammars
So far: grammar tells us whether a string is syntactically correct or not
We want more: convert a string into an AST
parse :: String -> AExpr
An attribute grammar associates a value with each node in the parse tree
- each production is annotated with a rule
- a rule computes the value of a non-terminal from the values of its children
- here value = AST (i.e. Haskell value of type
AExpr
)
Attribute grammar for arithmetic expressions:
-- Format Value
Aexpr : TNUM { AConst $1 }
| ID { AVar $1 }
| '(' Aexpr ')' { $2 }
| Aexpr '*' Aexpr { AMul $1 $3 }
| Aexpr '+' Aexpr { APlus $1 $3 }
| Aexpr '-' Aexpr { AMinus $1 $3 }
$1
refers to the value of the first child$2
refers to the value of the second child- …
Example: Computing the value (AST) of (x + 2)
:
Aexpr ===> APlus (AVar "x") (AConst 2)
/ | \
'(' | ')'
Aexpr ===> APlus (AVar "x") (AConst 2)
/ | \
AVar "x" <=== Aexpr '+' Aexpr ===> AConst 2
| |
'x' (ID) '2' (TNUM)
How do we compute the value of a terminal?
How do we map terminal
'x'
to string"x"
?How do we map terminal
'2'
to integer2
?
Terminals
Terminals correspond to the tokens returned by the lexer
In the .y
file, we have to declare which terminals in the rules
correspond to which tokens from the Token
datatype:
-- Terminals: Tokens from lexer:
TNUM { NUM _ $$ }
ID { ID _ $$ }
'+' { PLUS _ }
'-' { MINUS _ }
'*' { MUL _ }
'/' { DIV _ }
'(' { LPAREN _ }
')' { RPAREN _ }
Each thing on the left is terminal (as appears in the production rules)
Each thing on the right is a Haskell pattern for datatype
Token
We use
$$
to designate one parameter of a token constructor as the value of that token- we will refer back to it from the production rules
QUIZ
What is the value of the root AExpr
node when parsing 1 + 2 + 3
?
Aexpr : TNUM { AConst $1 }
| ID { AVar $1 }
| '(' Aexpr ')' { $2 }
| Aexpr '*' Aexpr { AMul $1 $3 }
| Aexpr '+' Aexpr { APlus $1 $3 }
| Aexpr '-' Aexpr { AMinus $1 $3 }
(A) Cannot be parsed as AExpr
(B) 6
(C) APlus (APlus (AConst 1) (AConst 2)) (AConst 3)
(D) APlus (AConst 1) (APlus (AConst 2) (AConst 3))
Answer: Could be C or D
Running the Parser
First, we should tell the parser that the top-level non-terminal is AExpr
:
%name aexpr
From the production rules and this line,
happy
generates a function aexpr
that tries to parse a sequence of tokens as AExpr
We package this function together with the lexer and the evaluator into a handy function
evalString :: Env -> String -> Int
We can test the function like so:
> evalString [] "1 + 3 + 6"
λ10
> evalString [("x", 100), ("y", 20)] "x - y"
λ80
> evalString [] "2 * 5 + 5"
λ20
> evalString [] "2 - 1 - 1"
λ2
Precedence
> evalString [] "2 * 5 + 5"
λ20
The problem is that our grammar is ambiguous!
There are multiple ways of parsing the string 2 * 5 + 5
, namely
-- Good: Bad:
Aexpr Aexpr
/ | \ / | \
Aexpr '+' Aexpr Aexpr '*' Aexpr
/ | \ | | / | \
Aexpr '*' Aexpr '5' '2' Aexpr '+' Aexpr
| | | |
'2' '5' '5' '5'
Wanted: tell happy
that *
has higher precedence than +
!
Solution 1: Grammar factoring
We can split the AExpr
non-terminal into multiple “levels”
Aexpr : Aexpr '+' Aexpr
| Aexpr '-' Aexpr
| Factor
Factor : Factor '*' Factor
| Factor '/' Factor
| TNUM
| ID
| '(' Aexpr ')'
Intuition:
- Only a
Factor
can be multiplied / divided - Any
Factor
can be promoted to anAExpr
- But to turn an
AExpr
into aFactor
, we need parentheses
Now the only way to parse 2 * 5 + 5
is:
-- Good:
Aexpr
/ | \
Aexpr '+' Aexpr
| |
Factor Factor
/ | \ |
Factor '*' Factor '5'
| |
'2' '5'
If we start parsing the wrong way, we get:
-- Bad???
Aexpr
|
Factor
/ | \
Factor '*' Factor
| |
'2' -- cannot parse "5 + 5" as Factor!
Associativity
> evalString [] "2 - 1 - 1"
λ2
With our original grammar,
there are multiple ways of parsing 2 - 1 - 1
, namely
-- Good: Bad:
Aexpr Aexpr
/ | \ / | \
Aexpr '-' Aexpr Aexpr '-' Aexpr
/ | \ | | / | \
Aexpr '-' Aexpr '1' '2' Aexpr '-' Aexpr
| | | |
'2' '1' '1' '1'
Wanted: tell happy
that -
is left-associative!
QUIZ
Can our new grammar still parse 2 - 1 - 1
the wrong way?
Aexpr : Aexpr '+' Aexpr
| Aexpr '-' Aexpr
| Factor
Factor : Factor '*' Factor
| Factor '/' Factor
| TNUM
| ID
| '(' Aexpr ')'
(A) Yes
(B) No
Answer: A
There are still multiple ways of parsing 2 - 1 - 1
, namely
-- Good: Bad:
Aexpr Aexpr
/ | \ / | \
Aexpr '-' Aexpr Aexpr '-' Aexpr
/ | \ | | / | \
Aexpr '-' Aexpr Factor Factor Aexpr '-' Aexpr
| | | | | |
Factor Factor '1' '2' Factor Factor
| | | |
'2' '1' '1' '1'
How do we fix this?
Hint: how do we disallow the RHS of a minus to be a minus?
More grammar factoring!
Aexpr : Aexpr '+' Factor
| Aexpr '-' Factor
| Factor
Factor : Factor '*' Atom
| Factor '/' Atom
| Atom
Atom : TNUM
| ID
| '(' Aexpr ')'
-- Good:
Aexpr
/ | \
Aexpr '-' Factor
/ | \ |
Aexpr '-' Factor Atom
| | |
Factor Atom '1'
| |
Atom '1'
|
'2'
-- Bad???
Aexpr
/ | \
Aexpr '-' Factor
| -- cannot parse "1 - 1" as Factor!
Factor
|
Atom
|
'2'
Solution 2: Parser directives
This problem is so common that parser generators have a special syntax for it!
%left '+' '-'
%left '*' '/'
What this means:
- All our operators are left-associative
- Operators on the lower line have higher precedence
That’s all folks!


m
Type
▾
Name
▾
URL
▾
Type
Type
Name
URL
No entities found within a
100
meter radius. Try moving to a different location and refreshing.


Spiral Dynamics measurement
Please select the fields that you feel resonate most with your beliefs.
(for best results, please look at all options!)
Score my results!
The results are in!
It looks like you're predominantly... “
”
Learn more about these types at
Wikipedia
Here's the breakdown of your other traits


Collective
Overview
This extension defines the Collective and DispensingCollective classes, which can help with situations where you have one object representing a collective (e.g. a bunch of grapes) and one or more objects representing items drawn from that collective (e.g. individual grapes). The Collective class helps overcome disambiguation issues when both the collective and individual items are in scope (e.g. when 'grapes' might refer either to the bunch of grapes or to one or more individual grapes) and the DispensingCollective class further assists with making the collective issue individual items on demand (e.g. TAKE GRAPE or TAKE GRAPE FROM BUNCH) can be made to move an individual grape into the player character's inventory even when no individual grape is yet in scope).
New Classes, Objects and Properties
In addition to a number of properties/methods intended purely for internal use, this extension defines the following new classes and properties/methods:
- New Classes: Collective, DispensingCollective
- Properties/Methods of Collective: collectiveToks, extraToks, collectiveDobjMatch, collectiveIobjMatch, numberWanted, isCollectiveFor(obj), collectiveAction(np, cmd).
- Additional properties/methods of DispensingCollective: dispensedClass, dispensedObjs, maxToDispense, numLeft, sayDispensed(obj), exhaustDispenser(), cannotTakeFromHereMsg, cannotDispenseMsg, notEnoughLeftMsg.
Usage
Include the collective.t file after the library files but before your game source files. Define an object to be of either the Collective Class or the DispensingCollective class as discussed further below.
Collective Class
The principal purpose of the Collective class is to resolve clashes that might otherwise occur between a collective (such as bunch of grapes) and individual items making up or taken from that collective. For example, supposed we defined an object representing a bunch of grapes together with a class to represent individual grapes thus:
bunch: Thing 'bunch of grapes[n]' ... ; class Grape: Food 'grape; round green juicy loose' "It's round, green and juicy. " disambigName = 'loose grape' ;
If we were then to define a number of grapes (objects of the Grape class) and any of them were in scope at the same time as the bunch object, the noun 'grapes' (by itself) would match either the bunch or any of the individual grapes (since adv3Lite automatically recognizes 'grapes' as the plural of 'grape'). This can lead to annoying disambiguation messages or unintended consequences in response to commands like TAKE GRAPES. Particularly when there is only one loose grape in scope along with the bunch of grapes, the player is likely to intend 'grapes' to refer to the bunch and 'grape' to the individual grape. The use of the disambigName alleviates this problem a little, but does not fully solve it. A better solution is to make the bunch a Collective:
bunch: Collective 'bunch of grapes[n]' isCollectiveFor(obj) { return obj.ofKind(Grape); } ;
The first thing to note here is the use of the isCollectiveFor() method. In order to decide whether it or some other object should respond to the player's command, a Collective needs to know what objects it needs to be distinguished from, that is, which objects it is acting as a collective for. In this case we want the parser to make the correct selection between the bunch of grapes and individual (loose) grapes; in other words the bunch is a collective for individual grapes, which in this case will be objects of the Grape class. We thus need isCollectiveFor(obj) to return true if and only if obj is a Grape.
But there's more going on behind the scenes that immediately meets the eye here. How does the parser know when what the player types is meant to refer to the bunch rather than to loose grapes? It's clear enough if the player types BUNCH, but not so clear if the player types GRAPES. The answer is that the parser looks to see if what the player typed to described the object in question has any words (or 'tokens') in common with those defined on the Collective's collectiveToks property. In the example above, however, this property seems not to have been defined. In fact, if it's not defined by the user (game author), the library defines it automatically from the Collective's name property; so in the example above the collectiveToks property is automatically set to ['bunch', 'of', 'grapes'].
Much of the time this will work perfectly well, but what if our Collective can be described in more than one way? Suppose, for example, we have a stack of cans object that can also be referred to as a pile of tins. If we left the library to set the collectiveToks property the parser would still take CANS to refer to the stack rather than to one or more individual cans, but could not make the same distinction if the player typed TINS. One way to deal with this would be to set the collectiveToks property manually:
canStack: Collective 'stack of cans;;pile tins' isCollectiveFor(obj) { return obj.ofKind(Can); } collectiveToks = ['stack', 'of', 'cans', 'pile', 'tins'] ;
This will work since the library will see that we've defined the collectiveToks property for ourselves, so it won't overwrite it with the tokens from the name property, but it's a bit more typing than we need to do, since we've had to repeat the 'stack', 'of', 'cans' that the library would otherwise have dealt with for us. A better alternative, then, is to use the extraToks property. If the extraToks property is defined as a list of tokens, these will be added to the list of tokens the library builds from the Collective's name when it initializes its collectiveToks property. The above example could then be re-written:
canStack: Collective 'stack of cans;;pile tins' isCollectiveFor(obj) { return obj.ofKind(Can); } extraToks = ['pile', 'tins'] ;
Finally, action-processing code (dobjFor and iobjFor methods) defined on a Collective can test the value of its collectiveDobjMatch and collectiveIobjMatch properties to see whether the Collective was matched as either the direct or indirect object of the command using any of the tokens in its collectiveToks property. This is useful when the Collective also includes singular words in its vocab (e.g. 'grape' or 'tin') so that the player can refer to an individual grape or tin before any have come into scope. The reason for this will become clearer when we go on to discuss the DispensingCollector class.
 Often, if we have a collective object such as bunch of grapes, we want the player to be able to take individual items, such as grapes, from it. For this purpose we can use the DispensingCollective class. This is best explained by way of an example: here's how we might define a bunch of grapes from which the player will be allowed to take up to five grapes:
bunch: DispensingCollective 'bunch of grapes[n]; another more; grape' dispensedClass = Grape maxToDispense = 5 cannotDispenseMsg = '{I}{\'ve} had quite enough grapes. ' notEnoughLeftMsg = '{I} {don\'t need} that many grapes. ' ;
There are a number of points to note here. First, the word 'grape' is included as an addition noun in the noun section of the vocab property. This allows the player to type TAKE GRAPE even when there are no individual grapes in scope, since the Collective (bunch) object will match 'grape'. When it matches, however, its collectiveDobjMatch property will be set to nil, since it did not match on any of the tokens on the bunch's collectiveToks property. This tells the bunch object that the player wants to take an individual grape from the bunch rather than the whole bunch. We don't have to do anything to make this happen; this behaviour is all defined on the DispensingCollective class, so that the command TAKE GRAPE (or TAKE GRAOE FROM BUNCH) will result in the bunch object creating a new object of the Grape class and moving into the player character's inventory.
Once a separate grape object exists, however, (and while it's in scope at the same time as the bunch), the parser will take TAKE GRAPE (or any other command using the singular GRAPE) to refer to the loose grape rather than the bunch of grapes. That's why it's a good idea to add words like 'another' and 'more' as adjectives for the bunch object. Since the individual, loose grapes don't have these words in their vocab, commands like TAKE ANOTHER GRAPE or TAKE TWO MORE GRAPES must refer to the bunch object, and not to a Grape object, and so the result will be that the bunch once again creates and dispenses a new Grape to the player character (or, two grapes, if the command was TAKE TWO MORE GRAPES).
Presumably we don't, however, want the player to be able to take an infinite number of grapes from the bunch, so that's where the maxToDispense property comes in. This property defines the maximum number of grapes the player will be allowed to take from the bunch. If the player tries to take more then the bunch will complain using either its cannotDispenseMsg, if the player has taken the maximum number of grapes, or the notEnoughLeftMsg if there are still some grapes left to take but the player wants to take more than that number (e.g. TAKE THREE GRAPES when only two remain). The library does define default messages for both these properties, but they are sufficiently bland that you'll normally want to override them with your own (it's impossible to know in general the precise reason why you might want to restrict the number of items to issue to the specific number you choose).
As you can probably guess, the dispensedClass method is used to tell the Collective what class of object to create when the player wants it to dispense an individual object. As an alternative you can use the dispensedObjs property to hold a list of the objects that the Collective will dispense. For example, if you only wanted to allow the player to take a single grape from the bunch, then instead of defining a Grape class you could define a single grape object and the bunch thus:
bunch: DispensingCollective 'bunch of grapes[n]; another more; grape' dispensedObjs = [grape] cannotDispenseMsg = '{I}{\'ve} had quite enough grapes. ' notEnoughLeftMsg = '{I} {don\'t need} that many grapes. ' ;
Note that in this instance there's no need to define the maxToDispense property, since the maximum number of objects that can be dispensed is simply the length of the dispensedObjs list. Note finally that whether you define dispensedClass or dispensedObjs, there is no need to define the isCollectiveFor(obj) method on a DispensingCollective, since it this method is already defined there, and can figure out whether obj is something the DispensingCollective is a collective for from its dispensedClass or dispensedObjs property.
The exhaustDispenser() provides a further way to customize the behaviour of a DispensingCollective. It's called when the DispensingCollective dispenses its final item. By default it does nothing, but it can easily be overridden to carry out some side-effect of running out of items to dispense, such as changing the description, for example:
bunch: DispensingCollective 'bunch of grapes[n]; another more ;grape' dispensedClass = Grape maxToDispense = 5 cannotDispenseMsg = '{I}{\'ve} had quite enough grapes. ' notEnoughLeftMsg = '{I} {don\'t need} that many grapes. ' exhaustDispenser() { setMethod(&desc, 'The bunch is looking a bit depleted; there aren\'t really any grapes left that are worth taking. '); } ;
If we had been dealing with a bunch of bananas rather than a bunch of grapes, the effect of taking the last banana from the bunch (when there are two bananas left) would be to leave a single banana behind, so we might do something like this:
bananaBunch: DispensingCollective 'bunch of bananas[n]; another more; banana' "There are <<spellNumber(numLeft)>> bananas left on the bunch. " dispensedClass = Banana maxToDispense = 5 exhaustDispenser() { local b = new Banana; b.moveInto(location); moveInto(nil); } ;
This covers most of what you need to know to use this extension. For additional information see the source code and comments in the collective.t file.


--- layout: post title: You got NoSQL in my Postgres! Using Hstore in Rails date: '2012-03-14T14:40:00-05:00' tags: - heroku - postgres - hstore - rails - ruby on rails permalink: /post/19298469372/you-got-nosql-in-my-postgres-using-hstore-in-rails ---
Heroku just announced their support of hstore in their dedicated Postgres 9.1 instances. Hstore is a schema less key value store inside of PostgreSQL that allows us to store data like hashes directly inside of a column. It’s great for when you don’t know exactly what types of attributes you need to store on a model, or if you need to support many different attributes for the same model.
Update: You can now use Hstore with development databases on Heroku
A good example is storing attributes for a Product model. We might start out only selling books, which have an author, number of pages, but then transition over to selling laptops which have cpu speed and display resolution. Using Hstore allows us to easily store all these values without having to make a bunch mostly blank columns.
To get started with Rails and hstore you can watch the screencast below or visit the hstore example app running on Heroku.
Hstore in Rails functions much like serializing hashes, except that we can query our data much faster since hstore is a native data type. It is supported natively in Rails 4, but until then we’ll need to use the activerecord-postgres-hstore gem.
You will need a version of PostgreSQL locally that supports the hstore extension. I recommend installing postgres using homebrew on OS X. Once you’ve done that you can enable hstore usage by running this in Postgres
CREATE EXTENSION hstore;
You can put this in a migration if you prefer
class SetupHstore < ActiveRecord::Migration
def self.up
execute "CREATE EXTENSION hstore"
end
def self.down
execute "DROP EXTENSION hstore"
end
end
Once that is done you will need to create a column with a type of hstore, here we are giving our Product model a column called data
with hstore type.
class CreateProducts < ActiveRecord::Migration
def change
create_table :products do |t|
t.string :name
t.hstore :data
t.timestamps
end
end
end
Once we’ve done that we can now store any type of attributes in the data column.
Product.create(:name => "Geek Love: A Novel", :data => {'author' => 'Katherine Dunn', 'pages' => 368, 'category' => 'fiction'})
Product.last.data['category'] # => 'fiction'
Not only does hstore allow us to store arbitrary keys and values it allows us to quickly query them.
# Find all products that have a key of 'author' in data
Product.where("data ? :key", :key => 'author')
# Find all products that have a 'pages' and '368' key value pair in data
Product.where("data @> (:key => :value)", :key => 'pages', :value => '368')
# Find all products that don't have a key value pair 'pages' and '999' in data
Product.where("not data @> (:key => :value)", :key => 'pages', :value => '999')
# Find all products having key 'author' and value like 'ba' in data
Product.where("data -> :key LIKE :value", :key => 'author, :value => "%Kat%")
More information available in the Postgres hstore docs.
Though like a normal column if you query it frequently, you can get even more speed by adding an index. You can do this using one of two indexes that also speed up full text searches. They’re GiST (Generalized Search Tree) or GIN (Generalized Inverted iNdex). Which sill speed up queries using the @>
and ?
postgres operators.
class Index < ActiveRecord::Migration
def up
execute "CREATE INDEX products_gin_data ON products USING GIN(data)"
end
def down
execute "DROP INDEX products_gin_data"
end
end
Try out the hstore example app, clone the Github repo, and let me know what cool things you build on twitter @schneems.
Special thanks to Aaron Patterson and Joel Hoffman for their work with hstore & Rails4, to the team at Softa for writing this gem, & and the team at Heroku for their contributions to Postgres, and supporting this feature.


How to improve your home more Guest-Friendly
Once you have your rental posted, you should focus on how yo…
Read articleNo matter what kind of home you have to share, you can increase your earnings.
Get to know guests before they stay by reading reviews to keep your home safe
Tired of secretive bids? Our platform provides a transparent rental auction with the ability to submit custom offers.
Share any space from a shared living room to a second home and everything in-between.
You’re free to choose your own schedule, prices, and requirements for guests.
Once your listing is live, qualified guests can reach out and you can message them.
Hosting has helped me pay for a new living room and other upgrades. I would continue to use this platform forever.
Daniel hosts in Yogyakarta to earn extra moneyTired of secretive bids? Our platform provides a transparent rental auction with the ability to submit custom offers.
Listing more than one?  Once you list a property on our platform, you only have to sit back and watch the world want it.
Once a guest checks in, we can send your money by Paypal, direct deposit, or other available methods.
Charge what you want
Free to list your space
Pay low fees
Customer Support
Get paid quickly
We share common trends and strategies for improving your rental income and making sure you stay in high demand.
Once you have your rental posted, you should focus on how yo…
Read articleWe share common trends and strategies for improving your rental income and making sure you stay in high demand.
Avarage stays each night
New hosts per month
Hosts on our platform
Total happy guests
Rent out your house, apartment or room & earn extra money quickly.


Weekly Updates : Javascript Rocks!
After quit from Idapted/Eleutian, I’d like to have a long term plan for writing blogs. However, as everything starts, it’s always hard. I thought it around, and finally decided to open my mind, and just try to start with a strong belief : Sharing! Then an idea comes out.
First of all, I think my weekly updates will help to share great things I came across or even I started to use, it has much fun to replay all these stuff, so I will do that in the weekend, that’s also part of my study review.
Ok, turn to the points:
- Now you must know JSLint. JSlint is a Javascript checker, which can help you ensure your JS’ quality. There are many great open source projects helping you to program in Javascript.
Sources: Github Proj: https://github.com/douglascrockford/JSLint Add JSLint to you TextMate: https://github.com/subtleGradient/javascript-tools.tmbundle
Are you familiar with BDD? You should start to learn it now if you did not have a change to give it a try. Specially, if you are using Ruby/Ruby on Rails, I strongly recommend you try rSpec for the BDD. Back to the topic, I wanna share with you some similar BDD way in programming Javascript, jspec and Jasmine, obviously, they are cool. I just mention this as a reminder people who want to try interesting programming. I didn’t try them yet, but I guess I will do that in soon.
Books to speed you up! • Javascript: The Good Parts (http://oreilly.com/catalog/9780596517748) • JavaScript Patterns (http://oreilly.com/catalog/9780596806767/) • High Performance JavaScript (http://oreilly.com/catalog/9780596802806/)
Javascript is a long journey, I am just on the start point. Anyway, exciting!


About AuctionHouse
AuctionHouse is a decentralized app built on Ethereum to facilitate the auction of on-chain goods. Here are some frequently asked questions about AuctionHouse.
What can I auction on AuctionHouse?
Any on-chain asset that conforms to the Asset contract interface can be auctioned. Some examples of things that could be auctioned are virtual goods, domain names, tokens that represent a role in an on chain economy, or tokens that represent real world assets like tickets.
Is AuctionHouse production ready and running on the Ethereum mainnet?
No. AuctionHouse is in alpha and is running on the Ethereum Ropsten Testnet. We will deploy it on the mainnet after a round of testing and security scrutiny from the community. There are also a number of semantic points that we'd like feedback on including how to best standardize representation of on chain non fungible assets in a secure way, so there may be changes before deploying to the mainnet.
I keep seeing errors about not having a connection to the network. What's this about?
As a decentralized app running on Ethereum, every user will need to be able to connect to an Ethereum node in order to interact with AuctionHouse. The easiest way to do this is to install MetaMask which is a browser extension that will connect you to Ethereum. You can also run your own Ethereum node locally. Remember that the contract is not live on the Mainnet yet, so point your Metamask or Node to the Ropsten Testnet to try out AuctionHouse.
When my auction ends, or when I get outbid, why do I have to "withdraw" my funds?
For security purposes, it is recommend that Dapps use the withdraw pattern to let people remove their funds from contracts. This prevents a class of attacks where you should receive funds, but the attacker prevents it via stack limits. Instead, we will always show you your withdraw balance, and let you hit the withdraw button to receive these funds as long as your balance is greater than zero.
When I go to create an auction, why do I have to register a name?
Since AuctionHouse is in alpha, and there aren't a lot of auctionable non-fungible goods on the blockchain yet, we wanted to make it easy for people to own an asset that they can auction. For demo purposes we created a simple contract called SampleName, and the "register a name" section on the create auction page is just an interface to register a fake domain name, so that you can create an auction with it. This step is optional, and if you were to deploy a contract that conformed to the Asset interface, then you could auction off a record from that contract.
Who created AuctionHouse?
AuctionHouse was built by Doug Petkanics and Eric Tang. We built it in order to get experience building an end-to-end Dapp complete with protocol layer, UI, and decentralized deployment. It's a work in progress, and we'd love to hear any feedback or bug reports through twitter or email, or on github issues.


SB Counsellor
Nominee faculty
Nominee faculty
Nominee faculty
President
Vice-President
Secretary
Vice-Secretary
Treasurer
Vice-Treasurer
Web Master
Web Master
Marketing/Event Handler
Event Handler
Marketing/Logistics
Marketing
Marketing
Marketing
Graphic Geek
Graphic Geek
Tech. Director
Tech. Director
Co ordinator Dr K Madhavi
Faculty Advisors
Core Committee Members


collision
namespace collision
Collision detection (and projection-based collision response) of 2D shapes.
Based on the Separating Axis Theorem and supports detecting collisions between simple Axis-Aligned Boxes, convex polygons and circles based shapes.
Summary
Properties from collision
| number |
|
| number |
|
Methods from collision
| Array<Renderable> |
|
Public Properties
static maxChildren: number = 8
number
The maximum number of children that a quadtree node can contain before it is split into sub-nodes.
static maxDepth: number = 4
number
The maximum number of levels that the quadtree will create.
Public Methods
rayCast(line: Line, result: Array<Renderable>) → {Array<Renderable>}
Checks for object colliding with the given line
// define a line accross the viewport
let ray = new me.Line(
// absolute position of the line
0, 0, [
// starting point relative to the initial position
new me.Vector2d(0, 0),
// ending point
new me.Vector2d(me.game.viewport.width, me.game.viewport.height)
]);
// check for collition
result = me.collision.rayCast(ray);
if (result.length > 0) {
// ...
}
| Name | Type | Attributes | Description |
|---|---|---|---|
| line | Line |
line to be tested for collision |
|
| result | Array<Renderable> |
<optional> |
a user defined array that will be populated with intersecting physic objects. |
| Type | Description |
|---|---|
| Array<Renderable> |
an array of intersecting physic objects |


---
layout: post
title: Get Started With Mozilla's WebIDE
date: 2014-11-11 20:24:00.000000000 +05:30
type: post
published: true
status: publish
categories: []
tags: []
meta:
blogger_blog: www.technoruler.com
blogger_author: VISWAPRASATH KS
blogger_cb097c283b9bbfde97b4822e7250157d_permalink: '6605839023261702719'
author:
login: viswaprasath
email<EMAIL_ADDRESS>display_name: Vitchu
first_name: ''
last_name: ''
---
Yesterday Mozilla has introduced their new Browser for developers. In that we have seen there are lot of tools which Mozilla has on its own to help developers. Among that most interesting developer tool is Web IDE. The aim of the WebIDE is to help developers to write the HTML5 codes which are very specific to Mobile devices.
|
| Web IDE on Firefox |
When you see to Toolbar the last button which is circular in shape with pencil is the WebIDE. We can easily click on it to open the WebIDE.
|
| Web IDE |
When we open we can see the simple screen of Web IDE as above. As you can see the structure of the WebIDE is very simple.
Project button : From this we Create the new app, we can open the existing packaged app or hosted app or we can set the preference according to our need.
Runtime button: We have the option to do Monitor of the application, and when the application is running in the simulator we can easily take the screenshot, we can know about the run time information and also the permission table information about the particular application.
View Button: It helps us to install / uninstall various simulators available.
Open App: We use it for open the existing apps, When the simulator is running we can ask interact with the Firefox OS apps (like alarm, call screen)
In the right most side we have a drop down with which we can select the list of Firefox OS Simulator, we can also connect with the devices attached like Firefox OS device.


Project Glinda: Overview
Version Control
| Version | Date | Changes |
|---|---|---|
| 0.1 | 05/01/2017 | Initial draft |
| 0.2 | 06/01/2017 | Added details for monitoring and auditing |
| 0.3 | 11/01/2017 | Details of logging on and device locking to account have been added. |
Overview
Obviously, dating apps are nothing new, with Tinder being the current market leader, however there is seen rather a dark side to Tinder. It’s too easy to get an account, and you’re never sure if the person you’ll meet aligns to the photograph.
This level of unease is greatest within the gay and lesbian community, for whom 2016 has seen increasing aggressive behaviour towards this community. We want to create a dating experience where people can feel more comfortable that the person they meet will be the person in the photograph.
We also are very mindful that we don’t want to become another organisation which relies of heavy data mining, and requires official documents. But for the safety of our users, we want to be sure the person with the account is someone we know about.
We started work in earnest to create a Gay and Lesbian Tinder, or Glinda, with the mission of providing as safe a dating experience for the gay and lesbian community as possible.
A key part is to have a validated account, you need to register an account with a valid credit card. One of our admins will then video phone you to speak with you – this conversation will be recorded, if you match the profile picture you’ve provided, you become a registered user – that’s all there is to it, no paperwork.
To achieve all this we are using some services which already exist such as Skype for communications and dcPay as our payment gateway to minimise what we need to build from scratch.
Scope
Mobile Support
Glinda will be an application which will work on iOS and Android phones. The application will require a relatively modern phone – we’ll only support iOS 10 and up, and Android KitKat and beyond. Likewise a selfie camera is a must, and when a user tries to install this software on a phone which lacks this, it must result in an error.
Out of scope - phase 1
Currently we’re trying to launch Glinda with provision for supporting gay and lesbian users only, we’re hoping to expand the base to support bisexual and transgender communities in later releases.
Account Status
A key part of Glinda is knowing you’re really dealing with someone who has been verified. It’s about making the community feel safer when dating.
Account registration includes several aspects to this – you need both a credit card, and to have been verified by back office staff to match the person who submitted a photograph.
The following outlines the lifecycle of account statuses as an application progresses,
Submitted
Users must enter their details together and then take a photo with the selfie camera. They then need to supply a valid credit card details (which match the name they’ve given), and are initially charged $1.
At this point, the user account is in a Submitted state. The users personal details are kept in a database, with their separate payment details kept in another, more secure database which only our payment applications will access. There will be a unique key linking user record to payment details.
In addition, a Skype account will be created on their behalf. This will be a unique key “_Glinda_XXXXXX” account. We don’t want to build our own communications channel, so are building on top of Skype. Our application will provide some throttling on communications.
Part Verified
Within 48 hours, one of our back office staff will video conference the applicant. They will hold a brief conversation using the selfie camera. Their goal is to confirm the applicant is real, and matches the photograph provided. This video conversation will be recorded, and stored in a video specific database with a unique key. The back office operator will record the unique video key at time of registration.
If the back office operator is satisfied that they have spoken to the person who submitted the application matches the person they spoke to the account is Part-Verified. Otherwise it will be set to either Resubmit if there are issues with the photo or Suspended if fraud is suspected.
The video chat uses a Skype channel to carry the conversation, but the call is launched through our Glinda app.
Fully Verified
Once an account is Part-Verified, a second admin user should review the account and the conversation, and once satisfied can make the account Verified. If they’re not satisfied, they can return to either Submitted or Suspended.
A user can choose to also add a new profile photo, which sets the account to New Photo.
This user has to be a different admin to the one who performed the Part-Verified process in line with our four eyes policy.
Only Verified accounts can use the dating applications.
Suspended
If the account is considered fraudulent or it’s felt a user has broken the Glinda code of conduct, their account can be suspended by Glinda admin staff. A note will be made against the card details supplied so it cannot be used to create a new account.
Resubmit Required
If the Glinda admin is not satisfied that the user she’s video calling relates to the photo submitted, or the photo is of poor quality, they can set the account to Resubmit.
Next time the user log in with the mobile application, they are taken to the selfie page to retake their photo. Once submitted, their account is set back to Submitted.
Payment Overdue
The account is charged an admin fee every 30 days. If this payment fails, the account is moved into Overdue Payment, and they’re unable to continue dating services until payment is received.
On Hold
The user can voluntarily put their account on hold. This takes effect the next time payment is due on the account, where instead of being charged, the account is moved from “verified” to “on hold”. They can removed this at any time, but will have to be immediately charged an account fee.
Dormant
A Verified user can toggle their account between Verified and Dormant. Dormant simply makes clear they’re not interested in dating at the moment.
New Photo Added
If the user wants to submit a new photo, it is taken out of ‘Verified’ mode and moved into ‘New Photo Added’.
They are taken to the ‘Take My Picture’ page, and a new photo taken.
The Glinda admin can review this picture versus the old one, and if satisfied update the photo and revert the account to Verified.
The user can choose to cancel ‘New Photo Added’ in which case their account will revert to their old profile picture.
Logging On
Registration automatic log on
After a user has successfully submitted their username and password from a phone, they are automatically logged into Glinda with their credentials.
As part of the submission process, details of the mobile phone used are sent as part of the registration process. The phone becomes locked to the account, and a new account cannot be used on this device.
This is to reduce people from using one device for multiple accounts, something which is associated with fraud.
If a user downloads the app on the phone, and tries to go to registration, a check will be performed, and they will receive a warning message ‘It looks like this device has been previously registered for an account. Welcome back!’.
Log on to a device registered to another user
If a user attempts to log on to a device which is already registered to another user, an error message will be raised, and they will not be allowed to log on.
Logging out
The user can log out of the app or webpage at any time.
Log on to new device with credentials
If the user logs into an existing account on a new device, that device is also locked to the user account. A user account can have a relationship to multiple devices.
Log on to a web page
When logging into the web page, the functionality available is limited, in this case, no form of device matching occurs.
Customer log on support
If the user has forgotten either their username or password, they can enter the email address they used to register, either their username, or a 1-hour, one-use-only password will be emailed to them.
The login service will only allow 3 incorrect logins to an account within an hour.
Most of this behaviour is considered industry-standard.
Dating services
The Glinda app requires permissions for GPS. We poll devices hourly and record their location – these are kept in a table with location, time, and people’s unique key only. We only record current location, no historical record will be kept.
A user can refresh their location at any time.
Using the app, a user perform a search and can select the radius of people they’re interested in, the system will then create a list of users who match sexual preference in that radius.
Every user has a list of user IDs for which they’re part-matched, matched, mis-matched, blocking.
The user will be shown other users profiles which match criteria (including the rules below).
Part-matched
A user shows an interest in a profile when they swipe left. At this point, they become part-matched.
The profile will then no longer be visible to them when they do a search, however right now they can’t make contact.
Matched
If the other party also has swiped left on this users, the couple is matched. They have both shown an interest in each other. It’s only at this point that communications can occur between couples on the Glinda app.
Mis-matched
If a user isn’t interested in a profile, they swipe right. We call this a mis-match, and record the user ID and date occurred together with a count.
We won’t show that profile again to the user for 2 days. If a user swipes right 3 times, we’ll but the profile under blocking.
Blocking
If a user has swiped right a profile 3 times, we put it to blocking. The user will not see that profile every again. Likewise, the person with the rejected profile will not be able to see the other users profile from dating searches.
In addition, if a user is uncomfortable with someone they’re matched to, they can select to block them. Users will be allowed to report if they feel they’ve had an interaction which has unsettled them, including a reason.
Use of Skype communications
Two users who are matched to each other can exchange communications. We use anonymised Skype accounts to allow this to occur. We also filter requests in our app service layer to make sure that only communication requests from either admins or matched users are allowed through.
Use of audio/video will only be allowed for admins. They must use video for part-verified confirmation. They can contact users when investigating any reported incidents. These interactions will be recorded.
Matched users are only allowed to interact by text or photo exchange. Each exchange will be recorded in a message table on the database including,
- Datetime
- From user
- To user
- Content
Admins will be able to retrieve conversations only if a user has reported another users actions.
Glinda will not use the Skype application on mobile devices, but will utilise the channel. Each Glinda account will use a secret, anonymous Skype account not normally available on Skype. People on Skype will not be able to make contact as users are only signed in via the Glinda application, and the app layer will not allow chats to get through which are not from admins or are a matched user.
Suggest meetup
We keep a list of nearby public venues which are gay and lesbian friendly and are located nearby.
The database keeps details of their name, location and a website link.
In future we might look to having these places fund for top selection places.
AI filter for photos sent
When a photo is sent between matched users, we will run the photo through the Google Cloud Vision API.
If this returns that the picture was obscene (containing genitalia), the photo will not be sent, and the sender warned our obscenity filter.
We expect to bring in a contractor used to working with this sort of material to deal with this aspect of behaviour, and limit exposure of indecent material to staff. We will also respect staffs wishes not to work or view material in this area.
We expect the fine tuning of this to take a while, but marketing expects this to be a key feature to promote safety online.
Channels
Mobile application
The mobile application will be the primary channel for users of Glinda, and available on iOS and Play stores.
The application will require the phone to be iOS 10 or Android KitKat and beyond.
It will require permissions to,
- Camera
- Microphone
- GPS location
Public Website
Most interaction on Glinda will be done via the mobile application, however we will maintain a static website to talk about the service we offer and frequently asked questions.
Users will be able to log in, but have a limited set of functions they can perform,
- Manage my account, including changing password
- Toggle account between verified and either ‘on hold’ or 'dormant' modes
- Edit their payment details
Admin site
An admin site will be supported, which will allow selected users to perform admin roles including,
- Trigger Skype call with users
- Change user state to part-verified, verified, suspended
- View users who have been reported
- Review conversations of a user who has been reported
The admin site will be locked so that it can only be accessed by an IP which relates to being on-site.
Billing
Glinda will use the dcPay payment engine to process payments. There will be an invoice table which includes a list of payment transaction. Only verified accounts will be charged monthly. If a payment fails, this will be recorded in the database, and the users account set to “overdue payment”.
Architecture
The architecture will be maintained in-house by the Glinda ops team. A diagram of this is provided in Appendix A.
The application aims to be highly available using multiple nodes in the database, application and web tiers.
The admin tier will only be single mode.
AppService tier
The app service tier will contain the business rules for Glinda, and also act as a webservice conduit between,
- Skype and both the Admin site and Mobile app
- Run payments through dcPay
- Bring through map data via Google maps
- Provide filtered information as required and authorised to the web tier
This architecture layer is usually called the application layer, but to avoid confusion between the application service layer in the architecture and the mobile application itself, we will refer to it as the app service to differentiate.
Web tier
The web tier will create pages from information and authorisations provided by the app tier for consumption on the admin site or user pages
Database tier
Data is replicated to the primary and secondary database.
Drive storage
Video and audio files of admin calls are stored as flat files within the drive storage, which is backed up hourly. Admins will have read-only access to this area.
High availability
Users will be load balanced across nodes on the web and app server as required. If the node a user is on suffers an outage, they will lose their session with different expected results according to where they’re using.
Users on the web page or admin site will lose their session, and will have to log in again. They might lose some work.
Users on the mobile application whose app node has gone will have their session moved to the other app node. We expect minimal disruption.
Backups
Each tier will have a backup drive which will be triggered hourly. Backup processes should not impact performance. Loss of up to an hour’s data during an incident is considered acceptable.
In the event of a database outage, a process will perform a difference check on the primary and secondary database, copying and missing records onto the other database.
It’s a requirement that the app or web node can be restored from backup in under an hour.
Performance
The system should be able to support,
- 90% of users should have their registration processed (including payment transaction and photo storage) within 30 seconds with a load of 1,000 concurrent users
- 95% of users should have their messages to a matched user delivered within 5 seconds under a load of 10,000 concurrent users
- 90% of users should be able to perform a search of nearby profiles within 20 seconds under a load of 5,000 concurrent users
Monitoring and alerts
Every physical server (equating to every node) will have monitoring enabled on it. This will allow the ops team to keep tabs on the system.
Monitoring
The following items will be monitored and available to ops staff when required,
- CPU usage
- Memory
- Disk free
- Network traffic – including on the app server separate traffic to web server, Skype, Mobile Phone, dcPay and Google Maps traffic
- Heartbeat
Heartbeat is a repeated ping to the node IP that is performed each minute to confirm it’s still working.
Alerts
The ops team can set thresholds for red and amber alerts for any monitored attribute. When these thresholds are exceeded, an email alert will be triggered to the ops staff informing them. This email alert occurs just once, and will not be repeated if that state remains.
These settings will be adjusted by ops as they go and learn more about the system. It’s expected an amber alert will occur if CPU usages is at 90%, with a red alert if CPU usage goes beyond 95%
Likewise if a node heartbeat is zero for 5 minutes it should raise an amber alert, with this raised to a red alert if still down after 15 minutes.
Auditing
All significant actions will be audited, with an entry created in the audit log table.
The initial expected audits are covered in Appendix C. It is expected they will be expanded with the design, and new audits added as needed.
It’s important to note – we’ll audit the location someone uses dating services from, but we’ll not provide historical passive tracking of where users are from our hourly poll. This needs to be communicated to users, and marketing is working on it to make them feel secure and not being spied on.
We do monitor more details on what our admins are doing, as we need to be more accountable.
APPENDIX A - Architecture Diagram
APPENDIX B - Screen Mock Ups
The following are included as examples of key screens, not all screens have been provided.
Welcome Page
Previously Registered Page
Notice how the Registration button has been removed.
Registration Page
Take My Picture Page
Review My Picture Page
Admin Video Call Page
This runs a Skype video call within our mobile application.
Find A Match Page
This page will use the location provided by the mobile phone, and overlay maps provided by Google maps service.
Profile Page
Match Chat Page
APPENDIX C - Auditing
Below is a table of expected results. Key field descriptions,
- ID is a number to categorise the audit number
- UserID is the unique identifier of the user who owns the account
- Body is the descriptor for the audit
- MatchID is the unique identified of a second user – typically that the main actor is interacting with through viewing profile or matching
- AdminID is only filled in for functions where an admin is looking up a user
| ID | UserID | Body | MatchID | AdminID | Datetime | Notes |
|---|---|---|---|---|---|---|
| 101 | N/A | Registration initiated | N/A | N/A | YES | User has opened the registration page. At this point they have no id. |
| 102 | N/A | Selfie initiated | N/A | N/A | YES | User has activated the selfie function |
| 103 | N/A | Photo taken | N/A | N/A | YES | User face recognised and photo taken |
| 104 | N/A | Review my picture | N/A | N/A | YES | User has been shown their picture |
| 105 | N/A | Retake photo | N/A | N/A | YES | User has selected to retake photo |
| 106 | N/A | Submit registration | N/A | N/A | YES | User has submitted their registration |
| 201 | N/A | Registration payment successful | N/A | N/A | YES | User payment has gone through |
| 202 | N/A | Registration payment failed – dcPay down | N/A | N/A | YES | User payment failed due to problem with dcPay system |
| 203 | N/A | Registration payment failed – incorrect card details | N/A | N/A | YES | User payment failed due to faulty card details |
| 204 | N/A | Registration payment failed – insufficient funds | N/A | N/A | YES | User payment failed due to insufficient funds |
| 211 | YES | Monthly payment failed – dcPay down | N/A | N/A | YES | User payment failed due to problem with dcPay system |
| 212 | YES | Monthly payment failed – incorrect card details | N/A | N/A | YES | User payment failed due to faulty card details |
| 212 | YES | Monthly payment failed – insufficient funds | N/A | N/A | YES | User payment failed due to insufficient funds |
| 220 | YES | User has changed payment details | N/A | N/A | YES | The user has changed their payment details |
| 300 | YES | Device ID ####### locked to user account | N/A | N/A | YES | The device has become locked to a user account |
| 301 | YES | User logged into app – automatic registration | N/A | N/A | YES | User has logged into the device automatically as post-registration |
| 302 | YES | User logged into app – logged in via log on page | N/A | N/A | YES | User has logged into the device via the app page |
| 303 | YES | User logged into webpage | N/A | N/A | YES | User has logged into account on a browser |
| 310 | YES | Login attempted, user ID relates to no known user | N/A | N/A | YES | Someone has attempted to login with unknown credentials |
| 320 | YES | Incorrect login | N/A | N/A | YES | Someone has attempted to login using an incorrect password |
| 321 | YES | Account locked for one hour after 3 consecutive incorrect logins | N/A | N/A | YES | Repeated attempts to login to an account has caused it to be locked. |
| 322 | YES | Repeated login attempt during lock out | N/A | N/A | YES | Login attempts have continued once the account was locked. |
| 330 | YES | User has attempted to login to a device locked to another user | N/A | N/A | YES | A user has attempted to login to a device which has been registered and locked to another user. |
| 1000 | YES | User account created, state SUBMITTED. | N/A | N/A | YES | Users account has been created (and payment has gone through) |
| 1001 | YES | User unique Skype account created ID ##### | N/A | N/A | YES | A Skype ID has been created for the user. |
| 1002 | YES | User account PART VERIFIED | N/A | YES | YES | Admin has confirmed identity via Skype |
| 1003 | YES | User account FULLY VERIFIED | N/A | YES | YES | Second admin has reviewed evidence of identity. Account is ready to use. |
| 1004 | YES | User account SUSPENDED reason FRAUD | N/A | YES | YES | Admin suspended account for suspected fraud |
| 1005 | YES | User account SUSPENDED reason ABUSIVE BEHAVIOUR | N/A | YES | YES | Admin suspended account for breaking code of conduct |
| 1006 | YES | User account RESUBMIT REQUIRED | N/A | YES | YES | Admin wants user to resubmit a selfie |
| 1007 | YES | User account OVERDUE PAYMENT | N/A | N/A | YES | Account on hold as monthly fees failed |
| 1008 | YES | User account ON HOLD | N/A | N/A | YES | User has put their account on hold |
| 1009 | YES | User account DORMANT | N/A | N/A | YES | User has put their account to sleep |
| 2000 | YES | User initiated dating services match from location #### | N/A | N/A | YES | User has triggered a dating search from this location |
| 2001 | YES | User initiated dating services match – location not found | N/A | N/A | YES | There was a problem getting the user’s location |
| 2010 | YES | User shown member profile | N/A | N/A | YES | User is shown the profile of another user who matches their criteria |
| 2011 | YES | User has shown interest in other user | YES | N/A | YES | User has shown interest in other user |
| 2012 | YES | User has shown disinterest in other user | YES | N/A | YES | User has shown disinterest in other user |
| 2013 | YES | User has chosen to block other user | YES | N/A | YES | User has chosen to block other user |
| 2020 | YES | Full match has occurred between users | YES | N/A | YES | Records that a match has occurred |
| 2100 | YES | Skype message set to other user | YES | N/A | YES | A Skype message was sent |
| 3001 | N/A | Admin login from IP ##.##.##.## | N/A | YES | YES | Admin user has logged in. We record the IP of the machine used. |
| 3002 | YES | Admin has looked up user | N/A | YES | YES | Admin has looked at a user profile |
| 3003 | YES | Admin initiates Skype call | N/A | YES | YES | Admin has activated phone call |
| 3004 | YES | Skype call ends | N/A | YES | YES | Skype call has ended |
| 3005 | YES | Skype call fails | N/A | YES | YES | System error in Skype has terminated call |
| 3006 | YES | Skype call unanswered | N/A | YES | YES | Skype call was not answered |
| 3005 | YES | Video chat stored as file ID #### | N/A | YES | YES | The video record was stored, including ID |


Henry Wadsworth Longfellow: Selected Works
by Henry Wadsworth Longfellow
To a Child
Additional Information
- Year Published: 1866
- Language: English
- Country of Origin: United States of America
- Source: Longfellow, H.W. (1866) The Complete Works of Henry Wadsworth Longfellow Boston, Massachusetts: Ticknor & Fields
-
Readability:
- Flesch–Kincaid Level: 9.0
- Word Count: 1,150
- Genre: Poetry
- ✎ Cite This
Downloads
Dear child! how radiant on thy mother’s knee,
With merry-making eyes and jocund smiles,
Thou gazest at the painted tiles,
Whose figures grace,
With many a grotesque form and face.
The ancient chimney of thy nursery!
The lady with the happy macaw,
The dancing girl, the grave bashaw
With bearded lip and chin;
And, leaning idly o’er his gate,
Beneath the imperial fan of state,
The Chinese mandarin.

Thousands of years in Indian seas
That coral grew, by slow degrees,
Until some deadly and wild monsoon
Dashed it on Coromandel’s sand!
Those silver bells
Reposed of yore,
As shapeless ore,
Far down in the deep-sunken wells
Of darksome mines,
In some obscure and sunless place,
Beneath huge Chimborazo’s base,
Or Potosi’s o’erhanging pines
And thus for thee, O little child,
Through many a danger and escape,
The tall ships passed the stormy cape;
For thee in foreign lands remote,
Beneath a burning, tropic clime,
The Indian peasant, chasing the wild goat,
Himself as swift and wild,
In falling, clutched the frail arbute,
The fibres of whose shallow root,
Uplifted from the soil, betrayed
The silver veins beneath it laid,
The buried treasures of the miser, Time.
But, lo! thy door is left ajar!
Thou hearest footsteps from afar!
And, at the sound,
Thou turnest round
With quick and questioning eyes,
Like one, who, in a foreign land,
Beholds on every hand
Some source of wonder and surprise!
And, restlessly, impatiently,
Thou strivest, strugglest, to be free,
The four walls of thy nursery
Are now like prison walls to thee.
No more thy mother’s smiles,
No more the painted tiles,
Delight thee, nor the playthings on the floor,
That won thy little, beating heart before;
Thou strugglest for the open door.
Through these once solitary halls
Thy pattering footstep falls.
The sound of thy merry voice
Makes the old walls
Jubilant, and they rejoice
With the joy of thy young heart,
O’er the light of whose gladness
No shadows of sadness
From the sombre background of memory start.
Once, ah, once, within these walls,
One whom memory oft recalls,
The Father of his Country, dwelt.
And yonder meadows broad and damp
The fires of the besieging camp
Encircled with a burning belt.
Up and down these echoing stairs,
Heavy with the weight of cares,
Sounded his majestic tread;
Yes, within this very room
Sat he in those hours of gloom,
Weary both in heart and head.
But what are these grave thoughts to thee?
Out, out! into the open air!
Thy only dream is liberty,
Thou carest little how or where.
I see thee eager at thy play,
Now shouting to the apples on the tree,
With cheeks as round and red as they;
And now among the yellow stalks,
Among the flowering shrubs and plants,
As restless as the bee.
Along the garden walks,
The tracks of thy small carriage-wheels I trace;
And see at every turn how they efface
Whole villages of sand-roofed tents,
That rise like golden domes
Above the cavernous and secret homes
Of wandering and nomadic tribes of ants.
Ah, cruel little Tamerlane,
Who, with thy dreadful reign,
Dost persecute and overwhelm
These hapless Troglodytes of thy realm!
What! tired already! with those suppliant looks,
And voice more beautiful than a poet’s books,
Or murmuring sound of water as it flows.
Thou comest back to parley with repose;
This rustic seat in the old apple-tree,
With its o’erhanging golden canopy
Of leaves illuminate with autumnal hues,
And shining with the argent light of dews,
Shall for a season be our place of rest.
Beneath us, like an oriole’s pendent nest,
From which the laughing birds have taken wing,
By thee abandoned, hangs thy vacant swing.
Dream-like the waters of the river gleam;
A sailless vessel drops adown the stream,
And like it, to a sea as wide and deep,
Thou driftest gently down the tides of sleep.
O child! O new-born denizen
Of life’s great city! On thy head
The glory of the morn is shed,
Like a celestial benison!
Here at the portal thou dost stand,
And with thy little hand
Thou openest the mysterious gate
Into the future’s undiscovered land.
I see its valves expand,
As at the touch of Fate!
Into those realms of love and hate,
Into that darkness blank and drear,
By some prophetic feeling taught,
I launch the bold, adventurous thought,
Freighted with hope and fear;
As upon subterranean streams,
In caverns unexplored and dark,
Men sometimes launch a fragile bark,
Laden with flickering fire,
And watch its swift-receding beams,
Until at length they disappear,
And in the distant dark expire.
By what astrology of fear or hope
Dare I to cast thy horoscope!
Like the new moon thy life appears;
A little strip of silver light,
And widening outward into night
The shadowy disk of future years;
And yet upon its outer rim,
A luminous circle, faint and dim,
And scarcely visible to us here,
Rounds and completes the perfect sphere;
A prophecy and intimation,
A pale and feeble adumbration,
Of the great world of light, that lies
Behind all human destinies.
Ah! if thy fate, with anguish fraught,
Should be to wet the dusty soil
With the hot tears and sweat of toil,—
To struggle with imperious thought,
Until the overburdened brain,
Weary with labor, faint with pain,
Like a jarred pendulum, retain
Only its motion, not its power,—
Remember, in that perilous hour,
When most afflicted and oppressed,
From labor there shall come forth rest.
And if a more auspicious fate
On thy advancing steps await
Still let it ever be thy pride
To linger by the laborer’s side;
With words of sympathy or song
To cheer the dreary march along
Of the great army of the poor,
O’er desert sand, o’er dangerous moor.
Nor to thyself the task shall be
Without reward; for thou shalt learn
The wisdom early to discern
True beauty in utility;
As great Pythagoras of yore,
Standing beside the blacksmith’s door,
And hearing the hammers, as they smote
The anvils with a different note,
Stole from the varying tones, that hung
Vibrant on every iron tongue,
The secret of the sounding wire.
And formed the seven-chorded lyre.
Enough! I will not play the Seer;
I will no longer strive to ope
The mystic volume, where appear
The herald Hope, forerunning Fear,
And Fear, the pursuivant of Hope.
Thy destiny remains untold;
For, like Acestes’ shaft of old,
The swift thought kindles as it flies,
And burns to ashes in the skies.


| ~[text:psx.html.admin_students.allenrollments.entered] | ~[text:psx.html.admin_students.allenrollments.exited] | ~[text:psx.html.admin_students.allenrollments.exp] | ~[text:psx.html.admin_students.allenrollments.course] | ~[text:psx.html.admin_students.allenrollments.teacher] | ~[text:psx.html.admin_students.allenrollments.view] | ~[text:psx.html.admin_students.allenrollments.edit] |
|---|---|---|---|---|---|---|
| [dateenrolled] | [dateleft] | [classexpression] | [coursename] | [teachername] | ~[text:psx.html.admin_students.allenrollments.view1] | ~[text:psx.html.admin_students.allenrollments.edit1] |


Envision a world where poverty and hunger no longer exist, where every child and woman has equal rights and access to resources and opportunities.
Saint Ann Foundation (SAF) is local independent non-profit organisation dedicated to work with children, women and promote volunteerism in Uganda. SAF is a registered and works with the poor to overcome poverty and injustice and alleviate the distress of the vulnerable and defenseless.
We have motivated and dedicated people who work hard to make everthing happen.
SEE THE TEAMMEMBERSVolunteers come from all around the world to help both the children and the community.
SEE THE VOLUNTEERSOur volunteers share their experience and achivements with Saint Ann Foundation
SEE THE VIDEOSWe work in partnership with rural villages in Uganda to create and establish sustainable solutions for community development. Our staff begin by asking a lot of questions, finding out what problems are seen by members of the community, especially the children. We then come up with a plan, decide on ways to evaluate and measure success, and get to work.
Through our effective volunteer service programs, we place volunteers in our initiatives and connect other volunteers from around the world with these communities along with our staff as we work together on projects implemented in different areas. We focus on one community at a time while maintaining a community-led committees monitoring each project stages, completion and its long-term impact.
SAF offers a variety of different projects, so whether you are a student or professional, you will be able to find a program in a field that will match your interest, experience or studies. Our volunteer programs are safe, reliable, hands-on and affordable with high-quality service guaranteed. Find out which program is right for you!
Check out the various projects that we offer
See ProjectsNam consequat lacus purus, ac hendrerit ipsum pellentesque ut. Mauris orci ante, sodales ut lorem sed, semper aliquam diam. Suspendisse scelerisque dolor felis, a consequat est eleifend vitae. Aliquam adipiscing, metus sit amet vehicula egestas, justo dolor ultricies mauris, vel porttitor augue nisi vitae metus. Ut et faucibus lorem. Vestibulum sit amet tellus velit.
Aliquam adipiscing, metus sit amet vehicula egestas, justo dolor ultricies mauris, vel porttitor augue nisi vitae metus. Ut et faucibus lorem. Vestibulum sit amet tellus velit. Sed bibendum nulla et ante tincidunt vestibulum. Aenean dapibus malesuada sodales. Vestibulum scelerisque tincidunt condimentum.
Click below to apply to become a volunteer.
Apply NowNam consequat lacus purus, ac hendrerit ipsum pellentesque ut. Mauris orci ante, sodales ut lorem sed, semper aliquam diam. Suspendisse scelerisque dolor felis, a consequat est eleifend vitae. Aliquam adipiscing, metus sit amet vehicula egestas, justo dolor ultricies mauris, vel porttitor augue nisi vitae metus. Ut et faucibus lorem. Vestibulum sit amet tellus velit.
Aliquam adipiscing, metus sit amet vehicula egestas, justo dolor ultricies mauris, vel porttitor augue nisi vitae metus. Ut et faucibus lorem. Vestibulum sit amet tellus velit. Sed bibendum nulla et ante tincidunt vestibulum. Aenean dapibus malesuada sodales. Vestibulum scelerisque tincidunt condimentum.
Kasangati Gayaza Road
Wakiso District
P.O BOX 14311
Uganda
+256 773 025 854
+256 704 818 746
<EMAIL_ADDRESS>We are located at Kasangati, nakanaku village in Uganda.


AKAN NAMES
Akan name correspondence calculator will match your DOB to known Akan name
Input your DOB to get started:
Date:
Month:
Year:
Female
Male
Akan Name: