Django-Select2: Select2 for Django

Select2

Select2 is an excellent Javascript framework which transforms mundane <select> fields to cool looking and searchable. This is a very handy when there are quite a number of options to select from.

Basic Select2 options field.

Select2 also allows dynamic fetching of options from server via Ajax. Select2’s webpage has a neat demo of this.

Select2 fetching data via Ajax. In the above screenshot it is using RottenTomatoes’ API to get them.

Django-Select2

Django includes basic select widget, which just generates <select><option>...</option>...</select> tags.  Although their ‘looks’ can be improved using basic CSS, but we hit a usability problem when there are too many options to select from. This is where Django-Select2 comes into picture.

Light Components

Django-Select2 includes many widgets suited to various use-cases. Select2Widget and Select2MultipleWidget widgets are suited for scenarios where we have a static list of choices which may not may not be large. They are not meant to be used when the options are too many, say, in thousands. This is because all those options would have to be pre-rendered onto the page and Javascript would be used to search through them. Said that, they are also one the most easiest to use. They are almost drop-in-replacement for Django’s default select widgets, and they look much much better.

Heavy Components

HeavySelect2Widget and HeavySelect2MultipleWidget widgets are suited for scenarios when the number of options are large and need complex queries (from maybe different data sources) to get the options. This dynamic fetching of options undoubtably requires Ajax communication with the server. Django-Select2 includes a helper JS file which is included automatically, so you need not worry about writing any Ajax related JS code. Although on the server side you do need to create a view specifically to respond to the queries. The format of the response is decided by the JS code being used on the client side. The included abstract view – Select2View, will make sure to format the response into the format expected by the helper JS code. Below is a example on how to use it.

#!python
from django.db.models import Q
from django_select2 import Select2View, NO_ERR_RESP
from .models import Employee

class EmployeeSelect2View(Select2View):
    def check_all_permissions(self, request, *args, **kwargs):
        user = request.user if not (user.is_authenticated() and user.has_perms('emp.view_employees')):
            raise PermissionDenied

    def get_results(self, request, term, page, context):
        emps = Employee.objects.filter( Q(first_name__icontains=term) | Q(last_name__icontains=term) | Q(emp_no__icontains=term))
        res = [ (emp.id, "%s %s" % (emp.first_name, emp.last_name),) for emp in emps ]
        return (NO_ERR_RESP, False, res) # Any error response, Has more results, options list

How many such views you will need depends totally on your use-case. From Django-Select2 there is no restriction on their reuse. If you feel that writing these views are too much of a hassle then you have an alternate option – sub-class AutoSelect2Field field. In your sub-classed field you need to override security_check(self, request, *args, **kwargs) and get_results(self, request, term, page, context) methods. When your field will be instantiated for the first time, it will register its own instance with AutoResponseView. When the related field is used in the browser, the queries would be directed to AutoResponseView which will direct it to your ‘auto’ field instance. For ‘auto’ fields to work you must use the following code in your urls.py to register the url for AutoResponseView.

#!python
urlpatterns += patterns("", url(r"^select2/", include("django_select2.urls")), )

Django-Select2 Fields

The following fields are available in Django-Select2.

  • Select2ChoiceField – Uses Select2Widget.
  • Select2MultipleChoiceField – Uses Select2MultipleWidget.
  • HeavySelect2ChoiceField – Uses HeavySelect2Widget.
  • HeavySelect2MultipleChoiceField – Uses HeavySelect2MultipleWidget.
  • ModelSelect2Field – Uses Select2ChoiceField. It additionally requires queryset argument. It similar to Django’s ModelChoiceField.
  • AutoSelect2Field – Uses HeavySelect2ChoiceField. Auto register’s itself with AutoResponseView.
  • AutoModelSelect2Field – Similar to AutoSelect2Field, but like ModelSelect2Field, normalizes values to Django model objects.

Download Django-Select2

You can download it or fork it from https://github.com/applegrew/django-select2. You can also add this to you pip requirement files as:-

-e git+https://github.com/applegrew/django-select2.git#egg=django-select2

Update: Now can simply add django_select2 to your pip requirement. If you want to install it manually then you can simply run:-

pip install django_select2

Update: Now you can install beta version of django_select2 compatible with Python3.

pip install Django-Select2-Py3

Closing Statement

It is recommended that you go through the codes to familiarize yourself with how to efficiently use Django-Select2. The code is not very complex so you should not face much problem in understanding it.

ADF Super Code Snippets

Oracle’s ADF (Application Developer Framework) is J2EE technology stack to rapidly develop enterprise applications. As with all enterprise applications, ADF focuses on data handling and makes it quite easy to present the data to the users in variety of ways. As you will already know that ADF is a blanket term which encompasses many layers of the full tech stack. So ADF is divided into: –
* ADF Faces – Based on Java Server Faces 1.2 specification, this provides the UI of the application. It is built over Apache MyFaces Trinidad 1.2.x. So this is the View layer of ADF stack.
* ADFc – ADF Controller controls the life cycle of a typical ADF application. In fact it is part of ADF Faces. ADF Task Flows too form part of this layer.
* ADFm – ADF Model provides the brick and mortar to bind ADF Faces to the Model layer. In fact ADFm includes the Binding layer. This allows for loose coupling between the View and Model layers.
* ADF BC – ADF Business Components form the Model layer. It forms the data abstraction layer which does quite a lot of heavy lifting for the ADF stack.

The aim of this post is to list out some tips and code snippets which you may undoubtedly need while coding in ADF.

Some Important notes first

1 Clearing the selection on a ADF tree/tree-table/table from bean

First make sure the tree or tree-table or table does not have selectedRowKeys attribute set. Now you will notice that no row will be selected when the tree loads for the first time, but when user makes a selection then tree builds and stores the RowKeySet of the node the user selected. So, now whenever the tree loads and if the last selected row is visible then it will be shown selected. To work around this, we need to manually clear the tree’s selected rowkeyset after the user has made his selection (e.g. When we hide the tree, just before the tree is shown). Use the following code for that:-

RowKeySet rks = getTree().getSelectedRowKeys();
if (rks != null) {
    rks.clear();
}

2 Get the VO bound to an iterator

Sometimes we may need to manually execute an iterator, for this we need to get the handle to the VO instance bound to an iterator.

private ViewObject getVOFromIterator(DCIteratorBinding it) {
    ViewObject vo = null;
    if (it.hasRSI()) {
        return it.getRowSetIterator().getRowSet().getViewObject();
    } else {
        return it.getViewObject();
    }
}

3 Programmatically add partial target

AdfFacesContext.getCurrentInstance().addPartialTarget(targetComponent);

Make sure that targetComponent is not null else we may get NullPointerException.

4 Get query entered by user in Adf quickQuery component

QueryDescriptor qd = queryEvent.getDescriptor();
AttributeCriterion ac = qd.getCurrentCriterion();
String attrLabel = ac.getAttribute().getLabel();
Object attrValue = ac.getValues().get(0);

Note if you try ac.getValues() then it will mention that it has zero elements, even when get(0) returns a value.

5 Building Disclosed RowKeySet for nodes in a tree/tree-table

/**
* Ref: http://www.oracle.com/technetwork/developer-tools/adf/learnmore/61search-in-rendered-trees-177577.pdf
* @param treeBinding
* @param keys
* @return
*/
private RowKeySet buildDiscloseRowKeySet(JUCtrlHierBinding treeBinding, RowKeySet keys) {
    RowKeySetImpl discloseRowKeySet = new RowKeySetImpl();
    Iterator iter = keys.iterator();
    while (iter.hasNext()) {
        List keyPath = (List)iter.next();
        JUCtrlHierNodeBinding node = treeBinding.findNodeByKeyPath(keyPath);
        if (node != null && node.getParent() != null && !node.getParent().getKeyPath().isEmpty()) {
            //store the parent path
            discloseRowKeySet.add(node.getParent().getKeyPath());
            //call method recursively until no parents are found
            RowKeySetImpl parentKeySet = new RowKeySetImpl();
            parentKeySet.add(node.getParent().getKeyPath());
            RowKeySet rks = buildDiscloseRowKeySet(treeBinding, parentKeySet);
            discloseRowKeySet.addAll(rks);
        }
    }
    return discloseRowKeySet;
}

6 Select a node in tree/tree-table

Variant 1

private void selectNode(List<List<?>> pathKeys, org.apache.myfaces.trinidad.component.UIXTree targetComponent) {
    List<Object> lstKeys = new ArrayList<Object>();
    for(int level = 0; level < pathKeys.size(); level++) {
        List<?> keys = pathKeys.get(level);
        Object[] oKeys = new Object[keys.size()];
        for (int index = 0; index < oKeys.length; index++) {
            oKeys[index] = keys.get(index);
        }
        lstKeys.add(new Key(oKeys));
    }
    if(lstKeys.size() == 1) {
        lstKeys = Collections.singletonList(lstKeys.get(0));
    } else {
        lstKeys = Collections.unmodifiableList(lstKeys);
    }
    RowKeySet keyset = new RowKeySetImpl();
    keyset.setCollectionModel((CollectionModel)(targetComponent.getValue()));
    keyset.add(lstKeys);
    targetComponent.setSelectedRowKeys(keyset); //After this targetComponent.setDisclosedRowKeys()
                                                //needs to be called to reveal the node is its parents
                                                //are collapsed.
}

Where pathKeys is of the following form:- Take an example, where we want to select node C, in the following tree.

A [Row from VO1, keys A1]
|--B [Row from VO2, keys B1, B2, B3]
    |--C [Row from VO3, keys C1, C2]

Then pathKeys will be a list like shown below:-

{ {A1}, {B1, B2, B3}, {C1, C2} }

Variant 2 (Maybe this is a better approach, but not tested)

private void selectNode(List<List<?>> pathKeys, org.apache.myfaces.trinidad.component.UIXTree targetComponent) {
    List<Object> lstKeys = new ArrayList<Object>();
    for(int level = 0; level < pathKeys.size(); level++) {
        List<?> keys = pathKeys.get(level);
        Object[] oKeys = new Object[keys.size()];
        for (int index = 0; index < oKeys.length; index++) {
            oKeys[index] = keys.get(index);
        }
        lstKeys.add(new Key(oKeys));
    }
    if(lstKeys.size() == 1) {
        lstKeys = Collections.singletonList(lstKeys.get(0));
    } else {
        lstKeys = Collections.unmodifiableList(lstKeys);
    }
    JUCtrlHierBinding treeBinding = (JUCtrlHierBinding) ((CollectionModel)targetComponent.getValue()).getWrappedData();
    JUCtrlHierNodeBinding nodeBinding = treeBinding.findNodeByKeyPath(lstKeys);
    Key rowKey = nodeBinding.getRowKey();
    JUIteratorBinding iterator = nodeBinding.getIteratorBinding();
    iterator.setCurrentRowWithKey(rowKey.toStringFormat(true));
}

7 Create a flexible query (search box) which allows you to use custom ViewCriteria with ADF quickQuery

Custom Quick Query ADF Component

Custom Quick Query ADF Component

To create search box like the one above.

UI code

<af:quickQuery id="qq1" queryListener="#{pageFlowScope.Bean.invokeSearch}"
    searchDesc="Quick Search" shortDesc="Quick Search" label="Quick Search">
    <af:inputText id="itQq1" value="#{pageFlowScope.Bean.searchKeyword}"
        simple="true" partialTriggers="cb3" shortDesc="Quick Search"/>
    <f:facet name="end">
        <af:commandButton text="Clear" id="cb3" actionListener="#{pageFlowScope.Bean.clearSearch}"/>
    </f:facet>
</af:quickQuery>
  • We have defined our own input box so that Go button and the auto-generated input boxes get disabled if quickQuery doesn’t have any model.
  • Our custom input box allows us to directly read and modify the input box’s content. This is needed by Clear functionality.
  • Input box has simple set to true, so that its label is not displayed.
  • Official quickQuery reference – http://jdevadf.oracle.com/adf-richclient-demo/docs/tagdoc/af_quickQuery.html.

Bean code (in Bean.java)

private String searchKeyword;

public String getSearchKeyword() {
    return searchKeyword;
}

public void setSearchKeyword(String keyword) {
    searchKeyword = keyword;
}

public void invokeHierSearch(QueryEvent queryEvent) {
    if (searchKeyword != null && !searchKeyword.isEmpty()) {
        DCIteratorBinding it = ADFUtil.findIterator("VOIterator");
        ViewObject vo = getVOFromIterator(it);
        vo.applyViewCriteria(vo.getViewCriteriaManager().getViewCriteria("SearchCriteria"));
        vo.setNamedWhereClauseParam("keyword", searchKeyword);
        vo.executeQuery(); //Iterator will be executed after UI is updated.
        searchVisible = true;
    } else {
        searchVisible = false;
    }
    AdfFacesContext.getCurrentInstance().addPartialTarget(...); //Update UI
}

public void clearSearch(ActionEvent e) {
    searchKeyword = null;
    invokeHierSearch(null);
}

private ViewObject getVOFromIterator(DCIteratorBinding it) {
    ViewObject vo = null;
    if (it.hasRSI()) {
        return it.getRowSetIterator().getRowSet().getViewObject();
    } else {
        return it.getViewObject();
    }
} 

8 Get reference to Bindings from bean

DCBindingContainer bindings = ((DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry());

9 Programmatically execute iterator

DCBindingContainer bindings = ((DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry());
bindings.findIteratorBinding("Iterator").executeQuery();

10 Get reference to method binding (operation binding) in bean

Get bindings from 8.

OperationBinding meth = bindings.getOperationBinding("methodBindingId");
Map args = meth.getParamsMap();
args.put(“arg1”, arg1Value);
…
meth.execute();

11 Get reference to pageFlowScope in bean

AdfFacesContext.getCurrentInstance().getPageFlowScope()

This returns a Map which we can use to read or set values.

12 Evaluate random EL value expressions in bean

FacesContext fc = FacesContext.getCurrentInstance();
ELContext elContext = fc.getELContext();
ExpressionFactory ef = fc.getApplication().getExpressionFactory();
ValueExpression exp = ef.createValueExpression(elContext, "#{expression}", ClassOfOutcomeOfExpression.class);
ClassOfOutcomeOfExpression obj = (ClassOfOutcomeOfExpression) exp.getValue(elContext);

ClassOfOutcomeOfExpression can be Object if you don’t care about the outcome. It has too much boilerplate code.

So place the following code in bean.

private <T> T evaluateEL(String el, Class<T> type) {
    FacesContext fc = FacesContext.getCurrentInstance();
    ELContext elContext = fc.getELContext();
    ExpressionFactory ef = fc.getApplication().getExpressionFactory();
    ValueExpression exp = ef.createValueExpression(elContext, el, type);
    return (T) exp.getValue(elContext);
}

13 Evaluate random EL method expressions in bean

FacesContext fc = FacesContext.getCurrentInstance();
ELContext elContext = fc.getELContext();
ExpressionFactory ef = fc.getApplication().getExpressionFactory();
MethodExpression meth = ef.createMethodExpression(elContext, "#{expression}", Outcome.class, new Class[]{Arg1.class, Arg2.class, …});
Outcome obj = (Outcome) meth.invoke(elContext, new Object[]{arg1Obj, arg2Obj, …});

Example: You need to code the below if you override treetable’s selectionListener.

MethodExpression meth = ef.createMethodExpression(elContext, "#{bindings.treeBinding.treeModel.makeCurrent}",
    Object.class, new Class[]{SelectionEvent.class});
meth.invoke(elContext, new Object[]{selectionEvent});

14 Get value from attribute binding in bean

Get bindings from 8.

((oracle.binding.AttributeBinding)bindings.getControlBinding("bindingName")).getInputValue();

15 Get value from list binding in bean

Get bindings from 8.

((JUCtrlListBinding)bindings.getControlBinding("bindingName")) .attributeValue();

16 Implementing custom selection listener

In this use-case we are providing our own selection listener for the TreeTable because we are interested in capturing the selected node’s VO row instance.

public void onTreeSelect(SelectionEvent selectionEvent) {
    //Invoking makeCurrent manually since it will no longer be called as we have provided our own.
    FacesContext fc = FacesContext.getCurrentInstance();
    ELContext elContext = fc.getELContext();
    ExpressionFactory ef = fc.getApplication().getExpressionFactory();
    MethodExpression meth = ef.createMethodExpression(elContext, "#{bindings.treeBinding.treeModel.makeCurrent}",
        Object.class, new Class[]{SelectionEvent.class});
    meth.invoke(elContext, new Object[]{selectionEvent}); //Selection done now getting data from model.
    RichTreeTable myTree = (RichTreeTable) selectionEvent.getSource(); //changed for Serialization
    RowKeySet rowKeys = selectionEvent.getAddedSet();
    if (rowKeys != null) {
        CollectionModel treeModel = (CollectionModel) myTree.getValue();
        JUCtrlHierBinding treeBinding = (JUCtrlHierBinding) treeModel.getWrappedData();
        Iterator selection = rowKeys.iterator();
        while (selection.hasNext()) {
            //Will loop only once since we have allowed single selection.
            List key = (List) selection.next();
            JUCtrlHierNodeBinding nodeBinding = treeBinding.findNodeByKeyPath(key);
            Row row = nodeBinding.getRow(); //Your custom code which will use row.
                                            //row will be the instance of ViewRowImpl
                                            //which backs the currently selected node.
        }
    }
}

17 Get all rows (nodes) of a Tree or TreeTable

In my use-case I wanted to iterate over all the rows in the TreeTable to find if it has any rows with pending changes. This was to prevent the user from carrying out some actions if there were unsaved changes. There are many ways to achieve this, like check if transaction or view port is dirty, but I did not care about the state of various other components on the page which are outside the tree.

public List<Row> getTreeNodes(CollectionModel treeModel) {
    List<Row> rows = new ArrayList<Row>();
    JUCtrlHierBinding treeBinding = (JUCtrlHierBinding) treeModel.getWrappedData();
    Queue<JUCtrlHierNodeBinding> nodeBindings = new LinkedList<JUCtrlHierNodeBinding>();
    nodeBindings.add(treeBinding.getRootNodeBinding());
    while (!nodeBindings.isEmpty()) {
        List<JUCtrlHierNodeBinding> childNodes = nodeBindings.remove().getChildren();
        if (childNodes != null) {
            for (JUCtrlHierNodeBinding _node : childNodes) {
                nodeBindings.add(_node); rows.add(_node.getRow());
            }
        }
    }
    return rows;
}

18 Programmatically loading resource bundle

Recently I had one requirement where I needed to programmatically load a resource bundle from a managed bean and read the translated strings. I could not find any official documentation on this, and the net is rife with many alternatives which did not work for me. Anyway here is my version which is simple and works well for me.

//import java.util.ResourceBundle;
//import oracle.javatools.resourcebundle.BundleFactory;
ResourceBundle myBundle = BundleFactory.getBundle("fully.qualified.class_name.of.the.bundle");
String msg = getMsg(myBundle, "myKey");

Where the method getMsg() is

public String getMsg(ResourceBundle bundle, String key) {
    if (bundle == null) {
        return "";
    }
    try {
        return bundle.getString(key);
    } catch (MissingResourceException ex) { //Log error here. }
    return "";
}

Please make sure that the bundle class is available on the classpath.

Disclaimer: The views expressed on this blog are my own and do not necessarily reflect the views of my employer. This is may or may not be the recommended way. This is provided here merely in the spirit of sharing. You are more than welcome to post a correction or make an addition in the comment section below. I will happily update this post accordingly.

Bertrand Russell (Famous Mathematician and Logician)

Digit Math: Introduction

The person in the above image is Bertrand Russell. Famous Mathematician and Logician.  An excellent graphic novel, Logicomix, is based around his life. That is a must read.

First thing first. Even if you are not a Math demi-god, it is fine, Digit Math is still for you. In fact my academic track record shows that I barely survived Mathematics! I invented this thing when I was in standard eleventh while trying to create some shortcut formula, because I kept forgetting long formulae and was very slow in arithmetic. Digit Math is a slightly different approach to classical Math. To understand this all you need to know is how to add and multiply, and a little bit of logic. That is it.

What is Digit Math?

Numbers are group of digits. Classical Mathematical operators are not meant to deal with few digits of a number. Digit Math allows that. You can interact with a single digit or a group of digits in a number. You might wonder, what is the use? Well, I would say, for fun. Like any good puzzle, this too forces you to think differently. It is not all pointless fun though.

Can you prove that any number which ends with 5 must be divisible by 5? Well try that using classical Math. I have used Digit Math to prove the same. There some more empirical facts which I have proved using Digit Math. One of that is proving that multiplying any number by \(10^n\) will indeed give us that number but followed by \(n\) zeroes. This might look obvious to you, but think for a moment, why does adding a number like \(6\), \(100\) times is guaranteed to yield the exact number \(600\).

Before we begin…

Before we begin I must list out the notations and symbols I would be using. They were introduced specifically for this, since I don’t know of any existing symbols which convey the same information.

Symbols and Operators

I would be using these symbols throughout the articles on Digit Math, so pay attention to them.

\(\omega\) (With-operator)

This is the main operator, which delimits the digits or groups of digits. I call this the ‘with-operator’. So, 251 can be written as \(2\omega51\) (spoken as two-with-fifty-one) or \(25\omega1\) (spoken as twenty-five-with-one) or \(2\omega5\omega1\) (spoken as two-with-five-with-one) or even \(0\omega251\). They all are the same, only the digit partitioning is different. How you partition depends on the problem at hand.

\(]x[\) (Digit Group operator)

This is used in multiple ways. Its primary use is to group digits into a digits group. While in \(2\omega51\) it is quite clear that 2 and 51 are different digit groups of one and two digits respectively, but sometimes we need to convey more information about a digit group.

  • \(]x[\) – This is equal to the number of digits in x, when used on its own. So, \(]25[ = 2\).
  • \(]x[^y\) – This means that x should have y digits. Note that I said ‘should’ have. So, it is possible that while solving the problem, at intermediate steps we can have less or more digits. Eventually, the final result must have exactly y digits. This kind of notation is usually used when actual value of x is not known and x is part of a bigger number. So, \(2\omega]x[^2\) means that this is a number which starts with 2 but ends with two digits.
  • Underline notation – Take the example of \(2\omega]345[^2\). Here 3 is the extra digit in this digit group which has the maximum capacity of two digits. In this case since all the digits are known, so here we can use a short-hand notation of underlining the excess digit in the group. So, \(2\omega]345[^2 = 2\omega\underline{3}45\). (Note that 3 is underlined.)

\(\omega\sum\) (Digits summation operator)

So,$$\omega\sum_{i=1}^3a_i = a_3{\omega}a_2{\omega}a_1$$

Note that \(a_1\) is the rightmost digit and \(a_3\) the leftmost. Maintaining the sequence in digitized equations are of massive importance.

Precedence of With-operator

\(\omega\) has lower precedence than multiply and division. So the full operator precedence would be:

B – Bracket
O – Orders (powers, roots, etc.)
M – Multiply
D – Divide
W – With (\(\omega\))
A – Add
S – Subtract

Properties of With-operator

  • This is not commutative. This is because, \(2\omega5 = 25 \neq 52 = 5\omega2\).
  • This is associative, but only when none of the digit groups have excess digits. That is, less number of digits than required is tolerated. So, \(a\omega(b{\omega}c) = (a{\omega}b){\omega}c\).
  • This is distributive, but only when all digit groups have the exact number of digits. So, less digits is not tolerated here. So, \(n \times (a{\omega}b) = (na)\omega(nb)\).

Some Important Formulae

General formula of transformation

This provides the relation between a normal equation and a digitized equation.

So,$$\omega\sum_{i=1}^{n}a_i = \sum_{i=1}^{n}a_i \times {10}^{n-i}$$

Provided \(a_i\) has one digit only (i.e. \(]a_i[ = 1\)). This is not hard to see, how the above is true.

Bi-multiplication formula

This formula is vital. This describes how the groups of digits behave when they are multiplied. This starts with how we usually manually multiply two numbers.

$$
\begin{align}
x\omega y&\\
\underline{\times\hspace1em a\omega b}&\\
bx\omega by&\\
ax \omega ay\hspace1.5em&\\
\overline{ax\omega (ay+bx)\omega by}&
\end{align}
$$

So,

$$\underline{x \omega y \times a \omega b = ax \omega (ay+bx) \omega by}\tag{bi-multiplication formula}$$

The above relation is pretty easy to understand, but it does say how many digits should be in each group (digits separated by \(\omega\)). To understand this, try multiplying 12 by 34, the manual way. This would be like

$$
\begin{align}
1\omega 2&\\
\underline{\times\hspace1em 3\omega 4}&\\
4\omega 8&\\
3 \omega 6\hspace1.5em&\\
\overline{3\omega (6+4)\omega 8}&\\
\Rightarrow 3\omega 10\omega 8&\\
\Rightarrow 4\omega 0\omega 8&\tag{adding carry over 1 from center to left group}\\
\Rightarrow 408&
\end{align}
$$

Now lets try multiplying 111 by 123.

$$
\begin{align}
01\omega 11&\tag{prefixing with 0 to equate digit counts}\\
\underline{\times\hspace1em 01\omega 23}&\\
23\omega 253&\\
1 \omega 11\hspace1.5em&\\
\overline{1\omega (11+23)\omega 253}&\\
\Rightarrow 1\omega (11+23+2)\omega 53&\tag{2 is the carry over from 253}\\
\Rightarrow 1\omega 36\omega 53&\\
\Rightarrow 13653&
\end{align}
$$

So you can observe from the above scenarios that:-

$$
]a\times b[ = n\hspace1em\ldots\text{where ]a[ = ]b[ = n}
$$

Similarly,

$$
]a + b[ = n\hspace1em\ldots\text{where ]a[ = ]b[ = n}
$$

Any excess digits should be carried over to the digit group on the left and added there.

So,

$$
\begin{align}
1&\\
\underline{+\hspace1em9}&\\
0\omega\underline{1}0&\\
\Rightarrow (0+1)\omega0\\
\Rightarrow 1\omega0\\
\Rightarrow 10
\end{align}
$$

If  \(]a[ \neq ]b[\) then I am not sure what should be \(]a \times b[\). So, we need to make sure that all digit groups have equal number of digits. This is restrictive but fortunately this does not cause problem as there is always a way around.

One more example. Let’s multiply 98 by 76.

$$
\begin{align}
9\omega 8&\\
\underline{\times\hspace1em 7\omega 6}&\\
54\omega 48&\\
63 \omega 56\hspace1.5em&\\
\overline{63\omega (56+54)\omega 48}&\\
\Rightarrow 63\omega 110\omega 48&\\
\Rightarrow 63\omega (110+4)\omega 8&\tag{since max size of each group is 1}\\
\Rightarrow 63\omega 114\omega 8&\\
\Rightarrow (63+11)\omega 4\omega 8&\\
\Rightarrow 74\omega4\omega8&\\
\Rightarrow 7448&
\end{align}
$$

Rules for using the bi-multiplication formula

You have already witness many of the rules, but let me summarize them for clearly. For the following assume that we are multiplying \(a\omega b\) by \(x\omega y\).

  • Make two fragments of both the numbers, by placing \(\omega\) at the positions suitable for you. Remember, the number of digits in all the four fragments should be equal. That is, \(]a[ = ]b[ = ]x[ = ]y[\).Partitioning \(255\) as \(2\omega55\) is perfectly fine, since \(]02[ = ]55[ = 2\).
  • If the number of digits in \(by\) or \(ay+bx\) terms are less than the required number then insert zeroes in front of them to get the exact number of digits.
  • If the number of digits in \(by\) or \(ay+bx\) terms are more than the required number then remove the excess digits from the front (left) of them and add these excess digits to the group on left. Note, that shifting of excess digits (carry over) should started from the rightmost group and then progress towards left sequentially.
  • When all the above conditions are met then you are good to remove the \(\omega\) (with-operator).

So, finally the bi-multiplication formula can be precisely expressed as,

$$\boxed{x \omega y \times a \omega b = ax\hspace2pt\omega\hspace2pt]ax+by[^n\hspace2pt\omega\hspace2pt]by[^n}\hspace1em\ldots\text{where ]a[ = ]b[ = ]x[ = ]y[ = n}$$

Squaring formula

This is directly derivable from bi-multiplication formula.

$$
\begin{align}
(a \omega b)^2&\\
\Rightarrow& a \omega b \times a \omega b\\
\Rightarrow& \boxed{a^2\omega ]2ab[^n \omega ]b^2[^n}\hspace1em\ldots\text{where ]a[ = ]b[ = n}
\end{align}
$$

General Powering Formula

For now I will provide only the formula. The proof is provided later in this article. The way I derived it was to I manually find the values of \((a\omega b)^2\), \((a\omega b)^3\), \((a\omega b)^4\) and so on. I found a pattern in all these and from there I got this formula. It was later when it struck me as to how to prove it.

$$
\boxed{(a\omega b)^n = \omega\sum_{r=0}^n\hspace1em{]^nC_r \times a^{(n-r)} \times b^r[}^d}\hspace1em\ldots\text{where ]a[ = ]b[ = d}
$$

Notice that the above equation looks very similar to Binomial equation. In fact I found this to be true for many equations. If we replace \(+\) by \(\omega\) then we end up with a ‘digitized’ version of that equation.

Now let me demonstrate how to use this equation. Let’s find cube of 99, i.e. \(99^3\).

$$
\begin{align}
&(9\omega9)^3\\
&= ]^3C_0 \times 9^3 \times 9^0[^1 \omega ]^3C_1 \times 9^2 \times 9^1[^1\\
&= 9^3 \omega 3\times9^3 \omega 3\times9^3 \omega 9^3\tag{for brevity removing ].[ operator}\\
&= 729 \omega \underline{218}7 \omega \underline{218}7 \omega \underline{72}9\\
&= 729 \omega \underline{218}7 \omega \underline{225}9 \omega 9\tag{adding carry overs}\\
&= 729 \omega \underline{241}2 \omega 9 \omega 9\\
&= 970 \omega 2 \omega 9 \omega 9\\
&= 970299
\end{align}
$$

I didn’t underline the excess digits in leftmost group, since that is anyway not going to affect the result. Now let us find \(241^4\).

$$
\begin{align}
&(02\omega41)^4\\
&= \omega\sum_{r=0}^4\hspace1em]^4C_r \times 2^{(4-r)} \times 41^r[^2\\
&= ^4C_0.2^4\hspace0.5em\omega\hspace0.5em^4C_1.2^3.41\hspace0.5em\omega\hspace0.5em^4C_2.2^2.41^2\hspace0.5em\omega\hspace0.5em^4C_3.2.41^3\hspace0.5em\omega\hspace0.5em^4C_4.41^4\\
&= 16\omega\underline{13}12\omega\underline{403}44\omega\underline{5513}68\omega\underline{28257}61\\
&= 16\omega\underline{13}12\omega\underline{403}44\omega\underline{5796}25\omega61\\
&= 16\omega\underline{13}12\omega\underline{461}40\omega25\omega61\\
&= 16\omega\underline{17}73\omega40\omega25\omega61\\
&= 33\omega73\omega40\omega25\omega61\\
&= 3373402561
\end{align}
$$

Note, one important observation. Here you can immediately identify the last couple of digits of the final result by evaluating for the last term. Unlike in Binomial Theorem, where all the terms need to be added. So, there at least last few digits of all the terms need to be added to get the  last few digits of the final result. This is an advantage for General Powering formula, but unfortunately we cannot say the for other digit groups which are at higher place order. The reason is simple, they may have to be added with carry-overs from the groups (terms) on the right.

A corollary of the General Formula of Transformation (for two digits)

With to Plus form

$$a\omega b = 10a+b$$

The above equation is true only when \(b\) has one digit. The more general equation is:-

$$\boxed{a\omega b = a.10^d+b}\hspace1em\text{where d}=]b[\hspace1em\ldots\text{Corollary 1}$$

Plus to With form

$$a\omega b = 10a +b \Rightarrow a\omega b = 9a + a + b \Rightarrow \boxed{a+b = a\omega b -9a}\hspace1em\ldots\text{Corollary 2}$$

Negative digits

We all are aware of negative numbers but in digit equations we might end up with negative digits! I see no physical significance of that but let’s try to find out some mathematical meaning of this.

$$
\begin{align}
a\omega(-b) &= 10a + (-b)\\
&= 10a -b\\
&= 10a + b -2b\\
&= a\omega b -2b
\end{align}
$$

Altire,

$$
\begin{align}
a\omega(-b) &= -(-10a + b)\\
&= -(10a+b -20a)\\
&= -(a\omega b – 20a)\\
&= -(a\omega b) +20a
\end{align}
$$

$$
\therefore \boxed{a\omega(-b) = a\omega b-2b = -(a\omega b)+20a}
$$

Remember, \((-a)\omega b \neq -(a\omega b)\). The former means that only the digit \(a\) is negative. The later means that the complete number is negative.

Similarly,

$$
\boxed{(-a)\omega b = a\omega b-20a = -(a\omega b)+2b}
$$

Also similarly evaluating, we get,

$$
\boxed{(-a)\omega(-b) = -(a\omega b)}\hspace1em\ldots\text{Corollary 3}
$$

So, we see that in a negative number all the digits too are negative. Not surprising, since in a positive number all digits are positive.

Proof of General Powering Formula

Since I already know this formula, so I just need to prove that it is correct. For this the best tool is Mathematical Induction. So, the formula to prove is

$$(a\omega b)^n = \omega\sum_{r=0}^n\hspace1em{]^nC_r \times a^{(n-r)} \times b^r[}^d$$

Case 1: n = 0

So,

$$
\begin{align}
L.H.S. &= (a\omega b)^0 = 1\\
\\
\\
R.H.S. &= \omega\sum_{r=0}^0\hspace1em ^0C_r \times a^{(0-r)} \times b^r\\
&= 1\times a^0 \times b^0\\
&= 1\tag{Proved}
\end{align}
$$

Case 2: n = 1

$$
\begin{align}
L.H.S.&= (a\omega b)^1 = a\omega b\\
\\
\\
R.H.S. &= \omega\sum_{r=0}^1\hspace1em ^1C_r \times a^{(1-r)} \times b^r\\
&= ^1C_0. a^1. b^0 \omega ^1C_1.a^0.b^1\\
&= a\omega b\tag{Proved}
\end{align}
$$

Case 3: For n

Suppose that the formula is true for all values of \(n\), then by Mathematical Induction, the formula must be valid for \(n+1\) too.

So,

$$
\begin{align}
&(a\omega b)^{(n+1)}\\
&= (a\omega b)^n \times (a\omega b)\\
&= (a\omega b)^n \times (a\times 10^d+b)\hspace1em\ldots\text{where d}=]b[\tag{By Corollary 1}\\
&= 10^da\times(a\omega b)^n + b\times (a\omega b)^n\\
&= 10^da\times(\omega\sum_{r=0}^n\hspace1em ^nC_r\times a^{(n-r)} \times b^r) +\\
&\hspace2em b\times(\omega\sum_{r’=0}^n\hspace1em ^nC_{r’}\times a^{(n-r’)} \times b^{r’}) \tag{Since formula is correct for n}\\
&= 10^d\times(a^{(n+1)} \hspace2pt\omega\hspace2pt \omega\sum_{r=1}^n\hspace1em ^nC_r\times a^{(n+1-r)} \times b^r) +\\
&\hspace2em(\omega\sum_{r’=0}^{(n-1)}\hspace1em ^nC_{r’}\times a^{(n-r’)} \times b^{(r’+1)} \hspace2pt\omega\hspace2pt b^{(n+1)})\tag{1}
\end{align}
$$

Since \(]a[\hspace2pt=\hspace2pt]b[\hspace2pt=\hspace2pt d\), so every term of \(\omega\sum\) too will have \(d\) digits, as per General Powering Formula. This implies that term \(b^{(n+1)}\) in equation (1) too has \(d\) digits. So, if we can rewrite (1) as below:-

$$
\begin{align}
a^{(n+1)} \hspace2pt\omega\hspace2pt&\omega\sum_{r=1}^n\hspace1em ^nC_r\times a^{(n+1-r)} \times b^r\tag{2}\\
+\hspace2em\hspace2em &\omega\sum_{r’=0}^{(n-1)}\hspace1em ^nC_{r’}\times a^{(n-r’)} \times b^{(r’+1)} \hspace2pt\omega\hspace2pt b^{(n+1)}\tag{3}\\
a^{n+1} \hspace2pt\omega\hspace2pt & \overline{\omega\sum_{r=1}^n\hspace1em {^nC_r\times a^{n+1-r} \times b^r \brace + ^nC_{(r-1)}\times a^{n-(r-1)} \times b^{(r-1)+1}} \hspace2pt\omega\hspace2pt b^{n+1}}\\
\Rightarrow a^{n+1} \hspace2pt\omega\hspace2pt &\bigg( \omega\sum_{r=1}^n\hspace1em ^nC_r\times a^{n+1-r} \times b^r + ^nC_{r-1}\times a^{n+1-r} \times b^{r}\bigg) \hspace2pt\omega\hspace2pt b^{n+1}\tag{4}
\end{align}
$$

Notice that in the above, I have removed the \(10^d\) factor since all it did is to shift its term by \(d\) digits to left, that is, by exact number of digits in the \(b^{(n+1)}\) group. After removing the first digit group from equation (2) and last digit group from equation (3), \(\omega\sum\) in both the equations have \(n-1\) digit groups left. Each of these digit groups will get added term by term, i.e. group \(1\) of (2) will get added to group \(0\) of (3) and so on. So this implies \(r’ = r – 1\).

Further simplifying,

$$
\begin{align}
(4)&= a^{n+1} \hspace2pt\omega\hspace2pt \bigg(\omega\sum_{r=1}^n\hspace1em a^{n+1-r}.b^r(\frac{n!}{r!(n-r)!}+\frac{n!}{(r-1)!(n-r+1)!})\bigg) \hspace2pt\omega\hspace2pt b^{n+1}\\
&= a^{n+1} \hspace2pt\omega\hspace2pt \bigg(\omega\sum_{r=1}^n\hspace1em a^{n+1-r}.b^r.\frac{(n+1)!}{r!(n+1-r)!}.(\frac{n+r-1}{n+1}+\frac{r}{n+1})\bigg) \hspace2pt\omega\hspace2pt b^{n+1}\\
&= a^{n+1} \hspace2pt\omega\hspace2pt \bigg(\omega\sum_{r=1}^n\hspace1em a^{n+1-r}.b^r.^{n+1}C_r.(1)\bigg) \hspace2pt\omega\hspace2pt b^{n+1}\\
&= \bigg(C_0^{n+1}.a^{n+1-0}.b^0\bigg) \hspace2pt\omega\hspace2pt \bigg(\omega\sum_{r=1}^n\hspace1em C_r^{n+1}.a^{n+1-r}.b^r\bigg) \hspace2pt\omega\hspace2pt \bigg(C_{n+1}^{n+1}.a{(n+1-(n+1))}.b{n+1}\bigg)\\
&= \omega\sum_{r=0}^{(n+1)}\hspace1em ^{(n+1)}C_r.a^{(n+1)-r}.b^r\\
\end{align}
$$

So, proved that \((a\omega b)^{(n+1)} = \omega\sum_{r=0}^{(n+1)}\hspace1em ^{(n+1)}C_r.a^{(n+1)-r}.b^r\) when \((a\omega b)^n = \omega\sum_{r=0}^n\hspace1em ^nC_r.a^{n-r}.b^r\).

So, all cases proved.

Application of Digit Math

Now time to see Digit Math in action. I will be using this to prove some empirical concepts.

Link to list of applications of Digit Math.

CInk version 2 finally released!

Finally CInk version 2 has been released.

Some key new things

  • New website with complete API documentation and guides on how you can use CInk JS code.
  • Finally released the full source code of CInk renderer and compiler. License – GPL v3.
  • CInk finally supports all the features of original CFDG, including Paths.
  • CInk has introduced support for texts which extends the capabilities of CFDG considerably. Check out the cool demo – Neon Letters. To learn more about it see – “Text transforms” section here.

Last but not the least, you can post comments on CInk website. The comments section is at the bottom of each page.

Goto Cink website – cink.applegrew.com.