And, since I know you want to get your hands on some code, point your friendly neighborhood browser to this link and enjoy!

]]>I recently received an inquiry regarding a problem to be included in the Typescript Programming Test Problems repo. This one requires a more extensive writeup for the analysis, so I decided to devote a blog post to the topic.

Problem Statement: You are given a function, *die5()* that simulates the roll of a five-sided die. Write a function that uses only *die5()* to simulate the roll of a die with a small prime number of faces. Discuss limitations or edge cases involved with the use of this function.

At first read, my guess is that the prime number of faces has nothing to do with the problem. It’s indicative of real-world applications where a person is often given either too little or too much information to solve a problem. One of the challenges is knowing what needs to be obtained for a solution and what can be discarded. So, let’s restate the problem slightly as how to simulate the roll of a k-sided die (where k is an integer > 5) using only a five-sided die.

A five-sided die can only generate five combinations, 1, 2, 3, 4, and 5. For a *k*-sided die, we need to be able to uniformly generate *k* combinations, but are restricted to a five-sided die as a generator. There was, however, no restriction placed on the number of rolls of the five-sided die. Let’s say we roll the five-side die twice. There are 25 possible combinations. Three rolls has 125 possible combinations. If r is a positive integer > 0, then *r* rolls generates *5^r* possible combinations.

I like to have some sort of mathematical model for such problems. Let [1,5] refer to a pseudo-random selection of integers in the interval one to five, inclusive. We expect *die5()* to approximately model uniform samples from [1,5]. The statistical quality of the final result, of course, depends on the quality of that sampling.

With 25 combinations, we could theoretically model the roll of up to a 25-sided die. For numbers between 6 and 25, modulo arithmetic can help by dividing 25 in to ‘bins’ of length *k*, with potential leftover. Consider the next two primes in sequence, 7 and 11. Here are the ‘bins’ for each case with the corresponding roll of the 7-sided die.

7-sided die with 25 combinations (3 full bins, one partial)

1 2 3 4 5 6 7 - 8 9 10 11 12 13 14 - 15 16 17 18 19 20 21 - 22 23 24 25 1 2 3 4 5 6 7 - 1 2 3 4 5 6 7 - 1 2 3 4 5 6 7 - * * * *

11-sided die with 25 combinations (two full bins, on partial)

1 2 3 4 5 6 7 8 9 10 11 - 12 13 14 15 16 17 18 19 20 21 22 - 23 34 25 1 2 3 4 5 6 7 8 9 10 11 - 1 2 3 4 5 6 7 8 9 10 11 - * * *

Consider a generating function, *g(r)*, that depends only on the number of rolls, *r*, of a five-sided die. For the above examples, *r = 2*. The generating function should return uniform samples of *[1,5^r] or [1,25]* for the two-roll case. If a generated number falls in a ‘full’ bin, then take the result mod *k* and add one. That generates a sample in* [1,k]*, which is the desired result.

A decision must be made on how to handle cases where the generating function produces a number in a ‘partial’ bin. The easiest solution is to simply call the generator recursively. This has the benefit of being simple, and recursive programming is a fad for testers, so you are likely to gain some style points :). It does have the side-effect of ’throwing away’ iterates in [1,5]. For a perfectly uniform generation in [1,5], that is not an issue. However, in practice, *die5()* is likely to suffer from statistical flaws that could have a downstream effect on the final result.

So, what does a generating function look like? A simple, linear polynomial is as good a start as any, so consider models of the following form for the two-roll generator

*g(2) = a[1,5] + b[1,5] – c*

where a, b, and c are positive integers. This generalizes in the multi-roll case to

*g(r) = a[1,5] + b(1)[1,5] + b(2)[1,5] + … + b(r-1)[1,5] – c*

Returning to the two-roll generator, what constraints can be placed on the generation? Define *n* to be the largest integer such that* nk <= 25*. This is the number of full bins, given the number of sides of the die, *k*. The smallest input roll is one and the largest is five. When a one is rolled both times, the result should be equal to one to cover the low range of [1,25]. When a five is rolled both times, the generator should produce 25 in order to cover the high range of [1,25].

*1 <= a[1,5] + b[1,5] – c <= 25*

At the high end of both [1,5] rolls, the generated value should be greater than or equal to *nk*.

This produces three unknowns, *a*, *b*, and *c*, but only two constraints. So, there is a family of possible generating functions.

The generator must also return uniform numbers in [1,25]. If a frequency count of each generated number is computed, the histogram should be perfectly flat as the number of generations tends to infinity. A way to test for this is to loop over the generations for each possible roll of the five-sided die and simply print out the result. Numbers that are skipped or repeated lead to bias in *g(r)*.

To resolve the underdetermined problem, set *b = 1* and use the two constraints to solve for *a* and *c*. Since addition is associative, the model is the same as setting* a = 1* and solving for *b* and *c*. Just reverse the order of the *a* and *b* terms.

The simplified model is

*g(2) = a[1,5] + [1,5] – c*

Use the two extreme cases where *[1,5] = 1* and *[1,5] = 5* for both rolls fo the five-side die. At the low end,

*1 = a*1 + 1 – c = 1(a + 1) – c*

At the high end,

*25 = a*5 + 5 – c = 5(a + 1) – c*

Let *j = a + 1*, then

*1 = j – c or j = 1 + c*

*25 = 5j – c = 5(1+c) – c = 5 + 4c*

This yields *c = 20/4 = 5* and* j = 1 + c = 6*, which means* a = 5*.

Computation of *n* given *k* is pretty simple and the entire model is created in the *getCoefs()* function in the code distribution for this problem. It only works for the two-roll problem. I do not like the idea of generalizing this particular family to a number of rolls greater than two as it puts too much weight on the first roll. The likelihood of generating a model that is non-uniform is too great.

I solved the three-roll model by hand and got *a = 29* and *c = 30*. I did verify that the two-roll model is uniform. I believe that the three-roll model is **not** uniform, but perhaps an astute reader will prove me wrong (I have very limited time to devote to these problems). So, I can address the two-roll model which is good for a ‘small’ number of primes as long as *k <= 25* and we have a ‘perfect’ quality generator for [1,5]. In practice, the latter will not be true.

To simulate the roll of a *k*-sided die using a generator for [1,5], pass in the two-roll model and generate two rolls in [1,5]. Use those results in the generating function for *k*. Then, test if the value is less than or equal to *n*k. If so, modulo-*k* math produces a result. Otherwise, call the generating function again. This code is provided in the simulate() function,

let roll: number = model.a*RandomIntInRange.generateInRange(1,5) + RandomIntInRange.generateInRange(1,5) - model.c; // test if number is within the integer multiple of k if (roll <= model.n*k) { // return result mod k plus 1 to compensate for 1-based index return (roll%k) + 1; } else { // too bad ... try again return simulateRoll(model, k); }

where the Typescript Math Toolkit *RandomIntInRange* class is used to generate iterates in [1,5]. Since it is based on the system-supplied *Math.random()*, it is a typical linear congruential generator and suffers from all the known flaws of such generators.

I supplied a *validate()* function that computes the frequency histogram for a given value of k. This is used in the specs for *k = 6, 7, 8, and 9*. The numbers look pretty good, but would be better for a higher-quality [1,5] generator. Try it for other values of *k* and a greater number of iterations per validation and study the results.

So, the two limitations are we can only simulate up to a 25-sided die using two rolls of a five-sided die and the final output is dependent on the statistical quality of the generator that simulates the five-sided die. The general problem (for any value of *k > 5*) requires extensive iteration and simulation to produce a model that meets the necessary constraints, particularly that of uniform distribution in [1, 5^r].

Here is the repo – experiment with the code and have fun!

]]>This project has a good writeup on the Github page and I know you want to go directly to the code, so point your friendly neighborhood browser in this direction.

https://github.com/theAlgorithmist/Angular5-Deeplearn

and don’t forget the three rules,

1 – Experiment

2 – Have Fun

3 – Drink Coffee

Rule #3 is the most important.

]]>This a follow-on to the developments in my Angular Dev Toolkit regarding data-driven application logic. When discussing the expression-based decision tree, I mentioned that it would be possible to have a leaf node contain the name and initial state of a finite-state machine. For that approach, the FSM must be data-driven and to fit nicely into the general Angular idiom, I also wanted a machine that was reactive.

The Decision Tree and Sequencing Engine are general-purpose derivatives of work previously implemented for NDA clients (I had to rewrite everything pretty much from memory). As such, they will remain in the client-only Angular Dev Toolkit. Subsequent private and online discussions led me to believe that the Finite State Machine was of suitable general interest to open-source the baseline version.

If you are interested in background, then here is one of my favorite online descriptions of a Finite State Machine and is closest to how I learned the topic ‘back in the day.’

The machine implementation in this repo is Mealy-style, although a state may be passed as data, so Moore-style machines can be used within this framework.

A FSM consists of a number of named states, and transitions from those states. One or more states may be defined as an *acceptance* state, meaning that after exercising the machine with a finite number of inputs, the machine may indicate acceptance of a certain criteria. This could be a valid string or that the number of zeros in a binary sequence is even. It is possible (and I’ve done this in C++) to define a *rejection* state with the convention that the machine may never transition out of this state. This is a way to catch and flag incorrect or other inputs.

The set of inputs to a FSM is called an *alphabet*.

The *FiniteStateMachine* class in this distribution has two possibly differentiating features from other implementations (particularly in Java, JS, and TS). One is that listening for specific state transitions is not performed by callback functions applied to those states. Instead, the machine is reactive. One or more (RxJs) *Observers* may subscribe to the machine and receive updates on all transitions that includes *from* and *to* states as well as relevant data.

**API**

The FiniteStateMachine class exposes the following models

// this is normally part of the Decision Tree library, but has been ripped out to make this distribution standalone export interface IDecisionTreeAction { success: boolean; // true if operation was successful node?: Object; // reference to the data Object in which an error was detected action: string; // action to take } /** * A state transition must have a 'from' and 'to' (named) state and may contain optional Object data */ export interface IStateTransition { from: string; to: string; data?: Object; } /** * Output from a state transition is the 'to' state and optional data obtained from the transition function */ export interface IStateOutput { to: string; data?: any; } /** * The transition function is Mealy-style, that is a transition to a new state is based on prior state and * input data. Since state is optional in this interface, pass the state name as data and a Moore-style * machine can be implemented. */ export interface transFunction { (data: any, state?: string): IStateOutput; }

The public API of the *FiniteStateMachine* class is as follows.

public static create(data: Object, name?: string): FiniteStateMachine | null public get numStates(): number public get numTransitions(): number public get currentState(): string public get states(): IterableIterator public get initialState(): string public get initialData(): Object | null public get isAcceptance(): boolean public get alphabet(): Array | null public fromJson(data: Object): IDecisionTreeAction public addState(stateName: string, acceptance: boolean=false): void public addTransition(from: string, to: transFunction): boolean public addSubscriber(observer: Observer ): void public next(input: any, initialState?: string): IStateOutput | null public clear(): void

**Usage**

The FSM in this distribution may be used by directly assigning states and transition functions. The machine may also be described with *Object* data. These cases are best illustrated by example.

First, consider one of the machines discussed in the above introductions to FSM’s. This machine is designed to test sequences consisting of the letters a, b, c, and d, which is the machine’s alphabet.

The states are S1, S2, S3, and S4, the latter of which is the acceptance state. The machine’s initial state is S1 and letters are input one at a time. The machine is tested for being in the acceptance state after the final transition (refer to the above link for the state diagram).

This machine can be implemented with the following code (which is provided in the specs located in the test folder) which presumes a string input Array, str, i.e.

const str: Array = [‘a’, ‘a’, ‘a’, ‘a’, ‘c’, ‘d’];

Note that the transition functions are defined statically in code, which allows them to use stronger typing than is described in the transFunction interface.

For compactness, testing the return value from adding a transition is not included in the example.

const f12: transFunction = (data: string) => { return data == 'a' ? {to: 'S2'} : {to: 'S1'} }; const f22: transFunction = (data: string) => { return data == 'a' ? {to: 'S2'} : (data == 'b' ? {to: 'S1'} : (data == 'c' ? {to: 'S4'} : {to: 'S2'})) }; const f32: transFunction = (data: string) => { return data == 'a' ? {to: 'S1'} : (data == 'b' ? {to: 'S4'} : {to: 'S3'}) }; const f42: transFunction = (data: string) => { return data == 'd' ? {to: 'S3'} : {to: 'S4'} }; . . . const __machine: FiniteStateMachine = new FiniteStateMachine(); __machine.addState('S1'); __machine.addState('S2'); __machine.addState('S3'); __machine.addState('S4', true); __machine.addTransition('S1', f12); __machine.addTransition('S2', f22); __machine.addTransition('S3', f32); __machine.addTransition('S4', f42); const n: number = str.length; let i: number; let state: IStateOutput = __machine.next(str[0], 'S1'); // set the initial state and first input for (i = 1; i < n; ++i) { state = __machine.next(str[i]); }

Based on the machine’s construction, we would expect *__machine.isAcceptance* to be false. This is also the answer to the quiz question posed on the site from which the example was taken

Many algorithms can be expressed as a FSM and Regex is equivalent to FSM (Kleene’s Theorem), for example. Whether or not one should implement an algorithm using a FSM is another topic. One example that often does not seem to fit a FSM architecture is the ‘change machine’ problem (this was a lab exercise in college).

Consider a machine that accepts coins (penny, nickel, dime, quarter) for an amount less than a dollar. After each coin is deposited, the machine updates the remaining balance, indicates if sufficient payment has been made, and computes any necessary change.

Alphabet and machine states may be considered equivalent in this machine, so we define states p, n, d, q, and c for the coin denominations and a ‘complete’ state. There is no transition out of the complete state, which is also the acceptance state for this machine.

Code to implement this machine is provided in the specs.

The most potentially useful feature of this FSM is the ability to describe a machine in data. This allows multiple machines to be defined in metadata and then dynamically applied based on real-time conditions in an application.

The string-test machine shown above can be described in the following data *Object*

const machine1: Object = { name: 'StringTest', initialState: "S1", alphabet: [ 'a', 'b', 'c', 'd' ], states: [ { name: 'S1', isAcceptance: false, transition: "return data == 'a' ? {to: 'S2'} : {to: 'S1'}" }, { name: 'S2', isAcceptance: false, transition: "return data == 'a' ? {to: 'S2'} : (data == 'b' ? {to: 'S1'} : (data == 'c' ? {to: 'S4'} : {to: 'S2'}))" }, { name: 'S3', isAcceptance: false, transition: "return data == 'a' ? {to: 'S1'} : (data == 'b' ? {to: 'S4'} : {to: 'S3'})" }, { name: 'S4', isAcceptance: true, transition: "return data == 'd' ? {to: 'S3'} : {to: 'S4'}" }, ] };

Transition functions defined in data will be called with arguments that strictly match the transFunction interface. They must adhere to the following conventions.

- Only the function body need be defined in a string.
- The variable names data and state are arguments. Use them as such.
- Transition functions must be pure.
- The self-referential pointer (this) is bound to the global context due to
*Function*constructor invocation. Do not use*this*inside the function body. - Do not reference any variables that are not defined in the function body as we can not be ‘loose’ regarding execution context of these functions.

In a typical application, the above Object description is input external to the application. Implementation of the machine reduces to

let result: IDecisionTreeAction = __machine.fromJson(machine1); let str: Array = ['a', 'b', 'a', 'c', 'd', 'a', 'a', 'c']; let n: number = str.length; let i: number; let state: IStateOutput; for (i = 0; i < n; ++i) { state = __machine.next(str[i]); }

We would expect _*_machine.isAcceptance* to be true.

Additional examples are provided in the specs that accompany the Github repo.

]]>Trees are evaluated based on an input data *Object*, whose property names correspond to independent variables in the nodes of each decision tree. Evaluation is *in-order* or *by-name*. An *in-order* evaluation begins at the first decision tree in the collection and proceeds in order of tree definition until the evaluation is resolved. A *by-name* evaluation begins at the named tree passed to the *evaluate()* method. In this path, it is acceptable for a successful tree evaluation to be an action that is a name of another tree to evaluate. If this is encountered, evaluation proceeds to the next named tree, and so on, until the evaluation is fully resolved.

This convention allows individual decision trees to remain smaller and more re-usable while allowing even more complex logic to be handled vs. that which is easily placed into a single tree.

As an example, consider the following data for a single decision tree with independent variables, *x* and *y*, *upper*, and *lower*.

{ id: '0', priority: 1, expression: '(x >= lower) && (x <= upper)', children: [ { id: '1', priority: 1, expression: 'x < 0', children: [ {id: '4', priority: 1, expression: '', action: 'A1'} ] }, { id: '2', priority: 2, expression: 'x = 0', children: [ { id: '5', priority: 2, expression: 'y < 0', children: [ {id: '8', priority: 1, expression: '', action: 'A2-1'} ] }, { id: '5a', priority: 3, expression: 'y = 0', children: [ {id: '9', priority: 1, expression: '', action: 'A2-2'} ] }, { id: '6', priority: 4, expression: 'y > 0', children: [ {id: '9', priority: 1, expression: '', action: 'A2-3'} ] } ] }, { id: '3', priority: 3, expression: 'x > 0', children: [ {id: '7', priority: 3, expression: '', action: 'A3'} ] } ]};

Now, suppose a change request is made that some additional logic must be evaluated if the y = 0 node is encountered. It is possible to add branches from that node, but the original tree structure could not be easily re-used in any other application. Instead, consider replacing this node,

{ id: '5a', priority: 3, expression: 'y = 0', children: [ {id: '9', priority: 1, expression: '', action: 'A2-2'} ] }

with this one

{ id: '5a', priority: 3, expression: 'y = 0', children: [ {id: '9', priority: 1, expression: '', action: 'tree2'} ] }

where ‘tree2′ is the name of a decision tree defined with the following data,

{ id: '0', priority: 1, expression: '', children: [ { id: '1', priority: 1, expression: '(z < 0) && (z > lower)', children: [ {id: '4', priority: 1, expression: '', action: 'F1'} ] }, { id: '2', priority: 2, expression: 'z = 0', children: [ {id: '5', priority: 2, expression: '', action: 'F2'} ] }, { id: '3', priority: 3, expression: '(z > 0) && (z < upper)', children: [ {id: '6', priority: 3, expression: '', action: 'F3'} ] } ] }

If these two trees are placed into the *SequencingEngine* with names ‘tree1′ and ‘tree2′, a *by-name* evaluation starting at ‘tree1′ is a simple, one-liner,

action = sequencer.evaluate({x: 0, y: 0, z: 0, lower: -10, upper: 10}, SequencingEngine.BY_NAME, 'tree1');

and this call results in the named action, ‘F2′, which can be acted upon inside the application.

Here is a screen shot of some of the *evaluate()* method. Click on the image for a full-size view.

And, at the risk of some repetition, I will point out that the Angular Dev Toolkit is installed for all my contract clients. This is just one part of my overall value proposition.

The next development in this process is a reactive, data-driven, finite-state machine.

]]>I’ve worked on two projects for NDA clients in the recent past in which it was necessary to clean up architecture and programming issues created by another developer who ‘left the project.’ Both cases had two aspects in common. The first was a myriad of problems created by stateful components and shared mutable state. These can be alleviated by refactoring the application to use a Redux-style architecture (I prefer @ngrx/store).

The second issue was more subtle and involved a complex nest of if-then-else-else-if-else blocks that were in the process of going as far as four levels deep at the time I was introduced to the project. These are difficult to debug (and test) and difficult for another developer to follow. This increases the complexity of both initial development and downstream maintenance. Interestingly, the actions that resulted from the rat’s nest of logic were quite simple; one was imperative routing (the final action was the route name and params). The second set of actions pertained to which components inside a particular page were made visible to the user (the action was to set a number of booleans that controlled *ngIf directives).

I was able to replace this logic with simple JSON and a decision tree in which each node contains an expression that evaluates to a boolean. While I no longer have access to these specific codes, the two low-level constituents to the solution are already in my Typescript Math Toolkit (an expression engine and a general Tree class). I recently sat down and created a *Decision Tree* for the Angular Dev Toolkit that provides a more production-worthy and reusable implementation of the above approach.

Let’s look at a trivial example. Suppose we have a block of logic that depends on a single, independent variable, *x*. An action is taken based on whether or not x is less than, equal to, or greater than zero. Of course, this is rather trivial to code, but it can be represented as the following decision tree.

The tree may be represented in the following format:

{ id: '0', priority: 1, expression: '', children: [ { id: '1', priority: 1, expression: 'x < 0', children: [ {id: '4', priority: 1, expression: '', action: 'A1'} ] }, { id: '2', priority: 2, expression: 'x = 0', children: [ {id: '5', priority: 2, expression: '', action: 'A2'} ] }, { id: '3', priority: 3, expression: 'x < 5', children: [ {id: '6', priority: 3, expression: '', action: 'A3'} ] } ] }

This seems like a lot of work to describe something that is a fundamentally simple code block, but the power of the approach is evident as the logic increases in complexity or the logic needs to change frequently, or alter during interaction with the user.

Note that the root node has no expression to evaluate and this is an indication that the tree is to be fully evaluated every time. Consider a change request that adds the following conditions:

1 – The decision process should only be fully evaluated if the variable, *x*, is in the interval [lower, upper] where ‘lower’ and ‘upper’ are two new independent variables.

2 – For the middle path, x = 0, introduce a new variable, *y*. Take action ‘A2-1′ if y is less than zero and ‘A2-2′ if *y* is greater than zero. This requirement may be visualized as the tree,

and represented in data as,

{ id: '0', priority: 1, expression: '(x >= lower) && (x <= upper)', children: [ { id: '1', priority: 1, expression: 'x < 0', children: [ {id: '4', priority: 1, expression: '', action: 'A1'} ] }, { id: '2', priority: 2, expression: 'x = 0', children: [ { id: '5', priority: 2, expression: 'y < 0', children: [{id: '8', priority: 1, expression: '', action: 'A2-1'} ] }, {id: '6', priority: 3, expression: 'y > 0', children: [ {id: '9', priority: 1, expression: '', action: 'A2-2'} ] } ] }, { id: '3', priority: 3, expression: 'x > 0', children: [ {id: '7', priority: 3, expression: '', action: 'A3'} ] } ] }

The expression on the root node is called a ‘guard expression.’ It must pass during the first step of evaluation in order for the remainder of the tree to be processed. In Angular parlance, it plays a similar role to a route guard.

Actions defined at leaf nodes are simply strings that serve either as raw data or symbolic codes for some other action to be taken. They could be anything from the name of a route to a symbolic code to lookup in a function table to determine which function to execute. Or, as will be important in the sequel, they could be the name of another tree to evaluate.

Again, for small examples with almost no likelihood of change, this approach is over-engineering. The value of the decision tree is realized as logic grows in complexity and/or value is gained from expressing decisions in a data format that can be server-generated, changed dynamically, or one of many trees to evaluate may be chosen imperatively at runtime.

Node expressions may be any expression allowed by the Typescript Math Toolkit expression engine. Independent variables are extracted by the Decision Tree engine dynamically as each node is processed. These variables are assigned to the expression engine and then the expression is evaluated. Although variable values may be of type *string*, *number*, or *boolean*, the expression must evaluate to a *boolean*. If a node’s expression evaluates to true, then the node’s children are processed in the order in which they are defined. Child nodes are presumed ordered by decreasing priority, left-to-right. Tree processing continues until a leaf node is encountered with a named action, and this action serves as the return of an evaluation.

Here is a screen shot of some of the code from the internal *__evaluateTree()* method. Click on the image to see the full-size version.

Usage of the Decision Tree is relatively straightforward. The tree is created and then initialized from an data object,

const tree: DecisionTree = new DecisionTree(); const result: IDecisionTreeAction = tree.fromJson( {...} );

The *result* variable contains a success code indicating whether or not parsing of the tree structure was successful. In the case of failure, the offending node is available in the return to aid in debugging.

Values of the independent variables are taken from another data object, where the *Object* must have the variable names as keys. Corresponding values are used as the actual values of the independent variables from which an expression is evaluated, i.e.

let action: IDecisionTreeAction = tree.evaluate({lower: -10, upper: 10, x: -20, y: 4});

In most applications, such *Objects* already exist as part of the application’s state.

Work is also finished on the Sequencing Engine, which manages a Map of Decision Trees. I will also add a data-driven Finite State Machine as the output of a tree evaluation may be considered as a starting state in such as machine.

I’m rather excited about this suite of tools as it opens up a wide variety of possibilities for complex, interactive applications. And, as always, since the code is developed in Typescript, it may be used on BOTH the front- and back-end with Node/Express.

I hope you are equally excited and if you wish to contract with me (which means you automatically obtain a copy of the Angular Dev Toolkit), please contact me at theAlgorithmist [at] gmail [dot] com.

Thank you!

]]>I have implemented an Angular 5 graphing component that is built on top of the Typescript Math Toolkit quadrant class (*TSMT$Quadrant*) and its supporting class library. This includes pre-release versions of solid/dashed/dotted line decorators for automatic line drawing without changing the underlying Canvas drawing code.

My initial thought was to restrict this engine to my client-only Angular Dev Toolkit, however, I believe the baseline code is useful for the entire Angular community. While I will continue to enhance this engine in the ADT, the baseline code is now fully open source.

Here is a quick example that uses the Typescript Math Toolkit linear regression class.

The template is extremely simple,

```
<adt-graph title="Typescript Math Toolkit Linear Regression" xAxisLabel="X-Data" yAxisLabel="Y-Data"></adt-graph>
```

More information and additional information on usage can be obtained from the Github repo and I know you want the code more than more verbiage, so point your friendly neighborhood browser here and enjoy!

]]>So, the demo made it into the 2018 New Year’s Resolution list In addition to using Angular 5 for the client, the server code (Node/Express) was developed entirely in Typescript.

There is a good writeup on the Github page and I know what you really want is the code, so this post is short and to the point. Go grab some code and have fun!

https://github.com/theAlgorithmist/Angular5-Server-Sent-Events

And, best wishes to all for 2018!

]]>