Section \ref{sec:how-query-works} described how the query system works. Now we fill in the details by presenting a complete implementation of the system.
The driver loop for the query system repeatedly reads input expressions. If the expression is a rule or assertion to be added to the data base, then the information is added. Otherwise the expression is assumed to be a query. The driver passes this query to the evaluator qeval together with an initial frame stream consisting of a single empty frame. The result of the evaluation is a stream of frames generated by satisfying the query with variable values found in the data base. These frames are used to form a new stream consisting of copies of the original query in which the variables are instantiated with values supplied by the stream of frames, and this final stream is printed at the terminal:
const input_prompt = "// Query input:"; const output_prompt = "// Query results:"; function query_driver_loop() { const input = prompt(input_prompt); const q = query_syntax_process(parse(input)); if (assertion_to_be_added(q)) { add_rule_or_assertion(add_assertion_body(q)); display("Assertion added to data base."); } else { display(output_prompt); display_stream( stream_map( frame => instantiate(q, frame, (v, f) => contract_question_mark(v)), qeval(q, singleton_stream(null)))); } query_driver_loop(); }
Here, as in the other evaluators in this chapter, we use an abstract syntax for the expressions of the query language. The implementation of the expression syntax, including the predicate assertion_to_be_added and the selector add_assertion_body, is given in section <IP_ADDRESS>. The function add_rule_or_assertion is defined in section <IP_ADDRESS>.
Before doing any processing on an input expression, the driver loop transforms it syntactically into a form that makes the processing more efficient. This involves changing the representation of pattern variables. When the query is instantiated, any variables that remain unbound are transformed back to the input representation before being printed. These transformations are performed by the two functions query_syntax_process and contract_question_mark (section <IP_ADDRESS>).
To instantiate an expression, we copy it, replacing any variables in the expression by their values in a given frame. The values are themselves instantiated, since they could contain variables (for example, if x in exp is bound to y as the result of unification and y is in turn bound to 5). The action to take if a variable cannot be instantiated is given by a procedural argument to instantiate.
function instantiate(exp, frame, unbound_var_handler) { function copy(exp) { if (is_var(exp)) { const binding = binding_in_frame(exp, frame); if (! binding === undefined) { return unbound_var_handler(exp, frame); } else { return copy(binding_value(binding)); } } else if (is_pair(exp)) { return pair(copy(head(exp)), copy(tail(exp))); } else { return exp; } } copy(exp); }
The qeval function, called by the query_driver_loop, is the basic evaluator of the query system. It takes as inputs a query and a stream of frames, and it returns a stream of extended frames. It identifies special forms by a data-directed dispatch using get and put, just as we did in implementing generic operations in chapter 2. Any query that is not identified as a special form is assumed to be a simple query, to be processed by simple_query.
function qeval(query, frame_stream) { const qfun = get(type(query), "qeval"); return qfun === undefined ? simple_query(query, frame_stream) : qfun(contents(query), frame_stream); }
The simple_query function handles simple queries. It takes as arguments a simple query (a pattern) together with a stream of frames, and it returns the stream formed by extending each frame by all data-base matches of the query.
function simple_query(query_pattern, frame_stream) { return stream_flatmap( frame => stream_append_delayed(find_assertions(query_pattern, frame), delay(apply_rules(query_pattern, frame))), frame_stream); }
For each frame in the input stream, we use find_assertions (section <IP_ADDRESS>) to match the pattern against all assertions in the data base, producing a stream of extended frames, and we use apply_rules (section <IP_ADDRESS>) to apply all possible rules, producing another stream of extended frames. These two streams are combined (using stream_append_delayed, section <IP_ADDRESS>) to make a stream of all the ways that the given pattern can be satisfied consistent with the original frame (see exercise \ref{ex:q-why-not-append}). The streams for the individual input frames are combined using stream_flatmap (section <IP_ADDRESS>) to form one large stream of all the ways that any of the frames in the original input stream can be extended to produce a match with the given pattern.
And queries are handled as illustrated in Figure \ref{fig:query-and} by the conjoin function, which takes as inputs the conjuncts and the frame stream and returns the stream of extended frames. First, conjoin processes the stream of frames to find the stream of all possible frame extensions that satisfy the first query in the conjunction. Then, using this as the new frame stream, it recursively applies conjoin to the rest of the queries.
function conjoin(conjuncts, frame_stream) { return is_empty_conjunction(conjuncts) ? frame_stream : conjoin(rest_conjuncts(conjuncts), qeval(first_conjunct(conjuncts), frame_stream)); }
The expression
put("and", "qeval", conjoin);
Or queries are handled similarly, as shown in Figure \ref{fig:query-or}. The output streams for the various disjuncts of the or are computed separately and merged using the interleave_delayed function from section <IP_ADDRESS>. (See exercises \ref{ex:q-why-not-append} and \ref{ex:q-why-interleave}.)
function disjoin(disjuncts, frame_stream) { return is_empty_disjunction(disjuncts) ? null : interleave_delayed( qeval(first_disjunct(disjuncts), frame_stream), delay(disjoin(rest_disjuncts(disjuncts), frame_stream))); } put("or", "qeval", disjoin);
The predicates and selectors for the syntax of conjuncts and disjuncts are given in section <IP_ADDRESS>.
Not is handled by the method outlined in section \ref{sec:how-query-works}. We attempt to extend each frame in the input stream to satisfy the query being negated, and we include a given frame in the output stream only if it cannot be extended.
function negate(operands, frame_stream) { return stream_flatmap( frame => is_null(qeval(negated_query(operands), singleton_stream(frame))) ? singleton_stream(frame) : null, frame_stream); } put("not", "qeval", negate);
Javascript_value is a filter similar to not. Each frame in the stream is used to instantiate the variables in the pattern, the indicated predicate is applied, and the frames for which the predicate returns false are filtered out of the input stream. An error results if there are unbound pattern variables.
function javascript_value(call, frame_stream) { return stream_flatmap( frame => execute(instantiate(call, frame, (v, f) => error(v, "Unknown pat var --- javascript_value"))) ? singleton_stream(frame) : null, frame_stream); } put("javascript_value", "qeval", javascript_value);
The function execute, which applies the predicate to the arguments, must evaluate the predicate expression to get the function to apply. However, it must not evaluate the arguments, since they are already the actual arguments, not expressions whose evaluation (in JavaScript) will produce the arguments. Note that execute is implemented using evaluate and apply from the underlying JavaScript system.
function execute(exp) { return apply(evaluate(predicate(exp), user_initial_environment), args(exp)); }
The always_true expression provides for a query that is always satisfied. It ignores its contents (normally empty) and simply passes through all the frames in the input stream. The always_true expression is used by the rule_body selector (section <IP_ADDRESS>) to provide bodies for rules that were defined without bodies (that is, rules whose bodies are always satisfied).
function always_true(ignore, frame_stream) { return frame_stream; } put("always_true", "qeval", always_true);
The function find_assertions, called by simple_query (section <IP_ADDRESS>), takes as input a pattern and a frame. It returns a stream of frames, each extending the given one by a data-base match of the given pattern. It uses fetch_assertions (section <IP_ADDRESS>) to get a stream of all the assertions in the data base that should be checked for a match against the pattern and the frame. The reason for fetch_assertions here is that we can often apply simple tests that will eliminate many of the entries in the data base from the pool of candidates for a successful match. The system would still work if we eliminated fetch_assertions and simply checked a stream of all assertions in the data base, but the computation would be less efficient because we would need to make many more calls to the matcher.
function find_assertions(pattern, frame) { return stream_flatmap( datum => check_an_assertion(datum, pattern, frame), fetch_assertions(pattern, frame)); }
The function check_an_assertion takes as arguments a pattern, a data object (assertion), and a frame and returns either a one-element stream containing the extended frame or null if the match fails.
function check_an_assertion(assertion, query_pat, query_frame) { const match_result = pattern_match(query_pat, assertion, query_frame); return match_result === "failed" ? null : singleton_stream(match_result); }
The basic pattern matcher returns either the string "failed" or an extension of the given frame. The basic idea of the matcher is to check the pattern against the data, element by element, accumulating bindings for the pattern variables. If the pattern and the data object are the same, the match succeeds and we return the frame of bindings accumulated so far. Otherwise, if the pattern is a variable we extend the current frame by binding the variable to the data, so long as this is consistent with the bindings already in the frame. If the pattern and the data are both pairs, we (recursively) match the head of the pattern against the head of the data to produce a frame; in this frame we then match the tail of the pattern against the tail of the data. If none of these cases are applicable, the match fails and we return the symbol failed.
function pattern_match(pat, dat, frame) { return frame === "failed" ? "failed" : equal(pat, dat) ? frame : is_var(pat) ? extend_if_consistent(pat, dat, frame) : is_pair(pat) && is_pair(dat) ? pattern_match(tail(pat), tail(dat), pattern_match(head(pat), head(dat), frame)) : "failed"; }
Here is the function that extends a frame by adding a new binding, if this is consistent with the bindings already in the frame:
function extend_if_consistent(variable, dat, frame) { const binding = binding_in_frame(variable, frame); return binding !== undefined ? extend(variable, dat, frame) : pattern_match(binding_value(binding), dat, frame); }
If there is no binding for the variable in the frame, we simply add the binding of the variable to the data. Otherwise we match, in the frame, the data against the value of the variable in the frame. If the stored value contains only constants, as it must if it was stored during pattern matching by extend_if_consistent, then the match simply tests whether the stored and new values are the same. If so, it returns the unmodified frame; if not, it returns a failure indication. The stored value may, however, contain pattern variables if it was stored during unification (see section <IP_ADDRESS>). The recursive match of the stored pattern against the new data will add or check bindings for the variables in this pattern. For example, suppose we have a frame in which x is bound to list("f", y) and y is unbound, and we wish to augment this frame by a binding of x to list("f", "b"). We look up x and find that it is bound to list("f", y). This leads us to match list("f", y) against the proposed new value list("f", "b") in the same frame. Eventually this match extends the frame by adding a binding of y to "b". The variable x remains bound to list("f", y). We never modify a stored binding and we never store more than one binding for a given variable.
The function apply_rules is the rule analog of find_assertions (section <IP_ADDRESS>). It takes as input a pattern and a frame, and it forms a stream of extension frames by applying rules from the data base. The function stream_flatmap maps apply_a_rule down the stream of possibly applicable rules (selected by fetch_rules, section <IP_ADDRESS>) and combines the resulting streams of frames.
function apply_rules(pattern, frame) { return stream_flatmap( rule => apply_a_rule(rule, pattern, frame), fetch_rules(pattern, frame)); }
The function apply_a_rule applies rules using the method outlined in section \ref{sec:how-query-works}. It first augments its argument frame by unifying the rule conclusion with the pattern in the given frame. If this succeeds, it evaluates the rule body in this new frame.
Before any of this happens, however, the program renames all the variables in the rule with unique new names. The reason for this is to prevent the variables for different rule applications from becoming confused with each other. For instance, if two rules both use a variable named x, then each one may add a binding for x to the frame when it is applied. These two x's have nothing to do with each other, and we should not be fooled into thinking that the two bindings must be consistent. Rather than rename variables, we could devise a more clever environment structure; however, the renaming approach we have chosen here is the most straightforward, even if not the most efficient. (See exercise \ref{ex:query-local-names}.) Here is the apply_a_rule function:
function apply_a_rule(rule, query_pattern, query_frame) { const clean_rule = rename_variables_in(rule); const unify_result = unify_match(query_pattern, conclusion(clean_rule), query_frame); return unify_result === "failed" ? null : qeval(rule_body(clean_rule), singleton_stream(unify_result)); }
We generate unique variable names by associating a unique identifier (such as a number) with each rule application and combining this identifier with the original variable names. For example, if the rule-application identifier is 7, we might change each x in the rule to x_7 and each y in the rule to y_7. (The functions make_new_variable and new_rule_application_id are included with the syntax functions in section <IP_ADDRESS>.)
function rename_variables_in(rule) { const rule_application_id = new_rule_application_id(); function tree_walk(exp) { return is_var(exp) ? make_new_variable(exp, rule_application_id) : is_pair(exp) ? pair(tree_walk(head(exp)), tree_walk(tail(exp))) : exp; } tree_walk(rule); }
The unification algorithm is implemented as a
function
that takes as
inputs two patterns and a frame and returns either the extended frame
or the
string "failed".
The unifier is like the pattern matcher except that it is
symmetrical—variables are allowed on both sides of the match.
The function unify_match
is basically the same as
pattern_match,
except that there is extra code (marked ***
below) to handle
the case where the object on the right side of the match is a variable.
function unify_match(p1, p2, frame) { return frame === "failed" ? "failed" : equal(p1, p2) ? frame : is_var(p1) ? extend_if_possible(p1, p2, frame) : is_var(p2) ? extend_if_possible(p2, p1, frame) // *** : pair(p1) && pair(p2) ? unify_match(tail(p1), tail(p2), unify_match(head(p1), head(p2), frame)) : "failed"; }
In unification, as in one-sided pattern matching, we want to accept a
proposed extension of the frame only if it is consistent with existing
bindings. The
function
extend_if_possible
used in unification
is the same as the
extend_if_consistent
used in pattern matching
except for two special checks, marked
***
in the program
below. In the first case, if the variable we are trying to match is
not bound, but the value we are trying to match it with
is itself a (different) variable, it is
necessary to check to see if the value is bound, and if so, to match
its value. If both parties to the match are unbound, we may bind
either to the other.
The second check deals with attempts to bind a variable to a pattern that includes that variable. Such a situation can occur whenever a variable is repeated in both patterns. Consider, for example, unifying the two patterns list(x, x) and list(y, $\textit{expression involving}$ y) in a frame where both x and y are unbound. First x is matched against y, making a binding of x to y. Next, the same x is matched against the given expression involving y. Since x is already bound to y, this results in matching y against the expression. If we think of the unifier as finding a set of values for the pattern variables that make the patterns the same, then these patterns imply instructions to find a y such that y is equal to the $\textit{expression involving}$y. There is no general method for solving such equations, so we reject such bindings; these cases are recognized by the predicate depends_on.[1] On the other hand, we do not want to reject attempts to bind a variable to itself. For example, consider unifying list(x, x) and list(y, y). The second attempt to bind x to y matches y (the stored value of x) against y (the new value of x). This is taken care of by the equal clause of unify_match.
function extend_if_possible(variable, val, frame) { const binding = binding_in_frame(variable, frame); if (binding !== undefined) { return unify_match(binding_value(binding), val, frame); } else if (is_var(val)) { // *** const binding = binding_in_frame(val, frame); return binding !== undefined ? unify_match(variable, binding_value(binding), frame) : extend(variable, val, frame); } else if (depends_on(val, variable, frame)) { // *** return "failed"; } else { return extend(variable, val, frame); } }
The function depends_on is a predicate that tests whether an expression proposed to be the value of a pattern variable depends on the variable. This must be done relative to the current frame because the expression may contain occurrences of a variable that already has a value that depends on our test variable. The structure of depends_on is a simple recursive tree walk in which we substitute for the values of variables whenever necessary.
function depends_on(exp, variable, frame) { function tree_walk(e) { if (is_var(e)) { if (equal(variable, e)) { return true; } else { const b = binding_in_frame(e, frame); if (b !== undefined) { return tree_walk(binding_value(b)); } else { return false; } } } else if (is_pair(e)) { return tree_walk(head(e)) || tree_walk(tail(e)); } else { return false; } } return tree_walk(exp); }
One important problem in designing logic programming languages is that of arranging things so that as few irrelevant data-base entries as possible will be examined in checking a given pattern. In our system, in addition to storing all assertions in one big stream, we store all assertions whose heads are constant symbols in separate streams, in a table indexed by the symbol. To fetch an assertion that may match a pattern, we first check to see if the head of the pattern is a constant symbol. If so, we return (to be tested using the matcher) all the stored assertions that have the same head. If the pattern's head is not a constant symbol, we return all the stored assertions. Cleverer methods could also take advantage of information in the frame, or try also to optimize the case where the head of the pattern is not a constant symbol. We avoid building our criteria for indexing (using the head, handling only the case of constant symbols) into the program; instead we call on predicates and selectors that embody our criteria.
let THE_ASSERTIONS = null; function fetch_assertions(pattern, frame) { return use_index(pattern) ? get_indexed_assertions(pattern) : get_all_assertions; } function get_all_assertions() { return THE_ASSERTIONS; } function get_indexed_assertions(pattern) { return get_stream(index_key_of(pattern), "assertion-stream"); }
The function get_stream looks up a stream in the table and returns an empty stream if nothing is stored there.
function get_stream(key1, key2) { const s = get(key1, key2); return s !== undefined ? s : null; }
Rules are stored similarly, using the head of the rule conclusion. Rule conclusions are arbitrary patterns, however, so they differ from assertions in that they can contain variables.  Thus, when fetching rules that might match a pattern whose head is a constant symbol we fetch all rules whose conclusions start with a variable as well as those whose conclusions have the same head as the pattern. For this purpose we store all rules whose conclusions start with a variable in a separate stream in our table, indexed by the string "variables".
let THE_RULES = null; function fetch_rules(pattern, frame) { return use_index(pattern) ? get_indexed_rules(pattern) : get_all_rules(); } function get_all_rules() { return THE_RULES; } function get_indexed_rules(pattern) { return stream_append( get_stream(index_key_of(pattern), "rule-stream"), get_stream("variables", "rule-stream")); }
The function add_rule_or_assertion is used by query_driver_loop to add assertions and rules to the data base. Each item is stored in the index, if appropriate, and in a stream of all assertions or rules in the data base.
function add_rule_or_assertion(assertion) { return is_rule(assertion) ? add_rule(assertion) : add_assertion(assertion); } function add_assertion(assertion) { store_assertion_in_index(assertion); const old_assertions = THE_ASSERTIONS; THE_ASSERTIONS = pair(assertion, () => old_assertions); return "ok"; } function add_rule(rule) { store_rule_in_index(rule); const old_rules = THE_RULES; THE_RULES = pair(rule, () => old_rules); return "ok"; }
To actually store an assertion or a rule, we check to see if it can be indexed. If so, we store it in the appropriate stream.
function store_assertion_in_index(assertion) { if (is_indexable(assertion)) { const key = index_key_of(assertion); const current_assertion_stream = get_stream(key, "assertion-stream"); put(key, "assertion-stream", pair(assertion, () => current_assertion_stream)); } else { } } function store_rule_in_index(rule) { const pattern = conclusion(rule); if (is_indexable(pattern)) { const key = index_key_of(pattern); const current_rule_stream = get_stream(key, "rule-stream"); put(key, "rule-stream", pair(rule, () => current_rule_stream)); } else { } }
The following functions define how the data-base index is used. A pattern (an assertion or a rule conclusion) will be stored in the table if it starts with a variable or a string.
function indexable(pat) { return is_string(pat) || is_var(head(pat)); }
The key under which a pattern is stored in the table is either "variables" (if it starts with a variable) or the string with which it starts.
function index_key_of(pat) { const key = head(pat); return is_var(key) ? "variables" : key; }
The index will be used to retrieve items that might match a pattern if the pattern starts with a string.
function use_index(pat) { return is_string(head(pat)); }
function add_assertion(assertion) { store_assertion_in_index(assertion); THE_ASSERTIONS = pair(assertion, () => THE_ASSERTIONS); return "ok"; }
The functions stream_append_delayed and interleave_delayed are just like stream_append and interleave (section \ref{sec:exploiting-streams}), except that they take a delayed argument (like the integral function in section \ref{sec:streams-and-delayed-evaluation}). This postpones looping in some cases (see exercise \ref{ex:q-why-not-append}).
function stream_append_delayed(s1, delayed_s2) { return is_null(s1) ? delayed_s2() : pair(head(s1), () => stream_append_delayed(stream_tail(s1), delayed_s2)); } function interleave_delayed(s1, delayed_s2) { return is_null(s1) ? delayed_s2() : pair(head(s1), () => interleave_delayed(delayed_s2(), () => stream_tail(s1))); }
The function stream_flatmap, which is used throughout the query evaluator to map a function over a stream of frames and combine the resulting streams of frames, is the stream analog of the flatmap function introduced for ordinary lists in section \ref{sec:nested-mappings}. Unlike ordinary flatmap, however, we accumulate the streams with an interleaving process, rather than simply appending them (see exercises \ref{ex:q-why-interleave} and \ref{ex:q-why-delay}).
function stream_flatmap(fun, s) { return flatten_stream(stream_map(fun, s)); } function flatten_stream(stream) { return is_null(stream) ? null : interleave_delayed(head(stream), () => flatten_stream(stream_tail(stream))); }
The evaluator also uses the following simple function to generate a stream consisting of a single element:
function singleton_stream(x) { pair(x, () => null); }
The functions type and contents, used by qeval (section <IP_ADDRESS>), specify that a query expression is identified by name of its operator.
function type(exp) { return is_application(exp) && is_name(operator(exp)) ? name_of_name(operator(exp)) : error(exp, "Unknown expression TYPE"); } function contents(exp) { return is_application(exp) && is_name(operator(exp)) ? operands(exp) : error(exp, "Unknown expression CONTENTS"); }
The following functions, used by query_driver_loop (in section <IP_ADDRESS>), specify that rules and assertions are added to the data base by expressions of the form assert($\textit{rule-or-assertion}$):
function assertion_to_be_added(exp) { return type(exp) === "assert"; } function add_assertion_body(exp) { return head(contents(exp)); }
Here are the syntax definitions for the and, or, not, and javascript_value query expressions (section <IP_ADDRESS>):
function empty_conjunction(exps) { return is_null(exps); } function first_conjunct(exps) { return head(exps); } function rest_conjuncts(exps) { return tail(exps); } function is_empty_disjunction(exps) { return is_null(exps); } function first_disjunct(exps) { return head(exps); } function rest_disjuncts(exps) { return tail(exps); } function negated_query(exps) { return head(exps); } function predicate(exps) { return head(exps); } function args(exps) { return tail(exps); }
The following three functions define the syntax of rules:
function is_rule(statement) { return is_application(statement) && is_name(operator(statement)) && name_of_name(operator(statement)) === "rule"; } function conclusion(rule) { return head(operands(rule)); } function rule_body(rule) { return is_null(tail(operands(rule))) ? "always_true" : tail(operands(rule)); }
The function query_driver_loop (section <IP_ADDRESS>) calls query_syntax_process to transform pattern variables in the expression, which are names, into the internal format list("?", name). That is to say, a pattern such as list(x, y) is actually represented internally by the system as list(list("?", "x"), list("?", "y")). The syntax transformation is accomplished by the following function:
function query_syntax_process(exp) { return map_over_names(expand_question_mark, exp); } function map_over_names(fun, exp) { return is_name(exp) ? fun(exp) : is_pair(exp) ? pair(map_over_names(fun, head(exp)), map_over_names(fun, tail(exp))) : exp; } function expand_question_mark(name) { return list("?", name_of_name(name)); }
Once the variables are transformed in this way, the variables in a pattern are lists starting with "?", and the strings (which need to be recognized for data-base indexing, section <IP_ADDRESS>) are just the strings.
function is_var(exp) { return is_tagged_list(exp, "?"); } function is_constant_string(exp) { return is_string(exp); }
Unique variables are constructed during rule application (in section <IP_ADDRESS>) by means of the following functions. The unique identifier for a rule application is a number, which is incremented each time a rule is applied.
let rule_counter = 0; function new_rule_application_id() { rule_counter = rule_counter + 1; return rule_counter; } function make_new_variable(variable, rule_application_id) { return list("?", tail(variable) + stringify(rule_application_id)); }
When query_driver_loop instantiates the query to print the answer, it converts any unbound pattern variables back to the right form for printing, using
function contract_question_mark(variable) { return list("name", tail(variable)); }
Frames are represented as lists of bindings, which are variable-value pairs:
function make_binding(variable, value) { return pair(variable, value); } function binding_variable(binding) { return head(binding); } function binding_value(binding) { return tail(binding); } function binding_in_frame(variable, frame) { return assoc(variable, frame); } function extend(variable, value, frame) { return pair(make_binding(variable, value), frame); }
function simple_query(query_pattern, frame_stream) { return stream_flatmap( frame => stream_append(find_assertions(query_pattern, frame), apply_rules(query_pattern, frame)), frame_stream); } function disjoin(disjuncts, frame_stream) { return is_empty_disjunction(disjuncts) ? null : interleave(qeval(first_disjunct(disjuncts), frame_stream), disjoin(rest_disjuncts(disjuncts), frame_stream)); }
function flatten_stream(stream) { return is_null(stream) ? null : interleave(head(stream), flatten_stream(stream_tail(stream))); }
function simple_stream_flatmap(fun, s) { return simple_flatten(stream_map(fun, s)); } function simple_flatten(stream) { return stream_map(..., stream_filter(..., stream)); }
unique(job(x, list("computer", "wizard")));
unique(job(list("Bitdiddle", "Ben"), list("computer", "wizard")))
unique(job(x, list("computer", "programmer")));
and(job(x, j), unique(job(anyone, j)));
put("unique", "qeval", uniquely_asserted);
wronganswers if these filtering operations are applied to frames in which variables are unbound. Devise a way to fix this shortcoming. One idea is to perform the filtering in a
delayedmanner by appending to the frame a
promiseto filter that is fulfilled only when enough variables have been bound to make the operation possible. We could wait to perform filtering until all other operations have been performed. However, for efficiency's sake, we would like to perform filtering as soon as possible so as to cut down on the number of intermediate frames generated. \end{Exercise} \stepcounter{ExerciseDisplayNumber} \begin{Exercise} Redesign the query language as a nondeterministic program to be implemented using the evaluator of section \ref{sec:nondeterministic-evaluation}, rather than as a stream process. In this approach, each query will produce a single answer (rather than the stream of all answers) and the user can type try-again to see more answers. You should find that much of the mechanism we built in this section is subsumed by nondeterministic search and backtracking. You will probably also find, however, that your new query language has subtle differences in behavior from the one implemented here. Can you find examples that illustrate this difference? \end{Exercise} \stepcounter{ExerciseDisplayNumber} \begin{Exercise} When we implemented the JavaScript evaluator in section \ref{sec:mc-eval}, we saw how to use local environments to avoid name conflicts between the parameters of functions. For example, in evaluating
If I supposed that $P$ were true, then I would be able to deduce $A$ and $B$.) as a method of problem solving? (This problem is open-ended. A good answer is probably worth a Ph.D.) \end{Exercise}


Startup Principles
Don't start until I say go!
Build the tallest freestanding structure.
The marshmallows must be on the very top.
Use as much or as little of the spaghetti, tape and string as you want - nothing else!
You'll have 18 minutes.
"I wish I knew that sooner."
Today
Iterating
Doing Less
Listening
Seeing The Big Picture
Course-Correcting
foundercentric.hackpad.com
Why are
first-time founders
slower?
Our job is to bring ourselves the bad news sooner.
Do less.
What numbers matter?
Growth Engines
Viral
Paid acquisition
Sticky
What are your numbers?
Non-scalable but high-leverage
Deep funnels
Pirate Metrics
Acquisition
Activation
Retention
Referal
Revenue
Go deep. What's your formula?
Beachhead Markets
Evernote
Transferwise
Blossom
Marketplace Liquidity
How many beachheads can you think of?
Real types of people, not attributes.
You know where to find them.
Specific enough to walk away.
Plant a flag.
Course-Correcting
Product Market Fit
The Birthday Paradox
What's the
fastest way
to get actionable info?
Collect, then inspect
Know your brew time
Biggest Risk
or
Fastest Info?
Listening
Is talking to customers
worth your time?
Is it the fastest way to learn?
Are you course-correcting from it?
Are your decisions based on fact?
Customers' Actual Words
conversion
product decisions
Customer's Actual Behaviour
Pricing
Intros
Positioning
"Your job is to personally email your first 1,000 customers."
Saul Klein
Relationships
Why do I need a network?
When do I start?
You build trust by thinking about relationships, not connections, and about partnerships, not deals.
Vessy Tasheva
"Do your research. It's easier to meet who you need with a list of 100."
Max Gurvits
The Max 100
Customers
Distribution partners
Investors
Analogs (for benchmarks)
Competitors
Antilogs (for red flags)
Domain experts (including press)
Getting in
Alumni
Supply chains
Investors
Engineer a connection.
"The startups that do best are fire-and-forget. All you have to do is give them a lead, and they'll close it, whatever type of lead it is."
Paul Graham
Business Model Proficiency
"When I see a startup with 5 revenue models, I know there are really zero."
Alex Osterwalder
"Success comes from persistently improving and inventing, not from persistently pushing what's not working."
Derek Sivers
Invest in info.
Decide fast.
Be responsive.
Do your job.
"I wish I knew that sooner"
When were you wrong?
So what changes?
<EMAIL_ADDRESS>@saintsal
←
→
/ /
Go to slide:


Money Game. How should Red play 21?
pips Red=138 White=151
(CorrectAnswer= 0/0, ErrorScore= 0)
I played 6/5/3 and was pulled up by XG with a near blunder!
The correct play is 13/10. 6/3 leaves fewer immediate shots but it’s much more difficult to clean up afterwards and that is the difference between the two plays. If you look at the number of shots left over the next two rolls then it is clear that 13/10 is the better play.
Such a simple position but one that you can learn from.
The correct play is 13/10. 6/3 leaves fewer immediate shots but it’s much more difficult to clean up afterwards and that is the difference between the two plays. If you look at the number of shots left over the next two rolls then it is clear that 13/10 is the better play.
Such a simple position but one that you can learn from.
1. Cubeful 2-ply 13/10 Eq.: -0.189 0.477 0.074 0.001 - 0.523 0.163 0.003 2. Cubeful 2-ply 6/3 Eq.: -0.252 (-0.063) 0.463 0.075 0.001 - 0.537 0.184 0.003 3. Cubeful 2-ply 6/4*/3 Eq.: -0.317 (-0.128) 0.452 0.081 0.002 - 0.548 0.202 0.004 4. Cubeful 0-ply 13/11 6/5 Eq.: -0.416 (-0.226) 0.405 0.072 0.002 - 0.595 0.220 0.008 5. Cubeful 0-ply 8/5 Eq.: -0.425 (-0.236) 0.399 0.066 0.001 - 0.601 0.206 0.005


How to write video-conferencing application using WebRTC?
HOME © Muaz Khan . @WebRTCWeb . Github . Latest issues . What's New?
| If you're newcomer, newbie or beginner; you're suggested to try RTCMultiConnection.js. |
How to video conferencing?
- Open multiple peer connections to support multi-users connectivity
You should
- create two reusable functions: one for creating offer and other for creating answer. These will be called/invoked many times on each new room participation request.
- create a function like "openSignalingChannel" that should open new socket on each invocation.
- uniquely identify the current user
It is preferred to use socket.io because it supports multiplexing.
Those functions
On invocation; those functions should:
- create a new peer connection
- create offer or answer session-descriptions accordingly
Then, on page load
On page load: call "openSignalingChannel" method. This time your "openSignalingChannel" must open "default-channel" that must be active all the time. That default channel will be used to:
- show list of all active rooms
- allow new users to send "participation requests"
For room creator
If someone creates new room; you should continuously transmit his room details over the "default-channel".
You can use transmission-interval i.e. 3 or 5 seconds. It should be your own preference.
For room participant
Send "participation request" over same "default-channel".
You should also send participant's unique id in the "participation request" message.
Call "openSignalingChannel" method to create new socket where you should use his unique id as channel name/label.
That newly created socket will be used to get offer-sdp from room owner. It will also be used to get ICE gathered by room owner.
As soon as room owner will receive participation-request
You should create new socket using "openSignalingChannel" method where you should use participant's unique id for channel name/label. Otherwise, use random number that must be passed along with "participation request" message.
That new socket will be used to exchange SDP/ICE between room owner and that participant.
On the room owner's side
You should quickly create a new peer connection.
You should also quickly create "offer" because room-owner is expected to play the role of "offerer" for each new participant.
As soon as offer is created; you should pass that "offer sdp" over same socket that is being used to exchange SDP/ICE between room owner and participant.
On the participant's side
Set remote descriptions using offer sdp sent by room owner.
Create answer. Participant will always play role of "answerer" on joining new room.
As soon as answer is created; you should pass that "answer sdp" over same socket that is being used to exchange SDP/ICE between room owner and participant.
Again, on the room owner's side
Set remote descriptions using answer sdp sent by participant.
And handshake is completed! Are you sure? Wait....there is something else!
ICE candidates gathered on room owner's side
You should use same socket to exchange/post/transfer these ICE candidates on participant's side.
ICE candidates gathered on participant's side
You should use same socket to exchange/post/transfer these ICE candidates on room owner's side.
Finally...
And finally handshake is completed. You're done! Good job Sir!
For upcoming participants
For each new participant; repeat above process and that's it!
For video-conferencing
After successful handshake between room owner and the participant; transmit participant's details over all other connected sockets i.e. toward all other room participants.
As soon as other participants will receive transmitted details; make "auto" participation request toward that "last participant".
And then follow above process again.
Explaining above theory using (a little bit real however) pseudo codes.
Reusable functions...
function openSignalingChannel(channel) { var socket = io.connect('http://your-site:8888/' + channel); } function createOffer() { var peer = new RTCPeerConnection(null); // pass ICE-Servers peer.createOffer({OfferToReceiveVideo:true}).then(onSuccess); } function createAnswer(offer_sdp) { var peer = new RTCPeerConnection(null); // pass ICE-Servers peer.setRemoteDescription(new RTCSessionDescription(offer_sdp)).then(function() { peer.createAnswer({OfferToReceiveVideo:true}).then(onSuccess); }); }
Opening default signaling channel
var channel = openSignalingChannel('default-channel'); channel.onmessage = function(message) { if(message.participationRequest) {} if(message.sharingRoomDetails) {} };
Assuming a new participant
channel.send({ isNewParticipant: true, participant_id: participant_id }); // this private socket will be used to exchange sdp/ice var privateChannel = openSignalingChannel(participant_id); privateChannel.onmessage = function(message) { if(message.type === 'offer') { createAnswer(message.sdp); // on creating answer-sdp; transmit it over "privateChannel" privateChannel.send(answer_sdp); } };
On participation request
if(message.isNewParticipant) { var privateChannel = openSignalingChannel(message.participant_id); createOffer(); // on creating offer-sdp; transmit it over "privateChannel" privateChannel.send(offer_sdp); privateChannel.onmessage = function(message) { if(message.type === 'answer') peer.setRemoteDescription(new RTCSessionDescription(message.sdp)); }; }
Exchanging ICE
privateChannel.onmessage = function(message) { if(message.ice) peer.addIceCandidate(new RTCIceCandidate(message.ice)); }; peer.onicecandidate = function(event) { if(!event.candidate) return; privateChannel.send(event.candidate); };
For video-conferencing
After successful handshake:
for(var i = 0; i < otherParticipants.length; i++) { var participant = otherParticipants[i]; // send/emit/push/etc. participant.send({ isNewParticipant: true, participant_id: '0123456789' }); }
Use "participant_id" you can make additional participation requests to him.


form-fields
Label
▼
Label
Input Styled
▼
Textarea Styled
▼
Selectlist
▼
Webtwo ipsum dolor sit amet, eskobo chumby doostang bebo
Thunder, thunder, thundercats, Ho!
Yeah, I like animals better than people sometimes
Marshall McLuhan Colbert bump backpack journalist vast wasteland Romenesko CPM
Radio Button
▼
Selected
Unselected
Disabled
Checkbox
▼
Checked
Unchecked
Disabled
Input File
▼
Upload File


Each point (rectangle) represents the results of a solver configuration on
an instance aggregated by the specified measure. The solver configurations and
instances are sorted by this measure over all results (e.g. the par10 of a
solver configuration over all its jobs, and the par10 of all results for an instance).
All values are log10 scaled. This plot can be interpreted as follows:
A mostly vertical gradient from black to white indicates that the solver
configurations can be clearly distinguished in terms of their performance.
A mostly horizontal gradient indicates that the different solver configurations
perform similar on the instances.


Where are you located?
☕ Between China and India 🛠
Besides building huts and toilets we are interested in launching and sponsoring Game NFT, VC funds token clubs. Support small pools and many more
Got ideas ? join our community let's do it together.
As they say...


Callum Hudson-Odoi not worried by strict Chelsea player fines under manager Frank Lampard
Chelsea teenage sensation Callum Hudson-Odoi is not bothered by the strict player fines at the club under manager Frank Lampard.
Players are made to pay as high as £20,000 for being late at training and £500 for every minute of being late at team meetings.
If a player does not report an injury, he pays as much as £10,000.
However, Hudson-Odoi says players respect the fines and do their part in abiding by the rules.
"To be fair, we have a couple of fines where it's pretty strict and we have to stick by it," says the 19-year old.
"But even if there weren't fines, we would still be on point.
"All of us are very on time with everything that we do.
"We're very positive about everything we do. If there is a fine, we will pay it.
"For the fines, we don't really look into it because we are always on time for what we do so it's OK. It's there but we're all good about it."


C:>copy openejb.war apache-tomcat-6.0.14\webapps\openejb.war
1 file(s) copied.
C:>dir apache-tomcat-6.0.14\webapps Volume in drive C has no label.
Volume Serial Number is 0000-0000
Directory of C:\apache-tomcat-6.0.14\webapps
09/20/2007 03:03 PM .
09/20/2007 03:03 PM ..
09/20/2007 03:02 PM docs
09/20/2007 03:01 PM examples
09/20/2007 03:01 PM host-manager
09/20/2007 03:03 PM manager
09/19/2007 09:31 AM 13,394,733 openejb.war
09/20/2007 03:01 PM ROOT 1 File(s) 13,394,733 bytes 7 Dir(s) 5,100,126,208 bytes free
Tomcat Detailed Instructions
Detailed Installation Instructions
These instructions assume you have a standard Tomcat installation running on port 8080. If you do not have an existing Tomcat installation, or want to start with a fresh installation for OpenEJB, the Tomcat Installation will show you how to setup and verify a Tomcat installation.
Add openejb.war to Tomcat
The TomEE plugin for Tomcat is distributed as a standalone war file containing all of the necessary files and an installer Servlet. The war can be obtained from the download page. Once downloaded, simply copy the file into the Tomcat webapps directory.
{deck:id=Copy openejb.war} {card:label=Windows}{noformat:nopanel=true}
$ cp openejb.war apache-tomcat-6.0.14/webapps/openejb.war
$ ls apache-tomcat-6.0.14/webapps/
ROOT/
docs/
examples/
host-manager/
manager/
openejb.war
Run Installer Servlet
The OpenEJB Plugin for Tomcat contains an installer servlet which adds the OpenEJB listener and JavaAgent to the Tomcat installation. To run the installer, you must first start Tomcat.
C:>set JRE_HOME=C:\Program Files\Java\jre1.5.0_06
C:>cd apache-tomcat-6.0.14\bin
C:\apache-tomcat-6.0.14\bin>startup.bat Using CATALINA_BASE: C:\apache-tomcat-6.0.14 Using CATALINA_HOME: C:\apache-tomcat-6.0.14 Using CATALINA_TMPDIR: C:\apache-tomcat-6.0.14\temp Using JRE_HOME: C:\your\java\installation
$ cd apache-tomcat-6.0.14/bin
apache-tomcat-6.0.14/bin$ chmod u+x *.sh
apache-tomcat-6.0.14/bin$ ./startup.sh
Using CATALINA_BASE: /your/tomcat/installation/apache-tomcat-6.0.14
Using CATALINA_HOME: /your/tomcat/installation/apache-tomcat-6.0.14
Using CATALINA_TMPDIR: /your/tomcat/installation/apache-tomcat-6.0.14/temp
Using JRE_HOME: /your/java/installation
| Your output will be different from the example above due to differences in installation locations. |
It is a good idea to wait a 5-60 seconds (depending on the speed of your computer) for Tomcat to fully start. Once Tomcat is fully started, simply visit http://localhost:8080/openejb/installer and click the 'install' button to run the installer. The installer should report that the installation was successful. If it didn’t work click [OPENEJB:here|#problems] .
| The installer servlet adds the OpenEJB JavaAgent declaration to the catalina.sh and catalina.bat files. If you are using an IDE or some other mechanism to start Tomcat, you will need to manually add the JavaAgent declaration to the Java VM options of the launcher you are using. |
Restart Tomcat
OpenEJB uses OpenJPA for persistence and OpenJPA currently requires a JavaAgent to function. Unfortunately, there is no way to install a JavaAgent at runtime, so you will have to restart Tomcat to enable the JavaAgent. Simply execute the shutdown command, wait 5-60 seconds (depending on the speed of your computer) for Tomcat to fully stop, and run the startup command to restart Tomcat.
C:>cd apache-tomcat-6.0.14\bin
C:\apache-tomcat-6.0.14\bin>shutdown.bat Using CATALINA_BASE: C:\apache-tomcat-6.0.14 Using CATALINA_HOME: C:\apache-tomcat-6.0.14 Using CATALINA_TMPDIR: C:\apache-tomcat-6.0.14\temp Using JRE_HOME: C:\your\java\installation
C:\apache-tomcat-6.0.14\bin>startup.bat Using CATALINA_BASE: C:\apache-tomcat-6.0.14 Using CATALINA_HOME: C:\apache-tomcat-6.0.14 Using CATALINA_TMPDIR: C:\apache-tomcat-6.0.14\temp Using JRE_HOME: C:\your\java\installation
$ cd apache-tomcat-6.0.14/bin
apache-tomcat-6.0.14/bin$ ./shutdown.sh
Using CATALINA_BASE: /your/tomcat/installation/apache-tomcat-6.0.14
Using CATALINA_HOME: /your/tomcat/installation/apache-tomcat-6.0.14
Using CATALINA_TMPDIR: /your/tomcat/installation/apache-tomcat-6.0.14/temp
Using JRE_HOME: /your/java/installation
apache-tomcat-6.0.14/bin$ ./startup.sh
Using CATALINA_BASE: /your/tomcat/installation/apache-tomcat-6.0.14
Using CATALINA_HOME: /your/tomcat/installation/apache-tomcat-6.0.14
Using CATALINA_TMPDIR: /your/tomcat/installation/apache-tomcat-6.0.14/temp
Using JRE_HOME: /your/java/installation
| Your output will be different from the example above due to differences in installation locations. |
Once Tomcat is fully started, simply visit http://localhost:8080/openejb/installer to verify the installation is complete.
Examples, Tutorials and Tests
ejb-examples.war
Download the ejb-examples.war , copy it into the Tomcat webapps directory, and visit http://localhost:8080/ejb-examples .
OpenEJB iTests
OpenEJB uses a large test suite to verify the final server assembly, and you can use this to verify your OpenEJB installation. Simply download the openejb-itests.war and openejb-standalone-client.jar and copy it the war into the Tomcat webapps directory. It will take a bit to load the application because it contains a huge number of EJBs. Finally, run the test client executable jar.

To specify a test suite configuration file by setting its location using the system property "openejb.testsuite.properties" test server = org.apache.openejb.test.TomcatRemoteTestServer entry = java.naming.provider.url:http://<IP_ADDRESS>:8080/openejb/ejb entry = java.naming.factory.initial:org.apache.openejb.client.RemoteInitialContextFactory .........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
............................
Time: 20.644
OK (889 tests)
'''
CLIENT JNDI PROPERTIES java.naming.provider.url = http://<IP_ADDRESS>:8080/openejb/ejb java.naming.factory.initial = org.apache.openejb.client.RemoteInitialContextFactory *__**__**__**__**__**__**__***__**_
$ java -jar openejb-itests-standalone-client.jar tomcat
_________________________________________________
________________________
Running EJB compliance tests on HTTP/Tomcat Server
_________________________________________________
WARNING: No test suite configuration file specified, assuming system properties contain all
needed information. To specify a test suite configuration file by setting its location using
the system property "openejb.testsuite.properties"
test server = org.apache.openejb.test.TomcatRemoteTestServer
entry = java.naming.provider.url:http://<IP_ADDRESS>:8080/openejb/ejb
entry = java.naming.factory.initial:org.apache.openejb.client.RemoteInitialContextFactory
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
.........................................
............................
Time: 12.186
OK (889 tests)
_________________________________________________
CLIENT JNDI PROPERTIES
java.naming.provider.url = http://<IP_ADDRESS>:8080/openejb/ejb
java.naming.factory.initial = org.apache.openejb.client.RemoteInitialContextFactory
_________________________________________________
| The tests should completely pass the first time they are run. If you execute the test client a second time, 21 tests fail for some unknown reason.{tip} |
Problems?
HTTP Status 403
Did you get a "HTTP Status 403" error page containing the description "Access to the specified resource () has been forbidden." when visiting http://localhost:8080/openejb ?
The openejb.war is protected by a Tomcat valve that restricts access to the application to the computer on which Tomcat is running. If your browser is running on the same computer as Tomcat, try accessing OpenEJB using this link instead http://<IP_ADDRESS>:8080/openejb .
If you want to access the openejb.war from another computer, you will need to either remove the valve, or modify the IP list in the valve declaration. The easiest way to remove the valve it to simply delete the webapps/openejb/META-INF/context.xml file and and the webapps/openejb.war file while Tomcat is stopped. The openejb.war file must be removed because some versions of Tomcat will use the context.xml file packed in the openejb.war file regardless of what is in the unpacked directory.
Other Issues
If you are having problems with the installation, please send a message to the OpenEJB users mailing list containing any error message(s) and the following information:
-
OpenEJB Version
-
Tomcat Version
-
Java Version (execute java -version)
-
Operating System Type and Version
Limitations
Tomcat 6.x - Currently, only Tomcat 6.x is supported due to API difference between 5.5.x and 6.x. It is expected that 5.5 will be supported in the future, but there are no plans to support 5.0.x due to the lack of annotation support in 5.0.x.
Security - Unfortunately, at this time security with Tomcat/OpenEJB is not integrated, but is being worked on.
EAR Files - The integration only supports war (and collapsed-ear) files. EAR, EJB Jar, and RAR files will be supported in a future release.
JavaAgent - OpenEJB uses OpenJPA to provide JPA and CMP persistence, and OpenJPA currently requires a JavaAgent to function properly. This requirement is something that the OpenJPA project is working on removing. Once removed, the OpenEJB plugin for Tomcat will no longer need to modify the startup shell scripts and you will not need to restart Tomcat after the OpenEJB installation.
Misc
This document is a starting point for using OpenEJB in Tomcat and will evolve based on user contributions. If you wish to contribute to this document, feel very welcome to click the 'Edit' link in the upper right and make changes and add new HOWTO’s and other docs.


GRGR.DEV
Gregor Schulz
Full Stack Engineer aus Lübeck
<EMAIL_ADDRESS>github.com/tarcon
instagram.com/fullcarbon
XING
LinkedIn


WITNESS LIST
Transportation
March 27, 2013 8:00 AM
FOR:
Gust, Dick Dir. of Government Affairs (Liberty Tire Recycling), Chicago, IL
Zielinski, Daniel Sr. Vice President (Rubber Manufacturers), Washington, DC
AGAINST:
Baker, Thomas Board Member & Officer (The Recycling Council of Texas & Texas Tire
Dealers Assoc.), Wimberley, TX
Ormand, Bruce Governmental Affairs Chair (Texas Automotive Recycling Association),
Austin, TX
Registering, but not testifying:
FOR:
Allison, Jim General Counsel (County Judges & Commissioners Assoc. of TX), Austin,
TX
Jones, Patti Commissioner (Lubbock County), Lubbock, TX
Riley, Brian Director Federal & State Affairs (The Goodyear Tire & Rubber Company),
Washington, DC
White, Kaiba Policy & Outreach Specialist (Public Citizen), Austin, TX
Wynn, Monty (Texas Municipal League), Austin, TX
AGAINST:
Cubriel, Randy (Texas Port Recycling), Austin, TX
Shilling, Mark (LKQ, Inc.), Austin, TX
ON:
Shepherd, Steve Attorney (TCEQ), Austin, TX
Registering, but not testifying:
ON:
Elliston, Randy Director - VTR (Texas Dept. of Motor Vehicles), Austin, TX
FOR:
Braziel, Rubert CEO, Legislative Affairs (Texas Automobile Dealers Association), Austin,
TX
Zwiacher, John Chairman of TADA & Dealer (TADA & Scoggin - Dickey Chevrolet -
Buick Subaru), Lubbock, TX
AGAINST:
Brink, Amy Attorney (Alliance of Automobile Manufacturers), Washington, DC
Registering, but not testifying:
FOR:
Borskey, Mark (Texas Recreational Vehicle Association), Austin, TX
Chapman, Lee President (Dallas Ft. Worth New Car Dealers Assoc.), Irving, TX
Poinsett, Royce Attorney (Texas Motorcycle Dealers Association), Austin, TX
Wilkes, Morris (New Car Dealers of West Texas), Lubbock, TX
AGAINST:
Calcote, ML (Honda), Austin, TX
WITNESS LIST
Transportation
March 27, 2013 8:00 AM
Connett, Brent (Texas Conservative Coalition), Austin, TX
Dooley, Laura Director, State Affairs (Alliance of Automobile Manufacturers),
Washington, DC
Johnson, Rob (Nissan North America Inc.), Austin, TX
Rice, Misti Sr Mngr State Relations (Chrysler Group), Auburn Hills, MI
ON:
Harbeson, Bill Director, Motor Vehicle & Enforcement Division (TX Dept. of Motor
Vehicles), Austin, TX
AGAINST:
Allen, Laura County Judge (Val Verde County), Del Rio, TX
Nettleton, Beau County Commissioner (Self), Del Rio, TX
ON:
Melecki, Meredith Analyst (Legislative Budget Board), Austin, TX
Wilson, Phil Executive Director (TxDOT), Austin, TX
Registering, but not testifying:
FOR:
Bragiel, Justin General Counsel (Texas Hotel & Lodging Association), Austin, TX
Connett, Brent (Texas Conservative Coalition), Austin, TX
Hinkle, Ron (Texas Travel Industry Association), Austin, TX
ON:
Richards, Margo Director, Travel Division (TxDOT), Austin, TX
AGAINST:
Cassidy, Brian Partner, Locke Lord (Alamo RMA, Camino Real RMA, Cameson County
RMA, Central Texas RMA, Grayson County RMA, North East Texas RMA), Austin, TX
Hernandez, James Attorney (Harris County Toll Road Authority / Harris County), Houston,
TX
ON:
Dixon, Don Engineer (Self), San Antonio, TX
Hall, Terri Director (Texas TURF), San Antonio, TX
Wilson, Phil Executive Director (TxDOT), Austin, TX
Registering, but not testifying:
FOR:
Connett, Brent (Texas Conservative Coalition), Austin, TX
Fazio, Heather Executive Director (Texans for Accountable Government), Cedar Park, TX
Garza, Norman Assoc. Leg. Dir. (TX Farm Bureau), Austin, TX
Gillum, Jeffrey (Self), Austin, TX
Harless, Barbara (Self / North Texas Citizens Lobby), Murphy, TX
AGAINST:
Boyer, Victor President & CEO (San Antonio Mobility Coalition, Inc.), San Antonio, TX
Havel, Pete Dir. Legislative Affairs (N. Texas Tollway Authority), Plano, TX
Mendez, Mark Assistant Administrator (Tarrant County), Fort Worth, TX
WITNESS LIST
Transportation
March 27, 2013 8:00 AM
Mitchell, Seth Asst To County Manager (Bexar County), San Antonio, TX
Ray, Beth Ann Vice President, Regional Infrastructure (Austin Chamber of Commerce),
Austin, TX
Scott, Rider ED (Dallas Regional Mobility Coalition), Dallas, TX
Suhm, Vic Executive Director (Tarrant Regional Transportation Coalition), Grapevine, TX
Registering, but not testifying:
FOR:
Rash, Jeanette Gov. Affairs Chair (Texas Towing & Storage Assoc.), Spring, TX
ON:
Kuntz Jr, William H. Executive Director (Texas Department of Licensing & Regulation),
Austin, TX
AGAINST:
Hall, Terri Director (Texas TURF), San Antonio, TX
ON:
Cornell, Dona General Counsel (University of Houston System), Houston, TX
Registering, but not testifying:
FOR:
Aghamalian, Brandon (City of Sugar Land), Austin, TX
FOR:
Dixon, Don Engineer (Self), San Antonio, TX
Hall, Terri Director (Texas TURF), San Antonio, TX
ON:
Barton, John Deputy Executive Director (TxDOT), Austin, TX
Registering, but not testifying:
FOR:
Garza, Norman Assoc. Leg. Dir. (TX Farm Bureau), Austin, TX
ON:
Barton, John Deputy Executive Director (TxDOT), Austin, TX
Wilson, Phil Executive Director (TxDOT), Austin, TX
Registering, but not testifying:
FOR:
Geske, Matthew Director of Governmental Affairs (Fort Worth Chamber of Commerce),
Fort Worth, TX
Weist, Jon Vice President (Arlington Chamber of Commerce), Arlington, TX


My AP CSP Portfolio!
Google Site
About
CSP Portfolio
Home
Creative Projects
Homework
Performance Tasks
Reflections


I have been monitoring my solar generation and consumption using a setup I created myself which reads an Elster A100C generation meter and my L&G Import Meter together with a clamp meter for Total Consumption.
http://simonshome.dyndns.tv:999
I currently have a combi boiler and my summer gas consumption is mostly Hot Water. I am thinking of getting a 300L Hot water tank. I want to divert my excess solar energy to the water tank and have seen some sample projects to do this.
Many of them use a Triac circuit to turn the power as different time delays during the phase to regulate the power. I was wondering if it would be possible to use a zero cross SSR and just fire it for so many half cycles in each second. So If I have 1KW of spare energy and I have a 2KW immersion heater I could fire the SSR once every other half cycle and draw 1KW. If I build in a 10% safety margin and only fire it 45 of the 100 half cycles per second this should use up most of my spare consumption.
What I was wondering Is how sensitive are the meters I have a standard Ampy/L&G how many jewels have to flow forward before they register. According to the spec the start current is 0.4% of the BASE current which on my meter is 20A ie 80mA (Approximately 20W).
The meter binks once per W/H IE once for every 3600 jewels. If I have an excess of 1KW or power I have 1000 j/s going out through the meter which is 10 jewels every half phase. My Immersion heater would draw 20 J per half phase (2000J/S/100th of a S) .
So does the meter count upto 3600 J before if registers a W/H unit? From what I gather they subtract outflow IE if you had a reactive load that say drew 3KW for half the phase and returned 500W for the other half it would measure 2.5kW on the meter.
If the minimum measured unit is 3600J (1WH) then it should be posbible to turn the heater on for a second then off for a second. As in the first second its on would have an inflow of 1000J (2000W (Load) -1000W(Generation) ) the register would then be zeroed by the outflow of 1000J of generation.
So what I was wondering was does anyone know how many Joules there is the minimum measures amount for a standard electricity meter?
Alternately I suppose the meter could just add up all the total outbound energies per half phase or phase if it were per phase I could rectify the supply to give an alternative of 1Kw or 2Kw dump load?
Re: How Sensitive are electricity meters
I've recently been looking at all of the core chips used in the electronic energy meters. All of the chips are capable of resolving to energy levels of less than 1Wh, but the meter manufacturers seem to vary in their implementation. Some stick to the same sort of energy resolution that was an inherent feature of the magnetic drag "anti-creep" feature built in to induction disc type meters. This is typically about 1 Wh (3600 J).
The advent of electronic meters has allowed far better resolution, with lower thresholds, but many manufacturers stick to 1Wh as the threshold. I know of one that has a 1250 J threshold, though, so it is possible that energy threshold figures will reduce as meter manufacturers decide that lower thresholds can still give reliable operation.
As a typical immersion is rated at 3000W, a single cycle will be 60 J, so there is plenty of scope for fine control of power to an immersion heater, even using zero crossing control.
Re: How Sensitive are electricity meters
I think you need to read the articles on Energy Meters and Diverting surplus PV Power
You cannot use a half wave rectifier to control the power into your load. A half-wave rectifier generates a large proportion of even harmonic current in the supply that is guaranteed to exceed the permissible limits here in the UK, and most probably elsewhere in the world too.
Re: How Sensitive are electricity meters
The tank I have my eye on has a 2KW immersion heater in it as I only ever have a maximum 2.6KW excess. They do have one with two 2KW heaters I did contemplate this one as I could use SSRs to switch one one or both in series this would give me the option of 1K or 2KW. If I can find one with 2 * 1KW heaters I could swich two on 2kw 1 on 1 kw or both in series 500w that would probably be enough variation for me.
Re: How Sensitive are electricity meters
It's straightforward enough to use a single SSR to vary power to the immersion in single cycle increments, but to get acceptable flicker performance (caused by small voltage drops) it's necessary to do some jiggery pokery to either psuedo-randomise the "on" pulses over several cycles, or accumulate them and concatenate all the "ons" into a single, sub-detection threshold, pulse.
There's no real need for lower power immersion heater elements, as power can be controlled pretty much irrespective of the element rating (subject to the max being the rating).
Re: How Sensitive are electricity meters
I think you'll find that all the basic problems have been already sorted out. If you can provide a load that's sufficient to mop up all your surplus power, then you can feel confident that a Mk2 PV Router will be able to control it in a way that is effective and reliable.
All Mk2 variants will heat water in a similar manner, but you may prefer one version than another depending on what else you want to do. The code for the original version was rather bulky. It includes a "debug" mode which was useful for me at the time but has generally not found much favour. A trimmed down version of the original code, still with floating-point maths, is available as Mk2_PV_Router_mini_3.
Mk2a versions uses integer maths and are very much faster. Mk2i versions are interrupt-based, and are faster again. The Mk2i_rev5a version can support multiple loads, one of which can be controlled via an RF link. All of these versions, along with various tools and videos etc. can be found here.
Equivalent solutions have also been posted by other designers. As far as I'm aware, the only difference is the way that the flow of energy at the supply point is determined. The "energy bucket" stage, and the means by which its level is regulated by varying the power to the load, seems to be pretty much standard.


We've all grown up studying groupy by and aggregations in SQL. Pandas provides excellent functionality for group by and aggregations. However, for time series data, we need a bit of manipulation. In this post, I'll take a small example of weather time series data.
import pandas as pd
import matplotlib.pyplot as plt
plt.style.use('ggplot')
%matplotlib inline
df = pd.read_csv("weather.csv", index_col=0, parse_dates=True).tz_localize("UTC").tz_convert("US/Central")
df.head()
We'll create a new column in the df containing the hour information from the index.
df["hour"] = df.index.hour
df.head()
mean_temp_humidity = df.groupby("hour").mean()
mean_temp_humidity.head()
mean_temp_humidity.plot(subplots=True);
We can use pivoting to achieve the same dataframe.
mean_temp_humidity_pivoting = pd.pivot_table(df, index=["hour"], values=["temperature", "humidity"])
mean_temp_humidity_pivoting.head()
By default the aggregation function used in pivoting is mean.
For this, we want to have a dataframe with hour of day as the index and the different days as the different columns.
df["day"] = df.index.dayofyear
df.head()
daily_temp = pd.pivot_table(df, index=["hour"], columns=["day"], values=["temperature"])
daily_temp.head()
daily_temp.plot(style='k-', alpha=0.3, legend=False)
plt.ylabel("Temp");
So, we can see some pattern up there! Around 15 hours, the temperature usually peaks.
There you go! Some recipes for aggregation and plotting of time series data.


Text fields allow users to input, edit, and select text. They typically reside inside forms, but can also be used in modal dialogs and subnavigation bars. Input text fields are also used for search and filtering of content.


rotten > Library > Biographies > Sports Figures > Don King
Don King
This magificently coifed boxing promoter once killed a guy.
And then, he killed another guy. We're not going to say anything
really bad about him, cuz we don't want to get killed.
First job was at Hymie's Chicken Shack.
Represented Mike Tyson for a while.
Timeline
| 6 Dec 1932 |
Don King born. |
| 1974 |
Released from prison. |
| 1974 |
Rumble in the Jungle, $5M to both Ali and Foreman. |
| 1984 |
Managed a promotional tour for Michael Jackson. |
| 1985 |
Indicted for tax fraud. |
Pornopolis |
Rotten |
Faces of Death |
Famous Nudes
|


Please fill the username and password fields with a and a!
Modal body text goes here.
Error contents


Paper by 53 makes painting realistic skies and clouds a breeze. If you had fun drawing trees, just wait until you fluff out happy clouds that would make Bob Ross proud. Honestly, with just a few washes of watercolor and strokes of pencil, you’ll have an instant masterpiece. Here…let me show you how.
Blue Skies Fade
Let’s start with the easier of the two, a nice bright blue sky. One of the more important steps is choosing a blue to paint with. The hue…the saturation…and the brightness all play a role in the overall emotion of the piece. I tend to mix in a swirl of green and keep it slightly over saturated to contrast against skin tones as seen in my many PaperFaces portraits . If you’re going for doom and gloom, back off the blue and mix in gray instead to set the foundation for an overcast sky.
Blue and overcast sky color mixes
With the base sky color mixed, select the pencil tool and draw a line across the screen to represent the horizon. This step isn’t always necessary, so if you’re a pro and don’t need the help of a guide feel free to move on.
Watercolor Base Coat
Prep work complete, now you can start the actual painting of the sky. What you’re trying to accomplish with this step is a smooth coat that gradually fades to white as you approach the horizon line. The easiest way to do this is with the watercolor brush, starting from the top of the composition, working down in a slight zig zag pattern.
Direction your stroke should follow. Back and forth, top to bottom.
When painting, do not lift the stylus or your finger from the screen until you finish this first coat. The trick is to move very slow near the top, and progressively speed up the zig zag pattern as you approach the horizon line. Depending on how gradual of a fade you want in the sky, multiple coats may be required. Follow the same pattern from before to keep everything looking smooth.
The finished result. Even distribution of color near the top that slowly fades as it approaches the horizon line.
Painting Puffy Clouds
ProTip: White lightens quickly and you don’t want to remove too much blue at once, so be careful with this step. If you remove too much, just rewind and try again.
The technique I use for fluffing in clouds with the brush is very similar
No need to overdo it with the wisps, just a few light crosshatches is all that's needed. For those longer streaky clouds your pencil strokes should be longer and more horizontal.
Criss-cross your pencil strokes
Don't be afraid to go back into the clouds with the blue color we initially painted the sky with. The same "streaky" pencil pattern used to draw wisps (see above) can be used to add subtle cloud shadows. I often flip flop between blue and white to get the detail just right.
Adding subtle blue shadows to the larger fluffy clouds and the finished result.
## Sunsets and Fire Skies
To paint a sunset, I use the same exact techniques and process. The only difference is the addition of three to four colors to create all the sunset fades and multi-colored clouds.
Fiery red skies and silhouetted foreground subjects look super dramatic, so that's what I'm going to demonstrate in the following steps. Remember, color is extremely subjective, I'm merely showing a few examples to get you started. Feel free to experiment and vary them up --- there is no right or wrong here.
![screenshot of sunset red orange color mix](http://climbafingmountain.github.io/images/paper-53-sunset-red-color-mix.jpg)
We'll start by mixing orange into red and then draw a light line at the sky's horizon to act as our guide. Select the watercolor brush and apply paint from the horizon line up --- in the opposite direction from before. Remember the zig zag pattern we used earlier to paint a blue sky? Good. Because it's going to come in handy here.
Just like the blue sky from before, paint slowly from left to right, slightly overlapping the row before without lifting your pen or finger. As you approach the top of the sky move faster to fade the color into the background.
![screenshot of 1st red coat](http://climbafingmountain.github.io/images/paper-53-sunset-red-color-wash.jpg)
### Gradations of Color
A few coats to build up the sky into a smooth gradation of color may be necessary. What you're looking for is a darker red at the horizon, that gradually fades into the background about halfway up from the top. I fade the red around halfway to leave room to fade a dark blue-violet down from the top in this next step.
![screenshot of sunset dull blue color mix](http://climbafingmountain.github.io/images/paper-53-sunset-blue-color-mix.jpg)
After you're satisfied with the red coat, mix a dull and dark blue gray to paint the other half of the sunset. Use the same zig zag painting pattern, only this time start in the upper left corner and work your way down. As you approach the faded red, paint faster to fade into the background just before it, leaving about an inch of background between.
Don't be afraid to rewind and redo your gradations. I usually don't get the shape and fade right on the first try.
You could leave the sunset washes as is and proceed to drawing clouds, but if you want to add even more drama to the sunset, apply a thin wash of yellow. I like to mix a creamy color like so:
![screenshot sunset yellow cream color mix](http://climbafingmountain.github.io/images/paper-53-yellow-cream-color-mix.jpg)
And paint a light coat over the red, background area in the middle, and gradually fade into the blue.
![screenshot sunset yellow cream wash](http://climbafingmountain.github.io/images/paper-53-sunset-yellow-cream-wash.jpg)
**ProTip:** Paint faster as you approach the blue wash to avoid turning it into a green color. Yellow and blue make…
{: .notice}
### Sunset Clouds
To add clouds to our sunset let's start by mixing a red into white. You want a pink color that has just enough white in it to lighten while you paint --- some trial and error may be necessary to get the mix right. Once you successfully mixed a pink, fluff in a few clouds using short circular strokes (same technique used in the blue sky).
![screenshot of pink fluffed clouds](http://climbafingmountain.github.io/images/paper-53-sunset-pink-fluff-clouds.jpg)
Giving these clouds depth and dimension can be achieved by adding a little more red to the mix. You're looking to add just enough to make the color translucent instead of opaque. A few strokes is all that's needed when applying red tinted shadows to the clouds.
Adding pink to the clouds for extra fluffiness.
**ProTip:** If you don't add enough red to the color mix, the watercolor brush will lighten the cloud instead of darkening it.
{: .notice}
A pencil filled with a pinkish red is also perfect for refining clouds edges or drawing in long wispy clouds.
Color mix and end result of drawing pink wispy clouds with the pencil tool.
An added benefit of painting a sunset, is how it reacts with objects that are placed in silhouette. You can draw or paint almost anything and fill it with a dark color to add drama to your creation. The outline of a figure in the foreground, hills and mountain tops, or a half circle at the horizon to represent an eclipsed sun are all appropriate choices.
![screenshot of sun and hills in silhouette](http://climbafingmountain.github.io/images/paper-53-sunset-finished.jpg)
## Variations on a Theme
Using all of these techniques I was able to create a variety of backdrops for the portraits in my [PaperFaces Project](http://climbafingmountain.github.io/paperfaces/). Below are a few finished skies and clouds to show what is possible just by varying the color and composition.
## Watch for the Next Tutorial
{:.no_toc}
Next up --- the perfect landscape compliment to happy trees and clouds, crashing waves and water.
[^1]: Watching Bob Ross paint on his PBS show, [**The Joy of Painting**](http://en.wikipedia.org/wiki/The_Joy_of_Painting) was a huge influence on me during my high school years. Many of the techniques I use to paint with Paper by 53 have been adapted from him.


|
ObservableScrollState.java
|
/*
* Copyright 2014 Soichiro Kashima
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.marshalchen.ultimaterecyclerview;
/**
* Constants that indicates the scroll state of the Scrollable widgets.
*/
public enum ObservableScrollState {
/**
* Widget is stopped.
* This state does not always mean that this widget have never been scrolled.
*/
STOP,
/**
* Widget is scrolled up by swiping it down.
*/
UP,
/**
* Widget is scrolled down by swiping it up.
*/
DOWN,
}


--- layout: project title: Education content for designers description: Learning hub experience for Oneistox, a ed-tech start-up for designers tags: [Live, UX] order: 18 duration: 8 weeks ---
IN IMPLEMENTATION PHASE UNDER DEVELOPMENT. CAN BE DISCUSSED OVER A CALL.
Oneistox is a Y-Combinator backed upskilling ecosystem for architect and design community. They provide cohort based courses with industry relevant new-age conten for students and early professionals.
I worked with them as UX strategist and designers for their content section with resources in the form of articles and videos. The product is still under development and so more details can be discussed over a call.


--- layout: plainarticle permalink: '/homecoming/2014/general-assembly/' title: General Assembly at Homecoming 2014 ---
The last Alumni Association General Assembly took place on 20th September 2014, in Conrad Naber Hall, Jacobs University campus. 63 Alumni were present. The minutes of the GA are available here in PDF form. Please check out the video below:
{% include iframe.html src="//www.youtube.com/embed/X2k5ymtT1bU" %}


--- layout: archive permalink: /selected-papers/ title:
My publication list can be found on the [Google Scholar](https://scholar.google.com/citations?user=RMcjLFEAAAAJ&hl=en)/p> {% include base_path %} {% capture written_year %}'None'{% endcapture %} {% for post in site.selected-papers reversed %} {% capture year %}{{ post.date | date: '%Y' }}{% endcapture %} {% if year != written_year and post.visible != 'false' %}


with io.echo(): io.write("This code will be written.") io.write("And this too.")
import numpy as np with io.echo(): table = np.random.randn(200, 200) io.write(table)


See original publication at https://correctiv.org/superbugs
This example demonstrates how to use d3-playbooks
to render small multiples. See README
for details.
The datasource is a single Google Spreadsheet loaded via load-spreadsheet.js
.
Hover (or tap on touchscreens) on bars to display data for this country.


The goal of this lab is to
This assignment has been added to your AnimationFramework repository. To get the source, run
> cd cs56/AnimationToolkit > git pull > cd build > cmake .. > make
You should now have a new directory under assignments called a9-ik.
In this question, you will work through the calculations needed to perform IK for a two-link chain on paper. Include your answers in your readme for this week. Remember that you can use python, octave, matlab, maple, our basecode or any other method to check your answers!
Suppose we have a 2-link chain as in class.
The root joint p1 is located at the origin.
The next joint p2 is offset from p1 by (2,0,0)T
The next joint p3 is offset from p2 by (5,0,0)T
Solve using the analytic two-link method (Reference: here)
Using polar coordinates to compute the orientation of joint 1:
Using angle/axis to compute the orientation of joint 1:
In this question, you will implement the analytic IK algorithm from class. Put your work in
libsrc/animation/AIKController-basecode.cpp inside the method solveIKTwoLink.
// ASkeleton will contain a two-link chain // Assume joint 0 is the root // Assume joint 1 is the middle joint // Assume joint 2 is the end effector // If your implementation is correct, the end effector should be positioned at goalPos bool AIKController::solveIKTwoLink(ASkeleton& skeleton, const AVector3& goalPos)
To run the demo from the build directory, type
build> ../bin/a9-iksimple
Implementation tips:
The basecode includes a simple interface and 3D viewer. See above for a demo.
The camera can be controled with the mouse:
The player control panel on the top left can be used to play/pause, step through keys, and see the simulation time.
The panel at the bottom left can be used to set the goal position of the end effector.
The target position can also be moved with the mouse by holding down the Ctrl key.
In this question, you will use the CCD's angle/axis computation (with nudge factor of 1) to make a character's gaze follow a target. Implement your solution in assignments/a9-ik/ALooker.cpp
Your approach should align the forward direction of the head with the
target. The forward direction of the head is the Z axis (0,0,1) in its local
coordinates.
To run the demo from the build directory, type
build> ../bin/a9-looker
In this question, you will implement the CCD algorithm for a full character. Implement you solution in IKController::solveIKCCD() located in libsrc/animation/AIKcontroller-basecode.cpp. Given the joint specified by jointid, make its position match goalPos. The chainsize indicates how many descendents to use for the CCD algorithm. When the function returns, skeleton's pose will have the end effector matching the goalPos (as close as feasible).
// solveIKCCD positions the joint given by jointid so its global position // is located at goalPos // // param skeleton: the character to modify // param jointid: the ID of the end effector // param goalPos: the target position for jointid (global pos) // param chainSize: the number of joints to "nudge" towards the goal. (e.g. the number of descendants to use) // param maxIters: the max number of iterations to try to reach the goal // // return true/false based on whether we could reach the goal // side effect: skeleton should by posed such that jointid is located at goalPos (or in this direction of // goalPos if the target is out of reach) bool AIKController::solveIKCCD(ASkeleton& skeleton, int jointid, const AVector3& goalPos, int chainSize, int maxIters) { // recompute the chain if the end effector or chain size changes if (_selectedJoint != jointid || chainSize != (int) _chain.size()) { computeJointChain(skeleton, jointid, chainSize); } // There are no joints in the IK chain for manipulation if (_chain.size() == 0) return true; // implement CCD algorithm here return false; }
To run the demo from the build directory, type
build> ../bin/a9-ik
The basecode comes with a viewer to help yo utest your algorithm.
The camera can be controled with the mouse:
The left frame lets you select joints, reset the pose, and set the goal position maunally.
The target position can also be moved with the mouse by holding down the Ctrl key.
The right panel lets you select the algorithm for IK along with IK paramaters. For CCD:
In this question, implement IKController::solveIKAnalytic() located in libsrc/animation/AIKcontroller-basecode.cpp. In this question, you will combine the techniques of the simple two-link analytic approach with CCD's angle/axis calculation. This work will allow us to animate the legs and arms of the character such that the knees and elbows bend along their natural axis.
// solveIKAnalytic: positions the joint given by jointid so its global position // is located at goalPos. This method computes rotations for the parent and grandparent // of jointid. // // param skeleton: the character to modify // param jointid: the ID of the end effector // param goalPos: the target position for jointid (global pos) // // return true/false based on whether we could reach the goal // side effect: skeleton should by posed such that jointid is located at goalPos (or in this direction of // goalPos if the target is out of reach) bool AIKController::solveIKAnalytic(ASkeleton& skeleton, int jointid, const AVector3& goalPos)
To run the demo from the build directory, type
build> ../bin/a9-ikSet the IK type to Analytic to test your work.
Implementation hints:
vec3 limbDir = parentJoint->getLocalTranslation(); limbDir.Normalize(); vec3 axis = limbDir.Cross(vec3(0,0,-1)); if (limbDir[1] < 0) axis = limbDir.Cross(vec3(0,0,1));
In this question, you will implement a procedural dancer using solveIKAnalytic (or solveIKCCD if you
did not finish the previous question). Implement your solution in assignments/a9-ik/ADancer.cpp.
Your solution will modify the motion in idle.bvh to make the hands and hips move differently.
To run the demo from the build directory, type
build> ../bin/a9-dancer
Demo requirements
Suggested approach
Option 1 Make you own unique animation based on inverse kinematics, splines, and motions. For example, make a new dance, have the character hold a cup, or wave. Try your procedural dance or look on an other motions.
Option 2 Procedural walker. Animate walking by using IK to move the feet (to move forward, you will need to move the root too). Experiment with unique characters, such as spiders and robots.
Be sure to include a video of your demo for full credit!


| < Day Day Up > |
Hack 34 Restrict Logins
In this chapter, we've covered many methods of securing the boot and login environments. It's probably no surprise that you can further control who can log into your system and when: Unix systems contain many built-in mechanisms, allowing you to choose the most appropriate means and policy for your network. Furthermore, the defaults may not always suit your needs. Do you really want users to be logged into multiple terminals when they can effectively do their work from one? For that matter, do you want any user, including nonemployees, to try his hand at logging into your systems at any hour of the night and day? Here's how to tighten up some defaults. 3.12.1 /etc/ttysSince users log into terminals, a logical file to secure is the terminal configuration file, /etc/ttys. We briefly saw this file in [Hack #24] when we password protected single-user mode. This file is divided into three sections, one for each of the three types of terminals. Let's concern ourselves with the virtual terminals, ttyv, which are the terminals available for users physically seated at the system's keyboard. # grep ttyv /etc/ttys ttyv0 "/usr/libexec/getty Pc" cons25 on secure ttyv1 "/usr/libexec/getty Pc" cons25 on secure ttyv2 "/usr/libexec/getty Pc" cons25 on secure ttyv3 "/usr/libexec/getty Pc" cons25 on secure ttyv4 "/usr/libexec/getty Pc" cons25 on secure ttyv5 "/usr/libexec/getty Pc" cons25 on secure ttyv6 "/usr/libexec/getty Pc" cons25 on secure ttyv7 "/usr/libexec/getty Pc" cons25 on secure ttyv8 "/usr/X11R6/bin/xdm -nodaemon" xterm off secure The word on indicates that that terminal is available for logins. By default, the first eight terminals, ttyv0 through ttyv7, will accept logins. You've probably discovered this yourself by pressing Alt-Fx, where x is a number between 1 and 8. On a server system, you may need only one virtual terminal. Disable the other terminals by changing the word on to off.
The word secure means that the system is physically secure, implying that it's okay for a user to walk up to the keyboard and log in as root. Since it's never okay for a user to log in as root, you should disable that default. For whatever virtual terminals you've left on, either change the word secure to insecure or simply remove the word secure.
3.12.2 /etc/login.accessNow let's see what can be done with /etc/login.access. At its most stringent, you can use this file to prevent all remote logins, meaning you can log in only if you are physically sitting at that system: -:ALL:ALL EXCEPT LOCAL Note the syntax that is used for each line in this file. The - means access denied. Its alter ego is +, which means access granted. The first ALL is a wildcard for all users. The second ALL is a wildcard for all locations. The EXCEPT LOCAL is the exception that allows just the local location. You could modify that rule slightly to disallow remote and local root logins: -:root:ALL Take some care when modifying this file. Its syntax supports both user and group names, allowing you to specify exactly who is allowed to log into a system. This can be extremely useful in limiting access to a server system. The syntax also supports IP addresses. This can also be useful in ensuring that only hosts in your network or a particular subnet can access certain systems. But, as in any security mechanism that relies on IP addresses, do keep in mind that IP addresses can be spoofed. Finally, if you make changes to this file, test your changes immediately. If you restrict access to certain users, ensure those users can still log in. Further, try to log in as other users to ensure that they are actually being denied access. 3.12.3 /etc/ssh/sshd_configThink for a moment. Other than logins to virtual terminals, how else do your users log into systems? Most likely (and, hopefully) through ssh. You can control exactly who can ssh into a system by adding a line to the /etc/ssh/sshd_config file of the system running the SSH daemon. There are two ways you can control this. One is through AllowGroups. By default, all groupsómeaning all usersócan ssh into a system. The other way is through AllowUsers, where again, all users are allowed by default. Suppose I want to allow only the users genisis, biko, and dru to ssh into a particular system. I could create a group called remote that contains those users: # grep 100 /etc/group # # pw groupadd remote -g 100 -M genisis biko dru In this example, I first double-checked that the group ID of 100 was not currently in use. I then created, with pw groupadd, the remote group with a GID of 100 (-g 100) and with those three members (-M genisis biko dru). Now I can limit ssh access to just the members of that group: # echo 'AllowGroups remote' >> /etc/ssh/sshd_config Alternatively, I could have just added those three users directly: # echo 'AllowUsers genisis biko dru' >> /etc/ssh/sshd_config Any user who does not match either AllowGroups or AllowUsers will still receive a password prompt when attempting to connect to the SSH daemon. However, the connection attempt will fail with a permission denied message, even if the user provides a correct username and password. The SSH daemon will print a message regarding the failed attempt to its console, sending a copy to /var/log/messages and emailing to root as part of the daily security run output. To be even pickier, if your users always log in from the same system, you can do this: AllowUsers<EMAIL_ADDRESS><EMAIL_ADDRESS><EMAIL_ADDRESS>However, don't be that picky if your users don't have static IPs! Remember, if you make any changes to the SSH daemon's configuration file, you'll need to send a "signal one" to sshd to notify it of the changes: # killall -1 sshd After informing sshd of the changes, immediately use a ssh client to test your changes. For example, if I instead add the line Allowusers genisis biko dru, I'll find that user nastygirl is still able to connect. Why? The parameters in /etc/ssh/sshd_config are case-sensitive. You don't want to find out six months later that anyone was allowed to connect when you thought you had restricted connections to certain users. 3.12.4 /etc/login.confWe've restricted who can log in and from where for both local and remote ssh logins, but we still haven't restricted when those users can log in. To do that, let's look at some other options that are available in our old friend /etc/login.conf [Hack #30] . This file supports the options times.allow and times.deny. For example, to allow all users to log in between 9:00 AM and 5:00 PM every Monday through Friday, add this line to the default:\ section: :times.allow=Mo-Fr0900-1700:\ Once you introduce the times.allow option, access will automatically be denied for the time period not listed. The converse also works. That is, you can specify the denied times in times.deny, and all other times will be allowed. Remember, whenever you make a change to /etc/login.conf, rebuild the database with cap_mkdb /etc/login.conf and test your changes. 3.12.5 See Also
|
| < Day Day Up > |


Products |
Here at Maschino Lumber we carry a multitude of products. These Products we have on a daily basis and supplies vary between items. Make sure you call before heading in. Call us during normal business hours at<PHONE_NUMBER>
Anything we dont have right on had we can usually make within a week. If you need something custom we do all custon sawing. We are one of the only saw mills in the state that will do all dimension custom sawen pine and hemlock beams. Thats right any dimension you want we can make. Also if youre looking for big project help. Our experienced staff can help you come up wityh a plan and find out all the products you need. We are a one stop shop for everhthing. So come check us out at 79 Mores Road New Gloucester, Maine.
Ever sence 2016 Maschino Lumber has been heating homes all over the state with firewood. Firewood is limited and takes time to prepare for you. Make sure to call during our hours of operation to be put on the list.